报销流程配置表设计说明.md 12.6 KB

报销流程配置表设计说明

一、表结构设计

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_WorkflowConfigIdlq_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_WorkflowConfigIdlq_reimbursement_workflow_config.F_Id(级联删除)
  • 外键2:F_NodeIdlq_reimbursement_workflow_node.F_Id(级联删除)
  • 普通索引:idx_workflow_config_ididx_node_ididx_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. 查询申请的所有节点:

    var nodes = await _db.Queryable<LqReimbursementApplicationNodeEntity>()
       .Where(x => x.ApplicationId == id)
       .OrderBy(x => x.NodeOrder)
       .ToListAsync();
    

    → 查询的是申请节点表,不受配置表影响 ✅

  2. 查询申请的审批人:

    var nodeUsers = await _db.Queryable<LqReimbursementApplicationNodeUserEntity>()
       .Where(x => x.ApplicationId == id)
       .OrderBy(x => x.NodeOrder)
       .ToListAsync();
    

    → 查询的是申请审批人表,不受配置表影响 ✅

  3. 查询当前节点的审批人:

    var approvers = await _db.Queryable<LqReimbursementApplicationNodeUserEntity>()
       .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 创建申请时的数据复制逻辑

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 注意事项

  1. 审批人配置是可选的:

    • 如果配置了审批人,创建申请时自动复制
    • 如果没配置审批人,创建申请时用户需要手动选择(使用现有的 nodes 数组)
  2. 流程配置修改不影响已有申请:

    • 配置表只作为模板,修改配置不影响已创建的申请
    • 已创建的申请使用申请节点表的数据
  3. 级联删除:

    • 删除流程配置时,自动删除所有节点和审批人配置
    • 不会影响已创建的申请(因为数据已复制到申请表)

五、总结

✅ 设计优势

  1. 完全兼容现有逻辑: 所有现有查询逻辑无需修改
  2. 字段完全匹配: 配置表字段与申请表字段类型、长度、默认值完全一致
  3. 数据隔离: 配置表作为模板,不影响已有申请
  4. 灵活使用: 支持配置审批人,也支持创建时选择审批人
  5. 前端友好: 提供清晰的接口,便于前端实现

✅ 实现确认

  • [x] 表结构设计完成
  • [x] 字段映射关系确认
  • [x] 现有逻辑兼容性确认
  • [x] 前端使用场景分析
  • [x] 数据复制逻辑设计
  • [x] SQL创建语句生成

结论:设计完全符合现有逻辑,前端可以顺利使用!