mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Better v3.0 (#506)
* N2NReferenceSupport * better tips * fix: triggers * fix: AutoApproval trigger * entity import * Update @rbv * fix: #504 * print size #505
This commit is contained in:
parent
68c9cc505f
commit
ead5219ece
|
@ -17,9 +17,9 @@ git:
|
|||
submodules: false
|
||||
|
||||
before_script:
|
||||
- mysql -e "CREATE DATABASE rebuild20 COLLATE utf8mb4_general_ci;"
|
||||
- mysql -e "CREATE USER 'rebuild'@'127.0.0.1' IDENTIFIED BY 'rebuild'; GRANT ALL PRIVILEGES ON rebuild20.* TO 'rebuild'@'127.0.0.1'; FLUSH PRIVILEGES;"
|
||||
- mysql -D rebuild20 < src/main/resources/scripts/db-init.sql
|
||||
- mysql -e "CREATE DATABASE rebuild30 COLLATE utf8mb4_general_ci;"
|
||||
- mysql -e "CREATE USER 'rebuild'@'127.0.0.1' IDENTIFIED BY 'rebuild'; GRANT ALL PRIVILEGES ON rebuild30.* TO 'rebuild'@'127.0.0.1'; FLUSH PRIVILEGES;"
|
||||
- mysql -D rebuild30 < src/main/resources/scripts/db-init.sql
|
||||
|
||||
# codecov
|
||||
after_success:
|
||||
|
|
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 263dc325c7903bb3115ba30a9032cf182ee7e4f9
|
||||
Subproject commit 5c05ac9fdc812ca7e218732dbaf553605d0ffb5a
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
<artifactId>rebuild</artifactId>
|
||||
<version>2.10.0-dev</version>
|
||||
<version>3.0.0-dev</version>
|
||||
<name>rebuild</name>
|
||||
<description>Building your business-systems freely!</description>
|
||||
<!-- UNCOMMENT USE TOMCAT -->
|
||||
|
|
|
@ -65,11 +65,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Version
|
||||
*/
|
||||
public static final String VER = "2.10.0-dev";
|
||||
public static final String VER = "3.0.0-dev";
|
||||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{1}
|
||||
*/
|
||||
public static final int BUILD = 210000;
|
||||
public static final int BUILD = 300000;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
|
|
@ -107,7 +107,7 @@ public class BootEnvironmentPostProcessor implements EnvironmentPostProcessor, I
|
|||
|
||||
// `application-bean.xml` 占位符必填
|
||||
if (dbUrl == null) {
|
||||
dbUrl = "jdbc:mysql://127.0.0.1:3306/rebuild20?characterEncoding=UTF8";
|
||||
dbUrl = "jdbc:mysql://127.0.0.1:3306/rebuild30?characterEncoding=UTF8";
|
||||
confPs.put("db.url", dbUrl);
|
||||
}
|
||||
if (env.getProperty("db.user") == null) confPs.put("db.user", "rebuild");
|
||||
|
|
|
@ -29,22 +29,20 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分类数据
|
||||
* 列表字段分类数据
|
||||
*
|
||||
* @author ZHAO
|
||||
* @since 07/23/2022
|
||||
*/
|
||||
public class DataListClass {
|
||||
public class DataListCategory {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param entity
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
public static JSON datas(Entity entity, ID user) {
|
||||
final Field classField = getFieldOfClass(entity);
|
||||
final Field classField = getFieldOfCategory(entity);
|
||||
if (classField == null) return null;
|
||||
|
||||
final String ckey = String.format("DLC1.%s.%s", entity.getName(), classField.getName());
|
||||
|
@ -107,9 +105,9 @@ public class DataListClass {
|
|||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Field getFieldOfClass(Entity entity) {
|
||||
String classField = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCLASS);
|
||||
if (StringUtils.isBlank(classField) || !entity.containsField(classField)) return null;
|
||||
return entity.getField(classField);
|
||||
public static Field getFieldOfCategory(Entity entity) {
|
||||
String categoryField = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCATEGORY);
|
||||
if (StringUtils.isBlank(categoryField) || !entity.containsField(categoryField)) return null;
|
||||
return entity.getField(categoryField);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import cn.devezhao.commons.ObjectUtils;
|
|||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.PersistManagerFactory;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
@ -18,8 +19,6 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.UserContextHolder;
|
||||
import com.rebuild.core.configuration.BaseConfigurationService;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.privileges.AdminGuard;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -89,7 +88,7 @@ public class PickListService extends BaseConfigurationService implements AdminGu
|
|||
|
||||
// MultiSelect 专用
|
||||
long nextMaskValue = 0;
|
||||
if (EasyMetaFactory.getDisplayType(field) == DisplayType.MULTISELECT) {
|
||||
if (field.getType() == FieldType.LONG) {
|
||||
Object[] max = Application.createQueryNoFilter(
|
||||
"select max(maskValue) from PickList where belongEntity = ? and belongField = ?")
|
||||
.setParameter(1, field.getOwnEntity().getName())
|
||||
|
|
|
@ -47,7 +47,7 @@ public class CopyEntity extends Entity2Schema {
|
|||
|
||||
// 导出
|
||||
|
||||
JSONObject schemadata = (JSONObject) new MetaSchemaGenerator(sourceEntity).generate();
|
||||
JSONObject schemadata = (JSONObject) new MetaSchemaGenerator(sourceEntity, false).generate();
|
||||
String uniqueEntityName = clearConfig(schemadata, entityName);
|
||||
|
||||
JSONObject detailSchema = schemadata.getJSONObject("detail");
|
||||
|
@ -74,10 +74,6 @@ public class CopyEntity extends Entity2Schema {
|
|||
schema.remove(MetaSchemaGenerator.CFG_TRIGGERS);
|
||||
schema.remove(MetaSchemaGenerator.CFG_FILTERS);
|
||||
|
||||
// 以下保留
|
||||
// schema.remove(MetaSchemaGenerator.CFG_FILLINS);
|
||||
// schema.remove(MetaSchemaGenerator.CFG_LAYOUTS);
|
||||
|
||||
String uniqueEntityName = toPinyinName(entityName);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (MetadataHelper.containsEntity(uniqueEntityName)) {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class EasyEntityConfigProps {
|
|||
/**
|
||||
* 列表分类
|
||||
*/
|
||||
public static final String ADV_LIST_SHOWCLASS = "advListShowClass";
|
||||
public static final String ADV_LIST_SHOWCATEGORY = "advListShowCategory";
|
||||
/**
|
||||
* 列表查询面板
|
||||
*/
|
||||
|
|
|
@ -17,6 +17,7 @@ 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.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.service.CommonsService;
|
||||
|
@ -26,7 +27,6 @@ import com.rebuild.core.support.i18n.Language;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -111,8 +111,9 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
|||
// 跳过
|
||||
ID skipGuardId;
|
||||
if ((skipGuardId = PrivilegesGuardContextHolder.getSkipGuardOnce()) != null) {
|
||||
log.info("Allow no permission({}) passed once : {}",
|
||||
action.getName(), ObjectUtils.defaultIfNull(recordId, skipGuardId));
|
||||
if (!EntityHelper.isUnsavedId(skipGuardId)) {
|
||||
log.info("Allow no permission({}) passed once : {}", action.getName(), skipGuardId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ import java.io.IOException;
|
|||
*/
|
||||
public class MetaSchemaGenerator {
|
||||
|
||||
// 保持 ID
|
||||
public static final String KEEP_ID = "_id";
|
||||
|
||||
public static final String CFG_FILLINS = "fillins";
|
||||
public static final String CFG_LAYOUTS = "layouts";
|
||||
public static final String CFG_FILTERS = "filters";
|
||||
|
@ -46,12 +49,14 @@ public class MetaSchemaGenerator {
|
|||
public static final String CFG_TRANSFORMS = "transforms";
|
||||
|
||||
final private Entity mainEntity;
|
||||
final private boolean keepId;
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
*/
|
||||
public MetaSchemaGenerator(Entity entity) {
|
||||
public MetaSchemaGenerator(Entity entity, boolean keepId) {
|
||||
this.mainEntity = entity;
|
||||
this.keepId = keepId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,12 +114,12 @@ public class MetaSchemaGenerator {
|
|||
schemaEntity.put(CFG_FILTERS, performFilters(entity));
|
||||
// 触发器
|
||||
schemaEntity.put(CFG_TRIGGERS, performTriggers(entity));
|
||||
// 记录转换
|
||||
schemaEntity.put(CFG_TRANSFORMS, performTransforms(entity));
|
||||
|
||||
if (!detail) {
|
||||
// 审批流程
|
||||
schemaEntity.put(CFG_APPROVALS, performApprovals(entity));
|
||||
// 字段转换
|
||||
schemaEntity.put(CFG_TRANSFORMS, performTransforms(entity));
|
||||
}
|
||||
|
||||
// TODO 报表模板?
|
||||
|
@ -163,7 +168,8 @@ public class MetaSchemaGenerator {
|
|||
JSONArray items = new JSONArray();
|
||||
for (ConfigBean e : entries) {
|
||||
items.add(new Object[] {
|
||||
e.getString("text"), e.getBoolean("default"), e.getLong("mask"), e.getString("color")
|
||||
e.getString("text"), e.getBoolean("default"), e.getLong("mask"),
|
||||
e.getString("color"), this.keepId ? e.getID("id") : null
|
||||
});
|
||||
}
|
||||
return items;
|
||||
|
@ -267,7 +273,7 @@ public class MetaSchemaGenerator {
|
|||
|
||||
private JSON performTransforms(Entity entity) {
|
||||
Object[][] array = Application.createQueryNoFilter(
|
||||
"select targetEntity,name,config from TransformConfig where belongEntity = ? and isDisabled = 'F'")
|
||||
"select targetEntity,name,config,configId from TransformConfig where belongEntity = ? and isDisabled = 'F'")
|
||||
.setParameter(1, entity.getName())
|
||||
.array();
|
||||
|
||||
|
@ -276,9 +282,11 @@ public class MetaSchemaGenerator {
|
|||
JSON mappingConfig = parseJSON(o[2]);
|
||||
if (mappingConfig == null) continue;
|
||||
|
||||
JSON config = JSONUtils.toJSONObject(
|
||||
JSONObject config = JSONUtils.toJSONObject(
|
||||
new String[] { "targetEntity", "name", "config" },
|
||||
new Object[] { o[0], o[1], mappingConfig });
|
||||
if (this.keepId) config.put(KEEP_ID, o[3]);
|
||||
|
||||
transforms.add(config);
|
||||
}
|
||||
return transforms;
|
||||
|
|
|
@ -11,6 +11,7 @@ import cn.devezhao.persist4j.Entity;
|
|||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.engine.PersistManagerImpl;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
@ -29,8 +30,6 @@ import com.rebuild.core.metadata.impl.Field2Schema;
|
|||
import com.rebuild.core.metadata.impl.MetadataModificationException;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.approval.RobotApprovalConfigService;
|
||||
import com.rebuild.core.service.trigger.ActionFactory;
|
||||
import com.rebuild.core.service.trigger.ActionType;
|
||||
import com.rebuild.core.service.trigger.RobotTriggerConfigService;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.task.HeavyTask;
|
||||
|
@ -42,6 +41,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.rebuild.core.rbstore.MetaSchemaGenerator.KEEP_ID;
|
||||
|
||||
/**
|
||||
* 元数据模型导入
|
||||
*
|
||||
|
@ -147,14 +148,32 @@ public class MetaschemaImporter extends HeavyTask<String> {
|
|||
try {
|
||||
for (Map.Entry<Field, JSONObject> e : picklistHolders.entrySet()) {
|
||||
Field field = e.getKey();
|
||||
Application.getBean(PickListService.class).updateBatch(
|
||||
MetadataHelper.getField(field.getOwnEntity().getName(), field.getName()), e.getValue());
|
||||
JSONObject config = e.getValue();
|
||||
|
||||
JSONArray options = config.getJSONArray("show");
|
||||
for (Object o : options) {
|
||||
String keepId = ((JSONObject) o).getString(KEEP_ID);
|
||||
if (ID.isId(keepId)) {
|
||||
Record c = EntityHelper.forNew(EntityHelper.PickList, UserService.SYSTEM_USER, true);
|
||||
c.setString("belongEntity", field.getOwnEntity().getName());
|
||||
c.setString("belongField", field.getName());
|
||||
c.setString("text", "temp");
|
||||
|
||||
((PersistManagerImpl) Application.getPersistManagerFactory().createPersistManager())
|
||||
.saveInternal(c, ID.valueOf(keepId));
|
||||
((JSONObject) o).put("id", keepId);
|
||||
}
|
||||
}
|
||||
|
||||
Application.getBean(PickListService.class).updateBatch(field, config);
|
||||
}
|
||||
} finally {
|
||||
if (sessionUser == null) UserContextHolder.clear();
|
||||
} catch (Exception ex) {
|
||||
log.warn("Importing PickList error : {}", ex.getLocalizedMessage());
|
||||
}
|
||||
|
||||
setCompleted(100);
|
||||
|
||||
if (sessionUser == null) UserContextHolder.clear();
|
||||
return entityName;
|
||||
}
|
||||
|
||||
|
@ -325,10 +344,10 @@ public class MetaschemaImporter extends HeavyTask<String> {
|
|||
if (item.size() > 2) {
|
||||
option.put("mask", item.getLongValue(2));
|
||||
}
|
||||
// v2.10: Color
|
||||
if (item.size() > 3) {
|
||||
option.put("color", item.getString(3));
|
||||
}
|
||||
|
||||
// v2.10: Color, Id
|
||||
if (item.size() > 3) option.put("color", item.getString(3));
|
||||
if (item.size() > 4) option.put(KEEP_ID, item.getString(4));
|
||||
|
||||
shown.add(option);
|
||||
}
|
||||
|
@ -367,25 +386,11 @@ public class MetaschemaImporter extends HeavyTask<String> {
|
|||
Application.getBean(AdvFilterService.class).create(record);
|
||||
}
|
||||
|
||||
private void performTrigger(String entity,JSONObject config) {
|
||||
private void performTrigger(String entity, JSONObject config) {
|
||||
Entity configEntity = MetadataHelper.getEntity(EntityHelper.RobotTriggerConfig);
|
||||
config.put("metadata", JSONUtils.toJSONObject("entity", configEntity.getName()));
|
||||
config.put("belongEntity", entity);
|
||||
|
||||
String actionType = config.getString("actionType");
|
||||
boolean available = false;
|
||||
for (ActionType type : ActionFactory.getAvailableActions()) {
|
||||
if (type.name().equalsIgnoreCase(actionType)) {
|
||||
available = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!available) {
|
||||
log.warn("Trigger `{}` unavailable", actionType);
|
||||
return;
|
||||
}
|
||||
|
||||
Record record = new EntityRecordCreator(configEntity, config, getUser())
|
||||
.create();
|
||||
Application.getBean(RobotTriggerConfigService.class).create(record);
|
||||
|
@ -408,6 +413,15 @@ public class MetaschemaImporter extends HeavyTask<String> {
|
|||
|
||||
Record record = new EntityRecordCreator(configEntity, config, getUser())
|
||||
.create();
|
||||
Application.getBean(TransformConfigService.class).create(record);
|
||||
String keepId = config.getString(KEEP_ID);
|
||||
if (ID.isId(keepId)) {
|
||||
((PersistManagerImpl) Application.getPersistManagerFactory().createPersistManager())
|
||||
.saveInternal(record, ID.valueOf(keepId));
|
||||
record = EntityHelper.forUpdate(record.getPrimary(), UserService.SYSTEM_USER, false);
|
||||
|
||||
Application.getBean(TransformConfigService.class).update(record);
|
||||
} else {
|
||||
Application.getBean(TransformConfigService.class).create(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class RBStore {
|
|||
*/
|
||||
public static JSON fetchMetaschema(String fileUri) {
|
||||
return fetchRemoteJson("metaschemas/" +
|
||||
StringUtils.defaultIfBlank(fileUri, "index-3.0.json"));
|
||||
StringUtils.defaultIfBlank(fileUri, "index-4.0.json"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -235,10 +235,10 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
|||
sorts.add(dim.getSqlName() + " " + fs.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
// NOTE 优先维度排序
|
||||
if (!sorts.isEmpty()) {
|
||||
return String.join(", ", sorts);
|
||||
}
|
||||
// // NOTE 优先维度排序
|
||||
// if (!sorts.isEmpty()) {
|
||||
// return String.join(", ", sorts);
|
||||
// }
|
||||
|
||||
for (Numerical num : getNumericals()) {
|
||||
FormatSort fs = num.getFormatSort();
|
||||
|
|
|
@ -35,7 +35,6 @@ import com.rebuild.core.service.notification.NotificationObserver;
|
|||
import com.rebuild.core.service.query.QueryHelper;
|
||||
import com.rebuild.core.service.trigger.*;
|
||||
import com.rebuild.core.service.trigger.impl.AutoApproval;
|
||||
import com.rebuild.core.service.trigger.impl.GroupAggregation;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.task.TaskExecutors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -51,7 +50,7 @@ import java.util.*;
|
|||
* <br>- 会带有系统设置规则的执行
|
||||
* <br>- 会开启一个事务,详见 <tt>application-bean.xml</tt> 配置
|
||||
*
|
||||
* 如有需要,其他实体可根据自身业务继承并复写
|
||||
* <p>如有需要,其他实体可根据自身业务继承并复写</p>
|
||||
*
|
||||
* FIXME 删除主记录时会关联删除明细记录(持久层实现),但明细记录不会触发业务规则
|
||||
*
|
||||
|
@ -99,13 +98,14 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
// 含明细
|
||||
final boolean hasDetails = details != null && !details.isEmpty();
|
||||
|
||||
boolean hasAutoApprovalForDetails = false;
|
||||
boolean lazyAutoApprovalForDetails = false;
|
||||
if (hasDetails) {
|
||||
Entity de = record.getEntity().getDetailEntity();
|
||||
TriggerAction[] hasTriggers = de == null ? null
|
||||
: RobotTriggerManager.instance.getActions(de, TriggerWhen.APPROVED);
|
||||
hasAutoApprovalForDetails = hasTriggers != null && hasTriggers.length > 0;
|
||||
AutoApproval.setLazyAutoApproval();
|
||||
TriggerAction[] hasTriggers = getSpecTriggers(
|
||||
record.getEntity().getDetailEntity(), null, TriggerWhen.APPROVED);
|
||||
lazyAutoApprovalForDetails = hasTriggers.length > 0;
|
||||
|
||||
// 自动审批延迟执行,因为明细尚未保存好
|
||||
if (lazyAutoApprovalForDetails) AutoApproval.setLazyAutoApproval();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -149,7 +149,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
return record;
|
||||
|
||||
} finally {
|
||||
if (hasAutoApprovalForDetails) {
|
||||
if (lazyAutoApprovalForDetails) {
|
||||
AutoApproval.executeLazyAutoApproval();
|
||||
}
|
||||
}
|
||||
|
@ -173,26 +173,16 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
record = super.update(record);
|
||||
|
||||
// 主记录修改时传导给明细(若有),以便触发分组聚合触发器
|
||||
Entity de = record.getEntity().getDetailEntity();
|
||||
if (de != null) {
|
||||
TriggerAction[] hasTriggers = RobotTriggerManager.instance.getActions(de, TriggerWhen.UPDATE);
|
||||
boolean hasGroupAggregation = false;
|
||||
for (TriggerAction ta : hasTriggers) {
|
||||
if (ta instanceof GroupAggregation) {
|
||||
hasGroupAggregation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TriggerAction[] hasTriggers = getSpecTriggers(record.getEntity().getDetailEntity(),
|
||||
ActionType.GROUPAGGREGATION, TriggerWhen.UPDATE);
|
||||
if (hasTriggers.length > 0) {
|
||||
RobotTriggerManual triggerManual = new RobotTriggerManual();
|
||||
ID opUser = UserService.SYSTEM_USER;
|
||||
|
||||
if (hasGroupAggregation) {
|
||||
RobotTriggerManual triggerManual = new RobotTriggerManual();
|
||||
ID opUser = UserService.SYSTEM_USER;
|
||||
|
||||
for (ID did : QueryHelper.detailIdsNoFilter(record.getPrimary(), 1)) {
|
||||
Record dUpdate = EntityHelper.forUpdate(did, opUser, false);
|
||||
triggerManual.onUpdate(
|
||||
OperatingContext.create(opUser, BizzPermission.UPDATE, dUpdate, dUpdate));
|
||||
}
|
||||
for (ID did : QueryHelper.detailIdsNoFilter(record.getPrimary(), 1)) {
|
||||
Record dUpdate = EntityHelper.forUpdate(did, opUser, false);
|
||||
triggerManual.onUpdate(
|
||||
OperatingContext.create(opUser, BizzPermission.UPDATE, dUpdate, dUpdate));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -723,14 +713,19 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
// 传导给明细(若有)
|
||||
// FIXME 此时明细可能尚未做好变更(例如新建自动审批)
|
||||
|
||||
Entity de = approvalRecord.getEntity().getDetailEntity();
|
||||
TriggerAction[] hasTriggers = de == null ? null : RobotTriggerManager.instance.getActions(de,
|
||||
TriggerAction[] hasTriggers = getSpecTriggers(approvalRecord.getEntity().getDetailEntity(), null,
|
||||
state == ApprovalState.APPROVED ? TriggerWhen.APPROVED : TriggerWhen.REVOKED);
|
||||
if (hasTriggers != null && hasTriggers.length > 0) {
|
||||
if (hasTriggers.length > 0) {
|
||||
for (ID did : QueryHelper.detailIdsNoFilter(recordId, 0)) {
|
||||
Record dAfter = EntityHelper.forUpdate(did, approvalUser, false);
|
||||
triggerManual.onApproved(
|
||||
OperatingContext.create(approvalUser, BizzPermission.UPDATE, null, dAfter));
|
||||
|
||||
if (state == ApprovalState.REVOKED) {
|
||||
triggerManual.onRevoked(
|
||||
OperatingContext.create(approvalUser, BizzPermission.UPDATE, null, dAfter));
|
||||
} else {
|
||||
triggerManual.onApproved(
|
||||
OperatingContext.create(approvalUser, BizzPermission.UPDATE, null, dAfter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -749,4 +744,18 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
new RevisionHistoryObserver().onApprovalManual(
|
||||
OperatingContext.create(approvalUser, InternalPermission.APPROVAL, before, approvalRecord));
|
||||
}
|
||||
|
||||
// 获取指定的触发器
|
||||
private TriggerAction[] getSpecTriggers(Entity entity, ActionType specType, TriggerWhen... when) {
|
||||
if (entity == null || when.length == 0) return new TriggerAction[0];
|
||||
|
||||
TriggerAction[] triggers = RobotTriggerManager.instance.getActions(entity, when);
|
||||
if (triggers.length == 0 || specType == null) return triggers;
|
||||
|
||||
List<TriggerAction> specTriggers = new ArrayList<>();
|
||||
for (TriggerAction t : triggers) {
|
||||
if (t.getType() == specType) specTriggers.add(t);
|
||||
}
|
||||
return specTriggers.toArray(new TriggerAction[0]);
|
||||
}
|
||||
}
|
|
@ -23,13 +23,12 @@ import com.rebuild.core.metadata.MetadataHelper;
|
|||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
||||
import com.rebuild.core.service.TransactionManual;
|
||||
import com.rebuild.core.service.general.GeneralEntityService;
|
||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||
import com.rebuild.core.service.query.FilterRecordChecker;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -49,6 +48,9 @@ public class RecordTransfomer extends SetUser {
|
|||
final private JSONObject transConfig;
|
||||
final private boolean skipGuard;
|
||||
|
||||
// 所有新建的记录
|
||||
private List<ID> newIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* @param trnasid
|
||||
*/
|
||||
|
@ -70,6 +72,13 @@ public class RecordTransfomer extends SetUser {
|
|||
this.skipGuard = skipGuard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public List<ID> getNewIds() {
|
||||
return newIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sourceRecordId
|
||||
* @return
|
||||
|
@ -95,61 +104,83 @@ public class RecordTransfomer extends SetUser {
|
|||
* @see #checkFilter(ID)
|
||||
*/
|
||||
public ID transform(ID sourceRecordId, ID mainId) {
|
||||
// 手动事务,因为可能要转换多条记录
|
||||
TransactionStatus tx = TransactionManual.newTransaction();
|
||||
// 检查配置
|
||||
Entity sourceEntity = MetadataHelper.getEntity(sourceRecordId.getEntityCode());
|
||||
Entity sourceDetailEntity = null;
|
||||
|
||||
// 主
|
||||
JSONObject fieldsMapping = transConfig.getJSONObject("fieldsMapping");
|
||||
if (fieldsMapping == null || fieldsMapping.isEmpty()) {
|
||||
throw new ConfigurationException("Invalid config of transform : " + transConfig);
|
||||
}
|
||||
|
||||
// 明细
|
||||
JSONObject fieldsMappingDetail = transConfig.getJSONObject("fieldsMappingDetail");
|
||||
Object[][] sourceDetails = null;
|
||||
if (fieldsMappingDetail != null && !fieldsMappingDetail.isEmpty()) {
|
||||
sourceDetailEntity = sourceEntity.getDetailEntity();
|
||||
Field sourceRefField;
|
||||
|
||||
// v2.10 1 > 2(主+明细)
|
||||
if (sourceDetailEntity == null) {
|
||||
sourceDetailEntity = sourceEntity;
|
||||
sourceRefField = sourceDetailEntity.getPrimaryField();
|
||||
} else {
|
||||
sourceRefField = MetadataHelper.getDetailToMainField(sourceDetailEntity);
|
||||
}
|
||||
|
||||
String sql = String.format(
|
||||
"select %s from %s where %s = '%s'",
|
||||
sourceDetailEntity.getPrimaryField().getName(), sourceDetailEntity.getName(), sourceRefField.getName(), sourceRecordId);
|
||||
sourceDetails = Application.createQueryNoFilter(sql).array();
|
||||
}
|
||||
|
||||
Map<String, Object> dvMap = null;
|
||||
if (mainId != null) {
|
||||
Field targetDtf = MetadataHelper.getDetailToMainField(targetEntity);
|
||||
dvMap = Collections.singletonMap(targetDtf.getName(), mainId);
|
||||
}
|
||||
|
||||
Record main = transformRecord(sourceEntity, targetEntity, fieldsMapping, sourceRecordId, dvMap);
|
||||
ID newId;
|
||||
|
||||
// 有多条(主+明细)
|
||||
if (sourceDetails != null && sourceDetails.length > 0) {
|
||||
Entity targetDetailEntity = targetEntity.getDetailEntity();
|
||||
List<Record> detailsList = new ArrayList<>();
|
||||
for (Object[] d : sourceDetails) {
|
||||
detailsList.add(transformRecord(sourceDetailEntity, targetDetailEntity, fieldsMappingDetail, (ID) d[0], null));
|
||||
}
|
||||
|
||||
newId = saveRecord(main, detailsList);
|
||||
} else {
|
||||
newId = saveRecord(main, null);
|
||||
}
|
||||
|
||||
// 回填
|
||||
fillback(sourceRecordId, newId);
|
||||
|
||||
return newId;
|
||||
}
|
||||
|
||||
private ID saveRecord(Record record, List<Record> detailsList) {
|
||||
if (this.skipGuard) {
|
||||
PrivilegesGuardContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
||||
}
|
||||
|
||||
if (detailsList != null && !detailsList.isEmpty()) {
|
||||
record.setObjectValue(GeneralEntityService.HAS_DETAILS, detailsList);
|
||||
GeneralEntityServiceContextHolder.setRepeatedCheckMode(GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS);
|
||||
} else {
|
||||
GeneralEntityServiceContextHolder.setRepeatedCheckMode(GeneralEntityServiceContextHolder.RCM_CHECK_ALL);
|
||||
}
|
||||
|
||||
try {
|
||||
// 主记录
|
||||
|
||||
Map<String, Object> map = null;
|
||||
if (mainId != null) {
|
||||
Field targetDtf = MetadataHelper.getDetailToMainField(targetEntity);
|
||||
map = Collections.singletonMap(targetDtf.getName(), mainId);
|
||||
}
|
||||
|
||||
JSONObject fieldsMapping = transConfig.getJSONObject("fieldsMapping");
|
||||
if (fieldsMapping == null || fieldsMapping.isEmpty()) {
|
||||
throw new ConfigurationException("Invalid config of transform : " + transConfig);
|
||||
}
|
||||
|
||||
final Entity sourceEntity = MetadataHelper.getEntity(sourceRecordId.getEntityCode());
|
||||
final ID newId = saveRecord(sourceEntity, targetEntity, fieldsMapping, sourceRecordId, map);
|
||||
if (newId == null) {
|
||||
throw new ConfigurationException("Cannot transform record of main : " + transConfig);
|
||||
}
|
||||
|
||||
// 明细记录(如有)
|
||||
|
||||
JSONObject fieldsMappingDetail = transConfig.getJSONObject("fieldsMappingDetail");
|
||||
if (fieldsMappingDetail != null && !fieldsMappingDetail.isEmpty()) {
|
||||
Entity sourceDetailEntity = sourceEntity.getDetailEntity();
|
||||
Field sourceDtf = MetadataHelper.getDetailToMainField(sourceDetailEntity);
|
||||
|
||||
String sql = String.format(
|
||||
"select %s from %s where %s = '%s'",
|
||||
sourceDetailEntity.getPrimaryField().getName(), sourceDetailEntity.getName(), sourceDtf.getName(), sourceRecordId);
|
||||
Object[][] details = Application.createQueryNoFilter(sql).array();
|
||||
|
||||
Entity targetDetailEntity = targetEntity.getDetailEntity();
|
||||
if (details.length > 0) {
|
||||
Field targetDtf = MetadataHelper.getDetailToMainField(targetDetailEntity);
|
||||
map = Collections.singletonMap(targetDtf.getName(), newId);
|
||||
}
|
||||
|
||||
for (Object[] o : details) {
|
||||
saveRecord(sourceDetailEntity, targetDetailEntity, fieldsMappingDetail, (ID) o[0], map);
|
||||
}
|
||||
}
|
||||
|
||||
// 回填
|
||||
fillback(sourceRecordId, newId);
|
||||
|
||||
TransactionManual.commit(tx);
|
||||
return newId;
|
||||
|
||||
} catch (Exception ex) {
|
||||
TransactionManual.rollback(tx);
|
||||
throw ex;
|
||||
record = Application.getEntityService(targetEntity.getEntityCode()).createOrUpdate(record);
|
||||
return record.getPrimary();
|
||||
} finally {
|
||||
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
||||
if (this.skipGuard) PrivilegesGuardContextHolder.getSkipGuardOnce();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,24 +224,19 @@ public class RecordTransfomer extends SetUser {
|
|||
return true;
|
||||
}
|
||||
|
||||
private ID saveRecord(
|
||||
Entity sourceEntity, Entity targetEntity, JSONObject fieldsMapping,
|
||||
ID sourceRecordId, Map<String, Object> defaultValue) {
|
||||
return (ID) transformRecord(sourceEntity, targetEntity, fieldsMapping, sourceRecordId, defaultValue, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
*
|
||||
* @param sourceEntity
|
||||
* @param targetEntity
|
||||
* @param fieldsMapping
|
||||
* @param sourceRecordId
|
||||
* @param defaultValue
|
||||
* @param save
|
||||
* @return Returns ID or Record
|
||||
* @return
|
||||
*/
|
||||
protected Object transformRecord(
|
||||
protected Record transformRecord(
|
||||
Entity sourceEntity, Entity targetEntity, JSONObject fieldsMapping,
|
||||
ID sourceRecordId, Map<String, Object> defaultValue, boolean save) {
|
||||
ID sourceRecordId, Map<String, Object> defaultValue) {
|
||||
|
||||
Record target = EntityHelper.forNew(targetEntity.getEntityCode(), getUser());
|
||||
|
||||
|
@ -222,7 +248,7 @@ public class RecordTransfomer extends SetUser {
|
|||
|
||||
List<String> validFields = checkAndWarnFields(sourceEntity, fieldsMapping.values());
|
||||
if (validFields.isEmpty()) {
|
||||
log.warn("No fields for transform");
|
||||
log.warn("No fields for transform : {}", fieldsMapping);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -255,20 +281,7 @@ public class RecordTransfomer extends SetUser {
|
|||
}
|
||||
}
|
||||
|
||||
if (!save) return target;
|
||||
|
||||
if (this.skipGuard) {
|
||||
PrivilegesGuardContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
||||
}
|
||||
|
||||
GeneralEntityServiceContextHolder.setRepeatedCheckMode(GeneralEntityServiceContextHolder.RCM_CHECK_MAIN);
|
||||
try {
|
||||
target = Application.getEntityService(targetEntity.getEntityCode()).createOrUpdate(target);
|
||||
return target.getPrimary();
|
||||
} finally {
|
||||
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
||||
if (this.skipGuard) PrivilegesGuardContextHolder.getSkipGuardOnce();
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private List<String> checkAndWarnFields(Entity entity, Collection<?> fields) {
|
||||
|
|
|
@ -17,8 +17,8 @@ import com.alibaba.fastjson.JSONArray;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.configuration.ConfigBean;
|
||||
import com.rebuild.core.configuration.ConfigurationException;
|
||||
import com.rebuild.core.configuration.general.FormsBuilderContextHolder;
|
||||
import com.rebuild.core.configuration.general.FormsBuilder;
|
||||
import com.rebuild.core.configuration.general.FormsBuilderContextHolder;
|
||||
import com.rebuild.core.configuration.general.TransformManager;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
|
@ -88,8 +88,8 @@ public class TransformerPreview {
|
|||
FormsBuilderContextHolder.setMainIdOfDetail(fakeMainid);
|
||||
try {
|
||||
for (ID did : ids) {
|
||||
Record targetRecord = (Record) transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, did, null, false);
|
||||
Record targetRecord = transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, did, null);
|
||||
fillLabelOfReference(targetRecord);
|
||||
|
||||
JSON model = UseFormsBuilder.instance.buildNewForm(targetEntity, targetRecord, user);
|
||||
|
@ -113,8 +113,8 @@ public class TransformerPreview {
|
|||
throw new ConfigurationException("Invalid config of transform : " + transConfig);
|
||||
}
|
||||
|
||||
Record targetRecord = (Record) transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, sourceId, null, false);
|
||||
Record targetRecord = transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, sourceId, null);
|
||||
fillLabelOfReference(targetRecord);
|
||||
|
||||
// 转为明细
|
||||
|
|
|
@ -78,8 +78,9 @@ public class ActionFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
log.warn("@rbv not attached");
|
||||
return "@rbv";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.general.OperatingObserver;
|
||||
import com.rebuild.core.service.general.RepeatedRecordsException;
|
||||
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
||||
import com.rebuild.core.support.CommonsLog;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -113,7 +114,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
|
||||
TriggerAction[] beExecuted = when == TriggerWhen.DELETE
|
||||
? DELETE_BEFORE_HOLD.get(primaryId)
|
||||
: RobotTriggerManager.instance.getActions(getEffectedId(context), when);
|
||||
: RobotTriggerManager.instance.getActions(getRealRecordId(context), when);
|
||||
if (beExecuted == null || beExecuted.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -125,6 +126,10 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
if (originTriggerSource) {
|
||||
TRIGGER_SOURCE.set(new TriggerSource(context, when));
|
||||
|
||||
// 强制清理一次,正常不会出现此情况
|
||||
Object o = FieldAggregation.cleanTriggerChain();
|
||||
if (o != null) log.warn("Force clean last trigger-chain : {}", o);
|
||||
|
||||
} else {
|
||||
// 是否自己触发自己,避免无限执行
|
||||
boolean isOriginRecord = primaryId.equals(triggerSource.getOriginRecord());
|
||||
|
@ -144,24 +149,35 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
int depth = triggerSource == null ? 1 : triggerSource.getSourceDepth();
|
||||
try {
|
||||
for (TriggerAction action : beExecuted) {
|
||||
log.info("Trigger.{} [ {} ] executing on record ({}) : {}", depth, action.getType(), when.name(), primaryId);
|
||||
String w = String.format("Trigger.%d [ %s ] executed on record (%s) : %s",
|
||||
depth, action.getType(), when.name(), primaryId);
|
||||
System.out.println("[dev] " + w.replace("executed", "executing"));
|
||||
|
||||
try {
|
||||
action.execute(context);
|
||||
CommonsLog.createLog(TYPE_TRIGGER, context.getOperator(), action.getActionContext().getConfigId());
|
||||
Object ret = action.execute(context);
|
||||
log.info(w + " > " + (ret == null ? "N" : ret));
|
||||
|
||||
CommonsLog.createLog(TYPE_TRIGGER,
|
||||
context.getOperator(), action.getActionContext().getConfigId(), String.valueOf(ret));
|
||||
|
||||
} catch (Throwable ex) {
|
||||
log.info(w);
|
||||
|
||||
// DataValidate 直接抛出
|
||||
if (ex instanceof DataValidateException) throw ex;
|
||||
|
||||
log.error("Trigger execution failed : {} << {}", action, context, ex);
|
||||
CommonsLog.createLog(TYPE_TRIGGER, context.getOperator(), action.getActionContext().getConfigId(), ex);
|
||||
CommonsLog.createLog(TYPE_TRIGGER,
|
||||
context.getOperator(), action.getActionContext().getConfigId(), ex);
|
||||
|
||||
// FIXME 触发器执行失败是否抛出
|
||||
if (ex instanceof MissingMetaExcetion
|
||||
|| ex instanceof ExpressionRuntimeException
|
||||
|| ex instanceof RepeatedRecordsException) {
|
||||
throw new TriggerException(Language.L("触发器执行失败 : %s", ex.getLocalizedMessage()));
|
||||
String errMsg = ex.getLocalizedMessage();
|
||||
if (ex instanceof RepeatedRecordsException) errMsg = Language.L("存在重复记录");
|
||||
|
||||
throw new TriggerException(Language.L("触发器执行失败 : %s", errMsg));
|
||||
} else if (ex instanceof TriggerException) {
|
||||
throw (TriggerException) ex;
|
||||
} else {
|
||||
|
@ -169,9 +185,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
}
|
||||
|
||||
} finally {
|
||||
if (originTriggerSource) {
|
||||
action.clean();
|
||||
}
|
||||
action.clean();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +193,8 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
if (originTriggerSource) {
|
||||
log.info("Clear trigger-source : {}", getTriggerSource());
|
||||
TRIGGER_SOURCE.remove();
|
||||
|
||||
FieldAggregation.cleanTriggerChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,12 +205,12 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
private ID getEffectedId(OperatingContext context) {
|
||||
ID effectId = context.getAnyRecord().getPrimary();
|
||||
if (effectId.getEntityCode() == EntityHelper.ShareAccess) {
|
||||
effectId = context.getAnyRecord().getID("recordId");
|
||||
private ID getRealRecordId(OperatingContext context) {
|
||||
ID recordId = context.getAnyRecord().getPrimary();
|
||||
if (recordId.getEntityCode() == EntityHelper.ShareAccess) {
|
||||
recordId = context.getAnyRecord().getID("recordId");
|
||||
}
|
||||
return effectId;
|
||||
return recordId;
|
||||
}
|
||||
|
||||
// --
|
||||
|
|
|
@ -30,7 +30,7 @@ public abstract class TriggerAction {
|
|||
|
||||
abstract public ActionType getType();
|
||||
|
||||
abstract public void execute(OperatingContext operatingContext) throws TriggerException;
|
||||
abstract public Object execute(OperatingContext operatingContext) throws TriggerException;
|
||||
|
||||
/**
|
||||
* 如果是删除动作,会先调用此方法。可在此方法中保持一些数据状态,以便删除后还可继续使用
|
||||
|
@ -59,6 +59,6 @@ public abstract class TriggerAction {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "#" + actionContext.getConfigId();
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "#" + actionContext.getConfigId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.trigger;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -17,6 +19,7 @@ import java.util.List;
|
|||
* @author RB
|
||||
* @since 2022/07/04
|
||||
*/
|
||||
@Slf4j
|
||||
public class TriggerSource {
|
||||
|
||||
private final List<Object[]> sources = new ArrayList<>();
|
||||
|
@ -24,6 +27,7 @@ public class TriggerSource {
|
|||
|
||||
protected TriggerSource(OperatingContext origin, TriggerWhen originAction) {
|
||||
addNext(origin, originAction);
|
||||
if (Application.devMode()) log.warn("[dev] New trigger-source : {}", this);
|
||||
}
|
||||
|
||||
public void addNext(OperatingContext next, TriggerWhen nextAction) {
|
||||
|
|
|
@ -51,20 +51,19 @@ public class AutoApproval extends TriggerAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
this.operatingContext = operatingContext;
|
||||
|
||||
List<AutoApproval> lazyed;
|
||||
if ((lazyed = isLazyAutoApproval(false)) != null) {
|
||||
lazyed.add(this);
|
||||
log.info("Lazy AutoApproval : {}", lazyed);
|
||||
return;
|
||||
return "lazy";
|
||||
}
|
||||
|
||||
ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
String useApproval = ((JSONObject) actionContext.getActionContent()).getString("useApproval");
|
||||
|
||||
// ID approver = operatingContext.getOperator();
|
||||
ID approver = UserService.SYSTEM_USER;
|
||||
ID approvalId = ID.isId(useApproval) ? ID.valueOf(useApproval) : null;
|
||||
|
||||
|
@ -77,6 +76,7 @@ public class AutoApproval extends TriggerAction {
|
|||
} else {
|
||||
Application.getBean(ApprovalStepService.class).txAutoApproved(recordId, approver, approvalId);
|
||||
}
|
||||
return "approval:" + recordId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -49,14 +49,15 @@ public class AutoAssign extends TriggerAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
|
||||
JSONArray assignTo = content.getJSONArray("assignTo");
|
||||
Set<ID> toUsers = UserHelper.parseUsers(assignTo, recordId, true);
|
||||
if (toUsers.isEmpty()) {
|
||||
return;
|
||||
log.warn("No andy users found : {}", assignTo);
|
||||
return null;
|
||||
}
|
||||
|
||||
ID toUser = null;
|
||||
|
@ -113,5 +114,6 @@ public class AutoAssign extends TriggerAction {
|
|||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
||||
GeneralEntityServiceContextHolder.isFromTrigger(true);
|
||||
}
|
||||
return "assign:" + toUser;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,14 +42,15 @@ public class AutoShare extends AutoAssign {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
|
||||
JSONArray shareTo = content.getJSONArray("shareTo");
|
||||
Set<ID> toUsers = UserHelper.parseUsers(shareTo, recordId, true);
|
||||
if (toUsers.isEmpty()) {
|
||||
return;
|
||||
log.warn("No andy users found : {}", shareTo);
|
||||
return null;
|
||||
}
|
||||
|
||||
String hasCascades = ((JSONObject) actionContext.getActionContent()).getString("cascades");
|
||||
|
@ -75,5 +76,6 @@ public class AutoShare extends AutoAssign {
|
|||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||
}
|
||||
}
|
||||
return "share:" + toUsers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,13 +93,17 @@ public class FieldAggregation extends TriggerAction {
|
|||
List<String> tschain = TRIGGER_CHAIN.get();
|
||||
if (tschain == null) {
|
||||
tschain = new ArrayList<>();
|
||||
if (Application.devMode()) log.warn("[dev] New trigger-chain : {}", this);
|
||||
} else {
|
||||
log.info("Occured trigger-chain : {} > {} (current)", StringUtils.join(tschain, " > "), chainName);
|
||||
String w = String.format("Occured trigger-chain : %s > %s (current)", StringUtils.join(tschain, " > "), chainName);
|
||||
|
||||
// 在整个触发链上只触发一次,避免循环调用
|
||||
// 在整个触发链上只触发1次,避免循环调用
|
||||
// FIXME 20220804 某些场景是否允许2次,而非1次???
|
||||
if (tschain.contains(chainName)) {
|
||||
if (Application.devMode()) log.warn("[dev] Record triggered only once on trigger-chain : {}", chainName);
|
||||
log.warn(w + "! TRIGGER ONCE ONLY : {}", chainName);
|
||||
return null;
|
||||
} else {
|
||||
log.info(w);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,15 +115,15 @@ public class FieldAggregation extends TriggerAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final String chainName = actionContext.getConfigId() + ":" + operatingContext.getAction().getName();
|
||||
final List<String> tschain = checkTriggerChain(chainName);
|
||||
if (tschain == null) return;
|
||||
if (tschain == null) return "trigger-once";
|
||||
|
||||
this.prepare(operatingContext);
|
||||
if (targetRecordId == null) {
|
||||
log.warn("No target record found");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 聚合数据过滤
|
||||
|
@ -187,7 +191,10 @@ public class FieldAggregation extends TriggerAction {
|
|||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
||||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||
}
|
||||
|
||||
return "target:" + targetRecord.getPrimary();
|
||||
}
|
||||
return "target-empty";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -223,8 +230,12 @@ public class FieldAggregation extends TriggerAction {
|
|||
this.followSourceWhere = String.format("%s = '%s'", followSourceField, targetRecordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean() {
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static Object cleanTriggerChain() {
|
||||
Object o = TRIGGER_CHAIN.get();
|
||||
TRIGGER_CHAIN.remove();
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,17 +80,17 @@ public class FieldWriteback extends FieldAggregation {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) throws TriggerException {
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final String chainName = actionContext.getConfigId() + ":" + operatingContext.getAction().getName();
|
||||
final List<String> tschain = checkTriggerChain(chainName);
|
||||
if (tschain == null) return;
|
||||
if (tschain == null) return "trigger-once";
|
||||
|
||||
this.prepare(operatingContext);
|
||||
if (targetRecordIds.isEmpty()) return;
|
||||
if (targetRecordIds.isEmpty()) return null;
|
||||
|
||||
if (targetRecordData.isEmpty()) {
|
||||
log.info("No data of target record available : {}", targetRecordId);
|
||||
return;
|
||||
return "target-empty";
|
||||
}
|
||||
|
||||
final ServiceSpec useService = MetadataHelper.isBusinessEntity(targetEntity)
|
||||
|
@ -100,6 +100,7 @@ public class FieldWriteback extends FieldAggregation {
|
|||
final boolean forceUpdate = ((JSONObject) actionContext.getActionContent()).getBooleanValue("forceUpdate");
|
||||
|
||||
boolean tschainAdded = false;
|
||||
List<ID> affected = new ArrayList<>();
|
||||
for (ID targetRecordId : targetRecordIds) {
|
||||
if (operatingContext.getAction() == BizzPermission.DELETE
|
||||
&& targetRecordId.equals(operatingContext.getAnyRecord().getPrimary())) {
|
||||
|
@ -129,12 +130,14 @@ public class FieldWriteback extends FieldAggregation {
|
|||
|
||||
try {
|
||||
useService.createOrUpdate(targetRecord);
|
||||
affected.add(targetRecord.getPrimary());
|
||||
} finally {
|
||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
||||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
||||
}
|
||||
}
|
||||
return "target:" + affected;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,10 +25,10 @@ import java.util.List;
|
|||
/**
|
||||
* 分组聚合目标数据刷新。
|
||||
* 场景举例:
|
||||
* 1. 新建产品A + 仓库A分组(组合A+A)
|
||||
* 2. 之后修改了仓库A > B(组合A+B),此时原(组合A+A)纪录不会更新
|
||||
* 3. 这里需要强制更新相关原纪录
|
||||
* 4. NOTE 如果组合值均为空,则无法匹配任何目标记录,此时需要全量刷新(通过任一字段必填解决)
|
||||
* 1.1 新建产品A + 仓库A分组(组合A+A)
|
||||
* 1.2 修改仓库A > B(组合A+B),此时原(组合A+A)纪录不会更新
|
||||
* 2. 这里需要强制更新相关原纪录
|
||||
* 3. NOTE 如果组合值均为空,则无法匹配任何目标记录,此时需要全量刷新(通过任一字段必填解决)
|
||||
*
|
||||
* @author RB
|
||||
* @since 2022/7/8
|
||||
|
|
|
@ -56,7 +56,7 @@ public class SendNotification extends TriggerAction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(OperatingContext operatingContext) {
|
||||
public Object execute(OperatingContext operatingContext) {
|
||||
ThreadPool.exec(() -> {
|
||||
try {
|
||||
// FIXME 等待事物完成
|
||||
|
@ -67,6 +67,7 @@ public class SendNotification extends TriggerAction {
|
|||
log.error(null, ex);
|
||||
}
|
||||
});
|
||||
return "async";
|
||||
}
|
||||
|
||||
private void executeAsync(OperatingContext operatingContext) {
|
||||
|
|
|
@ -14,6 +14,9 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* 多引用字段支持
|
||||
|
@ -78,4 +81,25 @@ public class N2NReferenceSupport {
|
|||
Field lastField = father.getField(paths[paths.length - 1]);
|
||||
return items(lastField, fatherRecordId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定字段的全部引用项(不含排除)
|
||||
*
|
||||
* @param field
|
||||
* @param excluded 排除指定记录
|
||||
* @return
|
||||
*/
|
||||
public static Set<ID> itemsUsed(Field field, ID excluded) {
|
||||
String sql = "select referenceId from NreferenceItem where belongEntity = ? and belongField = ?";
|
||||
if (excluded != null) sql += String.format(" and recordId <> '%s'", excluded);
|
||||
|
||||
Object[][] array = Application.createQueryNoFilter(sql)
|
||||
.setParameter(1, field.getOwnEntity().getName())
|
||||
.setParameter(2, field.getName())
|
||||
.array();
|
||||
|
||||
Set<ID> set = new HashSet<>();
|
||||
for (Object[] o : array) set.add((ID) o[0]);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.configuration.ConfigBean;
|
||||
import com.rebuild.core.configuration.general.AdvFilterManager;
|
||||
import com.rebuild.core.configuration.general.DataListClass;
|
||||
import com.rebuild.core.configuration.general.DataListCategory;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
|
@ -46,7 +46,7 @@ public class ProtocolFilterParser {
|
|||
final private String protocolExpr;
|
||||
|
||||
/**
|
||||
* @param protocolExpr via:xxx:[field] ref:xxx:[id] class:entity:value
|
||||
* @param protocolExpr via:xxx:[field] ref:xxx:[id] category:entity:value
|
||||
*/
|
||||
public ProtocolFilterParser(String protocolExpr) {
|
||||
this.protocolExpr = protocolExpr;
|
||||
|
@ -64,8 +64,8 @@ public class ProtocolFilterParser {
|
|||
case "ref": {
|
||||
return parseRef(ps[1], ps.length > 2 ? ps[2] : null);
|
||||
}
|
||||
case "class": {
|
||||
return parseClass(ps[1], ps[2]);
|
||||
case "category": {
|
||||
return parseCategory(ps[1], ps[2]);
|
||||
}
|
||||
default: {
|
||||
log.warn("Unknown protocol expr : {}", protocolExpr);
|
||||
|
@ -160,9 +160,9 @@ public class ProtocolFilterParser {
|
|||
* @return
|
||||
* @see AdvFilterParser#parseItem(JSONObject, JSONObject)
|
||||
*/
|
||||
public String parseClass(String entity, String value) {
|
||||
public String parseCategory(String entity, String value) {
|
||||
Entity rootEntity = MetadataHelper.getEntity(entity);
|
||||
Field classField = DataListClass.getFieldOfClass(rootEntity);
|
||||
Field classField = DataListCategory.getFieldOfCategory(rootEntity);
|
||||
if (classField == null) return "(9=9)";
|
||||
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(classField);
|
||||
|
|
|
@ -231,7 +231,7 @@ public class MetaEntityController extends BaseController {
|
|||
File dest = RebuildConfiguration.getFileOfTemp("schema-" + entity.getName() + ".json");
|
||||
if (dest.exists()) FileUtils.deleteQuietly(dest);
|
||||
|
||||
new MetaSchemaGenerator(entity).generate(dest);
|
||||
new MetaSchemaGenerator(entity, true).generate(dest);
|
||||
|
||||
if (ServletUtils.isAjaxRequest(request)) {
|
||||
writeSuccess(response, JSONUtils.toJSONObject("file", dest.getName()));
|
||||
|
|
|
@ -15,7 +15,7 @@ import com.rebuild.api.RespBody;
|
|||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.ConfigBean;
|
||||
import com.rebuild.core.configuration.general.BaseLayoutManager;
|
||||
import com.rebuild.core.configuration.general.DataListClass;
|
||||
import com.rebuild.core.configuration.general.DataListCategory;
|
||||
import com.rebuild.core.configuration.general.DataListManager;
|
||||
import com.rebuild.core.configuration.general.LayoutConfigService;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
|
@ -66,9 +66,9 @@ public class WidgetController extends BaseController implements ShareTo {
|
|||
return RespBody.ok(config == null ? null : config.toJSON());
|
||||
}
|
||||
|
||||
@GetMapping("widget-class-data")
|
||||
public RespBody getClassData(@PathVariable String entity) {
|
||||
JSON data = DataListClass.datas(MetadataHelper.getEntity(entity), null);
|
||||
@GetMapping("widget-category-data")
|
||||
public RespBody getCategoryData(@PathVariable String entity) {
|
||||
JSON data = DataListCategory.datas(MetadataHelper.getEntity(entity), null);
|
||||
return RespBody.ok(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,13 +83,13 @@ public class GeneralListController extends EntityController {
|
|||
// 扩展配置
|
||||
String advListHideFilters = easyEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_HIDE_FILTERS);
|
||||
String advListHideCharts = easyEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_HIDE_CHARTS);
|
||||
String advListShowClass = easyEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCLASS);
|
||||
String advListShowCategory = easyEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCATEGORY);
|
||||
mv.getModel().put(EasyEntityConfigProps.ADV_LIST_HIDE_FILTERS, advListHideFilters);
|
||||
mv.getModel().put(EasyEntityConfigProps.ADV_LIST_HIDE_CHARTS, advListHideCharts);
|
||||
mv.getModel().put(EasyEntityConfigProps.ADV_LIST_SHOWCLASS, StringUtils.isNotBlank(advListShowClass));
|
||||
mv.getModel().put(EasyEntityConfigProps.ADV_LIST_SHOWCATEGORY, StringUtils.isNotBlank(advListShowCategory));
|
||||
|
||||
mv.getModel().put("hideAside",
|
||||
BooleanUtils.toBoolean(advListHideFilters) && BooleanUtils.toBoolean(advListHideCharts) && StringUtils.isBlank(advListShowClass));
|
||||
BooleanUtils.toBoolean(advListHideFilters) && BooleanUtils.toBoolean(advListHideCharts) && StringUtils.isBlank(advListShowCategory));
|
||||
|
||||
// 查询面板
|
||||
|
||||
|
|
|
@ -77,9 +77,13 @@ public class TransformConfigController extends BaseController {
|
|||
mv.getModelMap().put("sourceEntity", buildEntity(sourceEntity, true));
|
||||
mv.getModelMap().put("targetEntity", buildEntity(targetEntity, false));
|
||||
|
||||
// 明细(两个实体均有明细才返回)
|
||||
if (sourceEntity.getDetailEntity() != null && targetEntity.getDetailEntity() != null) {
|
||||
mv.getModelMap().put("sourceDetailEntity", buildEntity(sourceEntity.getDetailEntity(), true));
|
||||
// v2.10 目标为主实体
|
||||
if (targetEntity.getDetailEntity() != null) {
|
||||
if (sourceEntity.getDetailEntity() != null) {
|
||||
mv.getModelMap().put("sourceDetailEntity", buildEntity(sourceEntity.getDetailEntity(), true));
|
||||
} else {
|
||||
mv.getModelMap().put("sourceDetailEntity", buildEntity(sourceEntity, true));
|
||||
}
|
||||
mv.getModelMap().put("targetDetailEntity", buildEntity(targetEntity.getDetailEntity(), false));
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ public class TriggerAdminController extends BaseController {
|
|||
public Object[][] triggerList(HttpServletRequest request) {
|
||||
String belongEntity = getParameter(request, "entity");
|
||||
String q = getParameter(request, "q");
|
||||
String sql = "select configId,belongEntity,belongEntity,name,isDisabled,modifiedOn,when,actionType,configId from RobotTriggerConfig" +
|
||||
String sql = "select configId,belongEntity,belongEntity,name,isDisabled,modifiedOn,when,actionType,configId,priority from RobotTriggerConfig" +
|
||||
" where (1=1) and (2=2)" +
|
||||
" order by modifiedOn desc, name";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# !!! `db.*` ONLY FOR DEV MODE !!!
|
||||
db.url: jdbc:mysql://127.0.0.1:3306/rebuild20?characterEncoding=UTF8&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B08:00
|
||||
db.url: jdbc:mysql://127.0.0.1:3306/rebuild30?characterEncoding=UTF8&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B08:00
|
||||
db.user: rebuild
|
||||
db.passwd: rebuild
|
||||
# Use built-in ehcache if redis not defined or unavailable
|
||||
|
|
|
@ -2264,5 +2264,16 @@
|
|||
"请选择驳回方式":"请选择驳回方式",
|
||||
"系统用户":"系统用户",
|
||||
"下载模板":"下载模板",
|
||||
"退回至":"退回至"
|
||||
"退回至":"退回至",
|
||||
"提交模式":"提交模式",
|
||||
"启用提交模式必须选择审批流程":"启用提交模式必须选择审批流程",
|
||||
"显示侧栏“分类”":"显示侧栏“分类”",
|
||||
"显示侧栏“常用查询”":"显示侧栏“常用查询”",
|
||||
"仅提交不做自动审批。选择的审批流程至少配置一个审批人,否则会提交失败":"仅提交不做自动审批。选择的审批流程至少配置一个审批人,否则会提交失败",
|
||||
"需要先添加 [记录转换](../transforms) 才能在此处选择":"需要先添加 [记录转换](../transforms) 才能在此处选择",
|
||||
"打开新建页面而非直接转换":"打开新建页面而非直接转换",
|
||||
"显示侧栏“图表”":"显示侧栏“图表”",
|
||||
"选择的审批流程至少配置一个审批人":"选择的审批流程至少配置一个审批人",
|
||||
"需要先添加 [审批流程](../approvals) 才能在此处选择":"需要先添加 [审批流程](../approvals) 才能在此处选择",
|
||||
"Category":"分类"
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
-- #1 database/user
|
||||
-- 首次使用请移除以下注释以创建数据库和用户
|
||||
/*
|
||||
CREATE DATABASE rebuild20 COLLATE utf8mb4_general_ci;
|
||||
CREATE DATABASE rebuild30 COLLATE utf8mb4_general_ci;
|
||||
CREATE USER 'rebuild'@'127.0.0.1' IDENTIFIED BY 'rebuild';
|
||||
GRANT ALL PRIVILEGES ON rebuild20.* TO 'rebuild'@'127.0.0.1';
|
||||
GRANT ALL PRIVILEGES ON rebuild30.* TO 'rebuild'@'127.0.0.1';
|
||||
GRANT RELOAD ON *.* TO 'rebuild'@'127.0.0.1';
|
||||
FLUSH PRIVILEGES;
|
||||
USE rebuild20;
|
||||
USE rebuild30;
|
||||
*/
|
||||
|
||||
-- #2 schemas
|
||||
|
|
|
@ -191,6 +191,6 @@
|
|||
<script th:src="@{/assets/js/metadata/field-valueset.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/metadata/field-formula.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/trigger/trigger-design.js}" type="text/babel"></script>
|
||||
<script th:src="|${baseUrl}/assets/js/trigger/trigger.${actionType}.js?v=2.3.0416|" type="text/babel"></script>
|
||||
<script th:src="|${baseUrl}/assets/js/trigger/trigger.${actionType}.js?v=2.10|" type="text/babel"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<th>[[${bundle.L('源实体')}]]</th>
|
||||
<th>[[${bundle.L('触发类型')}]]</th>
|
||||
<th class="no-sort">[[${bundle.L('触发动作')}]]</th>
|
||||
<th width="80" class="int-sort">[[${bundle.L('优先级')}]]</th>
|
||||
<th width="80" class="no-sort">[[${bundle.L('启用')}]]</th>
|
||||
<th width="120" class="no-sort">[[${bundle.L('修改时间')}]]</th>
|
||||
<th width="120" class="no-sort"></th>
|
||||
|
|
|
@ -3377,6 +3377,11 @@ form {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-aside.widgets .nav-tabs > li.nav-item a.nav-link {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.page-aside.widgets .tab-container {
|
||||
margin-top: 2px;
|
||||
min-width: 279px;
|
||||
|
|
|
@ -76,7 +76,16 @@ class ConfigList extends React.Component {
|
|||
})
|
||||
|
||||
// 简单排序
|
||||
if ($.tablesort) $('.tablesort').tablesort()
|
||||
if ($.tablesort) {
|
||||
$('.tablesort').tablesort()
|
||||
|
||||
// 数字排序
|
||||
$('table th.int-sort').each(function () {
|
||||
$(this).data('sortBy', (th, td) => {
|
||||
return ~~$(td).text()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
|
|
|
@ -161,7 +161,7 @@ class DatabaseConf extends React.Component {
|
|||
<div className="form-group row">
|
||||
<div className="col-sm-3 col-form-label text-sm-right">{$L('数据库名称')}</div>
|
||||
<div className="col-sm-7">
|
||||
<input type="text" className="form-control form-control-sm" name="dbName" value={this.state.dbName || ''} onChange={this.handleValue} placeholder="rebuild20" />
|
||||
<input type="text" className="form-control form-control-sm" name="dbName" value={this.state.dbName || ''} onChange={this.handleValue} placeholder="rebuild30" />
|
||||
<div className="form-text">{$L('如数据库不存在系统将自动创建')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -228,7 +228,7 @@ class DatabaseConf extends React.Component {
|
|||
dbType: 'mysql',
|
||||
dbHost: this.state.dbHost || '127.0.0.1',
|
||||
dbPort: this.state.dbPort || 3306,
|
||||
dbName: this.state.dbName || 'rebuild20',
|
||||
dbName: this.state.dbName || 'rebuild30',
|
||||
dbUser: this.state.dbUser || 'rebuild',
|
||||
dbPassword: this.state.dbPassword || 'rebuild',
|
||||
}
|
||||
|
|
|
@ -122,16 +122,16 @@ class DlgMode1Option extends RbFormHandler {
|
|||
<label className="col-sm-3 col-form-label text-sm-right">{$L('显示侧栏“分类”')}</label>
|
||||
<div className="col-sm-7">
|
||||
<div className="switch-button switch-button-xs">
|
||||
<input type="checkbox" id="advListShowClass" defaultChecked={wpc.extConfig && wpc.extConfig.advListShowClass} />
|
||||
<input type="checkbox" id="advListShowCategory" defaultChecked={wpc.extConfig && wpc.extConfig.advListShowCategory} />
|
||||
<span>
|
||||
<label htmlFor="advListShowClass" />
|
||||
<label htmlFor="advListShowCategory" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="clearfix"></div>
|
||||
<div className={`J_advListShowClass mt-2 ${this.state.advListShowClass ? '' : 'hide'}`}>
|
||||
<div className={`J_advListShowCategory mt-2 ${this.state.advListShowCategory ? '' : 'hide'}`}>
|
||||
<select className="form-control form-control-sm">
|
||||
{this.state.advListShowClassFields &&
|
||||
this.state.advListShowClassFields.map((item) => {
|
||||
{this.state.advListShowCategoryFields &&
|
||||
this.state.advListShowCategoryFields.map((item) => {
|
||||
return (
|
||||
<option key={item.name} value={item.name}>
|
||||
{item.label}
|
||||
|
@ -182,33 +182,33 @@ class DlgMode1Option extends RbFormHandler {
|
|||
componentDidMount() {
|
||||
const that = this
|
||||
let $class2
|
||||
$('#advListShowClass').on('change', function () {
|
||||
$('#advListShowCategory').on('change', function () {
|
||||
if ($val(this)) {
|
||||
that.setState({ advListShowClass: true })
|
||||
that.setState({ advListShowCategory: true })
|
||||
} else {
|
||||
that.setState({ advListShowClass: null })
|
||||
that.setState({ advListShowCategory: null })
|
||||
}
|
||||
|
||||
if (!$class2) {
|
||||
$class2 = $('.J_advListShowClass select')
|
||||
$class2 = $('.J_advListShowCategory select')
|
||||
$.get(`/commons/metadata/fields?entity=${wpc.entityName}`, (res) => {
|
||||
const _data = []
|
||||
res.data.forEach((item) => {
|
||||
if (CLASS_TYPES.includes(item.type)) _data.push(item)
|
||||
})
|
||||
|
||||
that.setState({ advListShowClassFields: _data }, () => {
|
||||
that.setState({ advListShowCategoryFields: _data }, () => {
|
||||
$class2
|
||||
.select2({ placeholder: $L('选择分类字段') })
|
||||
.val((wpc.extConfig && wpc.extConfig.advListShowClass) || null)
|
||||
.val((wpc.extConfig && wpc.extConfig.advListShowCategory) || null)
|
||||
.trigger('change')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (wpc.extConfig && wpc.extConfig.advListShowClass) {
|
||||
$('#advListShowClass').trigger('change')
|
||||
if (wpc.extConfig && wpc.extConfig.advListShowCategory) {
|
||||
$('#advListShowCategory').trigger('change')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +216,7 @@ class DlgMode1Option extends RbFormHandler {
|
|||
const o = {
|
||||
advListHideFilters: !$val('#advListHideFilters'),
|
||||
advListHideCharts: !$val('#advListHideCharts'),
|
||||
advListShowClass: this.state.advListShowClass ? $val('.J_advListShowClass select') : null,
|
||||
advListShowCategory: this.state.advListShowCategory ? $val('.J_advListShowCategory select') : null,
|
||||
advListFilterPane: $val('#advListFilterPane'),
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,14 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
const wpc = window.__PageConfig
|
||||
$(document).ready(() => {
|
||||
renderRbcomp(<PreviewTable data={wpc.content} />, 'preview-table')
|
||||
|
||||
const $size = $('.preview-tools select').on('change', function () {
|
||||
const s = $(this).val()
|
||||
$('.preview-content').css('font-size', (13 * ~~s) / 10)
|
||||
$storage.set('PRINTSIZE', s)
|
||||
})
|
||||
const s = $storage.get('PRINTSIZE')
|
||||
if (s) $size.val(s).trigger('change')
|
||||
})
|
||||
|
||||
class PreviewTable extends React.Component {
|
||||
|
|
|
@ -986,7 +986,7 @@ UserPopup.create = function (el) {
|
|||
}
|
||||
|
||||
// ~~ HTML 内容
|
||||
const WrapHtml = (htmlContent) => <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
|
||||
const WrapHtml = (htmlContent) => <span dangerouslySetInnerHTML={{ __html: htmlContent }} />
|
||||
|
||||
// ~~ short React.Fragment
|
||||
const RF = ({ children }) => <React.Fragment>{children}</React.Fragment>
|
||||
|
|
|
@ -1230,7 +1230,7 @@ const ClassWidget = {
|
|||
},
|
||||
|
||||
loadClass() {
|
||||
$.get(`/app/${wpc.entity[0]}/widget-class-data`, (res) => {
|
||||
$.get(`/app/${wpc.entity[0]}/widget-category-data`, (res) => {
|
||||
this._classLoaded = true
|
||||
|
||||
res.data &&
|
||||
|
@ -1247,7 +1247,7 @@ const ClassWidget = {
|
|||
|
||||
const v = $(this).data('id')
|
||||
if (v === '$ALL$') wpc.protocolFilter = null
|
||||
else wpc.protocolFilter = `class:${wpc.entity[0]}:${v}`
|
||||
else wpc.protocolFilter = `category:${wpc.entity[0]}:${v}`
|
||||
|
||||
RbListPage.reload()
|
||||
})
|
||||
|
|
|
@ -59,6 +59,9 @@ class TriggerList extends ConfigList {
|
|||
<td>{item[2] || item[1]}</td>
|
||||
<td>{item[7]}</td>
|
||||
<td>{item[6] > 0 ? $L('当 %s 时', formatWhen(item[6])) : <span className="text-warning">({$L('无触发动作')})</span>}</td>
|
||||
<td>
|
||||
<span className="badge badge-light font-weight-light">{item[9]}</span>
|
||||
</td>
|
||||
<td>{ShowEnable(item[4])}</td>
|
||||
<td>
|
||||
<DateShow date={item[5]} />
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="tab-container">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item" th:if="${advListHideFilters != 'true'}"><a class="nav-link" href="#asideFilters" data-toggle="tab">[[${bundle.L('常用查询')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListShowClass}"><a class="nav-link J_load-class" href="#asideClass" data-toggle="tab">[[${bundle.L('分类')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListShowCategory}"><a class="nav-link J_load-class" href="#asideClass" data-toggle="tab">[[${bundle.L('Category')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListHideCharts != 'true'}"><a class="nav-link J_load-charts" href="#asideWidgets" data-toggle="tab">[[${bundle.L('图表')}]]</a></li>
|
||||
</ul>
|
||||
<div class="tab-content rb-scroller">
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
padding: 10px 15px 5px;
|
||||
text-align: right;
|
||||
}
|
||||
.preview-tools select.form-control-sm {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
height: 36px !important;
|
||||
min-height: 36px;
|
||||
margin-right: 6px;
|
||||
transform: translateY(-1.5px);
|
||||
}
|
||||
.preview-content {
|
||||
margin: 0 20px 20px;
|
||||
}
|
||||
|
@ -22,9 +30,9 @@
|
|||
}
|
||||
.table th {
|
||||
background-color: #eee !important;
|
||||
width: auto;
|
||||
min-width: 110px;
|
||||
max-width: 180px;
|
||||
width: 13.33%;
|
||||
min-width: 120px;
|
||||
max-width: 240px;
|
||||
}
|
||||
.table th.divider {
|
||||
width: 100%;
|
||||
|
@ -55,6 +63,20 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="preview-tools d-print-none">
|
||||
<select class="form-control form-control-sm">
|
||||
<option value="10">[[${bundle.L('默认大小')}]]</option>
|
||||
<option value="10">100%</option>
|
||||
<option value="11">110%</option>
|
||||
<option value="12">120%</option>
|
||||
<option value="13">130%</option>
|
||||
<option value="14">140%</option>
|
||||
<option value="15">150%</option>
|
||||
<option value="16">160%</option>
|
||||
<option value="17">170%</option>
|
||||
<option value="18">180%</option>
|
||||
<option value="19">190%</option>
|
||||
<option value="20">200%</option>
|
||||
</select>
|
||||
<button class="btn btn-space btn-primary" onclick="window.print()"><i class="icon zmdi zmdi-print"></i> [[${bundle.L('打印')}]]</button>
|
||||
<button class="btn btn-space btn-secondary" onclick="window.close()">[[${bundle.L('关闭')}]]</button>
|
||||
</div>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="tab-container">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item" th:if="${advListHideFilters != 'true'}"><a class="nav-link" href="#asideFilters" data-toggle="tab">[[${bundle.L('常用查询')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListShowClass}"><a class="nav-link J_load-class" href="#asideClass" data-toggle="tab">[[${bundle.L('分类')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListShowCategory}"><a class="nav-link J_load-class" href="#asideClass" data-toggle="tab">[[${bundle.L('Category')}]]</a></li>
|
||||
<li class="nav-item" th:if="${advListHideCharts != 'true'}"><a class="nav-link J_load-charts" href="#asideWidgets" data-toggle="tab">[[${bundle.L('图表')}]]</a></li>
|
||||
</ul>
|
||||
<div class="tab-content rb-scroller">
|
||||
|
|
|
@ -23,7 +23,7 @@ public class MetaSchemaGeneratorTest extends TestSupport {
|
|||
public void testGenerate() {
|
||||
if (MetadataHelper.containsEntity(Account)) {
|
||||
Entity test = MetadataHelper.getEntity(Account);
|
||||
MetaSchemaGenerator generator = new MetaSchemaGenerator(test);
|
||||
MetaSchemaGenerator generator = new MetaSchemaGenerator(test, true);
|
||||
JSON schema = generator.generate();
|
||||
System.out.println(JSON.toJSONString(schema, true));
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class MetaSchemaGeneratorTest extends TestSupport {
|
|||
public void testGenerateHaveDetail() {
|
||||
if (MetadataHelper.containsEntity(SalesOrder)) {
|
||||
Entity test = MetadataHelper.getEntity(SalesOrder);
|
||||
MetaSchemaGenerator generator = new MetaSchemaGenerator(test);
|
||||
MetaSchemaGenerator generator = new MetaSchemaGenerator(test, true);
|
||||
JSON schema = generator.generate();
|
||||
System.out.println(JSON.toJSONString(schema, true));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue