Fix 3.6 beta4 (#729)

* fix: case TEXT to NTEXT

* fix: getReplacedUser

* be: view show

* fix: 字段类型转换属性冲突

* fix: *N


* CommonsUtils.DEVLOG

* feat: filter token: REP

* feat: advfilter valuesPlus

* lang

* fix: toJSONObject

* fix: DataList2Chart

* style

---------

Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
REBUILD 企业管理系统 2024-03-14 21:50:08 +08:00 committed by GitHub
parent ea86c421a9
commit 8504946d76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 337 additions and 142 deletions

2
@rbv

@ -1 +1 @@
Subproject commit a2b338694068b3bfa059af4e83f669686515ef00
Subproject commit c24b2e835e839341f850de2161cff51525ee6cfc

View file

@ -10,7 +10,7 @@
</parent>
<groupId>com.rebuild</groupId>
<artifactId>rebuild</artifactId>
<version>3.6.0-beta3</version>
<version>3.6.0-beta4</version>
<name>rebuild</name>
<description>Building your business-systems freely!</description>
<url>https://getrebuild.com/</url>

View file

@ -74,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
/**
* Rebuild Version
*/
public static final String VER = "3.6.0-beta3";
public static final String VER = "3.6.0-beta4";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
public static final int BUILD = 3060003;
public static final int BUILD = 3060004;
static {
// Driver for DB

View file

@ -44,8 +44,10 @@ public class UserContextHolder {
}
/**
* 设置当前用户
*
* @param user
* @see #clearUser()
* @see #replaceUser(ID)
*/
public static void setUser(ID user) {
Assert.notNull(user, "[user] cannot be null");
@ -119,20 +121,37 @@ public class UserContextHolder {
}
/**
* 设置当前用户并保持原始用户如有
*
* @param user
* @see #getReplacedUser()
* @see #restoreUser()
* @see #setUser(ID)
*/
public static void replaceUser(ID user) {
Assert.notNull(user, "[user] cannot be null");
ID e = getUser(Boolean.TRUE);
// Keep origin
ID e = CALLER_PREV.get();
if (e == null) e = getUser(Boolean.TRUE);
if (e != null) CALLER_PREV.set(e);
else CALLER_PREV.remove();
CALLER.set(user);
}
/**
* 获取原始用户
*
* @return
* @see #replaceUser(ID)
*/
public static ID getReplacedUser() {
ID prev = CALLER_PREV.get();
if (prev != null) return prev;
return getUser();
}
/**
* @return
* @see #replaceUser(ID)
@ -147,16 +166,6 @@ public class UserContextHolder {
return false;
}
/**
* @return
* @see #replaceUser(ID)
*/
public static ID getRestoreUser() {
ID prev = CALLER_PREV.get();
if (prev != null) return prev;
return getUser();
}
// --
/**

View file

@ -13,6 +13,7 @@ import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.dialect.Dialect;
import cn.devezhao.persist4j.dialect.FieldType;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.metadata.CascadeModel;
import cn.devezhao.persist4j.metadata.impl.AnyEntity;
@ -20,6 +21,7 @@ import cn.devezhao.persist4j.metadata.impl.FieldImpl;
import cn.devezhao.persist4j.util.StringHelper;
import cn.devezhao.persist4j.util.support.Table;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hankcs.hanlp.HanLP;
import com.rebuild.core.Application;
import com.rebuild.core.metadata.EntityHelper;
@ -38,12 +40,16 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.CharSet;
import org.apache.commons.lang.StringUtils;
import java.sql.DataTruncation;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_CALCFORMULA;
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_NOTNEGATIVE;
/**
* 创建字段
*
@ -421,9 +427,9 @@ public class Field2Schema extends SetUser {
* @return
*/
public boolean castType(Field field, DisplayType toType, boolean force) {
EasyField easyMeta = EasyMetaFactory.valueOf(field);
ID metaRecordId = easyMeta.getMetaId();
if (easyMeta.isBuiltin() || metaRecordId == null) {
EasyField fieldEasy = EasyMetaFactory.valueOf(field);
ID metaRecordId = fieldEasy.getMetaId();
if (fieldEasy.isBuiltin() || metaRecordId == null) {
throw new MetadataModificationException(Language.L("系统内置,不允许转换"));
}
@ -434,9 +440,27 @@ public class Field2Schema extends SetUser {
}
}
Record meta = EntityHelper.forUpdate(metaRecordId, getUser(), false);
meta.setString("displayType", toType.name());
Application.getCommonsService().update(meta, false);
Record fieldMeta = EntityHelper.forUpdate(metaRecordId, getUser(), false);
fieldMeta.setString("displayType", toType.name());
// 长度
if (toType.getMaxLength() != FieldType.NO_NEED_LENGTH) {
fieldMeta.setInt("maxLength", toType.getMaxLength());
}
// 保留部分扩展配置其余移除避免冲突
JSONObject extraAttrs = fieldEasy.getExtraAttrs();
if (!extraAttrs.isEmpty()) {
Object notNegative = extraAttrs.remove(NUMBER_NOTNEGATIVE);
Object calcFormula = extraAttrs.remove(NUMBER_CALCFORMULA);
extraAttrs.clear();
if (notNegative != null) extraAttrs.put(NUMBER_NOTNEGATIVE, notNegative);
if (calcFormula != null) extraAttrs.put(NUMBER_CALCFORMULA, calcFormula);
if (!extraAttrs.isEmpty()) {
fieldMeta.setString("extConfig", extraAttrs.toJSONString());
}
}
Application.getCommonsService().update(fieldMeta, false);
// 类型生效
DynamicMetadataContextHolder.setSkipLanguageRefresh();
@ -452,18 +476,25 @@ public class Field2Schema extends SetUser {
alterTypeSql = String.format("alter table `%s` change column `%s` ",
field.getOwnEntity().getPhysicalName(), field.getPhysicalName());
alterTypeSql += ddl.toString().trim().replace(" ", "");
alterTypeSql += ddl.toString().trim().replace(" ", " ");
Application.getSqlExecutor().executeBatch(new String[]{alterTypeSql}, DDL_TIMEOUT);
log.info("Cast field type : {}", alterTypeSql);
} catch (Throwable ex) {
// 还原
meta.setString("displayType", EasyMetaFactory.getDisplayType(field).name());
Application.getCommonsService().update(meta, false);
fieldMeta.setString("displayType", EasyMetaFactory.getDisplayType(field).name());
Application.getCommonsService().update(fieldMeta, false);
log.error("DDL ERROR : \n" + alterTypeSql, ex);
throw new MetadataModificationException(ThrowableUtils.getRootCause(ex).getLocalizedMessage());
Throwable cause = ThrowableUtils.getRootCause(ex);
String causeMsg = cause.getLocalizedMessage();
if (cause instanceof DataTruncation) {
causeMsg = Language.L("已有数据内容长度超出限制,无法完成转换");
}
throw new MetadataModificationException(causeMsg);
} finally {
MetadataHelper.getMetadataFactory().refresh();
DynamicMetadataContextHolder.isSkipLanguageRefresh(true);

View file

@ -351,7 +351,7 @@ public class ApprovalProcessor extends SetUser {
int bLength = nextNodes.size();
for (FlowNode node : nextNodes) {
// 匹配最后一个
// 匹配最后一个分支
if (--bLength == 0) {
return getNextNode(node.getNodeId());
}

View file

@ -60,15 +60,17 @@ public class DataList2Chart extends ChartData {
}
int pageSize = config.getJSONObject("option").getIntValue("pageSize");
if (pageSize == 0) pageSize = 40;
if (pageSize <= 0) pageSize = 40;
if (pageSize >= 2000) pageSize = 2000;
JSONObject listConfig = new JSONObject();
listConfig.put("pageNo", 1);
listConfig.put("pageSize", Math.max(pageSize, 1));
listConfig.put("pageSize", pageSize);
listConfig.put("reload", false);
listConfig.put("statsField", false);
listConfig.put("entity", entity.getName());
listConfig.put("fields", fields);
listConfig.put("filter", config.getJSONObject("filter"));
if (sort != null) listConfig.put("sort", sort);
DataListBuilder builder = new DataListBuilderImpl(listConfig, getUser());

View file

@ -12,7 +12,6 @@ import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.privileges.UserService;
@ -121,7 +120,7 @@ public abstract class BaseFeedsService extends ObservableService {
String atAllKey = "@" + Language.L("所有人");
if (fakeContent.contains(atAllKey)
&& Application.getPrivilegesManager().allow(UserContextHolder.getUser(), ZeroEntry.AllowAtAllUsers)) {
&& Application.getPrivilegesManager().allow(getCurrentUser(), ZeroEntry.AllowAtAllUsers)) {
fakeContent = fakeContent.replace(atAllKey, "@" + UserService.ALLUSERS);
}

View file

@ -11,7 +11,6 @@ import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.OperationDeniedException;
import com.rebuild.core.privileges.UserHelper;
@ -43,7 +42,7 @@ public class FeedsService extends BaseFeedsService {
public Record createOrUpdate(Record record) {
Integer type = record.getInt("type");
if (type != null && type == FeedsType.ANNOUNCEMENT.getMask()
&& !UserHelper.isAdmin(UserContextHolder.getUser())) {
&& !UserHelper.isAdmin(getCurrentUser())) {
throw new OperationDeniedException(Language.L("仅管理员可发布公告"));
}

View file

@ -19,7 +19,6 @@ import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.RebuildException;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.DeleteRecord;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
@ -273,7 +272,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
@Override
public int delete(ID recordId, String[] cascades) {
final ID currentUser = UserContextHolder.getUser();
final ID currentUser = getCurrentUser();
final RecycleStore recycleBin = useRecycleStore(recordId);
int affected = this.deleteInternal(recordId);
@ -317,7 +316,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
* @throws DataSpecificationException
*/
protected int deleteInternal(ID recordId) throws DataSpecificationException {
Record delete = EntityHelper.forUpdate(recordId, UserContextHolder.getUser());
Record delete = EntityHelper.forUpdate(recordId, getCurrentUser());
if (!checkModifications(delete, BizzPermission.DELETE)) {
return 0;
}
@ -374,14 +373,14 @@ public class GeneralEntityService extends ObservableService implements EntitySer
}
if (countObservers() > 0 && assignBefore != null) {
notifyObservers(OperatingContext.create(UserContextHolder.getUser(), BizzPermission.ASSIGN, assignBefore, assignAfter));
notifyObservers(OperatingContext.create(getCurrentUser(), BizzPermission.ASSIGN, assignBefore, assignAfter));
}
return affected;
}
@Override
public int share(ID recordId, ID toUserId, String[] cascades, int rights) {
final ID currentUser = UserContextHolder.getUser();
final ID currentUser = getCurrentUser();
final ID recordOrigin = recordId;
// v3.2.2 若为明细则转为主记录
if (MetadataHelper.getEntity(recordId.getEntityCode()).getMainEntity() != null) {
@ -459,7 +458,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
@Override
public int unshare(ID recordId, ID accessId) {
final ID currentUser = UserContextHolder.getUser();
final ID currentUser = getCurrentUser();
Record unsharedBefore = null;
if (countObservers() > 0) {
@ -543,7 +542,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
if (fromTriggerNoFilter) {
array = Application.createQueryNoFilter(sql).array();
} else {
Filter filter = Application.getPrivilegesManager().createQueryFilter(UserContextHolder.getUser(), action);
Filter filter = Application.getPrivilegesManager().createQueryFilter(getCurrentUser(), action);
array = Application.getQueryFactory().createQuery(sql, filter).array();
}

View file

@ -64,7 +64,7 @@ public abstract class ObservableService extends SafeObservable implements Servic
record = delegateService.create(record);
if (countObservers() > 0) {
notifyObservers(OperatingContext.create(UserContextHolder.getUser(), BizzPermission.CREATE, null, record));
notifyObservers(OperatingContext.create(getCurrentUser(), BizzPermission.CREATE, null, record));
}
return record;
}
@ -76,15 +76,14 @@ public abstract class ObservableService extends SafeObservable implements Servic
record = delegateService.update(record);
if (countObservers() > 0) {
notifyObservers(OperatingContext.create(UserContextHolder.getUser(), BizzPermission.UPDATE, before, record));
notifyObservers(OperatingContext.create(getCurrentUser(), BizzPermission.UPDATE, before, record));
}
return record;
}
@Override
public int delete(ID recordId) {
ID currentUser = UserContextHolder.getRestoreUser();
if (currentUser == null) currentUser = UserContextHolder.getUser();
final ID currentUser = getCurrentUser();
Record deleted = null;
if (countObservers() > 0) {
@ -126,7 +125,7 @@ public abstract class ObservableService extends SafeObservable implements Servic
* @return 返回 null 表示没开启
*/
protected RecycleStore useRecycleStore(ID recordId) {
final ID currentUser = UserContextHolder.getUser();
final ID currentUser = getCurrentUser();
RecycleStore recycleBin = null;
if (RecycleBinCleanerJob.isEnableRecycleBin()) {
@ -141,4 +140,13 @@ public abstract class ObservableService extends SafeObservable implements Servic
return null;
}
}
/**
* 获取原始用户
*
* @return
*/
protected ID getCurrentUser() {
return UserContextHolder.getReplacedUser();
}
}

View file

@ -9,7 +9,6 @@ package com.rebuild.core.service.project;
import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.configuration.ConfigBean;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.service.DataSpecificationException;
@ -37,7 +36,7 @@ public abstract class BaseTaskService extends ObservableService {
* @return
*/
protected boolean checkModifications(ID user, ID taskOrProject) {
if (user == null) user = UserContextHolder.getUser();
if (user == null) user = getCurrentUser();
Assert.notNull(taskOrProject, "taskOrProject");
ConfigBean c = taskOrProject.getEntityCode() == EntityHelper.ProjectTask

View file

@ -11,7 +11,6 @@ import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.OperationDeniedException;
import com.rebuild.core.service.feeds.FeedsHelper;
@ -40,7 +39,7 @@ public class ProjectCommentService extends BaseTaskService {
@Override
public Record create(Record record) {
final ID user = UserContextHolder.getUser();
final ID user = getCurrentUser();
checkModifications(user, record.getID("taskId"));
record = super.create(record);
@ -56,7 +55,7 @@ public class ProjectCommentService extends BaseTaskService {
@Override
public int delete(ID commentId) {
final ID user = UserContextHolder.getUser();
final ID user = getCurrentUser();
if (!ProjectHelper.isManageable(commentId, user)) throw new OperationDeniedException();
return super.delete(commentId);

View file

@ -12,7 +12,6 @@ import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.configuration.ConfigBean;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.OperationDeniedException;
@ -49,7 +48,7 @@ public class ProjectTaskService extends BaseTaskService {
@Override
public Record create(Record record) {
final ID user = UserContextHolder.getUser();
final ID user = getCurrentUser();
checkModifications(user, record.getID("projectId"));
ID projectId = record.getID("projectId");
@ -73,7 +72,7 @@ public class ProjectTaskService extends BaseTaskService {
@Override
public Record update(Record record) {
final ID user = UserContextHolder.getUser();
final ID user = getCurrentUser();
checkModifications(user, record.getPrimary());
// 自动完成
@ -118,7 +117,7 @@ public class ProjectTaskService extends BaseTaskService {
@Override
public int delete(ID taskId) {
final ID user = UserContextHolder.getUser();
final ID user = getCurrentUser();
if (!ProjectHelper.isManageable(taskId, user)) throw new OperationDeniedException();
// 先删评论

View file

@ -39,6 +39,7 @@ import org.apache.commons.lang.math.NumberUtils;
import org.springframework.util.Assert;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar;
@ -141,8 +142,26 @@ public class AdvFilterParser extends SetUser {
// 自动确定查询项
if (MODE_QUICK.equalsIgnoreCase(filterExpr.getString("type"))) {
JSONArray quickItems = buildQuickFilterItems(filterExpr.getString("quickFields"));
this.filterExpr.put("items", quickItems);
String quickFields = filterExpr.getString("quickFields");
JSONArray quickItems = buildQuickFilterItems(quickFields, 1);
// // v3.6-b4 值1|值2 UNTEST
// // 转义可输入 \|
// JSONObject values = filterExpr.getJSONObject("values");
// String[] valuesPlus = values.values().iterator().next().toString().split("(?<!\\\\)\\|");
// if (valuesPlus.length > 1) {
// values.clear();
// values.put("1", valuesPlus[0].trim());
//
// for (int i = 2; i <= valuesPlus.length; i++) {
// JSONArray quickItemsPlus = buildQuickFilterItems(quickFields, i);
// values.put(String.valueOf(i), valuesPlus[i - 1].trim());
// quickItems.addAll(quickItemsPlus);
// }
// filterExpr.put("values", values);
// }
filterExpr.put("items", quickItems);
}
JSONArray items = filterExpr.getJSONArray("items");
@ -167,7 +186,7 @@ public class AdvFilterParser extends SetUser {
indexItemSqls.put(index, itemSql.trim());
this.includeFields.add(item.getString("field"));
}
if (Application.devMode()) System.out.println("[dev] Parse item : " + item + " >> " + itemSql);
if (CommonsUtils.DEVLOG) System.out.println("[dev] Parse item : " + item + " >> " + itemSql);
}
if (indexItemSqls.isEmpty()) return null;
@ -182,7 +201,7 @@ public class AdvFilterParser extends SetUser {
} else if ("AND".equalsIgnoreCase(equation)) {
return "( " + StringUtils.join(indexItemSqls.values(), " and ") + " )";
} else {
// 高级表达式 eg. (1 AND 2) or (3 AND 4)
// 高级表达式 eg: (1 AND 2) or (3 AND 4)
String[] tokens = equation.toLowerCase().split(" ");
List<String> itemSqls = new ArrayList<>();
for (String token : tokens) {
@ -530,10 +549,15 @@ public class AdvFilterParser extends SetUser {
}
} else if (ParseHelper.SFT.equalsIgnoreCase(op)) {
if (value == null) value = "0"; // No any
// In Sql
// `in`
value = String.format(
"( select userId from TeamMember where teamId in ('%s') )",
StringUtils.join(value.split("\\|"), "', '"));
} else if (ParseHelper.REP.equalsIgnoreCase(op)) {
// `in`
value = MessageFormat.format(
"( select {0} from {1} group by {0} having (count({0}) > {2}) )",
field, rootEntity.getName(), NumberUtils.toInt(value, 1));
}
if (StringUtils.isBlank(value)) {
@ -564,7 +588,8 @@ public class AdvFilterParser extends SetUser {
// IN
if (op.equalsIgnoreCase(ParseHelper.IN) || op.equalsIgnoreCase(ParseHelper.NIN)
|| op.equalsIgnoreCase(ParseHelper.SFD) || op.equalsIgnoreCase(ParseHelper.SFT)) {
|| op.equalsIgnoreCase(ParseHelper.SFD) || op.equalsIgnoreCase(ParseHelper.SFT)
|| op.equalsIgnoreCase(ParseHelper.REP)) {
sb.append(value);
} else {
// LIKE
@ -702,12 +727,12 @@ public class AdvFilterParser extends SetUser {
* @param quickFields
* @return
*/
private JSONArray buildQuickFilterItems(String quickFields) {
private JSONArray buildQuickFilterItems(String quickFields, int valueIndex) {
Set<String> usesFields = ParseHelper.buildQuickFields(rootEntity, quickFields);
JSONArray items = new JSONArray();
for (String field : usesFields) {
items.add(JSON.parseObject("{ op:'LK', value:'{1}', field:'" + field + "' }"));
items.add(JSON.parseObject("{ op:'LK', value:'{" + valueIndex + "}', field:'" + field + "' }"));
}
return items;
}

View file

@ -99,6 +99,8 @@ public class ParseHelper {
public static final String EVW = "EVW"; // 每周几
public static final String EVM = "EVM"; // 每月几
public static final String REP = "REP"; // 重复的
// 日期时间
public static final String ZERO_TIME = " 00:00:00";
@ -165,7 +167,7 @@ public class ParseHelper {
return "=";
} else if (SFB.equalsIgnoreCase(token)) {
return "=";
} else if (SFD.equalsIgnoreCase(token) || SFT.equalsIgnoreCase(token)) {
} else if (SFD.equalsIgnoreCase(token) || SFT.equalsIgnoreCase(token) || REP.equalsIgnoreCase(token)) {
return "in";
} else if (YTA.equalsIgnoreCase(token)) {
return "=";

View file

@ -16,6 +16,7 @@ import com.rebuild.core.service.trigger.impl.FieldAggregation;
import com.rebuild.core.support.CommonsLog;
import com.rebuild.core.support.general.FieldValueHelper;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.web.KnownExceptionConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -167,7 +168,7 @@ public class RobotTriggerObserver extends OperatingObserver {
Object res = action.execute(context);
boolean hasAffected = res instanceof TriggerResult && ((TriggerResult) res).hasAffected();
System.out.println("[dev] " + w + " > " + (res == null ? "N" : res) + (hasAffected ? " < REALLY AFFECTED" : ""));
if (CommonsUtils.DEVLOG) System.out.println("[dev] " + w + " > " + (res == null ? "N" : res) + (hasAffected ? " < REALLY AFFECTED" : ""));
if (res instanceof TriggerResult) {
if (originTriggerSource) {

View file

@ -9,6 +9,7 @@ package com.rebuild.core.service.trigger;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.service.general.OperatingContext;
import com.rebuild.utils.CommonsUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
@ -38,7 +39,7 @@ public class TriggerSource {
protected TriggerSource(OperatingContext origin, TriggerWhen originAction) {
this.id = TSNO.incrementAndGet() + "-";
addNext(origin, originAction);
System.out.println("[dev] New trigger-source : " + this);
if (CommonsUtils.DEVLOG) System.out.println("[dev] New trigger-source : " + this);
// Clear
if (this.id.length() > 4) TSNO.set(0);

View file

@ -23,6 +23,7 @@ import com.rebuild.core.metadata.easymeta.EasyField;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.service.trigger.aviator.AviatorUtils;
import com.rebuild.core.support.general.ContentWithFieldVars;
import com.rebuild.core.support.general.FieldValueHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
@ -227,6 +228,11 @@ public class AggregationEvaluator {
Object n = o[0];
if (n == null) continue;
// *N
if (n instanceof ID && mode == 3) {
n = FieldValueHelper.getLabel((ID) n, StringUtils.EMPTY);
}
// 多引用
if (n instanceof ID[]) {
CollectionUtils.addAll(nvList, (ID[]) n);

View file

@ -119,7 +119,7 @@ public class FieldAggregation extends TriggerAction {
List<String> tschain = TRIGGER_CHAIN.get();
if (tschain == null) {
tschain = new ArrayList<>();
System.out.println("[dev] New trigger-chain : " + this);
if (CommonsUtils.DEVLOG) System.out.println("[dev] New trigger-chain : " + this);
} else {
String w = String.format("Occured trigger-chain : %s > %s (current)", StringUtils.join(tschain, " > "), chainName);

View file

@ -172,7 +172,7 @@ public class FieldWriteback extends FieldAggregation {
List<String> tschainCurrentLoop = new ArrayList<>(tschain);
tschainCurrentLoop.add(chainName);
TRIGGER_CHAIN.set(tschainCurrentLoop);
System.out.println("[dev] Use current-loop tschain : " + tschainCurrentLoop);
if (CommonsUtils.DEVLOG) System.out.println("[dev] Use current-loop tschain : " + tschainCurrentLoop);
try {
if (stopPropagation) {

View file

@ -11,6 +11,7 @@ import cn.devezhao.commons.ObjectUtils;
import cn.devezhao.commons.ReflectUtils;
import cn.devezhao.persist4j.engine.NullValue;
import com.rebuild.core.Application;
import com.rebuild.core.BootApplication;
import com.rebuild.core.RebuildException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@ -42,6 +43,9 @@ import java.util.regex.Pattern;
@Slf4j
public class CommonsUtils {
// 打印开发级别日志
public static final boolean DEVLOG = BootApplication.devMode();
// 通用分隔符
public static final String COMM_SPLITER = "$$$$";
// 通用分隔符 REGEX

View file

@ -44,7 +44,7 @@ public class JSONUtils {
* @return
*/
public static JSONObject toJSONObject(String[] keys, Object[] values) {
Assert.isTrue(keys.length == values.length, "K/V length mismatch");
Assert.isTrue(values.length >= keys.length, "K/V length mismatch");
Map<String, Object> map = new HashMap<>(keys.length);
for (int i = 0; i < keys.length; i++) {

View file

@ -174,7 +174,7 @@ public class NotificationController extends BaseController {
long time = (mm.getStartTime().getTime() - CalendarUtils.now().getTime()) / 1000;
String note = mm.getNote() == null ? "" : String.format(" (%s)", mm.getNote());
DateFormat df = CalendarUtils.getDateFormat("yyyy-MM-dd HH:mm");
String msg = Language.L("系统将于 %s (%d 分钟后) 进行维护%s预计 %s 完成。在此期间系统将无法使用,请及时保存数据,以免造成数据丢失![]如有重要操作正在进行,请联系系统管理员调整维护时间。",
String msg = Language.L("系统将于 %s (%d 分钟后) 进行维护%s预计 %s 完成。[]维护期间系统无法使用,请及时保存数据。如有重要操作正在进行,请联系系统管理员调整维护时间。",
df.format(mm.getStartTime()), Math.max(time / 60, 1), note, df.format(mm.getEndTime()));
return JSONUtils.toJSONObject(new String[] { "msg", "time" }, new Object[] { msg, time });

View file

@ -2941,5 +2941,15 @@
"选择设备":"选择设备",
"录制":"录制",
"拍摄":"拍摄",
"停止":"停止"
"停止":"停止",
"已有数据内容长度超出限制,无法完成转换":"已有数据内容长度超出限制,无法完成转换",
"确认打开外部网站?":"确认打开外部网站?",
"重复":"重复",
"审批已退回":"审批已退回",
"确认删除 APP 安装包?":"确认删除 APP 安装包?",
"系统将于 %s (%d 分钟后) 进行维护%s预计 %s 完成。[]维护期间系统无法使用,请及时保存数据。如有重要操作正在进行,请联系系统管理员调整维护时间。":"系统将于 %s (%d 分钟后) 进行维护%s预计 %s 完成。[]维护期间系统无法使用,请及时保存数据。如有重要操作正在进行,请联系系统管理员调整维护时间。",
"无法进行合并,因为你对部分记录无操作权限":"无法进行合并,因为你对部分记录无操作权限",
"主":"主",
"附加内容":"附加内容",
"禁用后子部门及其下用户将会同时禁用":"禁用后子部门及其下用户将会同时禁用"
}

View file

@ -104,7 +104,7 @@
<div>
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
<input class="custom-control-input" type="checkbox" id="disabledViewEditable" />
<span class="custom-control-label">[[${bundle.L('禁用详情页单字段编辑')}]] <i class="support-plat2 mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i></span>
<span class="custom-control-label">[[${bundle.L('禁用详情页单字段编辑')}]]</span>
</label>
</div>
<th:block th:if="${currentEntity == mainEntity || detailEntity == null}">

View file

@ -10,6 +10,9 @@
.support-plat2 {
margin-left: -22px;
}
.J_advOpt .custom-control-inline {
margin-bottom: 0.8rem;
}
</style>
</head>
<body>
@ -215,7 +218,7 @@
</div>
</div>
<div th:if="${fieldType == 'IMAGE'}" class="form-group row J_for-IMAGE">
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('仅允许拍照上传')}]] </label>
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('仅允许拍照上传')}]]</label>
<div class="col-md-12 col-xl-6 col-lg-8">
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
<input class="custom-control-input" type="checkbox" id="imageCapture" />
@ -449,28 +452,34 @@
<div class="form-group row hide J_advOpt">
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('高级选项')}]] <sup class="rbv"></sup></label>
<div class="col-md-12 col-xl-6 col-lg-8">
<label class="custom-control custom-control-sm custom-checkbox bosskey-show">
<input class="custom-control-input" type="checkbox" id="fieldQueryable" th:data-o="${fieldQueryable}" />
<span class="custom-control-label">
[[${bundle.L('允许使用')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('不允许使用的字段对普通用户不可见')}"></i>
</span>
</label>
<label class="custom-control custom-control-sm custom-checkbox">
<input class="custom-control-input" type="checkbox" id="textScanCode" th:data-o="${textScanCode}" />
<span class="custom-control-label">
[[${bundle.L('启用扫码')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('仅可在手机版企业微信、钉钉中使用')}"></i>
</span>
</label>
<label class="custom-control custom-control-sm custom-checkbox">
<input class="custom-control-input" type="checkbox" id="advDesensitized" />
<span class="custom-control-label">
[[${bundle.L('信息脱敏')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('可在权限角色中启用“允许查看明文”选项')}"></i>
</span>
</label>
<div>
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline bosskey-show">
<input class="custom-control-input" type="checkbox" id="fieldQueryable" th:data-o="${fieldQueryable}" />
<span class="custom-control-label">
[[${bundle.L('允许使用')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('不允许使用的字段对普通用户不可见')}"></i>
</span>
</label>
</div>
<div>
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
<input class="custom-control-input" type="checkbox" id="textScanCode" th:data-o="${textScanCode}" />
<span class="custom-control-label">
[[${bundle.L('启用扫码')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('仅可在手机版企业微信、钉钉中使用')}"></i>
</span>
</label>
</div>
<div>
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
<input class="custom-control-input" type="checkbox" id="advDesensitized" />
<span class="custom-control-label">
[[${bundle.L('信息脱敏')}]]
<i class="zmdi zmdi-help zicon" data-toggle="tooltip" th:title="${bundle.L('可在权限角色中启用“允许查看明文”选项')}"></i>
</span>
</label>
</div>
<div style="margin-top: 0.25rem">
<input
type="text"
class="form-control form-control-sm"
@ -493,6 +502,7 @@
</div>
</div>
</div>
<div class="form-group row footer">
<div class="col-md-12 col-xl-6 col-lg-8 offset-xl-3 offset-lg-4">
<div th:if="${!fieldBuildin}" class="J_action">

View file

@ -276,8 +276,12 @@ See LICENSE and COMMERCIAL in the project root for license information.
top: 0;
}
.chart-box.ApprovalList .progress-wrap .progress {
height: 26px;
}
.chart-box.ApprovalList .progress-wrap .progress-bar {
min-width: 20px;
min-width: 20%;
}
.chart-box.ApprovalList .progress-wrap .progress-bar:hover {

View file

@ -185,6 +185,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
overflow: auto;
}
.record-merge-table table > thead > tr > th {
border-bottom-color: #aaa;
}
.record-merge-table table th {
min-width: 200px;
}
@ -194,26 +198,62 @@ See LICENSE and COMMERCIAL in the project root for license information.
position: relative;
}
.record-merge-table table td.active {
background-color: #f5f8fd;
background-color: #dee2e6;
.record-merge-table table td > div,
.record-merge-table table th > a {
max-width: 500px;
word-wrap: break-word;
display: inline-block;
}
.record-merge-table table td.sysfield {
cursor: not-allowed;
.record-merge-table table td > div > a::after {
content: ', ';
color: #404040;
}
.record-merge-table table td > div > a:last-child::after {
content: '';
}
.record-merge-table table td > div > a:hover {
color: #4285f4 !important;
}
.record-merge-table table td.active {
border: 1px double #34a853;
background-color: #f5f8fd;
}
.record-merge-table table td.active::after {
font-family: 'Material Design Icons', serif;
content: '\F012C';
position: absolute;
right: 7px;
top: 5px;
right: 6px;
top: 6px;
color: #34a853;
font-size: 1.12rem;
font-size: 1rem;
font-weight: bold;
}
.record-merge-table table tr.bt2 {
border-top: 2px solid #dee2e6;
.record-merge-table table tbody tr:first-child td.active::before {
height: 1px;
line-height: 1;
font-size: 0;
content: '';
position: absolute;
background-color: #34a853;
top: -1px;
left: 0;
width: 100%;
}
.record-merge-table table td.sysfield {
cursor: not-allowed;
}
.record-merge-table table tr.bt2 {
border-top: 2px solid #aaa;
}
.record-merge-table table tr.bt2 label {
margin-top: 2px;
}

View file

@ -20,6 +20,14 @@ body {
padding: 41px 0;
}
.view-body.loading .main-content {
display: none;
}
.view-body.loading .view-header > .header-icon::before {
animation: flash 2s infinite;
}
.tab-container {
margin-top: -8px;
padding-right: 4px;

View file

@ -279,13 +279,18 @@ $(document).ready(() => {
$('button.J_MobileAppPath').on('click', () => $input[0].click())
const $del = $('button.J_MobileAppPath-del').on('click', () => {
$.post(location.href, JSON.stringify({ MobileAppPath: '' }), (res) => {
if (res.error_code === 0) {
$('a.J_MobileAppPath').removeAttr('href').text('')
$del.addClass('hide')
} else {
RbHighbar.error(res.error_msg)
}
RbAlert.create($L('确认删除 APP 安装包'), {
onConfirm: function () {
this.hide()
$.post(location.href, JSON.stringify({ MobileAppPath: '' }), (res) => {
if (res.error_code === 0) {
$('a.J_MobileAppPath').removeAttr('href').text('')
$del.addClass('hide')
} else {
RbHighbar.error(res.error_msg)
}
})
},
})
})

View file

@ -690,7 +690,7 @@ class ApprovalList extends BaseChart {
const s = APPROVAL_STATES[item[0]]
if (!s || s[1] <= 0) return null
const p = ((item[1] * 100) / statsTotal).toFixed(2) + '%'
const p = ((item[1] * 100) / statsTotal).toFixed(1) + '%'
return (
<div
key={s[0]}
@ -800,6 +800,7 @@ class ApprovalList extends BaseChart {
// eslint-disable-next-line react/jsx-no-undef
renderRbcomp(<ApprovalApproveForm id={record} approval={approval} entity={entity} call={close} />, null, function () {
that.__approvalForms[record] = this
that._lastStats = null
})
}
}

View file

@ -342,6 +342,7 @@ const OP_TYPE = {
EVW: $L('本周..'),
EVM: $L('本月..'),
DDD: $L('指定..'),
REP: $L('重复') + ' (LAB)',
}
const OP_NOVALUE = ['NL', 'NT', 'SFU', 'SFB', 'SFD', 'YTA', 'TDA', 'TTA', 'PUW', 'CUW', 'NUW', 'PUM', 'CUM', 'NUM', 'PUQ', 'CUQ', 'NUQ', 'PUY', 'CUY', 'NUY']
const OP_DATE_NOPICKER = [
@ -375,6 +376,7 @@ const OP_DATE_NOPICKER = [
'EVW',
'EVM',
'DDD',
'REP',
]
const REFENTITY_CACHE = {}
const PICKLIST_CACHE = {}
@ -493,6 +495,9 @@ class FilterItem extends React.Component {
op = ['IN', 'NIN']
}
// UNTEST
// if (['TEXT', 'PHONE', 'EMAIL', 'URL', 'DATE', 'PICKLIST', 'CLASSIFICATION'].includes(fieldType)) op.push('REP')
if (this.isApprovalState()) op = ['IN', 'NIN']
else if (this.state.field === VF_ACU) op = ['IN', 'SFU', 'SFB', 'SFT']
else op.push('NL', 'NT')

View file

@ -609,7 +609,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
RbHighbar.error(res.error_msg)
} else {
_alert && _alert.hide(true)
_reload(this, state === 10 ? $L('审批已同意') : $L('审批已驳回'))
_reload(this, state === 10 ? $L('审批已同意') : rejectNode ? $L('审批已退回') : $L('审批已驳回'))
typeof this.props.call === 'function' && this.props.call()
}
})

View file

@ -1842,6 +1842,7 @@ CellRenders.addRender('TAG', function (v, s, k) {
class RecordMerger extends RbModalHandler {
constructor(props) {
super(props)
this.state.keepMain = props.ids[0]
}
render() {
@ -1861,11 +1862,12 @@ class RecordMerger extends RbModalHandler {
idData.map((item, idx) => {
if (idx === 0) return null
return (
<th key={idx} data-id={item[0]}>
<strong>{item[1]}</strong>
<a href={`${rb.baseUrl}/app/redirect?id=${item[0]}&type=newtab`} target="_blank" title={$L('打开')}>
<th key={idx} data-id={item[0]} onClick={() => this.setState({ keepMain: item[0] })}>
<a href={`${rb.baseUrl}/app/redirect?id=${item[0]}&type=newtab`} target="_blank" title={$L('打开')} onClick={(e) => $stopEvent(e)}>
<b className="fs-12">{item[1]}</b>
<i className="icon zmdi zmdi zmdi-open-in-new ml-1" />
</a>
{this.state.keepMain === item[0] && <span className="badge badge-success badge-pill ml-1">{$L('主')}</span>}
</th>
)
})}
@ -1874,15 +1876,26 @@ class RecordMerger extends RbModalHandler {
<tbody ref={(c) => (this._$tbody = c)}>
{datas.map((item, idx) => {
if (idx === 0) return null
if ($isSysMask(item[0][1])) return null
let chk
const data4field = []
for (let i = 1; i < item.length; i++) {
let s = item[i]
let v = item[i]
let activeClazz
if ($empty(item[i])) {
s = <span className="text-muted">{$L('空')}</span>
if ($empty(v)) {
v = <span className="text-muted">{$L('空')}</span>
} else {
if ($.isArray(v)) {
v = v.map(function (item) {
return (
<a key={item} onClick={() => RbPreview.create(item)}>
{$fileCutName(item)}
</a>
)
})
}
activeClazz = 'active'
if (chk) activeClazz = null
if (activeClazz) chk = true
@ -1892,8 +1905,8 @@ class RecordMerger extends RbModalHandler {
if (IS_COMMONS) activeClazz = 'sysfield'
data4field.push(
<td key={`${idx}-${i}`} data-index={i} className={activeClazz} onClick={(e) => !IS_COMMONS && this._chkValue(e)}>
{s}
<td key={`${idx}-${i}`} data-index={i} className={activeClazz} onClick={(e) => !IS_COMMONS && this._selectValue(e)}>
<div>{v}</div>
</td>
)
}
@ -1951,8 +1964,8 @@ class RecordMerger extends RbModalHandler {
})
}
_chkValue(e) {
const $td = $(e.target)
_selectValue(e) {
const $td = $(e.currentTarget)
$td.parent().find('td').removeClass('active')
$td.addClass('active')
}
@ -1991,7 +2004,6 @@ class RecordMerger extends RbModalHandler {
merged[field] = id || null
}
})
console.log(merged)
const details = []
$(this._$mergeDetails)
@ -1999,15 +2011,26 @@ class RecordMerger extends RbModalHandler {
.each(function () {
details.push($(this).val())
})
const url = `/app/${this.props.entity}/record-merge/merge?ids=${this.props.ids.join(',')}&deleteAfter=${del || false}&mergeDetails=${details.join(',')}`
// 主第一排重
let ids = [this.state.keepMain]
this.props.ids.forEach(function (id) {
if (!ids.includes(id)) ids.push(id)
})
const url = `/app/${this.props.entity}/record-merge/merge?ids=${ids.join(',')}&deleteAfter=${del || false}&mergeDetails=${details.join(',')}`
const $btn = $(this._$btn).find('.btn').button('loading')
$.post(url, JSON.stringify(merged), (res) => {
if (res.error_code === 0) {
this.hide()
RbHighbar.success($L('合并成功'))
this.props.listRef.reload()
setTimeout(() => {
CellRenders.clickView({ id: res.data, entity: this.props.entity })
window.RbViewModal.create({ id: res.data, entity: this.props.entity })
if (window.RbListPage) {
location.hash = `!/View/${this.props.entity}/${res.data}`
}
}, 500)
} else {
RbHighbar.error(res.error_msg)

View file

@ -736,18 +736,13 @@ class RepeatedViewer extends RbModalHandler {
return <td key={`col-${idx}-${i}`}>{o || <span className="text-muted">{$L('无')}</span>}</td>
})}
<td className="actions">
<button type="button" className="btn btn-light btn-sm w-auto" onClick={() => this.openView(item[0])} title={$L('查看详情')}>
<a className="btn btn-light btn-sm w-auto" style={{ lineHeight: '28px' }} title={$L('打开')} href={`${rb.baseUrl}/app/redirect?id=${item[0]}&type=newtab`} target="_blank">
<i className="zmdi zmdi-open-in-new fs-16 down-2" />
</button>
</a>
</td>
</tr>
)
}
openView(id) {
if (window.RbViewModal) window.RbViewModal.create({ id: id, entity: this.props.entity })
else window.open(`${rb.baseUrl}/app/entity/view?id=${id}`)
}
}
// -- LiteForm

View file

@ -112,7 +112,7 @@ class RbViewForm extends React.Component {
if (res.error_code === 0) {
if (res.data.lastModified !== this.__lastModified) {
handle && handle.showLoading()
setTimeout(() => location.reload(), window.VIEW_LOAD_DELAY || 200)
setTimeout(() => location.reload(), 200)
}
} else if (res.error_msg === 'NO_EXISTS') {
this.renderViewError($L('记录已经不存在可能已被其他用户删除'))
@ -594,6 +594,7 @@ const RbViewPage = {
renderRbcomp(<RbViewForm entity={entity[0]} id={id} onViewEditable={ep && ep.U} />, 'tab-rbview', function () {
RbViewPage._RbViewForm = this
setTimeout(() => $('.view-body.loading').removeClass('loading'), 100)
})
$('.J_close').on('click', () => this.hide())

View file

@ -44,7 +44,6 @@ $(document).ready(function () {
// 正则
if (SHOW_ADVPATTERN.includes(dt)) {
$('.J_advOpt').removeClass('hide')
$('.J_advPattern .badge').on('click', function () {
$('#advPattern').val($(this).data('patt'))
})
@ -337,7 +336,9 @@ const _handlePicklist = function (dt) {
}
$('#picklist-items').empty()
$(res.data).each(function () {
const $item = $(`<li class="dd-item" data-key="${this.id}"><div class="dd-handle" style="color:${this.color || 'inherit'} !important">${this.text}</div></li>`).appendTo('#picklist-items')
const $item = $(`<li class="dd-item" data-key="${this.mask || this.id}"><div class="dd-handle" style="color:${this.color || 'inherit'} !important">${this.text}</div></li>`).appendTo(
'#picklist-items'
)
if ($isTrue(this['default'])) $item.addClass('default')
})
if (res.data.length > 5) $('#picklist-items').parent().removeClass('autoh')
@ -754,9 +755,7 @@ const __TYPE2TYPE = {
'PHONE': ['TEXT'],
'EMAIL': ['TEXT'],
'URL': ['TEXT'],
'NTEXT': ['TEXT'],
'IMAGE': ['FILE'],
'FILE': ['IMAGE'],
}
class FieldTypeCast extends RbFormHandler {
render() {

View file

@ -477,7 +477,7 @@ var _showStateMM = function (mm) {
RbGritter.create(WrapHtml($mm.prop('outerHTML')), {
timeout: (mm.time + 60) * 1000,
type: 'danger',
icon: 'mdi-server-network',
icon: 'mdi-server-off',
onCancel: function () {
var expires = moment()
.add(Math.min(mm.time - 30, 300), 'seconds')

View file

@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/view-page.css}" />
<title th:text="${entityLabel}"></title>
</head>
<body class="view-body">
<body class="view-body loading">
<div class="view-header">
<i class="header-icon zmdi" th:classappend="|zmdi-${entityIcon}|"></i>
<h3 class="title" th:text="${bundle.L('%s详情', entityLabel)}"></h3>

View file

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/view-page.css}" />
<title th:text="${entityLabel}"></title>
</head>
<body class="view-body">
<body class="view-body loading">
<div class="view-header">
<i class="header-icon zmdi" th:classappend="|zmdi-${entityIcon}|"></i>
<h3 class="title" th:text="${bundle.L('%s详情', entityLabel)}"></h3>

View file

@ -103,4 +103,15 @@ public class AdvFilterParserTest extends TestSupport {
items.add(JSON.parseObject("{ op:'FUD', field:'datetime', value:'1' }"));
System.out.println(new AdvFilterParser(filterExp).toSqlWhere());
}
@Test
void testRep() {
JSONObject filterExp = new JSONObject();
filterExp.put("entity", TestAllFields);
JSONArray items = new JSONArray();
filterExp.put("items", items);
items.add(JSON.parseObject("{ op:'REP', field:'TestAllFieldsName', value:2 }"));
System.out.println(new AdvFilterParser(filterExp).toSqlWhere());
}
}