# 报销流程配置表设计说明 ## 一、表结构设计 ### 1. 流程配置主表 (`lq_reimbursement_workflow_config`) | 字段名 | 类型 | 说明 | 是否必填 | 默认值 | |--------|------|------|----------|--------| | F_Id | varchar(50) | 流程配置ID | 是 | - | | F_WorkflowName | varchar(100) | 流程名称 | 是 | - | | F_IsEnabled | int | 是否启用(1-启用,0-禁用) | 是 | 1 | | F_Description | varchar(500) | 流程描述 | 否 | NULL | | F_CreateTime | datetime | 创建时间 | 否 | CURRENT_TIMESTAMP | | F_CreateUser | varchar(50) | 创建人ID | 否 | NULL | | F_ModifyTime | datetime | 修改时间 | 否 | NULL | | F_ModifyUser | varchar(50) | 修改人ID | 否 | NULL | **索引:** - 主键:`F_Id` - 普通索引:`idx_is_enabled`(用于查询启用的流程) - 普通索引:`idx_workflow_name`(用于按名称查询) ### 2. 流程节点配置表 (`lq_reimbursement_workflow_node`) | 字段名 | 类型 | 说明 | 是否必填 | 默认值 | 对应申请节点表字段 | |--------|------|------|----------|--------|-------------------| | F_Id | varchar(50) | 节点配置ID | 是 | - | - | | F_WorkflowConfigId | varchar(50) | 流程配置ID | 是 | - | - | | F_NodeOrder | int | 节点顺序(1,2,3...) | 是 | - | F_NodeOrder ✅ | | F_NodeName | varchar(100) | 节点名称 | 否 | NULL | F_NodeName ✅ | | F_ApprovalType | varchar(20) | 审批类型(会签/或签) | 否 | '会签' | F_ApprovalType ✅ | | F_IsRequired | int | 是否必审(1-必审,0-可选) | 否 | 1 | F_IsRequired ✅ | | F_CreateTime | datetime | 创建时间 | 否 | CURRENT_TIMESTAMP | F_CreateTime ✅ | **索引:** - 主键:`F_Id` - 外键:`F_WorkflowConfigId` → `lq_reimbursement_workflow_config.F_Id`(级联删除) - 普通索引:`idx_workflow_config_id`(用于查询流程的所有节点) - 联合索引:`idx_node_order`(用于按流程和顺序查询) **字段映射确认:** ✅ 所有字段类型、长度、默认值完全匹配 `lq_reimbursement_application_node` 表 ✅ 可以直接复制到申请节点表,无需转换 ### 3. 流程节点审批人配置表 (`lq_reimbursement_workflow_node_user`) | 字段名 | 类型 | 说明 | 是否必填 | 默认值 | 对应申请审批人表字段 | |--------|------|------|----------|--------|---------------------| | F_Id | varchar(50) | 记录ID | 是 | - | - | | F_WorkflowConfigId | varchar(50) | 流程配置ID | 是 | - | - | | F_NodeId | varchar(50) | 节点配置ID | 是 | - | - | | F_NodeOrder | int | 节点顺序(冗余字段) | 是 | - | F_NodeOrder ✅ | | F_UserId | varchar(50) | 审批人ID | 是 | - | F_UserId ✅ | | F_UserName | varchar(100) | 审批人姓名 | 否 | NULL | F_UserName ✅ | | F_SortOrder | int | 排序 | 否 | 0 | F_SortOrder ✅ | | F_CreateTime | datetime | 创建时间 | 否 | CURRENT_TIMESTAMP | F_CreateTime ✅ | **索引:** - 主键:`F_Id` - 外键1:`F_WorkflowConfigId` → `lq_reimbursement_workflow_config.F_Id`(级联删除) - 外键2:`F_NodeId` → `lq_reimbursement_workflow_node.F_Id`(级联删除) - 普通索引:`idx_workflow_config_id`、`idx_node_id`、`idx_user_id` - 联合索引:`idx_node_order`(用于按流程和顺序查询) - 唯一索引:`uk_workflow_node_user`(防止同一节点重复添加同一审批人) **字段映射确认:** ✅ 所有字段类型、长度、默认值完全匹配 `lq_reimbursement_application_node_user` 表 ✅ 可以直接复制到申请审批人表,无需转换 ## 二、与现有逻辑的兼容性分析 ### 2.1 数据流转逻辑 ``` ┌─────────────────────────────────┐ │ 流程配置表(模板数据) │ │ - lq_reimbursement_workflow_config │ │ - lq_reimbursement_workflow_node │ │ - lq_reimbursement_workflow_node_user │ └──────────────┬──────────────────┘ │ 创建申请时复制 ↓ ┌─────────────────────────────────┐ │ 申请节点表(实际数据) │ │ - lq_reimbursement_application_node │ │ - lq_reimbursement_application_node_user │ └──────────────┬──────────────────┘ │ 后续所有查询 ↓ ┌─────────────────────────────────┐ │ 现有查询逻辑(完全不变) │ │ - 基于 ApplicationId 查询 │ │ - 基于 NodeOrder 排序 │ │ - 基于 NodeId 关联审批人 │ └─────────────────────────────────┘ ``` ### 2.2 字段映射关系 **节点配置表映射:** | 配置表字段 | → | 申请节点表字段 | 转换逻辑 | |-----------|---|---------------|---------| | F_NodeOrder | → | F_NodeOrder | 直接复制 ✅ | | F_NodeName | → | F_NodeName | 直接复制 ✅ | | F_ApprovalType | → | F_ApprovalType | 直接复制 ✅ | | F_IsRequired | → | F_IsRequired | 直接复制 ✅ | | - | → | F_ApplicationId | 使用新创建的申请ID ✅ | | - | → | F_Id | 生成新的节点ID ✅ | **审批人配置表映射:** | 配置表字段 | → | 申请审批人表字段 | 转换逻辑 | |-----------|---|----------------|---------| | F_NodeOrder | → | F_NodeOrder | 直接复制 ✅ | | F_UserId | → | F_UserId | 直接复制 ✅ | | F_UserName | → | F_UserName | 直接复制 ✅ | | F_SortOrder | → | F_SortOrder | 直接复制 ✅ | | - | → | F_ApplicationId | 使用新创建的申请ID ✅ | | - | → | F_NodeId | 使用新创建的节点ID ✅ | | - | → | F_Id | 生成新的记录ID ✅ | ### 2.3 现有查询逻辑兼容性 **✅ 完全兼容,无需修改:** 1. **查询申请的所有节点:** ```csharp var nodes = await _db.Queryable() .Where(x => x.ApplicationId == id) .OrderBy(x => x.NodeOrder) .ToListAsync(); ``` → 查询的是申请节点表,不受配置表影响 ✅ 2. **查询申请的审批人:** ```csharp var nodeUsers = await _db.Queryable() .Where(x => x.ApplicationId == id) .OrderBy(x => x.NodeOrder) .ToListAsync(); ``` → 查询的是申请审批人表,不受配置表影响 ✅ 3. **查询当前节点的审批人:** ```csharp var approvers = await _db.Queryable() .Where(x => x.ApplicationId == id && x.NodeOrder == currentNodeOrder) .ToListAsync(); ``` → 查询的是申请审批人表,不受配置表影响 ✅ ## 三、前端使用场景分析 ### 3.1 流程配置管理(后端管理页面) **功能需求:** 1. 列表查询:显示所有流程配置,支持按启用状态筛选 2. 新增流程:创建新流程,配置流程名称、描述、启用状态 3. 编辑流程:修改流程名称、描述、启用状态 4. 删除流程:删除流程及其所有节点和审批人配置(级联删除) 5. 节点管理:为流程添加/编辑/删除节点 6. 审批人管理:为节点添加/编辑/删除审批人 **数据操作:** - ✅ 所有操作都在配置表进行,不影响已有申请 - ✅ 支持启用/禁用,前端列表只显示启用的流程 - ✅ 节点顺序可以调整,前端需要支持拖拽排序 ### 3.2 创建报销申请(前端申请页面) **功能需求:** 1. 流程选择:下拉框显示所有启用的流程配置 2. 流程预览:选择流程后,显示流程的节点和审批人信息 3. 审批人调整:允许用户修改审批人(如果配置了默认审批人) 4. 提交申请:传入 `workflowConfigId`,后端自动复制配置 **数据流转:** ``` 前端选择流程 → 传入 workflowConfigId → 后端读取配置 → 复制到申请表 → 创建成功 ``` **兼容性:** - ✅ 如果传入 `workflowConfigId`,使用配置表数据 - ✅ 如果不传入 `workflowConfigId`,使用现有的 `nodes` 数组(保持兼容) ### 3.3 前端接口需求 **1. 获取启用的流程列表:** ``` GET /api/Extend/LqReimbursementWorkflowConfig/GetEnabledList 返回:[{ id, workflowName, description, nodeCount }] ``` **2. 获取流程详情(包含节点和审批人):** ``` GET /api/Extend/LqReimbursementWorkflowConfig/{id} 返回:{ id, workflowName, description, isEnabled, nodes: [{ nodeOrder, nodeName, approvalType, isRequired, approvers: [...] }] } ``` **3. 创建报销申请(修改现有接口):** ``` POST /api/Extend/LqReimbursementApplication/Create 请求:{ ...其他字段, workflowConfigId: "xxx", // 新增字段,可选 nodes: [...] // 如果传了 workflowConfigId,此字段可选 } ``` ## 四、实现要点 ### 4.1 创建申请时的数据复制逻辑 ```csharp if (!string.IsNullOrEmpty(input.workflowConfigId)) { // 1. 验证流程配置存在且启用 var workflowConfig = await _db.Queryable() .Where(x => x.Id == input.workflowConfigId && x.IsEnabled == 1) .FirstAsync(); if (workflowConfig == null) throw new Exception("流程配置不存在或已禁用"); // 2. 读取流程节点配置 var workflowNodes = await _db.Queryable() .Where(x => x.WorkflowConfigId == input.workflowConfigId) .OrderBy(x => x.NodeOrder) .ToListAsync(); // 3. 读取流程审批人配置 var workflowNodeUsers = await _db.Queryable() .Where(x => x.WorkflowConfigId == input.workflowConfigId) .ToListAsync(); // 4. 复制节点配置到申请节点表 foreach (var workflowNode in workflowNodes) { var node = new LqReimbursementApplicationNodeEntity { Id = YitIdHelper.NextId().ToString(), ApplicationId = entity.Id, // 关键:关联到具体申请 NodeOrder = workflowNode.NodeOrder, NodeName = workflowNode.NodeName, ApprovalType = workflowNode.ApprovalType, IsRequired = workflowNode.IsRequired, CreateTime = DateTime.Now }; await _db.Insertable(node).ExecuteCommandAsync(); // 5. 复制审批人配置到申请审批人表 var nodeUsers = workflowNodeUsers .Where(x => x.NodeId == workflowNode.Id) .OrderBy(x => x.SortOrder) .ToList(); foreach (var workflowNodeUser in nodeUsers) { var nodeUser = new LqReimbursementApplicationNodeUserEntity { Id = YitIdHelper.NextId().ToString(), ApplicationId = entity.Id, // 关键:关联到具体申请 NodeId = node.Id, // 关键:使用新创建的节点ID NodeOrder = workflowNode.NodeOrder, UserId = workflowNodeUser.UserId, UserName = workflowNodeUser.UserName, SortOrder = workflowNodeUser.SortOrder, CreateTime = DateTime.Now }; await _db.Insertable(nodeUser).ExecuteCommandAsync(); } } } else { // 使用现有的前端传入方式(保持兼容) // ... 现有逻辑 } ``` ### 4.2 注意事项 1. **审批人配置是可选的:** - 如果配置了审批人,创建申请时自动复制 - 如果没配置审批人,创建申请时用户需要手动选择(使用现有的 `nodes` 数组) 2. **流程配置修改不影响已有申请:** - 配置表只作为模板,修改配置不影响已创建的申请 - 已创建的申请使用申请节点表的数据 3. **级联删除:** - 删除流程配置时,自动删除所有节点和审批人配置 - 不会影响已创建的申请(因为数据已复制到申请表) ## 五、总结 ### ✅ 设计优势 1. **完全兼容现有逻辑:** 所有现有查询逻辑无需修改 2. **字段完全匹配:** 配置表字段与申请表字段类型、长度、默认值完全一致 3. **数据隔离:** 配置表作为模板,不影响已有申请 4. **灵活使用:** 支持配置审批人,也支持创建时选择审批人 5. **前端友好:** 提供清晰的接口,便于前端实现 ### ✅ 实现确认 - [x] 表结构设计完成 - [x] 字段映射关系确认 - [x] 现有逻辑兼容性确认 - [x] 前端使用场景分析 - [x] 数据复制逻辑设计 - [x] SQL创建语句生成 **结论:设计完全符合现有逻辑,前端可以顺利使用!**