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:
REBUILD 企业管理系统 2024-07-09 21:30:05 +08:00 committed by GitHub
parent b657ef017c
commit db0c0fbf0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 105 additions and 53 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 90fb5173803d09abee44dfbf4547f824edd3deae
Subproject commit 9167b256a0cd21808387808346b4d76595ef0661

View file

@ -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>

View file

@ -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

View file

@ -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]);
}
}

View file

@ -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();
}

View file

@ -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;
}
}
}

View file

@ -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");

View file

@ -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();
}

View file

@ -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="是否删除"/>

View file

@ -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 '是否删除',

View file

@ -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,

View file

@ -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,
}
}

View file

@ -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) {