【若依】33、Flowable 项目实践
AskForLeave.bpmn20.xml yueyazhui/ask_for_leave 技术栈:
- Spring Boot
- Spring Security
- Vue
- Axios
- Flowable
- MySQL/MyBatis
1 绘制流程图
2 选择用户体系
选择自己系统中的用户体系
- 以自己系统中的用户体系为准,然后,根据自己系统的用户体系,为 Flowable 创建对应的视图即可。
🌰:删除 Flowable 中的用户表
ACT_ID_USER
,然后根据自己系统中的用户表,创建一个名为ACT_ID_USER
的视图,并且视图中的字段和 Flowable 中原本的字段保持一致。这样,在项目中,只需要维护自己的用户体系即可。
ACT_ID_USER | 用户 |
---|---|
ACT_ID_GROUP | 组 |
ACT_ID_MEMBERSHIP | 用户_组 |
ACT_ID_PRIV | 权限 |
ACT_ID_PRIV_MAPPING | 用户_权限 |
- 利用 Flowable 中的 IdentityService 服务类,去控制 Flowable 中的用户体系,当需要操作自己的用户体系时,顺便也操作一下 Flowable 中的用户体系。
3 创建项目
1
2
3
4
5
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency>
然后配置 application.properties:
1
2
3
4
5
6
7
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/ask_for_leave?serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 还原 SpringSecurity 默认密码加密策略(Flowable 会修改 SpringSecurity 的默认密码加密策略)
flowable.idm.password-encoder=spring_delegating
👀:
- 数据库的连接地址中,需要拼接
nullCatalogMeansCurrent=true
,用于自动生成 Flowable 所需要的表。 - 还原 SpringSecurity 的密码加密策略。
原本,SpringSecurity 默认密码加密策略是代理类 DelegatingPasswordEncoder,但是,Flowable 将默认加密策略改为 BCryptPasswordEncoder。
4 创建用户
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
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色代码',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'manager', '经理');
INSERT INTO `sys_role` VALUES (2, 'employee', '员工');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名称',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'yue', '{noop}123');
INSERT INTO `sys_user` VALUES (2, 'yueyazhui', '{noop}123');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` int NULL DEFAULT NULL COMMENT '用户ID',
`role_id` int NULL DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户_角色' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1, 1);
INSERT INTO `sys_user_role` VALUES (2, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;
5 系统登录
自定义两个类: 根据用户表创建用户类,并实现 UserDetails 接口:
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
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private List<Role> roleList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roleList.stream().map(role -> new SimpleGrantedAuthority(role.getCode())).collect(Collectors.toList());
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
创建用户服务类,并实现 UserDetailsService 接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
user.setRoleList(userMapper.getRoleListByUserId(user.getId()));
return user;
}
}
6 两个服务类
- 请假通过的服务类:
1 2 3 4 5 6 7 8 9 10 11 12 13
@Slf4j public class ApproveServiceTask implements JavaDelegate { @Override public void execute(DelegateExecution delegateExecution) { Map<String, Object> variables = delegateExecution.getVariables(); // 请假的用户 Object name = variables.get("applicant"); // 请假的天数 Object days = variables.get("days"); log.info("{} 请假 {} 天的申请审批通过", name, days); } }
- 请假拒绝的服务类:
1 2 3 4 5 6 7 8 9 10 11 12 13
@Slf4j public class RejectServiceTask implements JavaDelegate { @Override public void execute(DelegateExecution delegateExecution) { Map<String, Object> variables = delegateExecution.getVariables(); // 请假的用户 Object name = variables.get("applicant"); // 请假的天数 Object days = variables.get("days"); log.info("{} 请假 {} 天的申请审批拒绝", name, days); } }
7 部署流程
在流程图中添加服务类:
下载流程图,放到项目的 resources/processes 目录下,当 SpringBoot 项目启动时,流程文件会自动部署:
启动项目,如果
ACT_RE_DEPLOYMENT
、ACT_RE_PROCDEF
以及ACT_GE_BYTEARRAY
表中存在数据,表明流程部署成功。
8 开启请假流程
Dto:
1
2
3
4
5
6
7
8
9
10
11
12
@Data
public class AskForLeaveDto {
private Integer days;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
private Date startDate;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
private Date endDate;
private String reason;
private String approveUserId;
private String approveUserName;
}
Controller:
1
2
3
4
5
6
7
8
9
10
11
@RestController
public class AskForLeaveController {
@Autowired
AskForLeaveService askForLeaveService;
@PostMapping("apply")
public Response apply(@RequestBody AskForLeaveDto askForLeaveDto) {
return askForLeaveService.apply(askForLeaveDto);
}
}
Service:
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
@Service
public class AskForLeaveServiceImpl implements AskForLeaveService {
@Autowired
RuntimeService runtimeService;
@Override
@Transactional
public Response apply(AskForLeaveDto askForLeaveDto) {
try {
String currentUserId = UserUtil.getCurrentUser().getId().toString();
// 设置流程发起人(默认 UserUtil.getCurrentUser().getUserName())
Authentication.setAuthenticatedUserId(currentUserId);
Map<String, Object> vars = new HashMap<>();
vars.put("applicant", currentUserId);
vars.put("days", askForLeaveDto.getDays());
vars.put("startDate", askForLeaveDto.getStartDate());
vars.put("endDate", askForLeaveDto.getEndDate());
vars.put("reason", askForLeaveDto.getReason());
vars.put("approveUserId", askForLeaveDto.getApproveUserId());
runtimeService.startProcessInstanceByKey("AskForLeave", vars);
return Response.success("请假申请提交成功");
} catch (Exception e) {
e.printStackTrace();
return Response.error("请假申请提交失败");
}
}
}
配置 SpringSecurity,SpringSecurity 默认开启了 CSRF 攻击防御机制,这样使得 POST 请求会比较麻烦,因此,需要禁用 CSRF。
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().anyRequest().authenticated()
.and().formLogin()
// 禁用 CSRF 攻击防御机制
.and().csrf().disable();
return httpSecurity.build();
}
}
9 开发请假页面
Vue3+ElementPlus:
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>请假</title>
<!-- Import style -->
<link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
<!-- Import Vue 3 -->
<script src="//unpkg.com/vue@3"></script>
<!-- Import component library -->
<script src="//unpkg.com/element-plus"></script>
<script src="https://unpkg.com/axios@1.1.2/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<div>
<h1>请假申请</h1>
<table>
<tr>
<th>天数:</th>
<td>
<el-input-number v-model="askForLeaveApply.days" :precision="0" :step="1" :min="1" />
</td>
</tr>
<tr>
<th>日期:</th>
<td>
<el-date-picker v-model="daterange" type="daterange" format="YYYY-MM-DD" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"/>
</td>
</tr>
<tr>
<th>原因:</th>
<td>
<el-input v-model="askForLeaveApply.reason" :rows="1" type="textarea" placeholder="请输入原因"/>
</td>
</tr>
<tr>
<th>审批人:</th>
<td>
<el-select v-model="askForLeaveApply.approveUserId" placeholder="请选择审批人">
<el-option v-for="item in approveUserList" :key="item.id" :label="item.username" :value="item.id"/>
</el-select>
</td>
</tr>
</table>
</div>
</div>
<script>
Vue.createApp({
data() {
return {
askForLeaveApply: {
days: 1,
startDate: null,
endDate: null,
reason: '',
approveUserId: ''
},
daterange: [],
approveUserList: []
}
},
mounted() {
this.getApproveUserList()
},
methods: {
getApproveUserList() {
axios.get('/approveUserList').then((res) => {
this.approveUserList = res.data
})
}
},
}).use(ElementPlus).mount('#app')
</script>
</body>
</html>
在 Vue3 中,创建 Vue 实例的方法是Vue.createApp
。
10 配置审批人
获取审批人列表: Controller:
1
2
3
4
@GetMapping("approveUserList")
public List<User> getApproveUserList() {
return askForLeaveService.getApproveUserList();
}
Service:
1
2
3
4
@Override
public List<User> getApproveUserList() {
return userMapper.getApproveUserList(UserUtil.getCurrentUser().getId());
}
Mapper:
1
2
3
4
5
6
7
8
9
10
11
12
<select id="getApproveUserList" resultType="top.yueyazhui.ask_for_leave.entity.User">
SELECT
u.id,
u.username
FROM
sys_user u
LEFT JOIN sys_user_role ur ON ur.user_id = u.id
LEFT JOIN sys_role r ON r.id = ur.role_id
WHERE
r.code = 'manager'
AND u.id != #{currentUserId};
</select>
前端请求:
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
<script>
Vue.createApp({
data() {
return {
askForLeaveApply: {
days: 1,
startDate: null,
endDate: null,
reason: '',
approveUserId: ''
},
daterange: [],
approveUserList: []
}
},
mounted() {
this.getApproveUserList()
},
methods: {
getApproveUserList() {
axios.get('/approveUserList').then((res) => {
this.approveUserList = res.data
})
}
},
}).use(ElementPlus).mount('#app')
</script>
前端渲染:
1
2
3
4
5
6
7
8
<tr>
<th>审批人:</th>
<td>
<el-select v-model="askForLeaveApply.approveUserId" placeholder="请选择审批人">
<el-option v-for="item in approveUserList" :key="item.id" :label="item.username" :value="item.id"/>
</el-select>
</td>
</tr>
11 发起请假
发送请求:
1
2
3
4
5
<tr>
<td>
<el-button type="primary" @click="handleApply">申请</el-button>
</td>
</tr>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
handleApply() {
this.askForLeaveApply.startDate = this.daterange[0]
this.askForLeaveApply.endDate = this.daterange[1]
axios.post('/apply', this.askForLeaveApply).then((res) => {
this.$message.success(res.data.message)
this.getPendApproveList()
this.daterange = []
this.askForLeaveApply = {
days: 1,
startDate: null,
endDate: null,
reason: '',
approveUserId: ''
}
})
},
12 查看请假流程
1. 查看待审批的流程
Controller:
1
2
3
4
5
6
7
8
/**
* 当前用户待审批流程列表
* @return
*/
@GetMapping("pendApproveList")
public List<AskForLeaveDto> getPendApproveList() {
return askForLeaveService.getPendApproveList();
}
Service:
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
/**
* 当前用户待审批流程列表
* @return
*/
@Override
public List<AskForLeaveDto> getPendApproveList() {
List<AskForLeaveDto> askForLeaveDtoList = new ArrayList<>();
List<Execution> executionList = runtimeService.createExecutionQuery().startedBy(UserUtil.getCurrentUser().getId().toString()).list();
for (Execution execution : executionList) {
Integer days = ((Integer) runtimeService.getVariable(execution.getId(), "days"));
Date startDate = (Date) runtimeService.getVariable(execution.getId(), "startDate");
Date endDate = (Date) runtimeService.getVariable(execution.getId(), "endDate");
String reason = (String) runtimeService.getVariable(execution.getId(), "reason");
String approveUserId = ((String) runtimeService.getVariable(execution.getId(), "approveUserId"));
AskForLeaveDto askForLeaveDto = new AskForLeaveDto();
askForLeaveDto.setDays(days);
askForLeaveDto.setStartDate(startDate);
askForLeaveDto.setEndDate(endDate);
askForLeaveDto.setReason(reason);
askForLeaveDto.setApproveUserId(approveUserId);
askForLeaveDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(approveUserId)));
askForLeaveDto.setProcessInstanceId(execution.getProcessInstanceId());
askForLeaveDtoList.add(askForLeaveDto);
}
return askForLeaveDtoList;
}
前端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div>
<div>
<h1>我的请假列表(当前)</h1>
<el-table :data="pendApproveList" stripe border style="width: 100%">
<el-table-column prop="days" label="天数"></el-table-column>
<el-table-column prop="date" label="日期">
<template #default="scope">
至
</template>
</el-table-column>
<el-table-column prop="reason" label="原因"></el-table-column>
<el-table-column prop="approveUserName" label="审批人"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" text bg @click="showRealTimeProgressImage(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
待审批流程列表的请求:
1
2
3
4
5
getPendApproveList() {
axios.get('/pendApproveList').then((res) => {
this.pendApproveList = res.data
})
},
实时进度图: Controller:
1
2
3
4
5
6
7
8
9
/**
* 实时进度图
* @param processInstanceId
* @throws IOException
*/
@GetMapping("realTimeProgressImage/{processInstanceId}")
public void getRealTimeProgressImage(@PathVariable String processInstanceId) throws IOException {
askForLeaveService.getRealTimeProgressImage(processInstanceId);
}
Service:
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
/**
* 实时进度图
* @return
*/
@Override
public void getRealTimeProgressImage(String processInstanceId) throws IOException {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("AskForLeave").latestVersion().singleResult();
// 绘制流程图的对象
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
// 绘制流程图的生成器
DefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new DefaultProcessDiagramGenerator();
// 所有已经执行过的活动 ID,将之保存在这个集合中
List<String> highLightedActivities = new ArrayList<>();
// 所有已经执行过的线条 ID,将之保存在这个集合中
List<String> highLightedFlows = new ArrayList<>();
// 缩放因子
double scaleFactor = 1.0;
// 绘制连接线时,是否绘制对应的文本
boolean drawSequenceFlowNameWithNoLabelDI = true;
// 查询流程实例的所有活动信息
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
if (historicActivityInstance.getActivityType().equals("sequenceFlow")) {
// 如果活动类型是 sequenceFlow,那么就加入到线条的 ID 中
highLightedFlows.add(historicActivityInstance.getActivityId());
} else {
// 否则就加入到高亮活动的 ID 中
highLightedActivities.add(historicActivityInstance.getActivityId());
}
}
ProcessEngineConfiguration processEngineConfig = processEngine.getProcessEngineConfiguration();
InputStream is = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, "PNG", highLightedActivities, highLightedFlows, processEngineConfig.getActivityFontName(), processEngineConfig.getLabelFontName(), processEngineConfig.getAnnotationFontName(), processEngineConfig.getClassLoader(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
assert ObjectUtil.isNotNull(response);
response.setContentType(MediaType.IMAGE_PNG_VALUE);
FileCopyUtils.copy(is, response.getOutputStream());
}
前端渲染:
1
2
3
4
5
<div>
<el-dialog v-model="realTimeProgressImageDialogVisible" title="实时进度图" width="560" :close-on-click-modal="false" :draggable="true">
<img :src="realTimeProgressImageUrl" alt="实时进度图" />
</el-dialog>
</div>
前端设置属性:
1
2
3
4
showRealTimeProgressImage(row) {
this.realTimeProgressImageUrl = '/realTimeProgressImage/' + row.processInstanceId
this.realTimeProgressImageDialogVisible = true
},
2. 查看待审批的任务
每一个用户登录上来之后,可以查看到自己需要审批的任务。 Controller:
1
2
3
4
5
6
7
8
/**
* 当前用户任务列表
* @return
*/
@GetMapping("taskList")
public List<TaskDto> getTaskList() {
return askForLeaveService.getTaskList();
}
Service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public List<TaskDto> getTaskList() {
List<TaskDto> taskDtoList = new ArrayList<>();
List<Task> taskList = taskService.createTaskQuery().taskAssignee(UserUtil.getCurrentUser().getId().toString()).list();
for (Task task : taskList) {
TaskDto taskDto = new TaskDto();
Map<String, Object> variables = taskService.getVariables(task.getId());
taskDto.setApplicantId(((String) variables.get("applicantId")));
taskDto.setApplicantName(userService.getUsernameById(Integer.parseInt(variables.get("applicantId").toString())));
taskDto.setDays(((Integer) variables.get("days")));
taskDto.setStartDate(((Date) variables.get("startDate")));
taskDto.setEndDate(((Date) variables.get("endDate")));
taskDto.setReason(((String) variables.get("reason")));
taskDto.setApproveUserId(((String) variables.get("approveUserId")));
taskDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(variables.get("approveUserId").toString())));
taskDto.setTaskId(task.getId());
taskDtoList.add(taskDto);
}
return taskDtoList;
}
前端渲染:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div>
<div>
<h1>待我的审批列表</h1>
<el-table :data="taskList" stripe border style="width: 100%">
<el-table-column prop="applicantName" label="申请人"></el-table-column>
<el-table-column prop="days" label="天数"></el-table-column>
<el-table-column prop="date" label="日期">
<template #default="scope">
至
</template>
</el-table-column>
<el-table-column prop="reason" label="原因"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" @click="handleApprove(scope.row, true)">批准</el-button>
<el-button type="danger" @click="handleApprove(scope.row, false)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
获取数据:
1
2
3
4
5
getTaskList() {
axios.get('/taskList').then((res) => {
this.taskList = res.data
})
},
3. 流程审批
Controller:
1
2
3
4
5
6
7
8
9
/**
* 审批
* @param taskDto
* @return
*/
@PostMapping("approve")
public Response approve(@RequestBody TaskDto taskDto) {
return askForLeaveService.approve(taskDto);
}
Service:
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Response approve(TaskDto taskDto) {
try {
Map<String, Object> vars = new HashMap<>();
vars.put("approve", taskDto.getApprove());
taskService.complete(taskDto.getTaskId(), vars);
return Response.success("审批成功");
} catch (Exception e) {
e.printStackTrace();
return Response.success("审批失败");
}
}
前端请求:
1
2
3
4
5
6
7
handleApprove(row, approve) {
row.approve = approve
axios.post('/approve', row).then((res) => {
this.$message.success(res.data.message)
this.getTaskList()
})
},
4. 查看历史流程
Controller:
1
2
3
4
5
6
7
8
/**
* 当前用户历史流程列表
* @return
*/
@GetMapping("historyList")
public List<AskForLeaveDto> getHistoryList() {
return askForLeaveService.getHistoryList();
}
Service:
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
@Override
public List<AskForLeaveDto> getHistoryList() {
List<AskForLeaveDto> askForLeaveDtoList = new ArrayList<>();
List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().startedBy(UserUtil.getCurrentUser().getId().toString()).finished().list();
for (HistoricProcessInstance historicProcessInstance : historicProcessInstanceList) {
AskForLeaveDto askForLeaveDto = new AskForLeaveDto();
List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();
for (HistoricVariableInstance historicVariableInstance : historicVariableInstanceList) {
String variableName = historicVariableInstance.getVariableName();
Object value = historicVariableInstance.getValue();
switch (variableName) {
case "days":
askForLeaveDto.setDays(((Integer) value));
break;
case "startDate":
askForLeaveDto.setStartDate(((Date) value));
break;
case "endDate":
askForLeaveDto.setEndDate(((Date) value));
break;
case "reason":
askForLeaveDto.setReason(((String) value));
break;
case "approveUserId":
askForLeaveDto.setApproveUserId(((String) value));
askForLeaveDto.setApproveUserName(userService.getUsernameById(Integer.parseInt(askForLeaveDto.getApproveUserId())));
break;
case "approve":
askForLeaveDto.setApprove(((Boolean) value));
break;
}
}
askForLeaveDto.setProcessInstanceId(historicProcessInstance.getId());
askForLeaveDtoList.add(askForLeaveDto);
}
return askForLeaveDtoList;
}
前端渲染:
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
<div>
<div>
<h1>我的请假列表(历史)</h1>
<el-table :data="historyList" stripe border style="width: 100%">
<el-table-column prop="days" label="天数"></el-table-column>
<el-table-column prop="date" label="日期">
<template #default="scope">
至
</template>
</el-table-column>
<el-table-column prop="reason" label="原因"></el-table-column>
<el-table-column prop="approveUserName" label="审批人"></el-table-column>
<el-table-column prop="approve" label="审批结果">
<template #default="scope">
<el-tag v-if="scope.row.approve" type="success">通过</el-tag>
<el-tag v-else type="danger">拒绝</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" text bg @click="showRealTimeProgressImage(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
前端请求:
1
2
3
4
5
getHistoryList() {
axios.get('/historyList').then((res) => {
this.historyList = res.data
})
}