一.场景引入
我在实际开发中遇到过这样一个需求:公民登录建议征集系统向大冶市某个人大代表提建议,但建议提出后人大代表未能及时响应,此时如果建议越来越多,就会导致建议被搁置。很多建议不会被处理,公民自然也就得不到反馈,这对于汇聚民生民意和征集建议显然是不利的。于是,我开始构思:能否利用工作流的技术,当有公民的建议提交给人大代表的时候就创建一个流程实例,如果建议超过时间没有被处理,系统自动将建议转交到人大组,同组的人大可以拾取和处置该建议(我以地区作为分组,例如:张三,李四,王五为大冶市人大代表,赵六,王伟为黄石港区人大代表。公民提交建议到张三账号后张三未能及时对建议作出处理,建议超过7天就会自动转交到与张三同为大冶市人大代表的人大组代表中。也就是说,建议提出后7天内建议由张三处理,李四,王五以及其他人大无权查看和处置。建议超过7天后,李四,王五(大冶市人大代表)账号可以看到这条超时未被处理的建议,拾取和处置建议,张三可以重新拾取和处置已经超时的建议(任务);赵六,王伟(黄石港区人大)账号始终不能看到提交到大冶市人大代表的建议)。
还有,对于超时的建议(任务),人大代表必须先拾取再处置,如果拾取后的建议并不擅长处理,可以将建议(任务)归还,此后(建议)任务回到可拾取任务队列中,可以由同组的其他人大拾取和处置。如此往复......直至建议被解决。
定下需求后,我开始着手于实现。但是我发现网上的资源较少,不能支持我完整的开发出这样的功能,于是我开始自己摸索,买书,看网课,不断测试......完成了这个需求。
下面是我使用springboot框架集成Activiti7完成开发的过程:
1.开发所要用到的Maven依赖:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M4</version>
<type>pom</type>
</dependency>
<!-- 生成流程图 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>7.1.0.M4</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-process-validation</artifactId>
<version>7.1.0.M4</version>
</dependency>
2.下载BPMN流程图插件,我用的是2022年的IDE,大家可以参考。
3.根据业务需求设计流程图,如下图:
图3-1项目资源目录下新建processes->xxx.bpmn20.xml文件
图3-2 在bpmn20.xml文件中右键->选择View BPMN(Activiti)Diagram在视图中打开
图3-3 在打开的视图中右键选择流程符号定义流程图
流程图的定义因需求和场景各有差异,对于流程的定义大家可以看看我整理的 《BPMN流程符号介绍》 ,设计好自己的业务流程
下面是我设计好的工作流流程图,用于建议征集过程的自动化办公
3.业务代码实现个人任务查询、组任务查询,任务处理,任务归还,任务拾取以及完成任务。BusinessKey应该和自己的业务主键id相关联,我这里与自己数据库中建议表的主键id相关联,所以可以由BusinessKey对建议表中数据进行查询。
控制层
package com.yqj.imli.controller;
import com.yqj.imli.controller.Request.MotionRequest;
import com.yqj.imli.controller.Request.TaskRequest;
import com.yqj.imli.entity.Group;
import com.yqj.imli.entity.HistoryTask;
import com.yqj.imli.entity.Motion;
import com.yqj.imli.entity.Result;
import com.yqj.imli.service.impl.MyHistoryService;
import com.yqj.imli.service.impl.QueryCandidateUsersService;
import com.yqj.imli.service.impl.QueryTaskService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.*;
@Slf4j
@RestController
@CrossOrigin
@RequestMapping("/task")
public class TaskController {
//提供对流程定义和部署存储库的访问服务
@Autowired
HistoryService historyService;
//运行时的接口
@Autowired
RuntimeService runtimeService;
// 任务处理接口
@Autowired
TaskService taskService;
@Autowired
MyHistoryService myHistoryService;
@Autowired
QueryTaskService queryTaskService;
@Autowired
QueryCandidateUsersService queryCandidateUsersService;
/**
* 用户提交议案时生成流程实例, 用户完成任务
*/
@PostMapping("/startProcess")
public Result startProcess(@RequestBody MotionRequest motionRequest) {
System.out.println("motionRequest: " +motionRequest);
Map<String, Object> variablesMap = new HashMap<>();
String ID = String.valueOf(motionRequest.getId());
List<String> candidateUsers = queryCandidateUsersService.queryCandidateUsers(motionRequest.getAddress());
System.out.println("candidateUsers: " +candidateUsers);
variablesMap.put("candidateUserList", candidateUsers);
variablesMap.put("user", motionRequest.getFrom());
variablesMap.put("NPC", motionRequest.getTo());
//通过流程定义ID启动一个流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myMotion", ID, variablesMap);
log.info("流程实例:{}", processInstance);
Task task = taskService.createTaskQuery()
.processDefinitionKey("myMotion")
.taskAssignee(motionRequest.getFrom())
.singleResult();
taskService.complete(task.getId());
myHistoryService.saveHistoryTask(Integer.valueOf(processInstance.getBusinessKey()), task.getAssignee(), new SimpleDateFormat("YYYY年MM月dd日 HH:mm").format(new Date()), "提交待处理", null, null);
return Result.success("议案提交成功");
}
/**
* 完成个人任务
*/
@PostMapping("/completeTask")
public Result completeTask(@RequestBody TaskRequest taskRequest) {
System.out.println("taskRequest: " +taskRequest);
Task task = taskService.createTaskQuery()
.processInstanceBusinessKey(String.valueOf(taskRequest.getId()))
.singleResult();
taskService.complete(task.getId());
myHistoryService.saveHistoryTask(taskRequest.getId(), task.getAssignee(), new SimpleDateFormat("YYYY年MM月dd日 HH:mm").format(new Date()), "完成任务", taskRequest.getHandingRes() , taskRequest.getHandingOpinions());
return Result.success("");
}
/**
* 查询个人任务
*/
@GetMapping("/staticByAssigneeTask")
public Result staticByAssigneeTask(String NPC) {
Integer task = queryOwnerTask(NPC).size();
return Result.success(task);
}
@GetMapping("/queryByAssigneeTask")
public Result queryByAssigneeTask(@Param("NPC") String NPC, @Param("size") Integer size, @Param("page") Integer page) {
System.out.println("size: " +size);
System.out.println("page: " +page);
List<Task> taskList = queryOwnerTask(NPC);
if(taskList == null || taskList.size() == 0) {
System.out.println("个人任务不存在");
return Result.error("个人任务不存在");
}
List<Integer> businessKeyList = new ArrayList<>();
for (Task task : taskList) {
// 使用task对象获取实例id
String processInstanceId = task.getProcessInstanceId();
// 使用实例id, 获取流程实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 获取业务主键
businessKeyList.add(Integer.valueOf(processInstance.getBusinessKey()));
}
// 通过businessKey查询任务中所对应的业务内容
List<Motion> motions = queryTaskService.queryMotionByBusinessKey(businessKeyList, size, page);
Map<String, Object> taskMap = new HashMap<>();
taskMap.put("number", taskList.size());
taskMap.put("motions", motions);
return Result.success(taskMap);
}
@GetMapping("/staticAssigneeTask")
public Result staticAssigneeTask(String NPC) {
List<Task> taskList = queryOwnerTask(NPC);
if(taskList == null || taskList.size() == 0) {
System.out.println("个人任务不存在");
return Result.error("个人任务不存在");
}
List<Integer> businessKeyList = new ArrayList<>();
for (Task task : taskList) {
// 使用task对象获取实例id
String processInstanceId = task.getProcessInstanceId();
// 使用实例id, 获取流程实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 获取业务主键
businessKeyList.add(Integer.valueOf(processInstance.getBusinessKey()));
}
List<Group> groups = queryTaskService.staticAssigneeTask(businessKeyList);
Map<String, Object> taskMap = new HashMap<>();
taskMap.put("number", taskList.size());
taskMap.put("groups", groups);
return Result.success(taskMap);
}
/**
* 查询已完成任务
*/
@GetMapping("/staticFinishedTask")
public Result staticFinishedTask(String NPC) {
Integer completedTask = myHistoryService.queryFinishedTask(NPC).size();
List<Group> groups = myHistoryService.groupFinishedBySort(NPC);
System.out.println("分组后: " +groups);
Map<String, Object> completedTaskNumbermap = new HashMap<>();
completedTaskNumbermap.put("completedTask", completedTask);
completedTaskNumbermap.put("groups", groups);
return Result.success(completedTaskNumbermap);
}
@GetMapping("/queryFinishedTask")
public Result queryFinishedTask(String NPC) {
// 查询历史任务
List<Motion> motions = myHistoryService.queryFinishedTask(NPC);
for (Motion motion : motions) {
HistoryTask historyTask = myHistoryService.queryHandingMessage(motion.getMotion());
if (historyTask != null) {
motion.setHandingRes(historyTask.getHandingRes());
motion.setHandingOpinion(historyTask.getHandingOpinions());
}
}
return Result.success(motions);
}
@PutMapping("/removeFinished")
public Result remove (@RequestBody List<TaskRequest> taskRequestList) {
for (TaskRequest finishedTask : taskRequestList) {
myHistoryService.remove(finishedTask.getId());
}
return Result.success();
}
/**
*
* @param id
* @return
*/
@GetMapping("/hiddenFinishedTask/{id}")
public Result hiddenFinishedTask(@PathVariable Integer id) {
myHistoryService.updateHistoryTask(id);
return Result.success("");
}
/**
* 查询组任务
*/
@GetMapping("/staticGroupTask")
public Result staticGroupTask(String NPC) {
Integer claimableTask = taskService.createTaskQuery()
.processDefinitionKey("myMotion")
.taskCandidateUser(NPC)
.list()
.size();
return Result.success(claimableTask);
}
@GetMapping("/checkGroupTask")
public Result checkGroupTask(@Param("NPC") String NPC, @Param("size") Integer size, @Param("page") Integer page) {
// securityUtil.logInAs(NPC);
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionKey("myMotion")
.taskCandidateUser(NPC)
.list();
if (tasks == null || tasks.size() == 0) {
return Result.error("任务不存在");
}
List<Integer> businessKeyList = new ArrayList<>();
for (Task task : tasks) {
String processInstanceId = task.getProcessInstanceId();
// 使用实例id, 获取流程实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 获取业务主键
businessKeyList.add(Integer.valueOf(processInstance.getBusinessKey()));
}
List<Motion> motions = queryTaskService.queryMotionByBusinessKey(businessKeyList, size, page);
Map<String, Object> taskMap = new HashMap<>();
taskMap.put("number", tasks.size());
taskMap.put("motions", motions);
return Result.success(taskMap);
}
@GetMapping("/claimTask")
public Result claimTask(Integer id,String NPC) {
Task task = taskService.createTaskQuery()
.processInstanceBusinessKey(String.valueOf(id))
.singleResult();
taskService.claim(task.getId(), NPC);
myHistoryService.saveHistoryTask(id, NPC, new SimpleDateFormat("YYYY年MM月dd日 HH:mm").format(new Date()), "拾取任务", null , null);
return Result.success("任务拾取成功");
}
/**
* 归还任务
*/
@GetMapping("/{id}")
public Result returnTask(@PathVariable Integer id) {
Task task = taskService.createTaskQuery()
.processInstanceBusinessKey(String.valueOf(id))
.singleResult();
taskService.claim(task.getId(), null);
myHistoryService.saveHistoryTask(id, task.getAssignee(), new SimpleDateFormat("YYYY年MM月dd日 HH:mm").format(new Date()), "归还任务", null , null);
return Result.success("归还任务成功");
}
@GetMapping("/queryHistoryTask/{comment}")
public Result queryHistoryTask (@PathVariable Integer comment) {
List<HistoryTask> historyTasks = myHistoryService.queryHistoryTask(comment);
return Result.success(historyTasks);
}
public List<Task> queryOwnerTask(String NPC) {
List<Task> taskList = taskService.createTaskQuery()
//代办人姓名
.taskAssignee(NPC)
//活动状态
.active()
.list();
return taskList;
}
}
业务层 MyHistoryService.Java用于记录建议的所有状态,以便流程公开透明。具体代码根据自己的真实需求和数据库表设计编写,也可以没有。其他业务层方法由Activiti7相关jar包直接导入(不用编写,导入Maven依赖后可用)。
package com.yqj.imli.service.impl;
import com.yqj.imli.entity.Group;
import com.yqj.imli.entity.HistoryTask;
import com.yqj.imli.entity.Motion;
import com.yqj.imli.mapper.MyHistoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("historyService")
public class MyHistoryService {
@Autowired
MyHistoryMapper myHistoryMapper;
public void saveHistoryTask(Integer id, String assignee, String time, String activityName, String handingRes, String handingOpinions) {
myHistoryMapper.saveHistoryTask(id, assignee, time, activityName, handingRes, handingOpinions);
}
public List<HistoryTask> queryHistoryTask(Integer comment) {
List<HistoryTask> historyTasks = myHistoryMapper.queryHistoryTask(comment);
return historyTasks;
}
public List<Motion> queryFinishedTask(String NPC) {
List<Motion> motions = myHistoryMapper.queryFinishedTask(NPC);
return motions;
}
public List<Group> groupFinishedBySort(String NPC) {
List<Group> groups = myHistoryMapper.groupFinishedBySort(NPC);
return groups;
}
public HistoryTask queryHandingMessage(Integer id) {
HistoryTask historyTask = myHistoryMapper.queryHandingMessage(id);
return historyTask;
}
public void updateHistoryTask(Integer id) {
myHistoryMapper.updateHistoryTask(id);
}
public void remove(Integer id) {
myHistoryMapper.remove(id);
}
}
Mapper层
package com.yqj.imli.mapper;
import com.yqj.imli.entity.Group;
import com.yqj.imli.entity.HistoryTask;
import com.yqj.imli.entity.Motion;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface MyHistoryMapper {
void saveHistoryTask(@Param("id") Integer id, @Param("assignee") String assignee, @Param("time") String time, @Param("activityName") String activityName,@Param("handingRes") String handingRes, @Param("handingOpinions") String handingOpinions);
List<HistoryTask> queryHistoryTask(@Param("id") Integer id);
List<Motion> queryFinishedTask(@Param("NPC") String NPC);
List<Group> groupFinishedBySort(@Param("NPC") String NPC);
HistoryTask queryHandingMessage(@Param("id") Integer id);
void updateHistoryTask(@Param("motionId") Integer motionId);
void remove(@Param("id") Integer id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yqj.imli.mapper.MyHistoryMapper">
<insert id="saveHistoryTask">
insert into history_task (MOTION_ID, assignee, activity_name, time, handing_res, handing_opinions, `show`)
VALUES (#{id}, #{assignee}, #{activityName}, #{time}, #{handingRes}, #{handingOpinions}, 1)
</insert>
<update id="updateHistoryTask">
update history_task
set `show` = 0
where MOTION_ID = #{motionId}
</update>
<update id="remove">
update history_task
set `show` = 0
where MOTION_ID = #{id}
</update>
<select id="queryHistoryTask" resultType="com.yqj.imli.entity.HistoryTask">
select id, assignee, activity_name, time, handing_res, handing_opinions
from history_task
<where>
<if test="id != null and id!= ''">
MOTION_ID = #{id}
</if>
</where>
order by
unix_timestamp(time) asc
</select>
<select id="queryFinishedTask" resultType="com.yqj.imli.entity.Motion">
select * from motion
where id in (select MOTION_ID from history_task
where assignee = #{NPC}
and activity_name = '完成任务' and `show` = 1)
</select>
<select id="groupFinishedBySort" resultType="com.yqj.imli.entity.Group">
select sort, count(id) as num from motion
where id in (select MOTION_ID from history_task
where assignee = #{NPC}
and activity_name = '完成任务' and `show` = 1)
group by
sort
</select>
<select id="queryHandingMessage" resultType="com.yqj.imli.entity.HistoryTask">
select handing_res , handing_opinions from history_task
where
MOTION_ID = #{id}
and
activity_name = '完成任务'
</select>
</mapper>
在启动过程中如果遇到下面的报错,在数据库中添加缺少的字段就可以了。
以上就是我开发的所有过程了,谢谢大家的阅读,希望能够对大家有所帮助。
标签:task,springboot,NPC,String,Activiti7,OA,import,id,Result From: https://blog.csdn.net/weixin_74758471/article/details/139859840