|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+# 审批流框架使用示例
|
|
|
2
|
+
|
|
|
3
|
+## 概述
|
|
|
4
|
+
|
|
|
5
|
+本框架实现了一个简单而灵活的审批流系统,支持以下几种审批流程:
|
|
|
6
|
+
|
|
|
7
|
+1. **个人级别**: 无需审批,发送待办通知,点击确认知悉后归档
|
|
|
8
|
+2. **科级**: 科长选择整改班组 → 班组长填写整改详情 → 科长审批 → 质检科审批 → 归档
|
|
|
9
|
+3. **班组级**: 科长提交 → 班组长填写整改详情 → 科长审批 → 归档
|
|
|
10
|
+4. **查获上报**: 根据提交人角色选择不同流程
|
|
|
11
|
+ - 安检员提交: 班组长审批 → 归档
|
|
|
12
|
+ - 班组长提交: 科长审批 → 归档
|
|
|
13
|
+
|
|
|
14
|
+## API 使用示例
|
|
|
15
|
+
|
|
|
16
|
+### 1. 启动个人级别审批流程
|
|
|
17
|
+
|
|
|
18
|
+```java
|
|
|
19
|
+@PostMapping("/check/personal")
|
|
|
20
|
+public AjaxResult submitPersonalCheck(@RequestBody PersonalCheckDTO checkData) {
|
|
|
21
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
22
|
+ params.put("businessType", "SECURITY_CHECK");
|
|
|
23
|
+ params.put("businessId", checkData.getId());
|
|
|
24
|
+ params.put("title", "个人级别安检问题");
|
|
|
25
|
+
|
|
|
26
|
+ // 目标通知用户ID列表
|
|
|
27
|
+ List<Long> targetUserIds = Arrays.asList(checkData.getTargetUserIds());
|
|
|
28
|
+ params.put("targetUserIds", targetUserIds);
|
|
|
29
|
+
|
|
|
30
|
+ // 表单数据
|
|
|
31
|
+ Map<String, Object> formData = new HashMap<>();
|
|
|
32
|
+ formData.put("checkLevel", "PERSONAL");
|
|
|
33
|
+ formData.put("description", checkData.getDescription());
|
|
|
34
|
+ params.put("formData", formData);
|
|
|
35
|
+
|
|
|
36
|
+ // 业务数据
|
|
|
37
|
+ Map<String, Object> businessData = new HashMap<>();
|
|
|
38
|
+ businessData.put("checkType", checkData.getCheckType());
|
|
|
39
|
+ businessData.put("location", checkData.getLocation());
|
|
|
40
|
+ params.put("businessData", businessData);
|
|
|
41
|
+
|
|
|
42
|
+ return approvalController.startPersonalLevelProcess(params);
|
|
|
43
|
+}
|
|
|
44
|
+```
|
|
|
45
|
+
|
|
|
46
|
+### 2. 启动科级审批流程
|
|
|
47
|
+
|
|
|
48
|
+```java
|
|
|
49
|
+@PostMapping("/check/section")
|
|
|
50
|
+public AjaxResult submitSectionCheck(@RequestBody SectionCheckDTO checkData) {
|
|
|
51
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
52
|
+ params.put("businessType", "SECURITY_CHECK");
|
|
|
53
|
+ params.put("businessId", checkData.getId());
|
|
|
54
|
+ params.put("title", "科级安检问题处理");
|
|
|
55
|
+
|
|
|
56
|
+ // 表单数据
|
|
|
57
|
+ Map<String, Object> formData = new HashMap<>();
|
|
|
58
|
+ formData.put("checkLevel", "SECTION");
|
|
|
59
|
+ formData.put("description", checkData.getDescription());
|
|
|
60
|
+ formData.put("severity", checkData.getSeverity());
|
|
|
61
|
+ params.put("formData", formData);
|
|
|
62
|
+
|
|
|
63
|
+ // 业务数据
|
|
|
64
|
+ Map<String, Object> businessData = new HashMap<>();
|
|
|
65
|
+ businessData.put("checkType", checkData.getCheckType());
|
|
|
66
|
+ businessData.put("targetDeptId", checkData.getTargetDeptId());
|
|
|
67
|
+ params.put("businessData", businessData);
|
|
|
68
|
+
|
|
|
69
|
+ return approvalController.startSectionLevelProcess(params);
|
|
|
70
|
+}
|
|
|
71
|
+```
|
|
|
72
|
+
|
|
|
73
|
+### 3. 启动查获上报审批流程
|
|
|
74
|
+
|
|
|
75
|
+```java
|
|
|
76
|
+@PostMapping("/seizure/report")
|
|
|
77
|
+public AjaxResult submitSeizureReport(@RequestBody SeizureReportDTO reportData) {
|
|
|
78
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
79
|
+ params.put("businessType", "SEIZURE_REPORT");
|
|
|
80
|
+ params.put("businessId", reportData.getId());
|
|
|
81
|
+ params.put("title", "查获物品上报");
|
|
|
82
|
+
|
|
|
83
|
+ // 根据当前用户角色确定提交人角色
|
|
|
84
|
+ String submitterRole = getCurrentUserRole(); // "GROUP_LEADER" 或其他
|
|
|
85
|
+ params.put("submitterRole", submitterRole);
|
|
|
86
|
+
|
|
|
87
|
+ // 表单数据
|
|
|
88
|
+ Map<String, Object> formData = new HashMap<>();
|
|
|
89
|
+ formData.put("itemType", reportData.getItemType());
|
|
|
90
|
+ formData.put("quantity", reportData.getQuantity());
|
|
|
91
|
+ formData.put("location", reportData.getLocation());
|
|
|
92
|
+ params.put("formData", formData);
|
|
|
93
|
+
|
|
|
94
|
+ // 业务数据
|
|
|
95
|
+ Map<String, Object> businessData = new HashMap<>();
|
|
|
96
|
+ businessData.put("seizureTime", reportData.getSeizureTime());
|
|
|
97
|
+ businessData.put("passengerInfo", reportData.getPassengerInfo());
|
|
|
98
|
+ params.put("businessData", businessData);
|
|
|
99
|
+
|
|
|
100
|
+ return approvalController.startSeizureReportProcess(params);
|
|
|
101
|
+}
|
|
|
102
|
+```
|
|
|
103
|
+
|
|
|
104
|
+### 4. 审批任务(同意)
|
|
|
105
|
+
|
|
|
106
|
+```java
|
|
|
107
|
+@PutMapping("/approval/approve/{taskId}")
|
|
|
108
|
+public AjaxResult approveTask(@PathVariable Long taskId, @RequestBody ApprovalDTO approvalData) {
|
|
|
109
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
110
|
+ params.put("comment", approvalData.getComment());
|
|
|
111
|
+
|
|
|
112
|
+ // 如果是科长节点,需要选择整改班组
|
|
|
113
|
+ if ("SECTION_LEADER".equals(approvalData.getNodeType())) {
|
|
|
114
|
+ Map<String, Object> formData = new HashMap<>();
|
|
|
115
|
+ formData.put("targetGroupId", approvalData.getTargetGroupId());
|
|
|
116
|
+ formData.put("rectificationRequirement", approvalData.getRectificationRequirement());
|
|
|
117
|
+ params.put("formData", formData);
|
|
|
118
|
+ }
|
|
|
119
|
+
|
|
|
120
|
+ // 如果是班组长整改节点,需要填写整改详情
|
|
|
121
|
+ if ("GROUP_LEADER_RECTIFY".equals(approvalData.getNodeType())) {
|
|
|
122
|
+ Map<String, Object> formData = new HashMap<>();
|
|
|
123
|
+ formData.put("rectificationDetail", approvalData.getRectificationDetail());
|
|
|
124
|
+ formData.put("rectificationTime", approvalData.getRectificationTime());
|
|
|
125
|
+ formData.put("rectificationResult", approvalData.getRectificationResult());
|
|
|
126
|
+ params.put("formData", formData);
|
|
|
127
|
+ }
|
|
|
128
|
+
|
|
|
129
|
+ return approvalController.approveTask(taskId, params);
|
|
|
130
|
+}
|
|
|
131
|
+```
|
|
|
132
|
+
|
|
|
133
|
+### 5. 驳回任务
|
|
|
134
|
+
|
|
|
135
|
+```java
|
|
|
136
|
+@PutMapping("/approval/reject/{taskId}")
|
|
|
137
|
+public AjaxResult rejectTask(@PathVariable Long taskId, @RequestBody RejectDTO rejectData) {
|
|
|
138
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
139
|
+ params.put("comment", rejectData.getRejectReason());
|
|
|
140
|
+
|
|
|
141
|
+ return approvalController.rejectTask(taskId, params);
|
|
|
142
|
+}
|
|
|
143
|
+```
|
|
|
144
|
+
|
|
|
145
|
+### 6. 获取待办任务列表
|
|
|
146
|
+
|
|
|
147
|
+```java
|
|
|
148
|
+@GetMapping("/approval/tasks/pending")
|
|
|
149
|
+public TableDataInfo getPendingTasks() {
|
|
|
150
|
+ return approvalController.getPendingTasks();
|
|
|
151
|
+}
|
|
|
152
|
+```
|
|
|
153
|
+
|
|
|
154
|
+### 7. 获取已办任务列表
|
|
|
155
|
+
|
|
|
156
|
+```java
|
|
|
157
|
+@GetMapping("/approval/tasks/completed")
|
|
|
158
|
+public TableDataInfo getCompletedTasks() {
|
|
|
159
|
+ return approvalController.getCompletedTasks();
|
|
|
160
|
+}
|
|
|
161
|
+```
|
|
|
162
|
+
|
|
|
163
|
+### 8. 获取我发起的审批实例
|
|
|
164
|
+
|
|
|
165
|
+```java
|
|
|
166
|
+@GetMapping("/approval/instances/submitted")
|
|
|
167
|
+public TableDataInfo getSubmittedInstances(@RequestParam(required = false) String status) {
|
|
|
168
|
+ return approvalController.getSubmittedInstances(status);
|
|
|
169
|
+}
|
|
|
170
|
+```
|
|
|
171
|
+
|
|
|
172
|
+## 前端集成示例
|
|
|
173
|
+
|
|
|
174
|
+### 1. 待办任务列表页面
|
|
|
175
|
+
|
|
|
176
|
+```javascript
|
|
|
177
|
+// 获取待办任务
|
|
|
178
|
+function loadPendingTasks() {
|
|
|
179
|
+ $.get('/system/approval/tasks/pending', function(res) {
|
|
|
180
|
+ if (res.code === 200) {
|
|
|
181
|
+ renderTaskList(res.rows);
|
|
|
182
|
+ }
|
|
|
183
|
+ });
|
|
|
184
|
+}
|
|
|
185
|
+
|
|
|
186
|
+// 审批任务
|
|
|
187
|
+function approveTask(taskId, comment, formData) {
|
|
|
188
|
+ const params = {
|
|
|
189
|
+ comment: comment,
|
|
|
190
|
+ formData: formData
|
|
|
191
|
+ };
|
|
|
192
|
+
|
|
|
193
|
+ $.ajax({
|
|
|
194
|
+ url: `/system/approval/approve/${taskId}`,
|
|
|
195
|
+ type: 'PUT',
|
|
|
196
|
+ contentType: 'application/json',
|
|
|
197
|
+ data: JSON.stringify(params),
|
|
|
198
|
+ success: function(res) {
|
|
|
199
|
+ if (res.code === 200) {
|
|
|
200
|
+ $.modal.msgSuccess("审批成功");
|
|
|
201
|
+ loadPendingTasks();
|
|
|
202
|
+ } else {
|
|
|
203
|
+ $.modal.msgError(res.msg);
|
|
|
204
|
+ }
|
|
|
205
|
+ }
|
|
|
206
|
+ });
|
|
|
207
|
+}
|
|
|
208
|
+
|
|
|
209
|
+// 驳回任务
|
|
|
210
|
+function rejectTask(taskId, reason) {
|
|
|
211
|
+ const params = {
|
|
|
212
|
+ comment: reason
|
|
|
213
|
+ };
|
|
|
214
|
+
|
|
|
215
|
+ $.ajax({
|
|
|
216
|
+ url: `/system/approval/reject/${taskId}`,
|
|
|
217
|
+ type: 'PUT',
|
|
|
218
|
+ contentType: 'application/json',
|
|
|
219
|
+ data: JSON.stringify(params),
|
|
|
220
|
+ success: function(res) {
|
|
|
221
|
+ if (res.code === 200) {
|
|
|
222
|
+ $.modal.msgSuccess("驳回成功");
|
|
|
223
|
+ loadPendingTasks();
|
|
|
224
|
+ } else {
|
|
|
225
|
+ $.modal.msgError(res.msg);
|
|
|
226
|
+ }
|
|
|
227
|
+ }
|
|
|
228
|
+ });
|
|
|
229
|
+}
|
|
|
230
|
+```
|
|
|
231
|
+
|
|
|
232
|
+### 2. 个人级别检查提交
|
|
|
233
|
+
|
|
|
234
|
+```javascript
|
|
|
235
|
+function submitPersonalCheck(checkData) {
|
|
|
236
|
+ const params = {
|
|
|
237
|
+ businessType: 'SECURITY_CHECK',
|
|
|
238
|
+ businessId: checkData.id,
|
|
|
239
|
+ title: '个人级别安检问题',
|
|
|
240
|
+ targetUserIds: checkData.targetUserIds,
|
|
|
241
|
+ formData: {
|
|
|
242
|
+ checkLevel: 'PERSONAL',
|
|
|
243
|
+ description: checkData.description
|
|
|
244
|
+ },
|
|
|
245
|
+ businessData: {
|
|
|
246
|
+ checkType: checkData.checkType,
|
|
|
247
|
+ location: checkData.location
|
|
|
248
|
+ }
|
|
|
249
|
+ };
|
|
|
250
|
+
|
|
|
251
|
+ $.post('/system/approval/start/personal', params, function(res) {
|
|
|
252
|
+ if (res.code === 200) {
|
|
|
253
|
+ $.modal.msgSuccess("提交成功,已发送通知");
|
|
|
254
|
+ } else {
|
|
|
255
|
+ $.modal.msgError(res.msg);
|
|
|
256
|
+ }
|
|
|
257
|
+ });
|
|
|
258
|
+}
|
|
|
259
|
+```
|
|
|
260
|
+
|
|
|
261
|
+## 数据库初始化
|
|
|
262
|
+
|
|
|
263
|
+在使用审批流框架前,需要执行以下SQL脚本初始化数据库:
|
|
|
264
|
+
|
|
|
265
|
+```sql
|
|
|
266
|
+-- 执行审批流数据库脚本
|
|
|
267
|
+source sql/approval_workflow.sql
|
|
|
268
|
+```
|
|
|
269
|
+
|
|
|
270
|
+## 权限配置
|
|
|
271
|
+
|
|
|
272
|
+需要在系统中配置以下权限:
|
|
|
273
|
+
|
|
|
274
|
+```sql
|
|
|
275
|
+-- 审批流相关权限
|
|
|
276
|
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES
|
|
|
277
|
+('审批管理', 1, 8, 'approval', NULL, 1, 0, 'M', '0', '0', NULL, 'documentation', 'admin', sysdate(), '', NULL, '审批流程管理目录'),
|
|
|
278
|
+('待办任务', 2000, 1, 'pending', 'approval/pending/index', 1, 0, 'C', '0', '0', 'system:approval:query', '#', 'admin', sysdate(), '', NULL, '待办任务菜单'),
|
|
|
279
|
+('已办任务', 2000, 2, 'completed', 'approval/completed/index', 1, 0, 'C', '0', '0', 'system:approval:query', '#', 'admin', sysdate(), '', NULL, '已办任务菜单'),
|
|
|
280
|
+('我的申请', 2000, 3, 'submitted', 'approval/submitted/index', 1, 0, 'C', '0', '0', 'system:approval:query', '#', 'admin', sysdate(), '', NULL, '我的申请菜单');
|
|
|
281
|
+
|
|
|
282
|
+-- 审批流权限
|
|
|
283
|
+INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES
|
|
|
284
|
+('审批', '', 1, '', '', 1, 0, 'F', '0', '0', 'system:approval:approve', '#', 'admin', sysdate(), '', NULL, ''),
|
|
|
285
|
+('驳回', '', 2, '', '', 1, 0, 'F', '0', '0', 'system:approval:reject', '#', 'admin', sysdate(), '', NULL, ''),
|
|
|
286
|
+('取消', '', 3, '', '', 1, 0, 'F', '0', '0', 'system:approval:cancel', '#', 'admin', sysdate(), '', NULL, ''),
|
|
|
287
|
+('启动流程', '', 4, '', '', 1, 0, 'F', '0', '0', 'system:approval:start', '#', 'admin', sysdate(), '', NULL, '');
|
|
|
288
|
+```
|
|
|
289
|
+
|
|
|
290
|
+## 角色配置
|
|
|
291
|
+
|
|
|
292
|
+确保系统中存在以下角色:
|
|
|
293
|
+
|
|
|
294
|
+```sql
|
|
|
295
|
+-- 班组长角色
|
|
|
296
|
+INSERT INTO sys_role (role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, remark)
|
|
|
297
|
+VALUES ('班组长', 'group_leader', 3, '4', '0', '0', 'admin', sysdate(), '班组长角色');
|
|
|
298
|
+
|
|
|
299
|
+-- 科长角色
|
|
|
300
|
+INSERT INTO sys_role (role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, remark)
|
|
|
301
|
+VALUES ('科长', 'section_leader', 2, '3', '0', '0', 'admin', sysdate(), '科长角色');
|
|
|
302
|
+
|
|
|
303
|
+-- 质检员角色
|
|
|
304
|
+INSERT INTO sys_role (role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, remark)
|
|
|
305
|
+VALUES ('质检员', 'quality_inspector', 4, '4', '0', '0', 'admin', sysdate(), '质检员角色');
|
|
|
306
|
+```
|
|
|
307
|
+
|
|
|
308
|
+## 注意事项
|
|
|
309
|
+
|
|
|
310
|
+1. **审批人配置**: 确保各部门的班组长、科长角色配置正确
|
|
|
311
|
+2. **业务数据**: 根据实际业务需求调整 businessData 的数据结构
|
|
|
312
|
+3. **超时处理**: 系统会自动标记超时任务,建议定期清理
|
|
|
313
|
+4. **权限控制**: 确保只有相关人员才能审批对应的任务
|
|
|
314
|
+5. **数据备份**: 审批历史数据建议定期备份
|
|
|
315
|
+
|
|
|
316
|
+## 扩展开发
|
|
|
317
|
+
|
|
|
318
|
+如需扩展新的审批流程类型:
|
|
|
319
|
+
|
|
|
320
|
+1. 在数据库中添加新的流程定义和节点定义
|
|
|
321
|
+2. 在 `ApprovalConstants` 中添加相应常量
|
|
|
322
|
+3. 在 `IApprovalEngineService` 中添加新的方法
|
|
|
323
|
+4. 在 `ApprovalController` 中添加对应的接口
|
|
|
324
|
+5. 根据需要扩展 `IApprovalUserService` 的审批人获取逻辑
|