|
|
@@ -0,0 +1,1197 @@
|
|
|
+# S1 产销协同 — 订单管理 · 审批流程实施方案
|
|
|
+
|
|
|
+> **版本**:v2.0 · 2026-04-15(决策确认版)
|
|
|
+> **范围**:合同评审、订单评审、订单交付、订单发货
|
|
|
+> **目标**:基于 Admin.NET 现有审批流程插件(方案 B),补全流程运行时引擎,实现**仅通过配置(画流程图 + 设节点属性)即可创建和运行各类审批业务流程**
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 目录
|
|
|
+
|
|
|
+0. [决策记录](#0-决策记录)
|
|
|
+1. [现有审批流程插件评估](#1-现有审批流程插件评估)
|
|
|
+2. [差距分析与问题清单](#2-差距分析与问题清单)
|
|
|
+3. [整体架构方案](#3-整体架构方案)
|
|
|
+4. [数据模型设计](#4-数据模型设计)
|
|
|
+5. [后端核心改造](#5-后端核心改造)
|
|
|
+6. [前端核心改造](#6-前端核心改造)
|
|
|
+7. [通知机制设计](#7-通知机制设计)
|
|
|
+8. [业务联动:回调注册机制](#8-业务联动回调注册机制)
|
|
|
+9. [S1 订单管理业务接入示例](#9-s1-订单管理业务接入示例)
|
|
|
+10. [配置化使用指南](#10-配置化使用指南)
|
|
|
+11. [实施路线图](#11-实施路线图)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 0. 决策记录
|
|
|
+
|
|
|
+> 以下为 2026-04-15 商议确认的方案决策,作为后续实施的依据。
|
|
|
+
|
|
|
+### 0.1 总体方案
|
|
|
+
|
|
|
+| 决策项 | 决策结果 |
|
|
|
+|--------|---------|
|
|
|
+| 技术路线 | **方案 B**:基于现有 `Admin.NET.Plugin.ApprovalFlow` 插件增量扩展 |
|
|
|
+| 配置化目标 | 业务流程(合同评审、订单评审等)**仅通过配置**即可创建,无需写审批引擎代码 |
|
|
|
+| 配置化边界 | L1(流程图+审批人)+ L2(条件分支)纯配置化;L3(业务联动)通过轻量回调注册机制 |
|
|
|
+
|
|
|
+### 0.2 功能范围
|
|
|
+
|
|
|
+| 决策项 | 决策结果 |
|
|
|
+|--------|---------|
|
|
|
+| 流程发起方式 | **A+C 兼备**:业务表单内嵌入 `<ApprovalPanel>` 快捷发起 + 审批中心统一发起入口 |
|
|
|
+| 审批人类型 | **指定用户、指定角色、指定部门、发起人本人**(4 种);发起人上级、表单字段值后续扩展 |
|
|
|
+| 多人审批模式 | **或签 + 会签**;依次审批后续扩展 |
|
|
|
+| 审批操作 | **全部实现**:同意、拒绝、转办、撤回、退回上一步、加签、催办(共 7 种) |
|
|
|
+| 通知机制 | **全部实现**,按优先级分批交付:页面待办 → SignalR 站内信 → 钉钉/企微 → 邮件/短信 |
|
|
|
+| 条件分支 | 排他网关 + 简单条件表达式(配置化) |
|
|
|
+| 与现有代码关系 | **先并行**:保留原有 SeOrder 硬编码 API,新增审批流驱动路径,验证可用后再切换 |
|
|
|
+| 业务联动 | 回调注册机制(`IFlowBizHandler` 接口),每种业务类型实现一个 Handler(约 10-20 行代码) |
|
|
|
+
|
|
|
+### 0.3 插件现状说明
|
|
|
+
|
|
|
+> Admin.NET 框架定位为"通用权限开发平台"(RBAC),**审批流程不在其官方 26 项内置功能清单中**。
|
|
|
+> `Admin.NET.Plugin.ApprovalFlow` 是一个**插件骨架示例**,提供了流程定义 CRUD + LogicFlow 画布,
|
|
|
+> 但**不包含运行时引擎**(状态机推进、审批人分配、待办/已办、同意/拒绝等操作)。
|
|
|
+> 这是框架设计如此,非版本过旧或获取不完整。本方案的核心工作是补齐运行时部分。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 现有审批流程插件评估
|
|
|
+
|
|
|
+### 1.1 已有能力(可复用)
|
|
|
+
|
|
|
+| 层面 | 现状 | 可用性 |
|
|
|
+|------|------|--------|
|
|
|
+| **流程定义 CRUD** | `ApprovalFlowService` 提供增删改查、分页、详情 | ✅ 可用 |
|
|
|
+| **流程设计器(前端)** | LogicFlow 2.x 画布 + BPMN 节点 + 拖拽面板 + 小地图 + 快照导出 | ✅ 基本可用 |
|
|
|
+| **流程 JSON 持久化** | `FlowJson`(nodes/edges)+ `FormJson`(数据库/表/操作类型)存于 `approval_flow` 表 | ✅ 可用 |
|
|
|
+| **表单关联** | `editFormDialog.vue` 可选择数据库、数据表、操作类型 | ⚠️ 仅存储元数据,不生成动态表单 |
|
|
|
+| **菜单种子** | `SysMenuSeedData.cs` 已注册 `/platform/approvalFlow` 菜单 | ✅ 可用 |
|
|
|
+| **中间件匹配** | `ApprovalFlowMiddleware` + `SysApprovalService.MatchApproval` | ⚠️ 仅做记录,不阻断 |
|
|
|
+
|
|
|
+### 1.2 插件代码结构
|
|
|
+
|
|
|
+```
|
|
|
+server/Plugins/Admin.NET.Plugin.ApprovalFlow/
|
|
|
+├── Entity/
|
|
|
+│ ├── ApprovalFlow.cs # 流程定义(模板)
|
|
|
+│ ├── ApprovalFlowRecord.cs # 流程实例记录(简陋)
|
|
|
+│ ├── ApprovalForm.cs # 表单定义(未使用)
|
|
|
+│ └── ApprovalFormRecord.cs # 表单变更记录
|
|
|
+├── Enum/
|
|
|
+│ └── FlowTypeEnum.cs # 空枚举(占位)
|
|
|
+├── Middleware/
|
|
|
+│ └── ApprovalFlowMiddleware.cs # 请求中间件
|
|
|
+├── Service/
|
|
|
+│ ├── ApprovalFlow/
|
|
|
+│ │ ├── ApprovalFlowService.cs # CRUD + FlowList + FormRoutes
|
|
|
+│ │ └── Dto/
|
|
|
+│ │ ├── ApprovalFlowItem.cs # FlowJson 的 nodes/edges 结构
|
|
|
+│ │ ├── ApprovalFormItem.cs # FormJson 结构 + Route 计算
|
|
|
+│ │ ├── ApprovalFlowInput.cs # 分页/筛选/增删改查入参
|
|
|
+│ │ └── ApprovalFlowOutput.cs # 列表/详情出参
|
|
|
+│ └── SysApproval/
|
|
|
+│ └── SysApprovalService.cs # 中间件调用的匹配逻辑
|
|
|
+├── SeedData/
|
|
|
+│ └── SysMenuSeedData.cs
|
|
|
+├── Configuration/
|
|
|
+│ └── ApprovalFlow.json
|
|
|
+├── Startup.cs
|
|
|
+└── GlobalUsings.cs
|
|
|
+```
|
|
|
+
|
|
|
+**前端**(`Web/src/views/approvalFlow/`):
|
|
|
+
|
|
|
+```
|
|
|
+index.vue # 列表主页
|
|
|
+component/
|
|
|
+├── editDialog.vue # 基本信息编辑
|
|
|
+├── editFormDialog.vue # 表单(DB 元数据)编辑
|
|
|
+├── editFlowDialog.vue # LogicFlow 流程设计器
|
|
|
+├── detailDialog.vue # 详情查看
|
|
|
+└── LogicFlow/
|
|
|
+ ├── Panel/
|
|
|
+ │ ├── PanelControl.vue # 工具栏(缩放/撤销/截图/小地图)
|
|
|
+ │ ├── PanelNode.vue # 节点拖拽面板
|
|
|
+ │ └── PanelDataDialog.vue # JSON 数据查看
|
|
|
+ └── Property/
|
|
|
+ ├── PropertyDialog.vue # 属性面板容器
|
|
|
+ └── PropertyCommon.vue # 节点基本信息(只读 id/type)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 差距分析与问题清单
|
|
|
+
|
|
|
+### 2.1 关键缺失
|
|
|
+
|
|
|
+| # | 缺失项 | 严重度 | 说明 |
|
|
|
+|---|--------|--------|------|
|
|
|
+| G1 | **流程运行时引擎** | 🔴 致命 | 无节点推进、无状态机、无自动流转逻辑;中间件只插入记录不阻断请求 |
|
|
|
+| G2 | **审批人/角色指派** | 🔴 致命 | `FlowProperties` 是空类 `{}`;节点上无法配置审批人、角色、部门 |
|
|
|
+| G3 | **审批操作 API** | 🔴 致命 | 无"同意/拒绝/转办/加签/撤回/退回/催办"等运行时操作接口 |
|
|
|
+| G4 | **待办/已办查询** | 🔴 致命 | 无按用户查询待审批、已审批、我发起的流程等 API |
|
|
|
+| G5 | **条件分支执行** | 🟡 重要 | LogicFlow 画布可画网关,但后端无条件判断执行逻辑 |
|
|
|
+| G6 | **会签/或签** | 🟡 重要 | 无多人审批聚合模式 |
|
|
|
+| G7 | **通知机制** | 🟡 重要 | 无站内信/WebSocket/邮件/钉钉推送集成 |
|
|
|
+| G8 | **节点属性编辑** | 🟢 一般 | 前端 PropertyCommon 只读显示 id/type,扩展信息为空 |
|
|
|
+
|
|
|
+### 2.2 现有代码问题
|
|
|
+
|
|
|
+| # | 问题 | 风险 | 处理方式 |
|
|
|
+|---|------|------|---------|
|
|
|
+| B1 | `SysApprovalService.MatchApproval` 对**每个请求**都执行 DB 查询 | 高 | 改为启动时缓存 + 事件刷新 |
|
|
|
+| B2 | 路由匹配用 `Contains` 子串匹配,容易误匹配 | 中 | 改为精确路径比较 |
|
|
|
+| B3 | `FlowList` 用 `FirstAsync()` 查询,code 不存在时直接抛异常 | 中 | 改为 `FirstOrDefaultAsync` + 空校验 |
|
|
|
+| B4 | `ApprovalFormItem.Route` 在 `EntityName` 为 null/空时抛异常 | 中 | 加空值保护 |
|
|
|
+| B5 | `FlowProperties` 空类导致节点自定义属性无法存储 | 高 | 改为结构化定义 |
|
|
|
+| B6 | `FlowTypeEnum` 为空枚举 | 低 | 补充定义 |
|
|
|
+| B7 | 前端 undo/redo 按钮始终 disabled | 低 | 绑定 LogicFlow history 事件 |
|
|
|
+| B8 | 中间件构造函数用 `App.GetRequiredService` 而非 DI | 低 | 改为 `InvokeAsync` 参数注入 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 整体架构方案
|
|
|
+
|
|
|
+### 3.1 方案选型(已确认:方案 B)
|
|
|
+
|
|
|
+| 方案 | 描述 | 结论 |
|
|
|
+|------|------|------|
|
|
|
+| A. 引入第三方引擎(Elsa 3.x) | NuGet 引入 Elsa | ❌ 不采用 |
|
|
|
+| **B. 基于现有插件增量扩展** | 补齐运行时引擎 | ✅ **已确认采用** |
|
|
|
+| C. 完全自建新插件 | 从头新建 | ❌ 不采用 |
|
|
|
+
|
|
|
+### 3.2 架构全景
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────────────────────────────────────────────┐
|
|
|
+│ 前端 (Vue 3) │
|
|
|
+│ │
|
|
|
+│ ┌──────────────┐ ┌───────────────┐ ┌──────────────────────────┐ │
|
|
|
+│ │ 流程设计器 │ │ 业务表单 │ │ 审批中心 │ │
|
|
|
+│ │ (LogicFlow) │ │ + ApprovalPanel│ │ 待办/已办/我发起的/时间线 │ │
|
|
|
+│ └──────┬───────┘ └──────┬────────┘ └────────────┬─────────────┘ │
|
|
|
+└─────────┼─────────────────┼────────────────────────┼───────────────┘
|
|
|
+ │ 设计/发布 │ 发起/查看 │ 审批操作
|
|
|
+ ▼ ▼ ▼
|
|
|
+┌─────────────────────────────────────────────────────────────────────┐
|
|
|
+│ 后端 API 层 │
|
|
|
+│ │
|
|
|
+│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
|
|
|
+│ │ ApprovalFlow │ │ FlowInstance │ │ FlowTask │ │
|
|
|
+│ │ Service │ │ Service │ │ Service │ │
|
|
|
+│ │ (流程定义 │ │ (流程实例 │ │ (审批任务 │ │
|
|
|
+│ │ CRUD+发布) │ │ 发起/查询) │ │ 同意/拒绝/转办/撤回 │ │
|
|
|
+│ └──────────────┘ └──────────────┘ │ 退回/加签/催办) │ │
|
|
|
+│ └──────────────────────────┘ │
|
|
|
+│ │ │
|
|
|
+│ ┌────────▼────────┐ │
|
|
|
+│ │ FlowEngine │ ← 核心推进引擎 │
|
|
|
+│ │ ┌─────────────┐ │ │
|
|
|
+│ │ │ 状态机 │ │ │
|
|
|
+│ │ │ 条件分支 │ │ │
|
|
|
+│ │ │ 审批人解析 │ │ │
|
|
|
+│ │ │ 会签/或签 │ │ │
|
|
|
+│ │ └─────────────┘ │ │
|
|
|
+│ └────────┬────────┘ │
|
|
|
+│ │ │
|
|
|
+│ ┌─────────────────┼──────────────────┐ │
|
|
|
+│ ▼ ▼ ▼ │
|
|
|
+│ ┌────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
|
+│ │ 通知服务 │ │ 回调注册 │ │ 审计日志 │ │
|
|
|
+│ │ SignalR │ │ IFlowBiz- │ │ FlowLog │ │
|
|
|
+│ │ 钉钉/企微 │ │ Handler │ │ │ │
|
|
|
+│ │ 邮件/短信 │ │ (业务联动) │ │ │ │
|
|
|
+│ └────────────┘ └──────────────┘ └──────────────┘ │
|
|
|
+└─────────────────────────────────────────────────────────────────────┘
|
|
|
+ │
|
|
|
+ ▼
|
|
|
+┌─────────────────────────────────────────────────────────────────────┐
|
|
|
+│ 数据层 (MySQL) │
|
|
|
+│ │
|
|
|
+│ approval_flow 流程定义(模板) ← 已有,需扩展 │
|
|
|
+│ approval_flow_instance 流程实例 ← 新增 │
|
|
|
+│ approval_flow_task 审批任务 ← 新增 │
|
|
|
+│ approval_flow_log 操作日志 ← 新增 │
|
|
|
+└─────────────────────────────────────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+### 3.3 关键设计决策
|
|
|
+
|
|
|
+**1) 节点定义不单独建表,存在 FlowJson 中**
|
|
|
+
|
|
|
+经考量,`approval_flow_node` 独立表会导致 FlowJson 与 Node 表的双重维护和同步问题。改为:
|
|
|
+
|
|
|
+- **前端 LogicFlow** 保存时将节点属性(审批人、审批方式等)写入每个 node 的 `properties` 字段
|
|
|
+- **后端** 读取 `FlowJson` 并反序列化得到节点列表,无需额外 Node 表
|
|
|
+- `FlowProperties` 从空类改为结构化类型,包含审批人配置
|
|
|
+
|
|
|
+这样 LogicFlow 画布即唯一数据源,前端所见即所得。
|
|
|
+
|
|
|
+**2) 中间件改为可选审计模式**
|
|
|
+
|
|
|
+`ApprovalFlowMiddleware` 不再用于核心审批推进。审批操作全部通过显式 API 调用完成。中间件保留但优化:仅做审计日志,按需开关。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 数据模型设计
|
|
|
+
|
|
|
+### 4.1 流程定义表(扩展现有 `approval_flow`)
|
|
|
+
|
|
|
+保留现有字段(`Id`, `Code`, `Name`, `FormJson`, `FlowJson`, `Status`, `Remark`),**新增**:
|
|
|
+
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| `BizType` | `string(64)` | 业务类型编码,如 `CONTRACT_REVIEW`、`ORDER_REVIEW`。配置化关键字段 |
|
|
|
+| `Version` | `int` | 版本号,每次发布 +1 |
|
|
|
+| `IsPublished` | `bool` | 是否已发布(只有已发布的流程可以被发起) |
|
|
|
+
|
|
|
+### 4.2 FlowJson 中的节点属性结构(`FlowProperties` 改造)
|
|
|
+
|
|
|
+不单独建 Node 表,而是将节点配置嵌入 `FlowJson` 的每个 node.properties 中:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "nodes": [
|
|
|
+ {
|
|
|
+ "id": "node_1",
|
|
|
+ "type": "bpmn:userTask",
|
|
|
+ "x": 300, "y": 200,
|
|
|
+ "text": { "value": "意见评审" },
|
|
|
+ "properties": {
|
|
|
+ "nodeName": "意见评审",
|
|
|
+ "approverType": "Role",
|
|
|
+ "approverIds": "1001,1002",
|
|
|
+ "approverNames": "销售部,技术部",
|
|
|
+ "multiApproveMode": "Any",
|
|
|
+ "timeoutHours": 48,
|
|
|
+ "timeoutAction": "Notify"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "id": "gateway_1",
|
|
|
+ "type": "bpmn:exclusiveGateway",
|
|
|
+ "properties": {
|
|
|
+ "conditions": [
|
|
|
+ {
|
|
|
+ "targetNodeId": "node_gm",
|
|
|
+ "expression": "bizData.contractAmount > 1000000",
|
|
|
+ "label": "金额>100万"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "targetNodeId": "node_dept",
|
|
|
+ "expression": "bizData.contractAmount <= 1000000",
|
|
|
+ "label": "金额≤100万",
|
|
|
+ "isDefault": true
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "edges": [ ... ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+对应 C# 反序列化结构:
|
|
|
+
|
|
|
+```csharp
|
|
|
+public class FlowProperties
|
|
|
+{
|
|
|
+ // ── 用户任务节点属性 ──
|
|
|
+ public string? NodeName { get; set; }
|
|
|
+ public string? ApproverType { get; set; } // SpecificUser / Role / Department / Initiator
|
|
|
+ public string? ApproverIds { get; set; } // 逗号分隔
|
|
|
+ public string? ApproverNames { get; set; } // 冗余,便于前端展示
|
|
|
+ public string? MultiApproveMode { get; set; } // Any / All
|
|
|
+ public int? TimeoutHours { get; set; }
|
|
|
+ public string? TimeoutAction { get; set; } // AutoApprove / AutoReject / Notify
|
|
|
+
|
|
|
+ // ── 网关节点属性 ──
|
|
|
+ public List<GatewayCondition>? Conditions { get; set; }
|
|
|
+}
|
|
|
+
|
|
|
+public class GatewayCondition
|
|
|
+{
|
|
|
+ public string TargetNodeId { get; set; }
|
|
|
+ public string Expression { get; set; }
|
|
|
+ public string? Label { get; set; }
|
|
|
+ public bool IsDefault { get; set; }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4.3 流程实例表(新增 `approval_flow_instance`)
|
|
|
+
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| `Id` | `long` | 主键 |
|
|
|
+| `FlowId` | `long` | 关联流程定义 Id |
|
|
|
+| `FlowVersion` | `int` | 发起时的流程版本 |
|
|
|
+| `BizType` | `string(64)` | 业务类型 |
|
|
|
+| `BizId` | `long` | 关联业务单据 Id(如订单 Id) |
|
|
|
+| `BizNo` | `string(128)` | 业务单号(冗余) |
|
|
|
+| `Title` | `string(256)` | 流程标题,如"合同评审-CRM20260415001" |
|
|
|
+| `InitiatorId` | `long` | 发起人 Id |
|
|
|
+| `InitiatorName` | `string(64)` | 发起人姓名(冗余) |
|
|
|
+| `Status` | `enum` | `Draft / Running / Approved / Rejected / Cancelled / Terminated` |
|
|
|
+| `CurrentNodeId` | `string(64)` | 当前所在节点 Id |
|
|
|
+| `FlowJsonSnapshot` | `BigString` | 发起时的完整 FlowJson 快照(含节点属性) |
|
|
|
+| `StartTime` | `DateTime` | 发起时间 |
|
|
|
+| `EndTime` | `DateTime?` | 结束时间 |
|
|
|
+| 继承 `EntityBaseOrg` | | 含 CreateUserId / TenantId / OrgId 等 |
|
|
|
+
|
|
|
+### 4.4 审批任务表(新增 `approval_flow_task`)
|
|
|
+
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| `Id` | `long` | 主键 |
|
|
|
+| `InstanceId` | `long` | 关联流程实例 Id |
|
|
|
+| `NodeId` | `string(64)` | 对应 FlowJson 中的 node.id |
|
|
|
+| `NodeName` | `string(128)` | 节点名称(冗余) |
|
|
|
+| `AssigneeId` | `long` | 指派审批人 Id |
|
|
|
+| `AssigneeName` | `string(64)` | 审批人姓名(冗余) |
|
|
|
+| `Status` | `enum` | `Pending / Approved / Rejected / Transferred / Cancelled / Returned` |
|
|
|
+| `Comment` | `string(1024)` | 审批意见 |
|
|
|
+| `ActionTime` | `DateTime?` | 操作时间 |
|
|
|
+| `TransferToId` | `long?` | 转办目标人 Id |
|
|
|
+| `IsAddSign` | `bool` | 是否为加签产生的任务 |
|
|
|
+| `AddSignById` | `long?` | 加签发起人 Id |
|
|
|
+| 继承 `EntityBaseOrg` | | |
|
|
|
+
|
|
|
+### 4.5 操作日志表(新增 `approval_flow_log`)
|
|
|
+
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| `Id` | `long` | 主键 |
|
|
|
+| `InstanceId` | `long` | 关联流程实例 Id |
|
|
|
+| `TaskId` | `long?` | 关联任务 Id(可空,Submit 无 Task) |
|
|
|
+| `NodeId` | `string(64)` | 节点 Id |
|
|
|
+| `Action` | `enum` | `Submit / Approve / Reject / Transfer / Withdraw / Return / AddSign / Urge / Cancel / AutoTimeout` |
|
|
|
+| `OperatorId` | `long` | 操作人 Id |
|
|
|
+| `OperatorName` | `string(64)` | 操作人姓名 |
|
|
|
+| `Comment` | `string(1024)` | 操作说明/审批意见 |
|
|
|
+| `CreateTime` | `DateTime` | 操作时间 |
|
|
|
+
|
|
|
+### 4.6 ER 关系图
|
|
|
+
|
|
|
+```
|
|
|
+┌───────────────────────────┐
|
|
|
+│ approval_flow │ ← 流程定义(模板)
|
|
|
+│ (已有表,扩展 3 个字段) │
|
|
|
+│ │
|
|
|
+│ FlowJson 内嵌节点属性: │
|
|
|
+│ · approverType │
|
|
|
+│ · approverIds │
|
|
|
+│ · multiApproveMode │
|
|
|
+│ · conditions (网关) │
|
|
|
+└─────────────┬─────────────┘
|
|
|
+ │ 1:N(通过 BizType 匹配)
|
|
|
+ ▼
|
|
|
+┌───────────────────────────┐
|
|
|
+│ approval_flow_instance │ ← 流程实例(运行态)
|
|
|
+│ · BizType + BizId │
|
|
|
+│ · Status (状态机) │
|
|
|
+│ · FlowJsonSnapshot │
|
|
|
+└─────────────┬─────────────┘
|
|
|
+ │ 1:N
|
|
|
+ ┌────────┴────────┐
|
|
|
+ ▼ ▼
|
|
|
+┌───────────┐ ┌───────────┐
|
|
|
+│ _task │ │ _log │
|
|
|
+│ (审批任务) │ │ (操作日志) │
|
|
|
+│ · Pending │ │ · Approve │
|
|
|
+│ · Approved│ │ · Reject │
|
|
|
+│ · ... │ │ · Transfer│
|
|
|
+└───────────┘ │ · Return │
|
|
|
+ │ · AddSign │
|
|
|
+ │ · Urge │
|
|
|
+ │ · ... │
|
|
|
+ └───────────┘
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 后端核心改造
|
|
|
+
|
|
|
+### 5.1 枚举定义
|
|
|
+
|
|
|
+```csharp
|
|
|
+public enum FlowInstanceStatusEnum
|
|
|
+{
|
|
|
+ [Description("草稿")] Draft = 0,
|
|
|
+ [Description("审批中")] Running = 1,
|
|
|
+ [Description("已通过")] Approved = 2,
|
|
|
+ [Description("已拒绝")] Rejected = 3,
|
|
|
+ [Description("已撤销")] Cancelled = 4,
|
|
|
+ [Description("已终止")] Terminated = 5,
|
|
|
+}
|
|
|
+
|
|
|
+public enum FlowTaskStatusEnum
|
|
|
+{
|
|
|
+ [Description("待审批")] Pending = 0,
|
|
|
+ [Description("已同意")] Approved = 1,
|
|
|
+ [Description("已拒绝")] Rejected = 2,
|
|
|
+ [Description("已转办")] Transferred = 3,
|
|
|
+ [Description("已取消")] Cancelled = 4,
|
|
|
+ [Description("已退回")] Returned = 5,
|
|
|
+}
|
|
|
+
|
|
|
+public enum FlowLogActionEnum
|
|
|
+{
|
|
|
+ [Description("提交")] Submit = 1,
|
|
|
+ [Description("同意")] Approve = 2,
|
|
|
+ [Description("拒绝")] Reject = 3,
|
|
|
+ [Description("转办")] Transfer = 4,
|
|
|
+ [Description("撤回")] Withdraw = 5,
|
|
|
+ [Description("退回")] Return = 6,
|
|
|
+ [Description("加签")] AddSign = 7,
|
|
|
+ [Description("催办")] Urge = 8,
|
|
|
+ [Description("取消")] Cancel = 9,
|
|
|
+ [Description("超时自动")] AutoTimeout = 10,
|
|
|
+}
|
|
|
+
|
|
|
+public enum ApproverTypeEnum
|
|
|
+{
|
|
|
+ [Description("指定用户")] SpecificUser = 1,
|
|
|
+ [Description("指定角色")] Role = 2,
|
|
|
+ [Description("指定部门")] Department = 3,
|
|
|
+ [Description("发起人")] Initiator = 4,
|
|
|
+}
|
|
|
+
|
|
|
+public enum MultiApproveModeEnum
|
|
|
+{
|
|
|
+ [Description("或签(一人通过即可)")] Any = 1,
|
|
|
+ [Description("会签(所有人通过)")] All = 2,
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.2 核心服务:流程引擎 FlowEngineService
|
|
|
+
|
|
|
+这是整个方案的核心。以下为完整的方法签名和职责说明:
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>
|
|
|
+/// 流程推进引擎 — 核心状态机
|
|
|
+/// 不暴露为 API,由其他 Service 内部调用
|
|
|
+/// </summary>
|
|
|
+public class FlowEngineService : ITransient
|
|
|
+{
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+ // 核心生命周期
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 发起流程
|
|
|
+ /// 1. 按 BizType 查询最新已发布的流程定义
|
|
|
+ /// 2. 创建 Instance(Status=Running),快照 FlowJson
|
|
|
+ /// 3. 解析 StartEvent → 找到第一个 UserTask → 解析审批人 → 创建 Task(s)
|
|
|
+ /// 4. 写 Log(Action=Submit)
|
|
|
+ /// 5. 发送通知
|
|
|
+ /// 6. 触发回调 Handler.OnFlowStarted()
|
|
|
+ /// </summary>
|
|
|
+ public async Task<long> StartFlow(StartFlowInput input);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 审批操作:同意
|
|
|
+ /// 1. 校验当前用户是否为 Task 的 Assignee
|
|
|
+ /// 2. 更新 Task.Status = Approved
|
|
|
+ /// 3. 写 Log
|
|
|
+ /// 4. 判断节点是否完成:
|
|
|
+ /// - 或签:一人 Approved 即节点完成,取消同节点其他 Pending Task
|
|
|
+ /// - 会签:所有 Task 均 Approved 才节点完成
|
|
|
+ /// 5. 节点完成 → AdvanceToNext()
|
|
|
+ /// </summary>
|
|
|
+ public async Task Approve(long taskId, string? comment);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 审批操作:拒绝
|
|
|
+ /// 1. 更新 Task.Status = Rejected
|
|
|
+ /// 2. 取消同节点其他 Pending Task
|
|
|
+ /// 3. 更新 Instance.Status = Rejected
|
|
|
+ /// 4. 写 Log + 通知发起人 + 触发回调
|
|
|
+ /// </summary>
|
|
|
+ public async Task Reject(long taskId, string? comment);
|
|
|
+
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+ // 扩展操作
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 转办:将 Task 转给另一个人
|
|
|
+ /// 1. 当前 Task 标记 Transferred
|
|
|
+ /// 2. 新建 Task 给目标人
|
|
|
+ /// 3. 写 Log + 通知目标人
|
|
|
+ /// </summary>
|
|
|
+ public async Task Transfer(long taskId, long targetUserId, string? comment);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 撤回:发起人撤回流程
|
|
|
+ /// 条件:Instance.Status == Running 且当前节点的所有 Task 均为 Pending(无人操作过)
|
|
|
+ /// 效果:取消所有 Pending Task,Instance.Status = Cancelled
|
|
|
+ /// </summary>
|
|
|
+ public async Task Withdraw(long instanceId);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 退回上一步
|
|
|
+ /// 1. 取消当前节点所有 Pending Task
|
|
|
+ /// 2. 从 FlowJson 的 edges 反向找到上一个 UserTask 节点
|
|
|
+ /// 3. 为上一个节点重新创建 Task(原审批人)
|
|
|
+ /// 4. 更新 Instance.CurrentNodeId
|
|
|
+ /// 5. 写 Log + 通知
|
|
|
+ /// </summary>
|
|
|
+ public async Task ReturnToPrev(long taskId, string? comment);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 加签:在当前节点临时增加审批人
|
|
|
+ /// 1. 创建新的 Task(标记 IsAddSign=true)
|
|
|
+ /// 2. 如果当前节点是或签,加签 Task 不影响已通过的判定
|
|
|
+ /// 3. 如果当前节点是会签,加签 Task 也需通过才算节点完成
|
|
|
+ /// </summary>
|
|
|
+ public async Task AddSign(long taskId, long targetUserId, string? comment);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 催办:给当前节点所有 Pending 审批人发送催办通知
|
|
|
+ /// 写 Log(Action=Urge) + 发送通知
|
|
|
+ /// </summary>
|
|
|
+ public async Task Urge(long instanceId);
|
|
|
+
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+ // 内部引擎方法
|
|
|
+ // ═══════════════════════════════════════════
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 推进到下一个节点
|
|
|
+ /// 1. 从 edges 找到当前节点的出边
|
|
|
+ /// 2. 目标是 UserTask → 解析审批人 → 创建 Task
|
|
|
+ /// 3. 目标是 ExclusiveGateway → 评估 conditions → 递归 AdvanceToNext
|
|
|
+ /// 4. 目标是 EndEvent → Instance.Status = Approved → 触发回调
|
|
|
+ /// </summary>
|
|
|
+ private async Task AdvanceToNext(ApprovalFlowInstance instance, string currentNodeId);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 解析审批人
|
|
|
+ /// SpecificUser → 直接拆分 ApproverIds
|
|
|
+ /// Role → 查 SysUserRole 获取角色下所有用户
|
|
|
+ /// Department → 查 SysUserExtOrg + SysOrg 获取部门人员
|
|
|
+ /// Initiator → 返回发起人
|
|
|
+ /// </summary>
|
|
|
+ private async Task<List<(long userId, string userName)>> ResolveApprovers(
|
|
|
+ FlowProperties props, long initiatorId);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 评估网关条件表达式
|
|
|
+ /// 支持简单的字段比较表达式,如 "bizData.amount > 100000"
|
|
|
+ /// </summary>
|
|
|
+ private string EvaluateGateway(List<GatewayCondition> conditions,
|
|
|
+ Dictionary<string, object>? bizData);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.3 流程实例服务 FlowInstanceService(API 层)
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>
|
|
|
+/// 流程实例服务 — 暴露为 API
|
|
|
+/// </summary>
|
|
|
+[ApiDescriptionSettings(ApprovalFlowConst.GroupName, Order = 95)]
|
|
|
+public class FlowInstanceService : IDynamicApiController, ITransient
|
|
|
+{
|
|
|
+ /// <summary>发起流程</summary>
|
|
|
+ [HttpPost] public async Task<long> Start(StartFlowInput input);
|
|
|
+
|
|
|
+ /// <summary>查询实例详情(含当前节点、任务列表)</summary>
|
|
|
+ [HttpGet] public async Task<FlowInstanceDetailOutput> GetDetail([FromQuery] long id);
|
|
|
+
|
|
|
+ /// <summary>查询业务单据关联的流程实例</summary>
|
|
|
+ [HttpGet] public async Task<FlowInstanceDetailOutput?> GetByBiz(
|
|
|
+ [FromQuery] string bizType, [FromQuery] long bizId);
|
|
|
+
|
|
|
+ /// <summary>审批时间线</summary>
|
|
|
+ [HttpGet] public async Task<List<FlowTimelineItem>> GetTimeline([FromQuery] long instanceId);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.4 审批任务服务 FlowTaskService(API 层)
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>
|
|
|
+/// 审批任务服务 — 暴露为 API
|
|
|
+/// </summary>
|
|
|
+[ApiDescriptionSettings(ApprovalFlowConst.GroupName, Order = 90)]
|
|
|
+public class FlowTaskService : IDynamicApiController, ITransient
|
|
|
+{
|
|
|
+ // ── 查询 ──
|
|
|
+ /// <summary>我的待办(分页)</summary>
|
|
|
+ [HttpPost] public async Task<SqlSugarPagedList<TaskPageOutput>> MyPendingPage(TaskPageInput input);
|
|
|
+
|
|
|
+ /// <summary>我的已办(分页)</summary>
|
|
|
+ [HttpPost] public async Task<SqlSugarPagedList<TaskPageOutput>> MyDonePage(TaskPageInput input);
|
|
|
+
|
|
|
+ /// <summary>我发起的(分页)</summary>
|
|
|
+ [HttpPost] public async Task<SqlSugarPagedList<InstancePageOutput>> MyInitiatedPage(InstancePageInput input);
|
|
|
+
|
|
|
+ /// <summary>待办数量(用于菜单角标)</summary>
|
|
|
+ [HttpGet] public async Task<int> MyPendingCount();
|
|
|
+
|
|
|
+ // ── 操作 ──
|
|
|
+ /// <summary>同意</summary>
|
|
|
+ [HttpPost] public async Task Approve(TaskActionInput input);
|
|
|
+
|
|
|
+ /// <summary>拒绝</summary>
|
|
|
+ [HttpPost] public async Task Reject(TaskActionInput input);
|
|
|
+
|
|
|
+ /// <summary>转办</summary>
|
|
|
+ [HttpPost] public async Task Transfer(TaskTransferInput input);
|
|
|
+
|
|
|
+ /// <summary>撤回</summary>
|
|
|
+ [HttpPost] public async Task Withdraw(TaskWithdrawInput input);
|
|
|
+
|
|
|
+ /// <summary>退回上一步</summary>
|
|
|
+ [HttpPost] public async Task ReturnToPrev(TaskActionInput input);
|
|
|
+
|
|
|
+ /// <summary>加签</summary>
|
|
|
+ [HttpPost] public async Task AddSign(TaskAddSignInput input);
|
|
|
+
|
|
|
+ /// <summary>催办</summary>
|
|
|
+ [HttpPost] public async Task Urge(TaskUrgeInput input);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5.5 改造现有 ApprovalFlowService
|
|
|
+
|
|
|
+在现有 CRUD 基础上增加:
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>发布流程(Version+1,IsPublished=true)</summary>
|
|
|
+[HttpPost] public async Task Publish(long id);
|
|
|
+
|
|
|
+/// <summary>获取业务类型列表(供前端下拉选择)</summary>
|
|
|
+[HttpGet] public async Task<List<string>> GetBizTypes();
|
|
|
+
|
|
|
+/// <summary>获取指定业务类型的最新已发布流程定义</summary>
|
|
|
+[HttpGet] public async Task<ApprovalFlow?> GetPublishedByBizType([FromQuery] string bizType);
|
|
|
+```
|
|
|
+
|
|
|
+### 5.6 改造 SysApprovalService(中间件)
|
|
|
+
|
|
|
+```
|
|
|
+改造方向:
|
|
|
+- 中间件保留但默认关闭(配置开关)
|
|
|
+- 仅用于审计日志记录
|
|
|
+- 不再用于核心审批推进
|
|
|
+- 修复 B1-B4 的代码质量问题
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 前端核心改造
|
|
|
+
|
|
|
+### 6.1 节点属性编辑器增强
|
|
|
+
|
|
|
+改造 `PropertyCommon.vue` + `PropertyDialog.vue`:
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────────────┐
|
|
|
+│ 节点属性 │
|
|
|
+│ │
|
|
|
+│ 节点名称:[意见评审 ] │
|
|
|
+│ │
|
|
|
+│ ── 审批配置 ── │
|
|
|
+│ 审批类型:[指定角色 ▼ ] │
|
|
|
+│ 选择角色:[销售部 ✕] [技术部 ✕ ] │
|
|
|
+│ 审批方式:[或签(一人通过) ▼ ] │
|
|
|
+│ │
|
|
|
+│ ── 超时设置(可选)── │
|
|
|
+│ 超时时间:[48] 小时 │
|
|
|
+│ 超时动作:[自动通知 ▼ ] │
|
|
|
+│ │
|
|
|
+│ [保存到节点] │
|
|
|
+└─────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────────────┐
|
|
|
+│ 网关条件 │
|
|
|
+│ │
|
|
|
+│ 分支 1: │
|
|
|
+│ 目标节点:[总经理审批 ▼ ] │
|
|
|
+│ 条件表达式:[amount > 1000000 ] │
|
|
|
+│ 描述:[金额>100万 ] │
|
|
|
+│ │
|
|
|
+│ 分支 2: │
|
|
|
+│ 目标节点:[部门经理审批 ▼ ] │
|
|
|
+│ ☑ 默认分支 │
|
|
|
+│ 描述:[金额≤100万 ] │
|
|
|
+│ │
|
|
|
+│ [+ 添加分支] [保存到节点] │
|
|
|
+└─────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+关键实现:
|
|
|
+1. 根据 `nodeData.type` 显示不同的属性表单(UserTask vs Gateway vs Start/End)
|
|
|
+2. 审批类型切换时,动态展示对应选择器(用户搜索 / 角色多选 / 部门树选)
|
|
|
+3. 保存时调用 `lf.setProperties(nodeId, properties)` 更新 LogicFlow 节点数据
|
|
|
+4. 流程保存时 `getGraphData()` 自动包含所有节点属性
|
|
|
+
|
|
|
+### 6.2 流程定义页面增强
|
|
|
+
|
|
|
+在现有 `index.vue` 的列表中增加:
|
|
|
+
|
|
|
+| 增强项 | 说明 |
|
|
|
+|--------|------|
|
|
|
+| 业务类型列 | 显示 `BizType`,可筛选 |
|
|
|
+| 版本列 | 显示 `Version` |
|
|
|
+| 发布状态列 | Tag: 已发布(绿) / 未发布(灰) |
|
|
|
+| 发布按钮 | 操作列增加"发布"按钮 |
|
|
|
+| 编辑对话框 | 增加"业务类型"下拉选择 |
|
|
|
+
|
|
|
+### 6.3 审批中心页面(新增)
|
|
|
+
|
|
|
+```
|
|
|
+Web/src/views/approvalFlow/center/
|
|
|
+├── index.vue # 主页,el-tabs: 我的待办 / 我的已办 / 我发起的
|
|
|
+├── pendingList.vue # 待办列表(表格 + 操作按钮)
|
|
|
+├── doneList.vue # 已办列表(只读)
|
|
|
+├── initiatedList.vue # 我发起的列表
|
|
|
+├── approvalDialog.vue # 审批操作对话框(同意/拒绝/转办/退回/加签)
|
|
|
+└── timeline.vue # 审批时间线组件(可复用)
|
|
|
+```
|
|
|
+
|
|
|
+**待办列表 UI 示意**:
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────────────────────────────────────────┐
|
|
|
+│ 我的待办 (3) 我的已办 我发起的 │
|
|
|
+├─────────────────────────────────────────────────────────────────┤
|
|
|
+│ 流程标题 业务编号 发起人 发起时间 当前节点 操作 │
|
|
|
+│ 合同评审-C001 CRM20260415 张三 04-15 09:30 意见评审 [处理] │
|
|
|
+│ 订单评审-O023 SO20260414 李四 04-14 16:00 资源检查 [处理] │
|
|
|
+│ 合同评审-C003 CRM20260413 王五 04-13 11:00 二次评审 [处理] │
|
|
|
+└─────────────────────────────────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+**审批操作对话框 UI**:
|
|
|
+
|
|
|
+```
|
|
|
+┌──────────────────────────────────┐
|
|
|
+│ 审批操作 — 合同评审-C001 │
|
|
|
+│ │
|
|
|
+│ ┌──────────────────────────┐ │
|
|
|
+│ │ [审批时间线] │ │
|
|
|
+│ │ ● 张三 提交 04-15 09:30 │ │
|
|
|
+│ │ ● 当前: 意见评审(待审批) │ │
|
|
|
+│ │ ○ 意见反馈(未到达) │ │
|
|
|
+│ │ ○ ... │ │
|
|
|
+│ └──────────────────────────┘ │
|
|
|
+│ │
|
|
|
+│ 审批意见:[ ] │
|
|
|
+│ │
|
|
|
+│ [同意] [拒绝] [转办] [退回] │
|
|
|
+│ [加签] [催办] │
|
|
|
+└──────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+### 6.4 通用 ApprovalPanel 组件
|
|
|
+
|
|
|
+```vue
|
|
|
+<!-- 嵌入任何业务表单页面底部 -->
|
|
|
+<ApprovalPanel
|
|
|
+ biz-type="CONTRACT_REVIEW"
|
|
|
+ :biz-id="orderId"
|
|
|
+ :biz-no="orderBillNo"
|
|
|
+ :title="`合同评审-${orderBillNo}`"
|
|
|
+ @started="onFlowStarted"
|
|
|
+ @completed="onFlowCompleted"
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+组件内部逻辑:
|
|
|
+
|
|
|
+```
|
|
|
+1. onMounted → 调用 GET /flowInstance/getByBiz?bizType=...&bizId=... 查询是否已有实例
|
|
|
+2. 无实例 → 显示 [提交审批] 按钮
|
|
|
+ 点击 → POST /flowInstance/start → 刷新
|
|
|
+3. 有实例:
|
|
|
+ a. 显示审批时间线(GET /flowInstance/timeline)
|
|
|
+ b. 查询当前用户是否有 Pending Task
|
|
|
+ 是 → 显示 [同意] [拒绝] [转办] [退回] [加签] 按钮
|
|
|
+ 否 → 只读查看
|
|
|
+ c. 如果是发起人且可撤回 → 显示 [撤回] 按钮
|
|
|
+ d. 如果是发起人/审批人 → 显示 [催办] 按钮
|
|
|
+```
|
|
|
+
|
|
|
+### 6.5 菜单注册
|
|
|
+
|
|
|
+审批中心需要新增菜单种子:
|
|
|
+
|
|
|
+| 菜单 | 路径 | 位置 |
|
|
|
+|------|------|------|
|
|
|
+| 审批中心 | `/approvalFlow/center` | 一级菜单或挂在"工作台"下 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 通知机制设计
|
|
|
+
|
|
|
+### 7.1 通知架构
|
|
|
+
|
|
|
+```
|
|
|
+FlowEngine 操作完成
|
|
|
+ │
|
|
|
+ ▼
|
|
|
+FlowNotifyService.Send(notifyInput)
|
|
|
+ │
|
|
|
+ ├─→ 1. 页面待办列表(默认,查询即可见)
|
|
|
+ │
|
|
|
+ ├─→ 2. SignalR 实时推送(利用 Admin.NET 已有的 SysOnlineUserHub)
|
|
|
+ │ → 前端右上角弹出待办提醒 + 更新角标数字
|
|
|
+ │
|
|
|
+ ├─→ 3. 钉钉/企微工作通知(利用已有 DingTalk/WorkWeixin 插件)
|
|
|
+ │ → 调用 DingTalkService / WorkWeixinService 推送消息
|
|
|
+ │
|
|
|
+ └─→ 4. 邮件/短信(利用已有 SysEmailService / SysSmsService)
|
|
|
+ → 发送审批提醒邮件/短信
|
|
|
+```
|
|
|
+
|
|
|
+### 7.2 通知触发时机
|
|
|
+
|
|
|
+| 事件 | 通知对象 | 通知内容 |
|
|
|
+|------|---------|---------|
|
|
|
+| 流程发起 | 第一个节点的审批人 | "您有新的审批待办:{title}" |
|
|
|
+| 同意→推进到下一节点 | 下一节点审批人 | "您有新的审批待办:{title}" |
|
|
|
+| 同意→流程结束 | 发起人 | "您的{title}已审批通过" |
|
|
|
+| 拒绝 | 发起人 | "您的{title}已被拒绝" |
|
|
|
+| 转办 | 转办目标人 | "{operator}将审批任务转办给您:{title}" |
|
|
|
+| 退回上一步 | 上一步审批人 | "审批被退回,请重新处理:{title}" |
|
|
|
+| 加签 | 加签目标人 | "{operator}邀请您参与审批:{title}" |
|
|
|
+| 催办 | 当前节点所有 Pending 审批人 | "请尽快处理审批:{title}" |
|
|
|
+| 撤回 | 原审批人(被取消) | "发起人已撤回:{title}" |
|
|
|
+
|
|
|
+### 7.3 通知渠道优先级与配置
|
|
|
+
|
|
|
+```json
|
|
|
+// 可在系统配置中设置每种通知渠道的启用/禁用
|
|
|
+{
|
|
|
+ "FlowNotify": {
|
|
|
+ "SignalR": true,
|
|
|
+ "DingTalk": true,
|
|
|
+ "WorkWeixin": false,
|
|
|
+ "Email": true,
|
|
|
+ "Sms": false
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+各渠道独立开关,按需组合。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 业务联动:回调注册机制
|
|
|
+
|
|
|
+### 8.1 接口定义
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>
|
|
|
+/// 业务流程回调处理器接口
|
|
|
+/// 每种 BizType 实现一个 Handler,注册到 DI 容器
|
|
|
+/// </summary>
|
|
|
+public interface IFlowBizHandler
|
|
|
+{
|
|
|
+ /// <summary>业务类型编码(与流程定义的 BizType 匹配)</summary>
|
|
|
+ string BizType { get; }
|
|
|
+
|
|
|
+ /// <summary>流程发起后回调(可选:更新业务表状态为"审批中")</summary>
|
|
|
+ Task OnFlowStarted(long bizId, long instanceId) => Task.CompletedTask;
|
|
|
+
|
|
|
+ /// <summary>流程结束后回调(必须:更新业务表最终状态)</summary>
|
|
|
+ Task OnFlowCompleted(long bizId, FlowInstanceStatusEnum finalStatus);
|
|
|
+
|
|
|
+ /// <summary>单个节点审批完成回调(可选:按节点推进业务进度)</summary>
|
|
|
+ Task OnNodeCompleted(long bizId, string nodeId, string nodeName) => Task.CompletedTask;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 8.2 合同评审回调示例
|
|
|
+
|
|
|
+```csharp
|
|
|
+/// <summary>
|
|
|
+/// 合同评审业务回调 — 仅约 15 行业务代码
|
|
|
+/// </summary>
|
|
|
+public class ContractReviewBizHandler : IFlowBizHandler, ITransient
|
|
|
+{
|
|
|
+ public string BizType => "CONTRACT_REVIEW";
|
|
|
+
|
|
|
+ private readonly SqlSugarRepository<SeOrder> _orderRep;
|
|
|
+
|
|
|
+ public ContractReviewBizHandler(SqlSugarRepository<SeOrder> orderRep)
|
|
|
+ => _orderRep = orderRep;
|
|
|
+
|
|
|
+ public async Task OnFlowStarted(long bizId, long instanceId)
|
|
|
+ {
|
|
|
+ await _orderRep.AsUpdateable()
|
|
|
+ .SetColumns(u => new SeOrder { FlowState = "审批中", UpdateTime = DateTime.Now })
|
|
|
+ .Where(u => u.Id == bizId)
|
|
|
+ .ExecuteCommandAsync();
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task OnFlowCompleted(long bizId, FlowInstanceStatusEnum finalStatus)
|
|
|
+ {
|
|
|
+ var state = finalStatus == FlowInstanceStatusEnum.Approved ? "已通过" : "已拒绝";
|
|
|
+ await _orderRep.AsUpdateable()
|
|
|
+ .SetColumns(u => new SeOrder { FlowState = state, UpdateTime = DateTime.Now })
|
|
|
+ .Where(u => u.Id == bizId)
|
|
|
+ .ExecuteCommandAsync();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 8.3 引擎调用回调的方式
|
|
|
+
|
|
|
+```csharp
|
|
|
+// FlowEngineService 内部
|
|
|
+private async Task InvokeHandler(string bizType, Func<IFlowBizHandler, Task> action)
|
|
|
+{
|
|
|
+ var handlers = App.GetServices<IFlowBizHandler>();
|
|
|
+ var handler = handlers.FirstOrDefault(h => h.BizType == bizType);
|
|
|
+ if (handler != null)
|
|
|
+ await action(handler);
|
|
|
+}
|
|
|
+
|
|
|
+// 使用:
|
|
|
+await InvokeHandler(instance.BizType, h => h.OnFlowCompleted(instance.BizId, finalStatus));
|
|
|
+```
|
|
|
+
|
|
|
+### 8.4 接入新业务的步骤
|
|
|
+
|
|
|
+| 步骤 | 操作方 | 工作量 | 说明 |
|
|
|
+|------|--------|--------|------|
|
|
|
+| 1 | 管理员 | 配置 | 在审批流程管理页面新增流程定义,设 BizType,画流程图,发布 |
|
|
|
+| 2 | 开发者 | ~15 行 | 实现 `IFlowBizHandler`,注册到 DI |
|
|
|
+| 3 | 开发者 | ~3 行 | 在业务表单页面嵌入 `<ApprovalPanel>` |
|
|
|
+| 4 | — | 0 | 审批引擎代码无需修改 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. S1 订单管理业务接入示例
|
|
|
+
|
|
|
+### 9.1 五个功能的审批流程配置
|
|
|
+
|
|
|
+| 序号 | 子功能 | BizType | 流程节点设计 | 说明 |
|
|
|
+|------|--------|---------|-------------|------|
|
|
|
+| 1 | 合同评审 | `CONTRACT_REVIEW` | 意见评审(销售部,或签) → 意见反馈(技术部,会签) → 二次评审(售前组,或签) → 领导意见(分管领导) → 合同盖章(法务部,或签) | 5 环节 |
|
|
|
+| 2 | 产品设计 | `PRODUCT_DESIGN` | 设计提交(设计部) → 技术评审(技术部,会签) → BOM确认(工艺部) | 3 环节 |
|
|
|
+| 3 | 订单评审 | `ORDER_REVIEW` | DOP下载(计划员) → 模拟资源(产能组) → 检查交期(销售) → 交期确认(销售,会签) → 资源锁定(计划员) | 5 环节 |
|
|
|
+| 4 | 订单交付 | `ORDER_DELIVERY` | 交期确认 → 生产计划 → 物料计划 → 执行采购 → 生产执行 → 入库发运 | 6 环节,全流程规划 |
|
|
|
+| 5 | 订单发货 | `ORDER_SHIPPING` | 生成发货通知(销售) → 仓库确认(仓库) → 物流安排(物流) | 3 环节 |
|
|
|
+
|
|
|
+### 9.2 业务代码集成示例
|
|
|
+
|
|
|
+```csharp
|
|
|
+// SeOrderService.cs — 新增方法(与现有 review/confirm-delivery 并行)
|
|
|
+
|
|
|
+/// <summary>提交合同评审(走审批流)</summary>
|
|
|
+[HttpPost("seorder/{id}/submit-contract-review")]
|
|
|
+public async Task<object> SubmitContractReview(long id)
|
|
|
+{
|
|
|
+ var order = await _seOrderRep.GetFirstAsync(u => u.Id == id && u.IsDeleted == 0)
|
|
|
+ ?? throw Oops.Oh("订单不存在");
|
|
|
+
|
|
|
+ var instanceId = await _flowEngine.StartFlow(new StartFlowInput
|
|
|
+ {
|
|
|
+ BizType = "CONTRACT_REVIEW",
|
|
|
+ BizId = id,
|
|
|
+ BizNo = order.BillNo,
|
|
|
+ Title = $"合同评审-{order.BillNo}",
|
|
|
+ });
|
|
|
+
|
|
|
+ return new { instanceId, message = "已提交合同评审" };
|
|
|
+}
|
|
|
+
|
|
|
+/// <summary>提交订单评审(走审批流)</summary>
|
|
|
+[HttpPost("seorder/{id}/submit-order-review")]
|
|
|
+public async Task<object> SubmitOrderReview(long id)
|
|
|
+{
|
|
|
+ var order = await _seOrderRep.GetFirstAsync(u => u.Id == id && u.IsDeleted == 0)
|
|
|
+ ?? throw Oops.Oh("订单不存在");
|
|
|
+
|
|
|
+ var instanceId = await _flowEngine.StartFlow(new StartFlowInput
|
|
|
+ {
|
|
|
+ BizType = "ORDER_REVIEW",
|
|
|
+ BizId = id,
|
|
|
+ BizNo = order.BillNo,
|
|
|
+ Title = $"订单评审-{order.BillNo}",
|
|
|
+ });
|
|
|
+
|
|
|
+ return new { instanceId, message = "已提交订单评审" };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 9.3 前端集成示例
|
|
|
+
|
|
|
+```vue
|
|
|
+<!-- 订单详情页面底部 -->
|
|
|
+<template>
|
|
|
+ <div class="order-detail">
|
|
|
+ <!-- ...现有订单表单... -->
|
|
|
+
|
|
|
+ <!-- 审批区域 -->
|
|
|
+ <ApprovalPanel
|
|
|
+ biz-type="CONTRACT_REVIEW"
|
|
|
+ :biz-id="orderId"
|
|
|
+ :biz-no="orderBillNo"
|
|
|
+ :title="`合同评审-${orderBillNo}`"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 配置化使用指南
|
|
|
+
|
|
|
+### 10.1 管理员:创建新的审批流程(零代码)
|
|
|
+
|
|
|
+```
|
|
|
+步骤 1: 打开【平台管理】→【审批流程】
|
|
|
+步骤 2: 点击【新增】
|
|
|
+ - 名称:合同评审流程
|
|
|
+ - 业务类型:CONTRACT_REVIEW(下拉选择或手动输入)
|
|
|
+步骤 3: 点击行上的【流程】按钮 → 进入 LogicFlow 设计器
|
|
|
+ - 从左侧面板拖入节点:开始 → 用户任务 → 用户任务 → ... → 结束
|
|
|
+ - 用箭头连接各节点
|
|
|
+ - 点击每个用户任务节点 → 右侧抽屉设置:
|
|
|
+ · 节点名称:意见评审
|
|
|
+ · 审批类型:指定角色
|
|
|
+ · 选择角色:销售部
|
|
|
+ · 审批方式:或签
|
|
|
+ - (可选)拖入排他网关节点 → 配置条件分支
|
|
|
+步骤 4: 点击【保存】
|
|
|
+步骤 5: 回到列表,点击【发布】
|
|
|
+步骤 6: 完成!业务人员即可在订单页面发起此流程
|
|
|
+```
|
|
|
+
|
|
|
+### 10.2 开发者:接入新的业务类型
|
|
|
+
|
|
|
+```
|
|
|
+1. 【管理员】在审批流程页面创建并发布流程定义
|
|
|
+2. 【开发者】编写 Handler(约 15 行):
|
|
|
+ public class XxxBizHandler : IFlowBizHandler, ITransient
|
|
|
+ {
|
|
|
+ public string BizType => "YOUR_BIZ_TYPE";
|
|
|
+ public async Task OnFlowCompleted(long bizId, FlowInstanceStatusEnum status) { ... }
|
|
|
+ }
|
|
|
+3. 【开发者】在前端页面嵌入组件(约 3 行):
|
|
|
+ <ApprovalPanel biz-type="YOUR_BIZ_TYPE" :biz-id="id" />
|
|
|
+4. 无需修改审批引擎代码
|
|
|
+```
|
|
|
+
|
|
|
+### 10.3 条件分支配置示例
|
|
|
+
|
|
|
+在网关节点的属性面板中配置:
|
|
|
+
|
|
|
+| 分支 | 目标节点 | 条件表达式 | 说明 |
|
|
|
+|------|---------|-----------|------|
|
|
|
+| 分支 1 | 总经理审批 | `bizData.contractAmount > 1000000` | 金额 > 100万 |
|
|
|
+| 分支 2(默认) | 部门经理审批 | — | 其他情况 |
|
|
|
+
|
|
|
+条件表达式中的 `bizData` 由发起流程时传入的业务数据字典提供。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 实施路线图
|
|
|
+
|
|
|
+### Phase 1:基础引擎 + 数据层(约 2 周)
|
|
|
+
|
|
|
+| # | 任务 | 说明 | 估时 |
|
|
|
+|---|------|------|------|
|
|
|
+| 1.1 | 数据库建模 | 扩展 approval_flow(+3字段)、新增 3 张表 | 1d |
|
|
|
+| 1.2 | 实体 + 枚举 | C# Entity / Enum / DTO 定义 | 1d |
|
|
|
+| 1.3 | FlowProperties 改造 | 从空类改为结构化,含审批人/网关条件 | 0.5d |
|
|
|
+| 1.4 | FlowEngineService 核心 | StartFlow + Approve + Reject + AdvanceToNext + ResolveApprovers | 3d |
|
|
|
+| 1.5 | 扩展操作 | Transfer + Withdraw + ReturnToPrev + AddSign + Urge | 2d |
|
|
|
+| 1.6 | IFlowBizHandler 回调机制 | 接口 + DI 注册 + 引擎调用 | 0.5d |
|
|
|
+| 1.7 | 流程发布逻辑 | Publish API + 版本号管理 | 0.5d |
|
|
|
+| 1.8 | 审批中心 API | 待办/已办/我发起的/时间线/待办数 | 1.5d |
|
|
|
+
|
|
|
+### Phase 2:前端 UI(约 2 周)
|
|
|
+
|
|
|
+| # | 任务 | 说明 | 估时 |
|
|
|
+|---|------|------|------|
|
|
|
+| 2.1 | 节点属性编辑器 | PropertyCommon 增强:审批人选择/审批方式/超时/网关条件 | 3d |
|
|
|
+| 2.2 | 流程定义页面增强 | BizType / Version / 发布状态 / 发布操作 | 1d |
|
|
|
+| 2.3 | 审批中心页面 | 待办 + 已办 + 我发起的 + 审批操作对话框 + 时间线 | 3d |
|
|
|
+| 2.4 | 通用 ApprovalPanel | 嵌入式审批组件 | 1.5d |
|
|
|
+| 2.5 | 菜单 + 路由注册 | 审批中心菜单种子 | 0.5d |
|
|
|
+| 2.6 | 前端联调 | 端到端跑通完整流程 | 1d |
|
|
|
+
|
|
|
+### Phase 3:通知机制(约 1 周)
|
|
|
+
|
|
|
+| # | 任务 | 说明 | 估时 |
|
|
|
+|---|------|------|------|
|
|
|
+| 3.1 | FlowNotifyService | 统一通知调度服务 | 0.5d |
|
|
|
+| 3.2 | SignalR 站内信 | 利用 SysOnlineUserHub 推送 + 前端角标 | 1d |
|
|
|
+| 3.3 | 钉钉工作通知 | 利用 DingTalkService 推送 | 1d |
|
|
|
+| 3.4 | 企业微信通知 | 利用 WorkWeixinService 推送 | 1d |
|
|
|
+| 3.5 | 邮件/短信 | 利用已有邮件短信服务 | 1d |
|
|
|
+| 3.6 | 通知渠道配置 | 系统配置页面 + JSON 开关 | 0.5d |
|
|
|
+
|
|
|
+### Phase 4:业务接入 + 验证(约 1 周)
|
|
|
+
|
|
|
+| # | 任务 | 说明 | 估时 |
|
|
|
+|---|------|------|------|
|
|
|
+| 4.1 | 合同评审接入 | Handler + 前端 ApprovalPanel + 流程定义 | 1d |
|
|
|
+| 4.2 | 订单评审接入 | Handler + 前端 + 流程定义 | 1d |
|
|
|
+| 4.3 | 端到端验证 | 完整场景测试(发起→多节点审批→通过/拒绝→业务联动→通知) | 1.5d |
|
|
|
+| 4.4 | Bug 修复 + 优化 | 根据测试结果修复 | 1.5d |
|
|
|
+
|
|
|
+### 后续迭代
|
|
|
+
|
|
|
+| 功能 | 说明 |
|
|
|
+|------|------|
|
|
|
+| 依次审批模式 | MultiApproveMode.Sequential |
|
|
|
+| 发起人上级 | ApproverType.Superior(需组织架构上级关系) |
|
|
|
+| 表单字段审批人 | 根据业务表单字段值动态指定审批人 |
|
|
|
+| 并行网关 | 多条分支同时进行 |
|
|
|
+| 超时自动处理 | 定时任务扫描 + AutoApprove/AutoReject |
|
|
|
+| 流程版本对比 | 可视化对比两个版本的流程图差异 |
|
|
|
+| 流程监控看板 | 管理员查看所有流程实例状态统计 |
|
|
|
+| 订单交付/发货接入 | 更多 S1 业务场景 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附录 A:现有 SeOrder 数据模型参考
|
|
|
+
|
|
|
+| 表 | 实体 | 说明 |
|
|
|
+|----|------|------|
|
|
|
+| `crm_seorder` | `SeOrder` | 销售订单主表,含 `FlowState` 字段 |
|
|
|
+| `crm_seorderentry` | `SeOrderEntry` | 订单明细,含 `Progress` (0=再评审,1=新建,2=评审,3=确认) |
|
|
|
+| `crm_seorder_change` | `SeOrderChange` | 订单变更申请 |
|
|
|
+
|
|
|
+现有 `SeOrderService` API(与审批流**并行保留**,验证后再切换):
|
|
|
+- `GET seorder/list` / `GET seorder/{id}` / `POST seorder/save`
|
|
|
+- `POST seorder/{id}/review` — 硬编码设进度=2
|
|
|
+- `POST seorder/{id}/confirm-delivery` — 硬编码设进度=3
|
|
|
+- `POST seorder/{id}/unlock` — 空实现
|
|
|
+- `POST seorder/{id}/change` — 变更申请
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附录 B:与钉钉审批的关系
|
|
|
+
|
|
|
+项目中存在 `Admin.NET.Plugin.DingTalk` 插件,已有钉钉工作流 API 对接。本方案与钉钉审批的关系:
|
|
|
+
|
|
|
+- **内部审批**:使用本方案自建引擎,适合系统内全流程可控
|
|
|
+- **钉钉通知**:审批引擎发起/推进时,通过钉钉插件发送工作通知提醒(Phase 3 实现)
|
|
|
+- **钉钉审批**:如需在钉钉 App 内审批,可后续对接钉钉审批流 API(本期不做)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附录 C:技术选型对比参考
|
|
|
+
|
|
|
+| 维度 | 自建引擎(本方案) | Elsa 3.x | Camunda | 钉钉审批 |
|
|
|
+|------|-------------------|----------|---------|---------|
|
|
|
+| 集成难度 | 低(天然集成) | 中(需适配) | 高(独立部署) | 低(已有插件) |
|
|
|
+| 功能完整度 | 按需渐进 | 高 | 很高 | 仅审批 |
|
|
|
+| 学习成本 | 低 | 中 | 高 | 低 |
|
|
|
+| 灵活性 | 高(完全可控) | 中 | 中 | 低 |
|
|
|
+| 运维复杂度 | 低 | 中 | 高 | 零(SaaS) |
|
|
|
+| 适合场景 | 中小规模审批 | 复杂工作流 | 企业级 BPM | 移动审批 |
|