【同步】BOOT 和 CLOUD 的功能(bpm)
parent
3e5e60ce96
commit
8c7087ca2a
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -62,6 +62,10 @@ def load_and_clean(sql_file: str) -> str:
|
||||||
content = open(sql_file, encoding="utf-8").read()
|
content = open(sql_file, encoding="utf-8").read()
|
||||||
for replace_pair in REPLACE_PAIR_LIST:
|
for replace_pair in REPLACE_PAIR_LIST:
|
||||||
content = content.replace(*replace_pair)
|
content = content.replace(*replace_pair)
|
||||||
|
# 移除所有 CHARACTER SET / COLLATE 变体 (utf8mb3、utf8 等)
|
||||||
|
content = re.sub(r" CHARACTER SET \w+ COLLATE \w+", "", content)
|
||||||
|
content = re.sub(r" CHARACTER SET \w+", "", content)
|
||||||
|
content = re.sub(r" COLLATE \w+", "", content)
|
||||||
# 移除索引字段的前缀长度定义,例如: `name`(32) -> `name`
|
# 移除索引字段的前缀长度定义,例如: `name`(32) -> `name`
|
||||||
# 移除索引定义上的 USING BTREE COMMENT 部分
|
# 移除索引定义上的 USING BTREE COMMENT 部分
|
||||||
# 相关 issue:https://t.zsxq.com/96IFc 、https://t.zsxq.com/rC3A3
|
# 相关 issue:https://t.zsxq.com/96IFc 、https://t.zsxq.com/rC3A3
|
||||||
|
|
@ -77,7 +81,11 @@ class Convertor(ABC):
|
||||||
self.src = src
|
self.src = src
|
||||||
self.db_type = db_type
|
self.db_type = db_type
|
||||||
self.content = load_and_clean(self.src)
|
self.content = load_and_clean(self.src)
|
||||||
self.table_script_list = re.findall(r"CREATE TABLE [^;]*;", self.content)
|
# original_content 保留原始 COMMENT 信息,用于注释提取
|
||||||
|
self.original_content = open(src, encoding="utf-8").read()
|
||||||
|
# 剥离列级 COMMENT 以避免 COMMENT 值内的分号截断 CREATE TABLE 正则
|
||||||
|
content_no_comment = re.sub(r" COMMENT '(?:[^'\\]|\\.)*'", "", self.content)
|
||||||
|
self.table_script_list = re.findall(r"CREATE TABLE [^;]*;", content_no_comment)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]) -> str:
|
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]) -> str:
|
||||||
|
|
@ -182,7 +190,8 @@ class Convertor(ABC):
|
||||||
head = head.strip().replace("`", "").lower()
|
head = head.strip().replace("`", "").lower()
|
||||||
tail = tail.strip().replace(r"\"", '"')
|
tail = tail.strip().replace(r"\"", '"')
|
||||||
# tail = tail.replace("b'0'", "'0'").replace("b'1'", "'1'")
|
# tail = tail.replace("b'0'", "'0'").replace("b'1'", "'1'")
|
||||||
yield f"INSERT INTO {table_name.lower()} {head} VALUES {tail}"
|
col_part = f" {head}" if head else ""
|
||||||
|
yield f"INSERT INTO {table_name.lower()}{col_part} VALUES {tail}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def index(ddl: Dict) -> Generator:
|
def index(ddl: Dict) -> Generator:
|
||||||
|
|
@ -227,7 +236,8 @@ class Convertor(ABC):
|
||||||
yield field, comment_string
|
yield field, comment_string
|
||||||
|
|
||||||
def table_comment(self, table_sql: str) -> str:
|
def table_comment(self, table_sql: str) -> str:
|
||||||
match = re.search(r"COMMENT \='([^']+)';", table_sql)
|
# 兼容 COMMENT='xxx' / COMMENT = 'xxx' 等等号两侧空格变体;允许 COMMENT 内含转义单引号
|
||||||
|
match = re.search(r"COMMENT\s*=\s*'((?:[^'\\]|\\.)*)'", table_sql)
|
||||||
return match.group(1) if match else None
|
return match.group(1) if match else None
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
|
|
@ -251,7 +261,9 @@ class Convertor(ABC):
|
||||||
|
|
||||||
error_scripts = []
|
error_scripts = []
|
||||||
for table_sql in self.table_script_list:
|
for table_sql in self.table_script_list:
|
||||||
ddl = DDLParser(table_sql.replace("`", "")).run()
|
# 剥离 COMMENT 子句避免 DDLParser 无法处理中文等特殊字符
|
||||||
|
table_sql_for_parse = re.sub(r"\s+COMMENT\s+'[^']*'", "", table_sql)
|
||||||
|
ddl = DDLParser(table_sql_for_parse.replace("`", "")).run()
|
||||||
|
|
||||||
# 如果parse失败, 需要跟进
|
# 如果parse失败, 需要跟进
|
||||||
if len(ddl) == 0:
|
if len(ddl) == 0:
|
||||||
|
|
@ -266,17 +278,23 @@ class Convertor(ABC):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 解析注释
|
# 解析注释
|
||||||
|
# 从原始 SQL 提取注释(支持中文等 DDLParser 不能处理的内容)
|
||||||
|
# 按表名定位完整建表段(以「换行 + )」起始的 ENGINE 行作为终止),避免被列 COMMENT 内的分号截断
|
||||||
|
orig_match = re.search(
|
||||||
|
rf"CREATE TABLE\s+`{re.escape(table_name)}`[\s\S]*?\n\)[^;]*;",
|
||||||
|
self.original_content,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
orig_table_sql = orig_match.group() if orig_match else table_sql
|
||||||
|
comments_dict = dict(Convertor.filed_comments(orig_table_sql))
|
||||||
for column in table_ddl["columns"]:
|
for column in table_ddl["columns"]:
|
||||||
column["comment"] = bytes(column["comment"], "utf-8").decode(
|
column["comment"] = comments_dict.get(column["name"], "")
|
||||||
r"unicode_escape"
|
table_ddl["comment"] = self.table_comment(orig_table_sql) or ""
|
||||||
)[1:-1]
|
|
||||||
table_ddl["comment"] = bytes(table_ddl["comment"], "utf-8").decode(
|
|
||||||
r"unicode_escape"
|
|
||||||
)[1:-1]
|
|
||||||
|
|
||||||
# 为每个表生成个6个基本部分
|
# 为每个表生成个6个基本部分
|
||||||
create = self.gen_create(table_ddl)
|
create = self.gen_create(table_ddl)
|
||||||
pk = self.gen_pk(table_name)
|
has_id = any(col["name"].lower() == "id" for col in table_ddl["columns"])
|
||||||
|
pk = self.gen_pk(table_name) if has_id else ""
|
||||||
uk = self.gen_uk(table_ddl)
|
uk = self.gen_uk(table_ddl)
|
||||||
index = self.gen_index(table_ddl)
|
index = self.gen_index(table_ddl)
|
||||||
comment = self.gen_comment(table_ddl)
|
comment = self.gen_comment(table_ddl)
|
||||||
|
|
@ -320,25 +338,31 @@ class PostgreSQLConvertor(Convertor):
|
||||||
|
|
||||||
if type == "varchar":
|
if type == "varchar":
|
||||||
return f"varchar({size})"
|
return f"varchar({size})"
|
||||||
if type in ("int", "int unsigned"):
|
if type in ("int", "int unsigned", "int unsigned zerofill"):
|
||||||
return "int4"
|
return "int4"
|
||||||
if type in ("bigint", "bigint unsigned"):
|
if type in ("bigint", "bigint unsigned"):
|
||||||
return "int8"
|
return "int8"
|
||||||
if type == "datetime":
|
if type in ("tinyint", "smallint", "tinyint unsigned"):
|
||||||
|
return "int2"
|
||||||
|
if type in ("datetime", "timestamp null"):
|
||||||
return "timestamp"
|
return "timestamp"
|
||||||
|
if type == "date":
|
||||||
|
return "date"
|
||||||
|
if type == "json":
|
||||||
|
return "jsonb"
|
||||||
|
if type == "double":
|
||||||
|
return "double precision"
|
||||||
if type == "timestamp":
|
if type == "timestamp":
|
||||||
return f"timestamp({size})"
|
return f"timestamp({size})" if size else "timestamp"
|
||||||
if type == "bit":
|
if type == "bit":
|
||||||
return "bool"
|
return "bool"
|
||||||
if type in ("tinyint", "smallint"):
|
|
||||||
return "int2"
|
|
||||||
if type in ("text", "longtext"):
|
if type in ("text", "longtext"):
|
||||||
return "text"
|
return "text"
|
||||||
if type in ("blob", "mediumblob"):
|
if type in ("blob", "mediumblob", "longblob"):
|
||||||
return "bytea"
|
return "bytea"
|
||||||
if type == "decimal":
|
if type == "decimal":
|
||||||
return (
|
return (
|
||||||
f"numeric({','.join(str(s) for s in size)})" if len(size) else "numeric"
|
f"numeric({','.join(str(s) for s in size)})" if size and len(size) else "numeric"
|
||||||
)
|
)
|
||||||
|
|
||||||
def gen_create(self, ddl: Dict) -> str:
|
def gen_create(self, ddl: Dict) -> str:
|
||||||
|
|
@ -351,6 +375,10 @@ class PostgreSQLConvertor(Convertor):
|
||||||
|
|
||||||
type = col["type"].lower()
|
type = col["type"].lower()
|
||||||
full_type = self.translate_type(type, col["size"])
|
full_type = self.translate_type(type, col["size"])
|
||||||
|
if full_type is None:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"未支持的类型: '{col['type']}' (列: {name}, 表: {ddl['table_name']})"
|
||||||
|
)
|
||||||
nullable = "NULL" if col["nullable"] else "NOT NULL"
|
nullable = "NULL" if col["nullable"] else "NOT NULL"
|
||||||
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
|
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
|
||||||
return f"{name} {full_type} {nullable} {default}"
|
return f"{name} {full_type} {nullable} {default}"
|
||||||
|
|
@ -407,6 +435,8 @@ CREATE TABLE {table_name} (
|
||||||
"""生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence"""
|
"""生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence"""
|
||||||
|
|
||||||
inserts = list(Convertor.inserts(table_name, self.content))
|
inserts = list(Convertor.inserts(table_name, self.content))
|
||||||
|
# 转换 MySQL 字符串转义为 PostgreSQL 格式:\\ -> \,\' -> ''
|
||||||
|
inserts = [re.sub(r"\\\\|\\'", lambda m: "\\" if m.group() == "\\\\" else "''", s) for s in inserts]
|
||||||
## 生成 insert 脚本
|
## 生成 insert 脚本
|
||||||
script = ""
|
script = ""
|
||||||
last_id = 0
|
last_id = 0
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,24 @@ public class JsonUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||||
|
*
|
||||||
|
* @param text 字符串
|
||||||
|
* @param clazz 类型
|
||||||
|
* @return 指定类型的对象
|
||||||
|
*/
|
||||||
|
public static <T> T parseObjectQuietly(String text, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||||
if (StrUtil.isEmpty(text)) {
|
if (StrUtil.isEmpty(text)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,7 +5,12 @@ import cn.iocoder.yudao.framework.ip.core.Area;
|
||||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AreaUtils} 的单元测试
|
* {@link AreaUtils} 的单元测试
|
||||||
|
|
@ -31,6 +36,46 @@ public class AreaUtilsTest {
|
||||||
assertEquals(AreaUtils.format(110105), "北京市 北京市 朝阳区");
|
assertEquals(AreaUtils.format(110105), "北京市 北京市 朝阳区");
|
||||||
assertEquals(AreaUtils.format(1), "中国");
|
assertEquals(AreaUtils.format(1), "中国");
|
||||||
assertEquals(AreaUtils.format(2), "蒙古");
|
assertEquals(AreaUtils.format(2), "蒙古");
|
||||||
|
// 中国台湾省:省/市/区三级
|
||||||
|
assertEquals(AreaUtils.format(710101), "台湾省 台北市 中正区");
|
||||||
|
// 自定义分隔符
|
||||||
|
assertEquals(AreaUtils.format(110105, "/"), "北京市/北京市/朝阳区");
|
||||||
|
// 不存在的编号
|
||||||
|
assertNull(AreaUtils.format(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseArea() {
|
||||||
|
// 调用:通过路径解析得到地区
|
||||||
|
Area area = AreaUtils.parseArea("北京市/北京市/朝阳区");
|
||||||
|
// 断言
|
||||||
|
assertNotNull(area);
|
||||||
|
assertEquals(area.getId(), 110105);
|
||||||
|
// 路径不存在时返回 null
|
||||||
|
assertNull(AreaUtils.parseArea("不存在/路径"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetParentIdByType() {
|
||||||
|
// 调用:朝阳区向上找省
|
||||||
|
Integer provinceId = AreaUtils.getParentIdByType(110105, AreaTypeEnum.PROVINCE);
|
||||||
|
// 断言
|
||||||
|
assertEquals(provinceId, 110000);
|
||||||
|
// 自身就是目标类型
|
||||||
|
assertEquals(AreaUtils.getParentIdByType(110000, AreaTypeEnum.PROVINCE), 110000);
|
||||||
|
// 不存在的编号返回 null
|
||||||
|
assertNull(AreaUtils.getParentIdByType(-1, AreaTypeEnum.PROVINCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetByType() {
|
||||||
|
// 调用:获取所有省份
|
||||||
|
List<Area> provinces = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
|
||||||
|
// 断言:包含北京、台湾、香港、澳门
|
||||||
|
assertTrue(provinces.stream().anyMatch(area -> "北京市".equals(area.getName())));
|
||||||
|
assertTrue(provinces.stream().anyMatch(area -> "台湾省".equals(area.getName())));
|
||||||
|
assertTrue(provinces.stream().anyMatch(area -> "香港特别行政区".equals(area.getName())));
|
||||||
|
assertTrue(provinces.stream().anyMatch(area -> "澳门特别行政区".equals(area.getName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package cn.iocoder.yudao.module.bpm.enums.definition;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BPM 条件表达式操作符枚举
|
||||||
|
*
|
||||||
|
* @author Lesan
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum BpmConditionOpCodeEnum {
|
||||||
|
|
||||||
|
EQ("==", "等于", " var:getOrDefault(%s, null) == %s "),
|
||||||
|
NE("!=", "不等于", " var:getOrDefault(%s, null) != %s "),
|
||||||
|
GT(">", "大于", " var:getOrDefault(%s, null) > %s "),
|
||||||
|
GE(">=", "大于等于", " var:getOrDefault(%s, null) >= %s "),
|
||||||
|
LT("<", "小于", " var:getOrDefault(%s, null) < %s "),
|
||||||
|
LE("<=", "小于等于", " var:getOrDefault(%s, null) <= %s "),
|
||||||
|
|
||||||
|
CONTAINS("contain", "包含", " var:contains(%s, %s) "),
|
||||||
|
NOT_CONTAINS("!contain", "不包含", " !var:contains(%s, %s) ");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String des;
|
||||||
|
private final String symbol;
|
||||||
|
|
||||||
|
public static BpmConditionOpCodeEnum fromCode(String code) {
|
||||||
|
for (BpmConditionOpCodeEnum op : BpmConditionOpCodeEnum.values()) {
|
||||||
|
if (op.code.equalsIgnoreCase(code)) {
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("未知操作符: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程表单字段 VO
|
* 流程表单字段 VO
|
||||||
*/
|
*/
|
||||||
|
|
@ -20,5 +22,9 @@ public class BpmFormFieldVO {
|
||||||
* 字段标题
|
* 字段标题
|
||||||
*/
|
*/
|
||||||
private String title;
|
private String title;
|
||||||
|
/**
|
||||||
|
* 子表单字段(处理布局组件)
|
||||||
|
*/
|
||||||
|
private List<BpmFormFieldVO> children;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,15 +66,15 @@ public class BpmProcessInstanceCopyController {
|
||||||
Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
|
Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
|
||||||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
|
||||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
|
||||||
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
|
copy -> Stream.of(copy.getStartUserId(), copy.getUserId())));
|
||||||
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
|
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||||
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));
|
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));
|
||||||
return success(convertPage(pageResult, copy -> {
|
return success(convertPage(pageResult, copy -> {
|
||||||
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
|
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
|
||||||
MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),
|
MapUtils.findAndThen(userMap, copy.getUserId(),
|
||||||
user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
|
||||||
MapUtils.findAndThen(userMap, copy.getStartUserId(),
|
|
||||||
user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
||||||
|
MapUtils.findAndThen(userMap, copy.getStartUserId(),
|
||||||
|
user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
|
||||||
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
|
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
|
||||||
processInstance -> {
|
processInstance -> {
|
||||||
copyVO.setSummary(FlowableUtils.getSummary(
|
copyVO.setSummary(FlowableUtils.getSummary(
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class BpmOALeaveDO extends BaseDO {
|
||||||
/**
|
/**
|
||||||
* 请假类型
|
* 请假类型
|
||||||
*/
|
*/
|
||||||
private String type;
|
private Integer type;
|
||||||
/**
|
/**
|
||||||
* 原因
|
* 原因
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import org.springframework.stereotype.Component;
|
||||||
/**
|
/**
|
||||||
* 根据流程变量 variable 的类型,转换参数的值
|
* 根据流程变量 variable 的类型,转换参数的值
|
||||||
*
|
*
|
||||||
* 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中
|
* @deprecated 已无调用方
|
||||||
*
|
|
||||||
* @author jason
|
* @author jason
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // TODO @芋艿:兼容老版本,预计 27 年删除;
|
||||||
@Component
|
@Component
|
||||||
public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction {
|
public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
|
|
@ -247,9 +248,7 @@ public class FlowableUtils {
|
||||||
Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
|
Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
|
||||||
processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
|
processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
|
||||||
BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
|
BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
|
||||||
if (formField != null) {
|
parseFormField(formField, formFieldsMap);
|
||||||
formFieldsMap.put(formField.getField(), formField);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 情况一:当自定义了摘要
|
// 情况一:当自定义了摘要
|
||||||
|
|
@ -273,6 +272,26 @@ public class FlowableUtils {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归解析表单字段
|
||||||
|
*/
|
||||||
|
private static void parseFormField(BpmFormFieldVO formField, Map<String, BpmFormFieldVO> formFieldsMap) {
|
||||||
|
if (formField == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 如果存在 children -> 说明是布局组件
|
||||||
|
if (formField.getChildren() != null && !formField.getChildren().isEmpty()) {
|
||||||
|
for (BpmFormFieldVO child : formField.getChildren()) {
|
||||||
|
parseFormField(child, formFieldsMap);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 真实字段才加入 map
|
||||||
|
if (StrUtil.isNotBlank(formField.getField())) {
|
||||||
|
formFieldsMap.put(formField.getField(), formField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Task 相关的工具方法 ==========
|
// ========== Task 相关的工具方法 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -707,10 +707,9 @@ public class SimpleModelUtils {
|
||||||
List<String> list = convertList(item.getRules(), (rule) -> {
|
List<String> list = convertList(item.getRules(), (rule) -> {
|
||||||
String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
|
String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide()
|
||||||
: "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
|
: "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号
|
||||||
return String.format(" vars:getOrDefault(%s, null) %s var:convertByType(%s,%s) ",
|
return String.format(BpmConditionOpCodeEnum.fromCode(rule.getOpCode()).getSymbol(),
|
||||||
rule.getLeftSide(), // 左侧:读取变量
|
rule.getLeftSide(), // 左侧:读取变量
|
||||||
rule.getOpCode(), // 中间:操作符,比较
|
rightSide); // 右侧:取值变量
|
||||||
rule.getLeftSide(), rightSide); // 右侧:转换变量,VariableConvertByTypeExpressionFunction
|
|
||||||
});
|
});
|
||||||
// 构造条件组的表达式
|
// 构造条件组的表达式
|
||||||
Boolean and = item.getAnd();
|
Boolean and = item.getAnd();
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.bpm.service.definition.dto;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BPM 流程 MetaInfo Response DTO
|
|
||||||
* 主要用于 { Model#setMetaInfo(String)} 的存储
|
|
||||||
*
|
|
||||||
* 最终,它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class BpmModelMetaInfoRespDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流程图标
|
|
||||||
*/
|
|
||||||
private String icon;
|
|
||||||
/**
|
|
||||||
* 流程描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 表单类型
|
|
||||||
*/
|
|
||||||
private Integer formType;
|
|
||||||
/**
|
|
||||||
* 表单编号
|
|
||||||
* 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
|
|
||||||
*/
|
|
||||||
private Long formId;
|
|
||||||
/**
|
|
||||||
* 自定义表单的提交路径,使用 Vue 的路由地址
|
|
||||||
* 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
|
|
||||||
*/
|
|
||||||
private String formCustomCreatePath;
|
|
||||||
/**
|
|
||||||
* 自定义表单的查看路径,使用 Vue 的路由地址
|
|
||||||
* 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
|
|
||||||
*/
|
|
||||||
private String formCustomViewPath;
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue