Files
flow-center-service/src/main/java/com/yunhai/flowcenter/engine/FlowDefModel.java
T

211 lines
8.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.flowcenter.engine;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.utils.exception.ErrorCode;
import java.util.List;
import java.util.Map;
/**
* 流程定义 JSON 模型(概览 §5.1 规范,一期 5 节点)。
* 序列化/反序列化统一走 {@link #parse(ObjectMapper, String)},未知属性忽略(向前兼容)。
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class FlowDefModel {
/** 节点类型(一期 5 种,二期按需扩展 NOTIFY/CONDITION) */
public static final String TYPE_START = "START";
public static final String TYPE_END = "END";
public static final String TYPE_APPROVAL = "APPROVAL";
public static final String TYPE_PARALLEL_SPLIT = "PARALLEL_SPLIT";
public static final String TYPE_PARALLEL_JOIN = "PARALLEL_JOIN";
/** 抄送节点(2026-09-23 二期第一批):瞬时放行,逐人落 flw_cc + CC_SENT,不建任务不阻塞 */
public static final String TYPE_NOTIFY = "NOTIFY";
/**
* 条件分支节点(2026-09-24 二期第二批 §4):激活时同步调外部判断服务(flw_router 注册,
* routerKey 寻址)按返回出口名选路;失败挂起待管理端重跑,禁止配默认出口。
* 出边=互斥分支,name 必填且唯一(出口名即判断接口的 outlets 契约)。
*/
public static final String TYPE_CONDITION = "CONDITION";
/** CONDITION 判断方式(2026-10-10 四议题):外部判断服务(缺省=旧口径,routerKey 寻址) */
public static final String JUDGE_MODE_SERVICE = "SERVICE";
/** CONDITION 判断方式:内置表达式引擎(出边 condition 即 AviatorScript 表达式) */
public static final String JUDGE_MODE_EXPRESSION = "EXPRESSION";
/** 会签模式 */
public static final String COUNTERSIGN_ALL = "ALL";
public static final String COUNTERSIGN_ANY = "ANY";
/** 拒绝策略(一期仅 END_REJECTED) */
public static final String REJECT_END_REJECTED = "END_REJECTED";
/** 办理人类型(概览 §六:一期 DEPT_KEY/USERS;SUBMITTER_DEPT/FORM_FIELD 二期) */
public static final String ASSIGNEE_DEPT_KEY = "DEPT_KEY";
public static final String ASSIGNEE_USERS = "USERS";
/** 发起时指派选择模式:SINGLE=恰 1 人;MULTI=多选(min~max) */
public static final String ASSIGN_MODE_SINGLE = "SINGLE";
public static final String ASSIGN_MODE_MULTI = "MULTI";
public String code;
public String name;
public String start;
public List<Node> nodes;
public List<Edge> edges;
public Settings settings;
/**
* 画布坐标(nodeId → 左上角),仅前端设计器/查看器复用;引擎不消费。
* 缺失时前端自行做自动布局,故可选、可空。
*/
public Map<String, Layout> layout;
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Node {
public String id;
public String type;
public String name;
public Assignee assignee;
/** NOTIFY 抄送接收人(复用 Assignee 结构:DEPT_KEY/USERS 两档,解析走 AssigneeResolver) */
public Assignee receivers;
/** CONDITION 判断服务注册键(flw_router.router_key,发布期校验须已注册且启用) */
public String routerKey;
/** CONDITION 判断方式:SERVICE(缺省,routerKey 外部服务)|EXPRESSION(出边 condition 表达式) */
public String judgeMode;
/** 是否表达式判断(null 视作 SERVICE,旧 contentJson 无此字段天然走服务口径) */
public boolean expressionJudge() {
return JUDGE_MODE_EXPRESSION.equals(judgeMode);
}
/** ALL(默认)|ANY */
public String countersign;
/** PARALLEL_JOIN 汇聚条件,一期仅 ALL */
public String waitFor;
/** 办理时限小时数(自然时间,任务 arrive_time + N 小时;可空) */
public Integer deadlineHours;
/** 发起时指派策略(2026-10-13 方案):存在即允许指派;缺省/缺失=不允许(向前兼容) */
public AssignPolicy assignPolicy;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Assignee {
/** DEPT_KEY | USERS */
public String type;
public String deptKey;
public List<Integer> userIds;
}
/** 发起时指派策略:mode 必填(容错 null=MULTI);min/max 可空(缺省 1/候选全量数);required 仅子应用 UI 用 */
@JsonIgnoreProperties(ignoreUnknown = true)
public static class AssignPolicy {
/** SINGLE | MULTI */
public String mode;
public Integer min;
public Integer max;
public Boolean required;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Edge {
public String from;
public String to;
/**
* 出边名(按钮文案)。单出口可空;多出口节点必填且同节点内唯一,
* 办理人在办理页选它决定走哪条边(见 {@link #edgeByName})。
*/
public String name;
/** 出口条件(二期条件自动路由预留;一期只校验透传,引擎不消费) */
public String condition;
}
/** 画布坐标(前端设计器专用) */
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Layout {
public Integer x;
public Integer y;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Settings {
public String rejectPolicy = REJECT_END_REJECTED;
public Urge urge = new Urge();
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Urge {
public Boolean enabled = true;
public Integer intervalHours = 24;
public Integer maxTimes = 3;
}
public static FlowDefModel parse(ObjectMapper objectMapper, String json) {
try {
FlowDefModel model = objectMapper.readValue(json, FlowDefModel.class);
if (model == null || model.nodes == null || model.edges == null) {
throw new BizException(ErrorCode.PARAM_ERROR, "流程定义 JSON 缺少 nodes/edges");
}
if (model.settings == null) {
model.settings = new Settings();
}
return model;
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new BizException(ErrorCode.PARAM_ERROR, "流程定义 JSON 不合法:" + e.getMessage());
}
}
public Node node(String nodeId) {
for (Node n : nodes) {
if (n.id != null && n.id.equals(nodeId)) {
return n;
}
}
return null;
}
public Node nodeById(String nodeId) {
Node n = node(nodeId);
if (n == null) {
throw new BizException(ErrorCode.PARAM_ERROR, "流程定义缺少节点:" + nodeId);
}
return n;
}
/** 出边集合(保持定义顺序) */
public List<Edge> outEdges(String nodeId) {
java.util.List<Edge> out = new java.util.ArrayList<>();
for (Edge e : edges) {
if (nodeId != null && nodeId.equals(e.from)) {
out.add(e);
}
}
return out;
}
public List<Edge> inEdges(String nodeId) {
java.util.List<Edge> in = new java.util.ArrayList<>();
for (Edge e : edges) {
if (nodeId != null && nodeId.equals(e.to)) {
in.add(e);
}
}
return in;
}
/** 按出边名取边(多出口节点路由用);未命中返回 null */
public Edge edgeByName(String fromNodeId, String name) {
if (name == null) {
return null;
}
for (Edge e : outEdges(fromNodeId)) {
if (name.equals(e.name)) {
return e;
}
}
return null;
}
}