报销流程配置表设计说明
一、表结构设计
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 现有查询逻辑兼容性
✅ 完全兼容,无需修改:
查询申请的所有节点:
var nodes = await _db.Queryable<LqReimbursementApplicationNodeEntity>() .Where(x => x.ApplicationId == id) .OrderBy(x => x.NodeOrder) .ToListAsync();→ 查询的是申请节点表,不受配置表影响 ✅
查询申请的审批人:
var nodeUsers = await _db.Queryable<LqReimbursementApplicationNodeUserEntity>() .Where(x => x.ApplicationId == id) .OrderBy(x => x.NodeOrder) .ToListAsync();→ 查询的是申请审批人表,不受配置表影响 ✅
查询当前节点的审批人:
var approvers = await _db.Queryable<LqReimbursementApplicationNodeUserEntity>() .Where(x => x.ApplicationId == id && x.NodeOrder == currentNodeOrder) .ToListAsync();→ 查询的是申请审批人表,不受配置表影响 ✅
三、前端使用场景分析
3.1 流程配置管理(后端管理页面)
功能需求:
- 列表查询:显示所有流程配置,支持按启用状态筛选
- 新增流程:创建新流程,配置流程名称、描述、启用状态
- 编辑流程:修改流程名称、描述、启用状态
- 删除流程:删除流程及其所有节点和审批人配置(级联删除)
- 节点管理:为流程添加/编辑/删除节点
- 审批人管理:为节点添加/编辑/删除审批人
数据操作:
- ✅ 所有操作都在配置表进行,不影响已有申请
- ✅ 支持启用/禁用,前端列表只显示启用的流程
- ✅ 节点顺序可以调整,前端需要支持拖拽排序
3.2 创建报销申请(前端申请页面)
功能需求:
- 流程选择:下拉框显示所有启用的流程配置
- 流程预览:选择流程后,显示流程的节点和审批人信息
- 审批人调整:允许用户修改审批人(如果配置了默认审批人)
- 提交申请:传入
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 创建申请时的数据复制逻辑
if (!string.IsNullOrEmpty(input.workflowConfigId))
{
// 1. 验证流程配置存在且启用
var workflowConfig = await _db.Queryable<LqReimbursementWorkflowConfigEntity>()
.Where(x => x.Id == input.workflowConfigId && x.IsEnabled == 1)
.FirstAsync();
if (workflowConfig == null)
throw new Exception("流程配置不存在或已禁用");
// 2. 读取流程节点配置
var workflowNodes = await _db.Queryable<LqReimbursementWorkflowNodeEntity>()
.Where(x => x.WorkflowConfigId == input.workflowConfigId)
.OrderBy(x => x.NodeOrder)
.ToListAsync();
// 3. 读取流程审批人配置
var workflowNodeUsers = await _db.Queryable<LqReimbursementWorkflowNodeUserEntity>()
.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 注意事项
审批人配置是可选的:
- 如果配置了审批人,创建申请时自动复制
- 如果没配置审批人,创建申请时用户需要手动选择(使用现有的
nodes数组)
流程配置修改不影响已有申请:
- 配置表只作为模板,修改配置不影响已创建的申请
- 已创建的申请使用申请节点表的数据
级联删除:
- 删除流程配置时,自动删除所有节点和审批人配置
- 不会影响已创建的申请(因为数据已复制到申请表)
五、总结
✅ 设计优势
- 完全兼容现有逻辑: 所有现有查询逻辑无需修改
- 字段完全匹配: 配置表字段与申请表字段类型、长度、默认值完全一致
- 数据隔离: 配置表作为模板,不影响已有申请
- 灵活使用: 支持配置审批人,也支持创建时选择审批人
- 前端友好: 提供清晰的接口,便于前端实现
✅ 实现确认
- [x] 表结构设计完成
- [x] 字段映射关系确认
- [x] 现有逻辑兼容性确认
- [x] 前端使用场景分析
- [x] 数据复制逻辑设计
- [x] SQL创建语句生成
结论:设计完全符合现有逻辑,前端可以顺利使用!