mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Fix 3.7.2 (#780)
* v3.7.2 * fix: time filter * be: long fileSize * be file upload * use onServerProgress * fix: 重复检查 * fix: 高级查询变量字段无值:(1=2) * fix: 3.6 自动审批配置兼容 --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
b657ef017c
commit
db0c0fbf0e
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 90fb5173803d09abee44dfbf4547f824edd3deae
|
||||
Subproject commit 9167b256a0cd21808387808346b4d76595ef0661
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
<artifactId>rebuild</artifactId>
|
||||
<version>3.7.1</version>
|
||||
<version>3.7.2</version>
|
||||
<name>rebuild</name>
|
||||
<description>Building your business-systems freely!</description>
|
||||
<url>https://getrebuild.com/</url>
|
||||
|
|
|
@ -75,11 +75,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Version
|
||||
*/
|
||||
public static final String VER = "3.7.1";
|
||||
public static final String VER = "3.7.2";
|
||||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
*/
|
||||
public static final int BUILD = 3070106;
|
||||
public static final int BUILD = 3070207;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
|
|
@ -39,7 +39,7 @@ public class FilesHelper {
|
|||
// 私有
|
||||
public static final String SCOPE_SELF = "SELF";
|
||||
|
||||
private static final LRUMap<String, Integer> FILESIZES = new LRUMap<>(2000);
|
||||
private static final LRUMap<String, Long> FILESIZES = new LRUMap<>(2000);
|
||||
|
||||
/**
|
||||
* 暂存文件大小,以便在创建文件记录时使用
|
||||
|
@ -48,7 +48,7 @@ public class FilesHelper {
|
|||
* @param fileSize in bytes
|
||||
* @see #createAttachment(String, ID)
|
||||
*/
|
||||
public static void storeFileSize(String filePath, int fileSize) {
|
||||
public static void storeFileSize(String filePath, long fileSize) {
|
||||
FILESIZES.put(filePath, fileSize);
|
||||
}
|
||||
|
||||
|
@ -71,14 +71,14 @@ public class FilesHelper {
|
|||
}
|
||||
|
||||
if (FILESIZES.containsKey(filePath)) {
|
||||
attach.setInt("fileSize", FILESIZES.get(filePath));
|
||||
attach.setLong("fileSize", FILESIZES.get(filePath));
|
||||
} else {
|
||||
Object[] db = Application.createQueryNoFilter(
|
||||
"select fileSize from Attachment where filePath = ?")
|
||||
.setParameter(1, filePath)
|
||||
.unique();
|
||||
if (db != null) {
|
||||
attach.setInt("fileSize", (Integer) db[0]);
|
||||
attach.setLong("fileSize", (Long) db[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import cn.devezhao.persist4j.Query;
|
|||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.engine.NullValue;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.metadata.DeleteRecord;
|
||||
|
@ -57,7 +58,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -715,16 +716,28 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
public List<Record> getAndCheckRepeated(Record checkRecord, int limit) {
|
||||
final Entity entity = checkRecord.getEntity();
|
||||
|
||||
List<String> checkFields = new ArrayList<>();
|
||||
for (Iterator<String> iter = checkRecord.getAvailableFieldIterator(); iter.hasNext(); ) {
|
||||
Field field = entity.getField(iter.next());
|
||||
Record existingRecord = null;
|
||||
Map<String, Object> checkFields = new LinkedHashMap<>();
|
||||
for (Field field : entity.getFields()) {
|
||||
if (field.isRepeatable()
|
||||
|| !checkRecord.hasValue(field.getName(), false)
|
||||
|| MetadataHelper.isCommonsField(field)
|
||||
|| EasyMetaFactory.getDisplayType(field) == DisplayType.SERIES) {
|
||||
continue;
|
||||
}
|
||||
checkFields.add(field.getName());
|
||||
|
||||
Object checkValue = checkRecord.getObjectValue(field.getName());
|
||||
if (checkValue == null) {
|
||||
// 从数据库补充完整的字段值
|
||||
if (existingRecord == null) {
|
||||
if (checkRecord.getPrimary() == null) {
|
||||
existingRecord = EntityHelper.forNew(entity.getEntityCode(), UserService.SYSTEM_USER, false);
|
||||
} else {
|
||||
existingRecord = Application.getQueryFactory().recordNoFilter(checkRecord.getPrimary());
|
||||
}
|
||||
}
|
||||
checkValue = existingRecord.getObjectValue(field.getName());
|
||||
}
|
||||
checkFields.put(field.getName(), checkValue);
|
||||
}
|
||||
|
||||
if (checkFields.isEmpty()) return Collections.emptyList();
|
||||
|
@ -735,14 +748,17 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
StringBuilder checkSql = new StringBuilder("select ")
|
||||
.append(entity.getPrimaryField().getName()).append(", ") // 增加一个主键列
|
||||
.append(StringUtils.join(checkFields.iterator(), ", "))
|
||||
.append(StringUtils.join(checkFields.keySet().iterator(), ", "))
|
||||
.append(" from ")
|
||||
.append(entity.getName())
|
||||
.append(" where ( ");
|
||||
for (String field : checkFields) {
|
||||
checkSql.append(field).append(" = ? ").append(orAnd).append(" ");
|
||||
for (Map.Entry<String, Object> e : checkFields.entrySet()) {
|
||||
checkSql.append(e.getKey());
|
||||
if (NullValue.isNull(e.getValue())) checkSql.append(" is null ");
|
||||
else checkSql.append(" = ? ");
|
||||
checkSql.append(orAnd).append(" ");
|
||||
}
|
||||
checkSql.delete(checkSql.lastIndexOf("?") + 1, checkSql.length()).append(" )");
|
||||
checkSql.delete(checkSql.length() - 4, checkSql.length()).append(" )");
|
||||
|
||||
// 排除自己
|
||||
if (checkRecord.getPrimary() != null) {
|
||||
|
@ -755,12 +771,20 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
String globalRepeat = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.DETAILS_GLOBALREPEAT);
|
||||
// v3.4
|
||||
if (!BooleanUtils.toBoolean(globalRepeat)) {
|
||||
String dtf = MetadataHelper.getDetailToMainField(entity).getName();
|
||||
ID mainid = checkRecord.getID(dtf);
|
||||
String dtfName = MetadataHelper.getDetailToMainField(entity).getName();
|
||||
ID mainid = checkRecord.getID(dtfName);
|
||||
// 3.7.2 增强兼容
|
||||
if (mainid == null && existingRecord != null) {
|
||||
mainid = existingRecord.getID(dtfName);
|
||||
}
|
||||
if (mainid == null && checkRecord.getPrimary() != null) {
|
||||
mainid = QueryHelper.getMainIdByDetail(checkRecord.getPrimary());
|
||||
}
|
||||
|
||||
if (mainid == null) {
|
||||
log.warn("Check all records of detail for repeatable");
|
||||
} else {
|
||||
checkSql.append(String.format(" and (%s = '%s')", dtf, mainid));
|
||||
checkSql.append(String.format(" and (%s = '%s')", dtfName, mainid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -768,8 +792,10 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
Query query = ((BaseService) delegateService).getPersistManagerFactory().createQuery(checkSql.toString());
|
||||
|
||||
int index = 1;
|
||||
for (String field : checkFields) {
|
||||
query.setParameter(index++, checkRecord.getObjectValue(field));
|
||||
for (Map.Entry<String, Object> e : checkFields.entrySet()) {
|
||||
if (!NullValue.isNull(e.getValue())) {
|
||||
query.setParameter(index++, e.getValue());
|
||||
}
|
||||
}
|
||||
return query.setLimit(limit).list();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import cn.devezhao.persist4j.Field;
|
|||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.dialect.Type;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
||||
import cn.devezhao.persist4j.query.compiler.QueryCompiler;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
|
@ -40,6 +41,8 @@ import org.springframework.util.Assert;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
@ -294,7 +297,9 @@ public class AdvFilterParser extends SetUser {
|
|||
&& lastFieldMeta.getReferenceEntity().getEntityCode() == EntityHelper.User;
|
||||
|
||||
String op = item.getString("op");
|
||||
String value = useValueOfVarRecord(item.getString("value"), lastFieldMeta);
|
||||
Object checkValue = useValueOfVarField(item.getString("value"), lastFieldMeta);
|
||||
if (checkValue instanceof VarFieldNoValue37) return "(1=2)";
|
||||
String value = (String) checkValue;
|
||||
String valueEnd = null;
|
||||
|
||||
if (dt == DisplayType.N2NREFERENCE) {
|
||||
|
@ -610,7 +615,9 @@ public class AdvFilterParser extends SetUser {
|
|||
// 区间
|
||||
final boolean isBetween = op.equalsIgnoreCase(ParseHelper.BW);
|
||||
if (isBetween && valueEnd == null) {
|
||||
valueEnd = useValueOfVarRecord(item.getString("value2"), lastFieldMeta);
|
||||
Object checkValueEnd = useValueOfVarField(item.getString("value2"), lastFieldMeta);
|
||||
if (checkValueEnd instanceof VarFieldNoValue37) return "(1=2)";
|
||||
valueEnd = (String) checkValueEnd;
|
||||
valueEnd = parseValue(valueEnd, op, lastFieldMeta, true);
|
||||
if (valueEnd == null) valueEnd = value;
|
||||
}
|
||||
|
@ -775,20 +782,27 @@ public class AdvFilterParser extends SetUser {
|
|||
private static final String CURRENT_ANY = "CURRENT";
|
||||
private static final String CURRENT_DATE = "NOW";
|
||||
|
||||
// 获取字段变量值
|
||||
private String useValueOfVarRecord(String value, Field queryField) {
|
||||
/**
|
||||
* 获取字段变量值
|
||||
* fix 3.7.2 获取不到原样返回
|
||||
*
|
||||
* @param value
|
||||
* @param queryField
|
||||
* @return
|
||||
*/
|
||||
protected Object useValueOfVarField(final String value, Field queryField) {
|
||||
if (StringUtils.isBlank(value) || !value.matches(PATT_FIELDVAR)) return value;
|
||||
|
||||
// {@FIELD}
|
||||
// {@FIELD} > FIELD
|
||||
final String fieldName = value.substring(2, value.length() - 1);
|
||||
|
||||
Object useValue = null;
|
||||
|
||||
// {@CURRENT} for DATE
|
||||
// {@CURRENT} for DATE,TIME and Ref:User,Department
|
||||
if (CURRENT_ANY.equals(fieldName) || CURRENT_DATE.equals(fieldName)) {
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(queryField);
|
||||
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME || dt == DisplayType.TIME) {
|
||||
useValue = CalendarUtils.now();
|
||||
useValue = dt == DisplayType.TIME ? LocalTime.now() : CalendarUtils.now();
|
||||
|
||||
} else if (dt == DisplayType.REFERENCE) {
|
||||
if (queryField.getReferenceEntity().getEntityCode() == EntityHelper.User) {
|
||||
|
@ -798,8 +812,8 @@ public class AdvFilterParser extends SetUser {
|
|||
if (dept != null) useValue = dept.getIdentity();
|
||||
}
|
||||
} else {
|
||||
log.warn("Cannot use `{}` in `{}` (None date fields)", value, queryField);
|
||||
return StringUtils.EMPTY;
|
||||
log.warn("Cannot use `{}` in `{}` (None date/ref fields)", value, queryField);
|
||||
return new VarFieldNoValue37(value);
|
||||
}
|
||||
}
|
||||
// {@CURRENT.} for USER
|
||||
|
@ -808,7 +822,7 @@ public class AdvFilterParser extends SetUser {
|
|||
Object[] o = Application.getQueryFactory().uniqueNoFilter(getUser(), userField);
|
||||
if (o == null || o[0] == null) {
|
||||
log.warn("Cannot use `{}` in `{}` (No value found)", value, queryField);
|
||||
return StringUtils.EMPTY;
|
||||
return new VarFieldNoValue37(value);
|
||||
} else {
|
||||
useValue = o[0];
|
||||
}
|
||||
|
@ -819,25 +833,27 @@ public class AdvFilterParser extends SetUser {
|
|||
|
||||
Field valueField = MetadataHelper.getLastJoinField(rootEntity, fieldName);
|
||||
if (valueField == null) {
|
||||
log.warn("Invalid var-field : {} in {}", value, rootEntity.getName());
|
||||
return StringUtils.EMPTY;
|
||||
throw new MissingMetaExcetion(fieldName, rootEntity.getName());
|
||||
}
|
||||
|
||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(varRecord, fieldName);
|
||||
if (o == null || o[0] == null) return StringUtils.EMPTY;
|
||||
else useValue = o[0];
|
||||
if (o == null || o[0] == null) {
|
||||
log.warn("Cannot use `{}` in `{}` (None value found)", value, queryField);
|
||||
return new VarFieldNoValue37(value);
|
||||
}
|
||||
useValue = o[0];
|
||||
}
|
||||
|
||||
if (useValue instanceof Date) {
|
||||
useValue = CalendarUtils.getUTCDateFormat().format(useValue);
|
||||
} else if (useValue instanceof TemporalAccessor) {
|
||||
useValue = CalendarUtils.getDateFormat("HH:mm").format(CalendarUtils.now());
|
||||
useValue = DateTimeFormatter.ofPattern(DisplayType.TIME.getDefaultFormat()).format((TemporalAccessor) useValue);
|
||||
} else if (useValue instanceof BigDecimal) {
|
||||
useValue = String.valueOf(((BigDecimal) useValue).doubleValue());
|
||||
} else {
|
||||
useValue = String.valueOf(useValue);
|
||||
}
|
||||
return (String) useValue;
|
||||
return useValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -899,13 +915,13 @@ public class AdvFilterParser extends SetUser {
|
|||
}
|
||||
|
||||
/**
|
||||
* 是否含有字段变量 `{@FIELD}`
|
||||
* 是否含有变量字段 `{@FIELD}`
|
||||
*
|
||||
* @param filterExpr
|
||||
* @return
|
||||
* @see #useValueOfVarRecord(String, Field)
|
||||
* @see #useValueOfVarField(String, Field)
|
||||
*/
|
||||
public static boolean hasFieldVars(JSONObject filterExpr) {
|
||||
public static boolean hasVarFields(JSONObject filterExpr) {
|
||||
for (Object o : filterExpr.getJSONArray("items")) {
|
||||
JSONObject item = (JSONObject) o;
|
||||
String value = item.getString("value");
|
||||
|
@ -915,4 +931,14 @@ public class AdvFilterParser extends SetUser {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变量字段无值
|
||||
*/
|
||||
static class VarFieldNoValue37 {
|
||||
String varField;
|
||||
VarFieldNoValue37(String varField) {
|
||||
this.varField = varField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver
|
|||
if (request.getRequestURI().contains("/assets/")) return null;
|
||||
|
||||
ModelAndView error;
|
||||
if (ServletUtils.isAjaxRequest(request)) {
|
||||
if (ServletUtils.isAjaxRequest(request) || request.getRequestURI().contains("/filex/upload")) {
|
||||
error = new ModelAndView(new FastJsonJsonView());
|
||||
} else {
|
||||
error = new ModelAndView("/error/error");
|
||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.web.commons;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.service.files.FilesHelper;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
|
@ -95,12 +96,12 @@ public class FileUploader extends BaseController {
|
|||
}
|
||||
|
||||
/**
|
||||
* @see FilesHelper#storeFileSize(String, int)
|
||||
* @see FilesHelper#storeFileSize(String, long)
|
||||
*/
|
||||
@RequestMapping("store-filesize")
|
||||
@ResponseBody
|
||||
public RespBody storeFilesize(HttpServletRequest request) {
|
||||
int fileSize = getIntParameter(request, "fs");
|
||||
long fileSize = ObjectUtils.toLong(getParameter(request, "fs"));
|
||||
if (fileSize < 1) {
|
||||
return RespBody.error();
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@
|
|||
<field name="relatedRecord" type="any-reference" updatable="false" description="相关记录" cascade="ignore"/>
|
||||
<field name="filePath" type="string" max-length="300" nullable="false" description="文件路径"/>
|
||||
<field name="fileType" type="string" max-length="20" description="文件类型"/>
|
||||
<field name="fileSize" type="int" default-value="0" description="文件大小"/>
|
||||
<field name="fileSize" type="long" default-value="0" description="文件大小"/>
|
||||
<field name="fileName" type="string" max-length="100" description="文件名称"/>
|
||||
<field name="inFolder" type="reference" ref-entity="AttachmentFolder" description="所在目录"/>
|
||||
<field name="isDeleted" type="bool" default-value="F" description="是否删除"/>
|
||||
|
|
|
@ -336,7 +336,7 @@ create table if not exists `attachment` (
|
|||
`RELATED_RECORD` char(20) comment '相关记录',
|
||||
`FILE_PATH` varchar(300) not null comment '文件路径',
|
||||
`FILE_TYPE` varchar(20) comment '文件类型',
|
||||
`FILE_SIZE` int(11) default '0' comment '文件大小',
|
||||
`FILE_SIZE` bigint(20) default '0' comment '执行顺序',
|
||||
`FILE_NAME` varchar(100) comment '文件名称',
|
||||
`IN_FOLDER` char(20) comment '所在目录',
|
||||
`IS_DELETED` char(1) default 'F' comment '是否删除',
|
||||
|
|
|
@ -128,7 +128,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
.chart.index > .data-item strong {
|
||||
font-size: 3.1rem;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.chart.index > .data-item .with p,
|
||||
|
|
|
@ -771,6 +771,7 @@ class FilterItem extends React.Component {
|
|||
startView: 'month',
|
||||
todayBtn: true,
|
||||
clearBtn: this.props.allowClear || false,
|
||||
forceParse: false,
|
||||
}
|
||||
|
||||
// 仅时间
|
||||
|
@ -781,6 +782,9 @@ class FilterItem extends React.Component {
|
|||
maxView: 1,
|
||||
startView: 1,
|
||||
title: $L('选择时间'),
|
||||
todayBtn: true,
|
||||
clearBtn: this.props.allowClear || false,
|
||||
forceParse: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
/* !!! KEEP IT ES5 COMPATIBLE !!! */
|
||||
|
||||
// GA
|
||||
;(function () {
|
||||
(function () {
|
||||
var gaScript = document.createElement('script')
|
||||
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-ZCZHJPMEG7'
|
||||
gaScript.async = true
|
||||
|
@ -704,11 +704,7 @@ var $createUploader = function (input, next, complete, error) {
|
|||
return false
|
||||
}
|
||||
},
|
||||
onClientProgress: function (e, file) {
|
||||
typeof next === 'function' && next({ percent: (e.loaded * 100) / e.total, file: file })
|
||||
},
|
||||
onServerProgress: function (e, file) {
|
||||
// fix:v3.7 不触发 onClientProgress???
|
||||
typeof next === 'function' && next({ percent: (e.loaded * 100) / e.total, file: file })
|
||||
},
|
||||
onSuccess: function (e, file) {
|
||||
|
|
Loading…
Reference in a new issue