mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Fix 3.5.0 beta2 (#683)
1. 表单引用支持任意引用 2. 分组关联触发器设置 (LAB),必填,格式验证 3. ANYREF 允许重复选项 4. 去重连接*N * export fields * refform 3 * be: validAdvFilter * fix: GroupAggregationRefresh fake源记录 * be: escapeSql
This commit is contained in:
parent
67e12fb774
commit
87e64ab0d0
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 15d7f439a84cef4be39dadada2d51e2e6ab4f375
|
||||
Subproject commit 222f88ffd578ce0a87622e472a855dc061ae97a7
|
|
@ -27,6 +27,7 @@ import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
|||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.query.AdvFilterParser;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
|
@ -217,17 +218,17 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
|||
}
|
||||
}
|
||||
|
||||
JSONObject filterExp = config.getJSONObject("filter");
|
||||
if (filterExp == null || filterExp.isEmpty()) {
|
||||
return previewFilter + "(1=1)";
|
||||
JSONObject filterExpr = config.getJSONObject("filter");
|
||||
if (ParseHelper.validAdvFilter(filterExpr)) {
|
||||
AdvFilterParser filterParser = new AdvFilterParser(filterExpr);
|
||||
String sqlWhere = filterParser.toSqlWhere();
|
||||
if (sqlWhere != null) {
|
||||
sqlWhere = previewFilter + sqlWhere;
|
||||
}
|
||||
return StringUtils.defaultIfBlank(sqlWhere, "(1=1)");
|
||||
}
|
||||
|
||||
AdvFilterParser filterParser = new AdvFilterParser(filterExp);
|
||||
String sqlWhere = filterParser.toSqlWhere();
|
||||
if (sqlWhere != null) {
|
||||
sqlWhere = previewFilter + sqlWhere;
|
||||
}
|
||||
return StringUtils.defaultIfBlank(sqlWhere, "(1=1)");
|
||||
return previewFilter + "(1=1)";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -231,7 +231,7 @@ public class RecordCheckout {
|
|||
if (refEntity.getEntityCode() == EntityHelper.User) {
|
||||
String sql = MessageFormat.format(
|
||||
"select userId from User where loginName = ''{0}'' or email = ''{0}'' or fullName = ''{0}''",
|
||||
CommonsUtils.escapeSql(val2Text.toString()));
|
||||
CommonsUtils.escapeSql(val2Text));
|
||||
query = Application.createQueryNoFilter(sql);
|
||||
} else {
|
||||
// 查找引用实体的名称字段和自动编号字段
|
||||
|
@ -248,7 +248,7 @@ public class RecordCheckout {
|
|||
String.format("select %s from %s where ", refEntity.getPrimaryField().getName(), refEntity.getName()));
|
||||
for (String qf : queryFields) {
|
||||
sql.append(
|
||||
String.format("%s = '%s' or ", qf, CommonsUtils.escapeSql(val2Text.toString())));
|
||||
String.format("%s = '%s' or ", qf, CommonsUtils.escapeSql(val2Text)));
|
||||
}
|
||||
sql = new StringBuilder(sql.substring(0, sql.length() - 4));
|
||||
|
||||
|
|
|
@ -105,6 +105,12 @@ public class AdvFilterParser extends SetUser {
|
|||
this.filterExpr = filterExpr;
|
||||
this.rootEntity = rootEntity;
|
||||
this.varRecord = null;
|
||||
|
||||
String entityName = filterExpr.getString("entity");
|
||||
if (entityName != null) {
|
||||
Assert.isTrue(entityName.equalsIgnoreCase(this.rootEntity.getName()),
|
||||
"Filter#2 uses different entities : " + entityName + "/" + this.rootEntity.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,6 +121,12 @@ public class AdvFilterParser extends SetUser {
|
|||
this.filterExpr = filterExpr;
|
||||
this.rootEntity = MetadataHelper.getEntity(varRecord.getEntityCode());
|
||||
this.varRecord = License.isRbvAttached() ? varRecord : null;
|
||||
|
||||
String entityName = filterExpr.getString("entity");
|
||||
if (entityName != null) {
|
||||
Assert.isTrue(entityName.equalsIgnoreCase(this.rootEntity.getName()),
|
||||
"Filter#3 uses different entities : " + entityName + "/" + this.rootEntity.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,10 +32,7 @@ public class FilterRecordChecker {
|
|||
* @return
|
||||
*/
|
||||
public boolean check(ID recordId) {
|
||||
if (filterExpr == null || filterExpr.isEmpty()
|
||||
|| filterExpr.getJSONArray("items") == null || filterExpr.getJSONArray("items").isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (!ParseHelper.validAdvFilter(filterExpr)) return true;
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
|||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.general.RecordDifference;
|
||||
import com.rebuild.core.service.query.AdvFilterParser;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.core.service.query.QueryHelper;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.core.service.trigger.ActionType;
|
||||
|
@ -161,7 +162,7 @@ public class FieldAggregation extends TriggerAction {
|
|||
// 聚合数据过滤
|
||||
JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter");
|
||||
String dataFilterSql = null;
|
||||
if (dataFilter != null && !dataFilter.isEmpty()) {
|
||||
if (ParseHelper.validAdvFilter(dataFilter)) {
|
||||
dataFilterSql = new AdvFilterParser(dataFilter, operatingContext.getFixedRecordId()).toSqlWhere();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ public class FieldWritebackRefresh {
|
|||
fa.targetEntity = parent.targetEntity;
|
||||
fa.targetRecordIds = Collections.singleton(beforeValue);
|
||||
|
||||
Record fakeSourceRecord = EntityHelper.forUpdate(EntityHelper.newUnsavedId(fa.sourceEntity.getEntityCode()), triggerUser, false);
|
||||
ID fakeSourceId = EntityHelper.newUnsavedId(fa.sourceEntity.getEntityCode());
|
||||
Record fakeSourceRecord = EntityHelper.forUpdate(fakeSourceId, triggerUser, false);
|
||||
OperatingContext oCtx = OperatingContext.create(triggerUser, BizzPermission.NONE, fakeSourceRecord, fakeSourceRecord);
|
||||
fa.targetRecordData = fa.buildTargetRecordData(oCtx, true);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.rebuild.core.service.trigger.ActionContext;
|
|||
import com.rebuild.core.service.trigger.ActionType;
|
||||
import com.rebuild.core.service.trigger.TriggerException;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -223,8 +224,8 @@ public class GroupAggregation extends FieldAggregation {
|
|||
}
|
||||
}
|
||||
|
||||
qFields.add(String.format("%s = '%s'", targetField, val));
|
||||
qFieldsFollow.add(String.format("%s = '%s'", sourceField, val));
|
||||
qFields.add(String.format("%s = '%s'", targetField, CommonsUtils.escapeSql(val)));
|
||||
qFieldsFollow.add(String.format("%s = '%s'", sourceField, CommonsUtils.escapeSql(val)));
|
||||
allNull = false;
|
||||
}
|
||||
|
||||
|
@ -232,11 +233,11 @@ public class GroupAggregation extends FieldAggregation {
|
|||
}
|
||||
|
||||
if (allNull) {
|
||||
// 如果分组字段值全空将会触发全量更新
|
||||
if (!valueChanged.isEmpty()) {
|
||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
|
||||
} else {
|
||||
if (valueChanged.isEmpty()) {
|
||||
log.warn("All values of group-fields are null, ignored");
|
||||
} else {
|
||||
// 如果分组字段值全空将会触发全量更新
|
||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh, operatingContext.getFixedRecordId());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -244,7 +245,7 @@ public class GroupAggregation extends FieldAggregation {
|
|||
this.followSourceWhere = StringUtils.join(qFieldsFollow.iterator(), " and ");
|
||||
|
||||
if (isGroupUpdate) {
|
||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
|
||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh, operatingContext.getFixedRecordId());
|
||||
}
|
||||
|
||||
sql = String.format("select %s from %s where ( %s )",
|
||||
|
|
|
@ -16,10 +16,14 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 分组聚合目标数据刷新
|
||||
|
@ -39,15 +43,17 @@ public class GroupAggregationRefresh {
|
|||
|
||||
final private GroupAggregation parent;
|
||||
final private List<String[]> fieldsRefresh;
|
||||
final private ID originSourceId;
|
||||
|
||||
// fieldsRefresh = [TargetField, SourceField, Value]
|
||||
protected GroupAggregationRefresh(GroupAggregation parent, List<String[]> fieldsRefresh) {
|
||||
protected GroupAggregationRefresh(GroupAggregation parent, List<String[]> fieldsRefresh, ID originSourceId) {
|
||||
this.parent = parent;
|
||||
this.fieldsRefresh = fieldsRefresh;
|
||||
this.originSourceId = originSourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 存在性能问题(可能刷新过多)
|
||||
* NOTE 存在性能问题,因为可能刷新过多
|
||||
*/
|
||||
public void refresh() {
|
||||
List<String> targetFields = new ArrayList<>();
|
||||
|
@ -55,79 +61,52 @@ public class GroupAggregationRefresh {
|
|||
for (String[] s : fieldsRefresh) {
|
||||
targetFields.add(s[0]);
|
||||
if (s[2] != null) {
|
||||
targetWhere.add(String.format("%s = '%s'", s[0], s[2]));
|
||||
targetWhere.add(String.format("%s = '%s'", s[0], CommonsUtils.escapeSql(s[2])));
|
||||
}
|
||||
}
|
||||
|
||||
// 全量刷新,性能较低
|
||||
// 只有1个字段会全量刷新,性能较低
|
||||
if (targetWhere.size() <= 1) {
|
||||
targetWhere.clear();
|
||||
targetWhere.add("(1=1)");
|
||||
log.warn("Force refresh all aggregation target(s)");
|
||||
}
|
||||
|
||||
Entity entity = this.parent.targetEntity;
|
||||
// 1.获取待刷新的目标
|
||||
Entity targetEntity = this.parent.targetEntity;
|
||||
String sql = String.format("select %s,%s from %s where ( %s )",
|
||||
StringUtils.join(targetFields, ","),
|
||||
entity.getPrimaryField().getName(),
|
||||
entity.getName(),
|
||||
targetEntity.getPrimaryField().getName(),
|
||||
targetEntity.getName(),
|
||||
StringUtils.join(targetWhere, " or "));
|
||||
|
||||
Object[][] targetArray = Application.createQueryNoFilter(sql).array();
|
||||
log.info("Maybe refresh target record(s) : {}", targetArray.length);
|
||||
Object[][] targetRecords4Refresh = Application.createQueryNoFilter(sql).array();
|
||||
log.info("Maybe refresh target record(s) : {}", targetRecords4Refresh.length);
|
||||
|
||||
ID triggerUser = UserService.SYSTEM_USER;
|
||||
ActionContext parentAc = parent.getActionContext();
|
||||
|
||||
// 避免重复的无意义更新
|
||||
// NOTE 220905 不能忽略触发源本身
|
||||
// NOTE 220905 更新时不能忽略触发源本身的更新
|
||||
Set<ID> refreshedIds = new HashSet<>();
|
||||
|
||||
for (Object[] o : targetArray) {
|
||||
// 2.逐一刷新目标
|
||||
for (Object[] o : targetRecords4Refresh) {
|
||||
final ID targetRecordId = (ID) o[o.length - 1];
|
||||
// 排重
|
||||
if (refreshedIds.contains(targetRecordId)) continue;
|
||||
else refreshedIds.add(targetRecordId);
|
||||
|
||||
List<String> qFieldsFollow = new ArrayList<>();
|
||||
List<String> qFieldsFollow2 = new ArrayList<>();
|
||||
for (int i = 0; i < o.length - 1; i++) {
|
||||
String[] source = fieldsRefresh.get(i);
|
||||
if (o[i] == null) {
|
||||
qFieldsFollow.add(String.format("%s is null", source[1]));
|
||||
} else {
|
||||
qFieldsFollow.add(String.format("%s = '%s'", source[1], o[i]));
|
||||
qFieldsFollow2.add(String.format("%s = '%s'", source[1], o[i]));
|
||||
qFieldsFollow.add(String.format("%s = '%s'", source[1], CommonsUtils.escapeSql(o[i])));
|
||||
}
|
||||
}
|
||||
|
||||
ID fakeUpdateReferenceId = null;
|
||||
|
||||
// 1.尝试获取触发源
|
||||
for (int i = 0; i < o.length - 1; i++) {
|
||||
Object mayId = o[i];
|
||||
if (ID.isId(mayId) && ((ID) mayId).getEntityCode() > 100) {
|
||||
fakeUpdateReferenceId = (ID) mayId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 2.强制获取
|
||||
if (fakeUpdateReferenceId == null) {
|
||||
sql = String.format("select %s from %s where %s",
|
||||
parent.sourceEntity.getPrimaryField().getName(),
|
||||
parent.sourceEntity.getName(),
|
||||
StringUtils.join(qFieldsFollow2, " or "));
|
||||
Object[] found = Application.getQueryFactory().unique(sql);
|
||||
fakeUpdateReferenceId = found == null ? null : (ID) found[0];
|
||||
} else {
|
||||
|
||||
// 1.1.排重
|
||||
if (refreshedIds.contains(targetRecordId)) continue;
|
||||
else refreshedIds.add(fakeUpdateReferenceId);
|
||||
}
|
||||
|
||||
if (fakeUpdateReferenceId == null) {
|
||||
log.warn("No any source-id found, ignored : {}", Arrays.toString(o));
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionContext actionContext = new ActionContext(null,
|
||||
parentAc.getSourceEntity(), parentAc.getActionContent(), parentAc.getConfigId());
|
||||
|
||||
|
@ -137,7 +116,8 @@ public class GroupAggregationRefresh {
|
|||
ga.targetRecordId = targetRecordId;
|
||||
ga.followSourceWhere = StringUtils.join(qFieldsFollow, " and ");
|
||||
|
||||
Record fakeSourceRecord = EntityHelper.forUpdate(fakeUpdateReferenceId, triggerUser, false);
|
||||
// FIXME v35 可能导致数据聚合条件中的字段变量不准
|
||||
Record fakeSourceRecord = EntityHelper.forUpdate(originSourceId, triggerUser, false);
|
||||
OperatingContext oCtx = OperatingContext.create(triggerUser, BizzPermission.NONE, fakeSourceRecord, fakeSourceRecord);
|
||||
|
||||
try {
|
||||
|
|
|
@ -20,8 +20,10 @@ import com.rebuild.core.metadata.easymeta.DisplayType;
|
|||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -70,6 +72,12 @@ public class TargetWithMatchFields {
|
|||
return o == null ? new ID[0] : (ID[]) o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionContext
|
||||
* @param m
|
||||
* @return
|
||||
* @see GroupAggregation#prepare(OperatingContext)
|
||||
*/
|
||||
private Object match(ActionContext actionContext, boolean m) {
|
||||
if (sourceEntity != null) return targetRecordId; // 已做匹配
|
||||
|
||||
|
@ -186,6 +194,7 @@ public class TargetWithMatchFields {
|
|||
}
|
||||
}
|
||||
|
||||
val = CommonsUtils.escapeSql(val);
|
||||
qFields.add(String.format("%s = '%s'", targetField, val));
|
||||
qFieldsFollow.add(String.format("%s = '%s'", sourceField, val));
|
||||
allNull = false;
|
||||
|
|
|
@ -14,8 +14,8 @@ import cn.devezhao.persist4j.engine.ID;
|
|||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
@ -63,7 +63,7 @@ public class ShortUrls {
|
|||
*/
|
||||
public static boolean invalid(String shortKey) {
|
||||
String dsql = String.format(
|
||||
"delete from `short_url` where SHORT_KEY = '%s'", StringEscapeUtils.escapeSql(shortKey));
|
||||
"delete from `short_url` where SHORT_KEY = '%s'", CommonsUtils.escapeSql(shortKey));
|
||||
int a = Application.getSqlExecutor().execute(dsql);
|
||||
return a > 0;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,18 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.query.AdvFilterParser;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 列表查询解析
|
||||
|
@ -200,13 +207,13 @@ public class QueryParser {
|
|||
|
||||
// append: QuickQuery
|
||||
JSONObject quickFilter = queryExpr.getJSONObject("filter");
|
||||
if (quickFilter != null) {
|
||||
if (ParseHelper.validAdvFilter(quickFilter)) {
|
||||
String where = new AdvFilterParser(quickFilter, entity).toSqlWhere();
|
||||
if (StringUtils.isNotBlank(where)) wheres.add(where);
|
||||
}
|
||||
// v3.3
|
||||
JSONObject quickFilterAnd = queryExpr.getJSONObject("filterAnd");
|
||||
if (quickFilterAnd != null) {
|
||||
if (ParseHelper.validAdvFilter(quickFilterAnd)) {
|
||||
String where = new AdvFilterParser(quickFilterAnd, entity).toSqlWhere();
|
||||
if (StringUtils.isNotBlank(where)) wheres.add(where);
|
||||
}
|
||||
|
@ -270,7 +277,9 @@ public class QueryParser {
|
|||
ConfigBean advFilter = AdvFilterManager.instance.getAdvFilter(filterId);
|
||||
if (advFilter != null) {
|
||||
JSONObject filterExpr = (JSONObject) advFilter.getJSON("filter");
|
||||
return new AdvFilterParser(filterExpr, entity).toSqlWhere();
|
||||
if (ParseHelper.validAdvFilter(filterExpr)) {
|
||||
return new AdvFilterParser(filterExpr, entity).toSqlWhere();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -110,10 +110,10 @@ public class CommonsUtils {
|
|||
* @return
|
||||
* @see StringEscapeUtils#escapeSql(String)
|
||||
*/
|
||||
public static String escapeSql(String text) {
|
||||
public static String escapeSql(Object text) {
|
||||
// https://github.com/getrebuild/rebuild/issues/594
|
||||
text = text.replace("\\'", "'");
|
||||
return StringEscapeUtils.escapeSql(text);
|
||||
text = text.toString().replace("\\'", "'");
|
||||
return StringEscapeUtils.escapeSql((String) text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,8 +18,8 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.configuration.RebuildApiService;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.support.i18n.I18nUtils;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
@ -88,7 +88,7 @@ public class ApisManagerController extends BaseController {
|
|||
String sql = "select remoteIp,requestTime,responseTime,requestUrl,requestBody,responseBody,requestId from RebuildApiRequest" +
|
||||
" where appId = ? and requestTime > ? and (1=1) order by requestTime desc";
|
||||
if (StringUtils.isNotBlank(q)) {
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
q = CommonsUtils.escapeSql(q);
|
||||
sql = sql.replace("(1=1)", String.format("(requestBody like '%%%s%%' or responseBody like '%%%s%%')", q, q));
|
||||
}
|
||||
|
||||
|
|
|
@ -56,13 +56,17 @@ public class ListAndViewRedirection extends BaseController {
|
|||
|| entity.getEntityCode() == EntityHelper.ProjectTaskComment) {
|
||||
Object[] found = findProjectAndTaskId(anyId);
|
||||
if (found != null) {
|
||||
url = MessageFormat.format(
|
||||
"../project/{0}/tasks#!/View/ProjectTask/{1}", found[1], found[0]);
|
||||
url = MessageFormat.format("../project/{0}/tasks#!/View/ProjectTask/{1}", found[1], found[0]);
|
||||
}
|
||||
|
||||
} else if (entity.getEntityCode() == EntityHelper.User) {
|
||||
url = MessageFormat.format(
|
||||
"../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), anyId);
|
||||
url = MessageFormat.format("../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), anyId);
|
||||
} else if (entity.getEntityCode() == EntityHelper.Department) {
|
||||
url = MessageFormat.format("../admin/bizuser/departments#!/View/{0}/{1}", entity.getName(), anyId);
|
||||
} else if (entity.getEntityCode() == EntityHelper.Team) {
|
||||
url = MessageFormat.format("../admin/bizuser/teams#!/View/{0}/{1}", entity.getName(), anyId);
|
||||
} else if (entity.getEntityCode() == EntityHelper.Role) {
|
||||
url = MessageFormat.format("../admin/bizuser/role/{0}", anyId);
|
||||
|
||||
} else if (MetadataHelper.isBusinessEntity(entity)) {
|
||||
if ("dock".equalsIgnoreCase(type)) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.rebuild.core.service.NoRecordFoundException;
|
|||
import com.rebuild.core.service.project.ProjectHelper;
|
||||
import com.rebuild.core.service.project.ProjectManager;
|
||||
import com.rebuild.core.service.query.AdvFilterParser;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import com.rebuild.core.support.i18n.I18nUtils;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
|
@ -106,7 +107,7 @@ public class ProjectTaskController extends BaseController {
|
|||
|
||||
// 高级查询
|
||||
JSON advFilter = ServletUtils.getRequestJson(request);
|
||||
if (advFilter != null) {
|
||||
if (ParseHelper.validAdvFilter((JSONObject) advFilter)) {
|
||||
String filterSql = new AdvFilterParser((JSONObject) advFilter).toSqlWhere();
|
||||
if (filterSql != null) {
|
||||
queryWhere += " and (" + filterSql + ")";
|
||||
|
|
|
@ -84,7 +84,7 @@ public class FieldAggregationController extends BaseController {
|
|||
|
||||
// 源字段
|
||||
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> {
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> {
|
||||
if (field instanceof EasyField) {
|
||||
EasyField easyField = (EasyField) field;
|
||||
return !easyField.isQueryable() || easyField.getDisplayType() == DisplayType.BARCODE;
|
||||
|
@ -104,6 +104,7 @@ public class FieldAggregationController extends BaseController {
|
|||
for (Field field : MetadataSorter.sortFields(targetEntity,
|
||||
DisplayType.NUMBER, DisplayType.DECIMAL, DisplayType.DATE, DisplayType.DATETIME,
|
||||
DisplayType.N2NREFERENCE, DisplayType.NTEXT, DisplayType.FILE)) {
|
||||
|
||||
EasyField easyField = EasyMetaFactory.valueOf(field);
|
||||
if (easyField.isBuiltin()) continue;
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ public class FieldWritebackController extends BaseController {
|
|||
|
||||
// 源字段
|
||||
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> {
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> {
|
||||
if (field instanceof EasyField) {
|
||||
EasyField easyField = (EasyField) field;
|
||||
return easyField.getDisplayType() == DisplayType.BARCODE;
|
||||
|
@ -134,7 +134,7 @@ public class FieldWritebackController extends BaseController {
|
|||
|
||||
JSONArray targetFields = new JSONArray();
|
||||
if (targetEntity != null) {
|
||||
targetFields = MetaFormatter.buildFieldsWithRefs(targetEntity, 1, field -> {
|
||||
targetFields = MetaFormatter.buildFieldsWithRefs(targetEntity, 1, true, field -> {
|
||||
EasyField easyField = (EasyField) field;
|
||||
return easyField.getDisplayType() == DisplayType.SERIES
|
||||
|| easyField.getDisplayType() == DisplayType.BARCODE
|
||||
|
|
|
@ -54,7 +54,7 @@ public class GroupAggregationController extends BaseController {
|
|||
paddingType2(sourceGroupFields, sourceEntity);
|
||||
|
||||
// 源-聚合字段
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> {
|
||||
JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> {
|
||||
if (field instanceof EasyField) {
|
||||
EasyField easyField = (EasyField) field;
|
||||
return !easyField.isQueryable() || easyField.getDisplayType() == DisplayType.BARCODE;
|
||||
|
|
|
@ -2177,6 +2177,10 @@ th.column-fixed {
|
|||
content: '\f2fb';
|
||||
}
|
||||
|
||||
.form-line.hover fieldset legend > span {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.form-line.v33 {
|
||||
background-color: #ebebeb;
|
||||
border-radius: 2px;
|
||||
|
@ -5014,7 +5018,7 @@ pre.unstyle {
|
|||
|
||||
.tablesort thead th.sorted.ascending::after,
|
||||
.tablesort thead th.sorted.descending::after {
|
||||
font: normal normal normal 18px/1 "Material Design Icons";
|
||||
font: normal normal normal 18px/1 'Material Design Icons';
|
||||
content: '\F0140';
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
@ -5377,4 +5381,4 @@ div.dataTables_wrapper.compact div.dataTables_oper .btn-space {
|
|||
|
||||
.dataTables_oper.invisible2 > .btn.J_view {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
const wpc = window.__PageConfig
|
||||
const __gExtConfig = {}
|
||||
|
||||
const SHOW_REPEATABLE = ['TEXT', 'DATE', 'EMAIL', 'URL', 'PHONE', 'REFERENCE', 'CLASSIFICATION']
|
||||
const SHOW_REPEATABLE = ['TEXT', 'DATE', 'EMAIL', 'URL', 'PHONE', 'REFERENCE', 'CLASSIFICATION', 'ANYREFERENCE']
|
||||
const SHOW_DEFAULTVALUE = ['TEXT', 'NTEXT', 'EMAIL', 'PHONE', 'URL', 'NUMBER', 'DECIMAL', 'DATETIME', 'DATE', 'TIME', 'BOOL', 'CLASSIFICATION', 'REFERENCE', 'N2NREFERENCE']
|
||||
const SHOW_ADVDESENSITIZED = ['TEXT', 'PHONE', 'EMAIL', 'NUMBER', 'DECIMAL']
|
||||
const SHOW_ADVPATTERN = ['TEXT']
|
||||
|
|
|
@ -67,11 +67,12 @@ const renderList = function () {
|
|||
}
|
||||
|
||||
function exportFields() {
|
||||
const rows = [[$L('内部标识'), $L('字段名称'), $L('类型'), $L('必填'), $L('备注')].join(', ')]
|
||||
const rows = [[$L('内部标识'), $L('字段名称'), $L('类型'), $L('必填'), $L('只读'), $L('备注')].join(', ')]
|
||||
rows.push([`${wpc.entityName}Id`, $L('主键'), 'ID', 'N', 'Y', ''].join(', '))
|
||||
fields_data.forEach((item) => {
|
||||
let type = item.displayType
|
||||
if (item.displayTypeRef) type += ` (${item.displayTypeRef[1]})`
|
||||
rows.push([item.fieldName, item.fieldLabel, type, item.nullable ? 'N' : 'Y', item.comments ? item.comments.replace(/[,;]/, ' ') : ''].join(', '))
|
||||
rows.push([item.fieldName, item.fieldLabel, type, item.nullable ? 'N' : 'Y', item.creatable ? 'N' : 'Y', item.comments ? item.comments.replace(/[,;]/, ' ') : ''].join(', '))
|
||||
})
|
||||
|
||||
const encodedUri = encodeURI('data:text/csv;charset=utf-8,\ufeff' + rows.join('\n'))
|
||||
|
|
|
@ -514,12 +514,13 @@ class DlgEditRefform extends DlgEditField {
|
|||
<option value="">{$L('无')}</option>
|
||||
{Object.keys(_ValidFields).map((k) => {
|
||||
const field = _ValidFields[k]
|
||||
if (field.displayTypeName === 'REFERENCE' && !(field.fieldName === 'approvalId'))
|
||||
if (['REFERENCE', 'ANYREFERENCE'].includes(field.displayTypeName) && field.fieldName !== 'approvalId') {
|
||||
return (
|
||||
<option key={field.fieldName} value={field.fieldName}>
|
||||
{field.fieldLabel}
|
||||
</option>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</select>
|
||||
|
|
|
@ -2765,7 +2765,7 @@ class RbFormDivider extends React.Component {
|
|||
<div className={`form-line hover -v33 ${this.state.collapsed && 'collapsed'}`} ref={(c) => (this._$formLine = c)}>
|
||||
<fieldset>
|
||||
<legend onClick={() => this._toggle()} title={$L('展开/收起')}>
|
||||
{this.props.label || ''}
|
||||
{this.props.label && <span>{this.props.label}</span>}
|
||||
</legend>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
@ -2805,8 +2805,11 @@ class RbFormRefform extends React.Component {
|
|||
|
||||
const $$$parent = this.props.$$$parent
|
||||
if ($$$parent && $$$parent.__ViewData && $$$parent.__ViewData[this.props.reffield]) {
|
||||
const s = $$$parent.__ViewData[this.props.reffield]
|
||||
this._renderViewFrom({ ...s })
|
||||
// 避免循环嵌套死循环
|
||||
if (($$$parent.__nestDepth || 0) < 3) {
|
||||
const s = $$$parent.__ViewData[this.props.reffield]
|
||||
this._renderViewFrom({ ...s })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2819,6 +2822,10 @@ class RbFormRefform extends React.Component {
|
|||
return
|
||||
}
|
||||
|
||||
// 支持嵌套
|
||||
this.__ViewData = {}
|
||||
this.__nestDepth = (this.props.$$$parent.__nestDepth || 0) + 1
|
||||
|
||||
const VFORM = (
|
||||
<RF>
|
||||
<a title={$L('在新页面打开')} className="close open-in-new" href={`${rb.baseUrl}/app/redirect?id=${props.id}&type=newtab`} target="_blank">
|
||||
|
@ -2826,6 +2833,7 @@ class RbFormRefform extends React.Component {
|
|||
</a>
|
||||
<div className="row">
|
||||
{res.data.elements.map((item) => {
|
||||
if (![TYPE_DIVIDER, TYPE_REFFORM].includes(item.field)) this.__ViewData[item.field] = item.value
|
||||
item.$$$parent = this
|
||||
// eslint-disable-next-line no-undef
|
||||
return detectViewElement(item, props.entity)
|
||||
|
@ -2840,7 +2848,7 @@ class RbFormRefform extends React.Component {
|
|||
|
||||
// 确定元素类型
|
||||
var detectElement = function (item, entity) {
|
||||
if (!item.key) item.key = `field-${item.field === TYPE_DIVIDER ? $random() : item.field}`
|
||||
if (!item.key) item.key = `field-${item.field === TYPE_DIVIDER || item.field === TYPE_REFFORM ? $random() : item.field}`
|
||||
|
||||
if (entity && window._CustomizedForms) {
|
||||
const c = window._CustomizedForms.useFormElement(entity, item)
|
||||
|
@ -2898,6 +2906,7 @@ var detectElement = function (item, entity) {
|
|||
} else if (item.field === TYPE_DIVIDER || item.field === '$LINE$') {
|
||||
return <RbFormDivider {...item} />
|
||||
} else if (item.field === TYPE_REFFORM) {
|
||||
console.log(item)
|
||||
return <RbFormRefform {...item} />
|
||||
} else {
|
||||
return <RbFormUnsupportted {...item} />
|
||||
|
|
|
@ -8,8 +8,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
/* !!! KEEP IT ES5 COMPATIBLE !!! */
|
||||
|
||||
// GA
|
||||
;(function () {
|
||||
const gaScript = document.createElement('script')
|
||||
(function () {
|
||||
var gaScript = document.createElement('script')
|
||||
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-ZCZHJPMEG7'
|
||||
gaScript.async = true
|
||||
gaScript.onload = function () {
|
||||
|
@ -20,7 +20,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
gtag('js', new Date())
|
||||
gtag('config', 'G-ZCZHJPMEG7')
|
||||
}
|
||||
const s = document.getElementsByTagName('script')[0]
|
||||
var s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(gaScript, s)
|
||||
})()
|
||||
|
||||
|
@ -259,7 +259,7 @@ var _initNav = function () {
|
|||
$sidebar.toggleClass('rb-collapsible-sidebar-collapsed')
|
||||
$('.sidebar-elements>li>a').tooltip('toggleEnabled')
|
||||
|
||||
const collapsed = $sidebar.hasClass('rb-collapsible-sidebar-collapsed')
|
||||
var collapsed = $sidebar.hasClass('rb-collapsible-sidebar-collapsed')
|
||||
$.cookie('rb.sidebarCollapsed', collapsed, { expires: 180 })
|
||||
|
||||
if (collapsed) {
|
||||
|
|
|
@ -61,7 +61,7 @@ class RbViewForm extends React.Component {
|
|||
{hadApproval && <ApprovalProcessor id={this.props.id} entity={this.props.entity} />}
|
||||
<div className="row">
|
||||
{res.data.elements.map((item) => {
|
||||
if (!(item.field === TYPE_DIVIDER || item.field === TYPE_REFFORM)) this.__ViewData[item.field] = item.value
|
||||
if (![TYPE_DIVIDER, TYPE_REFFORM].includes(item.field)) this.__ViewData[item.field] = item.value
|
||||
if (item.field === TYPE_REFFORM) this.__hasRefform = true
|
||||
item.$$$parent = this
|
||||
return detectViewElement(item, this.props.entity)
|
||||
|
|
|
@ -10,7 +10,7 @@ const CALC_MODES2 = {
|
|||
...FormulaAggregation.CALC_MODES,
|
||||
RBJOIN: $L('连接'),
|
||||
RBJOIN2: $L('去重连接'),
|
||||
// RBJOIN3: $L('去重连接*'),
|
||||
// RBJOIN3: $L('去重连接*N'),
|
||||
}
|
||||
|
||||
const __LAB_MATCHFIELDS = false
|
||||
|
@ -70,7 +70,7 @@ class ContentFieldAggregation extends ActionContentSpec {
|
|||
<label className="col-md-12 col-lg-3 col-form-label text-lg-right"></label>
|
||||
<div className="col-md-12 col-lg-9">
|
||||
<div>
|
||||
<h5 className="mt-0 text-bold">{$L('目标实体/记录匹配规则')}</h5>
|
||||
<h5 className="mt-0 text-bold">{$L('目标实体/记录匹配规则')} (LAB)</h5>
|
||||
<textarea className="formula-code" style={{ height: 72 }} ref={(c) => (this._$matchFields = c)} placeholder="## [{ sourceField:XXX, targetField:XXX }]" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -406,6 +406,22 @@ class ContentFieldAggregation extends ActionContentSpec {
|
|||
return false
|
||||
}
|
||||
|
||||
if (this.state.showMatchFields) {
|
||||
let badFormat = !content.targetEntityMatchFields
|
||||
if (!badFormat) {
|
||||
try {
|
||||
badFormat = JSON.parse(content.targetEntityMatchFields)
|
||||
badFormat = !$.isArray(badFormat)
|
||||
} catch (err) {
|
||||
badFormat = true
|
||||
}
|
||||
}
|
||||
if (badFormat) {
|
||||
RbHighbar.create($L('请正确填写目标实体/记录匹配规则'))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ class ContentFieldWriteback extends ActionContentSpec {
|
|||
<label className="col-md-12 col-lg-3 col-form-label text-lg-right"></label>
|
||||
<div className="col-md-12 col-lg-9">
|
||||
<div>
|
||||
<h5 className="mt-0 text-bold">{$L('目标实体/记录匹配规则')}</h5>
|
||||
<h5 className="mt-0 text-bold">{$L('目标实体/记录匹配规则')} (LAB)</h5>
|
||||
<textarea className="formula-code" style={{ height: 72 }} ref={(c) => (this._$matchFields = c)} placeholder="## [{ sourceField:XXX, targetField:XXX }]" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -377,6 +377,22 @@ class ContentFieldWriteback extends ActionContentSpec {
|
|||
return false
|
||||
}
|
||||
|
||||
if (this.state.showMatchFields) {
|
||||
let badFormat = !content.targetEntityMatchFields
|
||||
if (!badFormat) {
|
||||
try {
|
||||
badFormat = JSON.parse(content.targetEntityMatchFields)
|
||||
badFormat = !$.isArray(badFormat)
|
||||
} catch (err) {
|
||||
badFormat = true
|
||||
}
|
||||
}
|
||||
if (badFormat) {
|
||||
RbHighbar.create($L('请正确填写目标实体/记录匹配规则'))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const one2one = this.state.targetEntities.find((x) => `${x[2]}.${x[0]}` === content.targetEntity)
|
||||
if (one2one && one2one[3] === 'one2one') content.one2one = true
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ const CALC_MODES2 = {
|
|||
...FormulaAggregation.CALC_MODES,
|
||||
RBJOIN: $L('连接'),
|
||||
RBJOIN2: $L('去重连接'),
|
||||
// RBJOIN3: $L('去重连接*'),
|
||||
// RBJOIN3: $L('去重连接*N'),
|
||||
}
|
||||
|
||||
// ~~ 分组聚合
|
||||
|
|
Loading…
Reference in a new issue