Blame view

项目文档相关/docs/报销流程配置表设计说明.md 12.6 KB
83a6fd1f   “wangming”   feat: 添加员工确认状态及相关...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  # 报销流程配置表设计说明
  
  ## 一、表结构设计
  
  ### 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<LqReimbursementApplicationNodeEntity>()
         .Where(x => x.ApplicationId == id)
         .OrderBy(x => x.NodeOrder)
         .ToListAsync();
     ```
     → 查询的是申请节点表,不受配置表影响 ✅
  
  2. **查询申请的审批人:**
     ```csharp
     var nodeUsers = await _db.Queryable<LqReimbursementApplicationNodeUserEntity>()
         .Where(x => x.ApplicationId == id)
         .OrderBy(x => x.NodeOrder)
         .ToListAsync();
     ```
     → 查询的是申请审批人表,不受配置表影响 ✅
  
  3. **查询当前节点的审批人:**
     ```csharp
     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 创建申请时的数据复制逻辑
  
  ```csharp
  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创建语句生成
  
  **结论:设计完全符合现有逻辑,前端可以顺利使用!**