一、mysql表结构
#工作流主表
DROP TABLE IF EXISTS `work_flow`;
CREATE TABLE `work_flow` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`work_type` int(2) COMMENT '流程类型',
`work_type_name` varchar(255) COMMENT '流程类型名称',
`create_time` datetime(0) COMMENT '创建时间',
`modify_time` datetime(0) COMMENT '修改时间,时间戳',
`process_code` varchar(255) COMMENT '流程号(雪花算法)',
`states` int(255) COMMENT '流程状态(1.待审核/2.撤销/3.驳回/4.通过)',
PRIMARY KEY (`id`) USING BTREE
);
#流程实例表
DROP TABLE IF EXISTS `work_flow_process`;
CREATE TABLE `work_flow_process`(
`id` int(11) COMMENT '主键',
`work_flow_id` int(11) COMMENT '关联流程主表ID',
`business_id` int(11) COMMENT '业务流程节点(1校长/2副校长/3班主任)',
`audit_user_id` int(11) COMMENT '审核人id',
`audit_user_name` varchar(255) COMMENT '审核人名称',
`audit_user_remark` varchar(500) COMMENT '审核意见',
`audit_time` datetime(0) COMMENT '审核时间',
`audit_status` int(10) COMMENT '1审核中/2待我审核/3通过/4驳回',
`delete_state` int(1) COMMENT '删除状态,默认0'
);
二、Java核心逻辑实现
public class workFlowProcess {
/**
* @descript 查询我的待办
*/
@Override
public PageUtils selectAgendaList(Query query) {
PageHelper.startPage(query.getPage(), query.getLimit(),query.getOrderBy());
//我的待办
List<MeteWorkProcessNodeNongHang> agendaList = this.mapper.selectAgendaList(query);
List<WorkFlowProssNodeDefinition> definitions = WorkFlowProssNodeDefinition.workFlowIDCNongHang();
WorkFlowProssNodeDefinition head = definitions.get(0);
for (MeteWorkProcessNodeNongHang node : agendaList) {
//可操作的权限
List<String> btns = new ArrayList<>();
btns.add("审核通过");
//当前节点是否为头结点
if (head.getUserId() != node.getAuditUserId()){
btns.add("驳回");
}
//当前节点的可驳回节点
List<Map<String,Object>> backNodes = new ArrayList<>();
for (WorkFlowProssNodeDefinition definition : definitions) {
if (definition.getUserId() == node.getAuditUserId()){
break;
}
Map<String,Object> backInfo = new HashMap<>();
backInfo.put("workId",node.getWorkId());
backInfo.put("userId",definition.getUserId());
backInfo.put("userName",definition.getUserName());
backNodes.add(backInfo);
}
node.setAuditBtns(btns);
node.setBackNodes(backNodes);
}
//return new PageUtils(agendaList);
List<Map<String,Object>> datas = new ArrayList<>();
for (MeteWorkProcessNodeNongHang l : agendaList) {
try {
Map<String, Object> map = mergerData(l, l.getOneToOne());
datas.add(map);
} catch (IllegalAccessException e) {
log.error("属性合并失败:",e);
}
}
return new PageUtils(datas);
}
/**
* @descript 审核通过
*/
public String auditPass(WorkFlowModel model) {
int processNodeId = model.getProcessNodeId();
int workId = model.getWorkId();
Integer userId = model.getUserId();
String comment = model.getComment();
//查询当前工单
MeteWorkProcessNodeNongHang workProcessNode = Optional
.ofNullable(this.selectById(processNodeId))
.orElseThrow(() -> new RuntimeException("没有找id为" + processNodeId + "工单的流程节点"));
//审核人必须是当前用户且审核状态必须为待我审批
if (userId.equals(workProcessNode.getAuditUserId()) && WorkFlowProcessNodeStateEnun.CURR_USER_APPROVAL.getId() == workProcessNode.getAuditStatus()){
//审核通过
Map<String,Object> auditParams = new HashMap<>();
auditParams.put("processNodeId",processNodeId);
auditParams.put("workId",workId);
auditParams.put("userId",userId);
auditParams.put("status",WorkFlowProcessNodeStateEnun.PASS.getId());
auditParams.put("comment",comment);
auditParams.put("auditTime",new Date());
//工单审核通过
if (this.mapper.auditPass(auditParams) == 0){
throw new RuntimeException("工单[" + workId + "]审核通过失败");
}else {
//查询当前工单的 待审 节点
List<MeteWorkProcessNodeNongHang> workProcessNodes = this.mapper.selectWorkProcessNodeInfoByWorkFlowStatus(new HashMap<String,Object>(){{
this.put("workId",workId);
this.put("status",WorkFlowProcessNodeStateEnun.UNDER_REVIEW.getId());
}});
//当前节点为最后一个节点
if (workProcessNodes.size() == 0){
//工单流程完成
if (this.workMapper.updateStatusById(workId, WorkFlowStateEnun.PASS.getId()) == 0){
return "工单[" + workId + "]流程审批完成失败";
}
//最后一个节点推送数据
new Thread(() -> this.ftpPush(workId)).start();
return "工单[" + workId + "]流程已全部审批完成";
}else{
//流转到下一个节点
MeteWorkProcessNodeNongHang next = workProcessNodes.get(0);
next.setAuditStatus(WorkFlowProcessNodeStateEnun.CURR_USER_APPROVAL.getId());
if (this.mapper.flowNext(next) == 0){
throw new RuntimeException("工单[" + workId + "]流转到下一个节点失败");
}
//生成账单
this.calculateBill(model);
return "工单[" + workId + "]审核通过,流程流转到下一个节点,等待用户[ " + next.getAuditUserId() + " ]审核";
}
}
}
return "当前工单[" + workId + "] 用户[" + userId + "]没有待办任务不能审核";
}
/**
* @descript 流程审核历史记录 (1.审核中, 2待我审批,3审核通过,4审核通过)
*/
@Override
public List<MeteWorkProcessNodeNongHang> auditHistory(Query query) {
PageHelper.startPage(query.getPage(), query.getLimit(),"id asc");
return this.mapper.auditHistory(query);
}
/**
* @descript 审核驳回
*/
public String auditReject(WorkFlowModel model) {
int processNodeId = model.getProcessNodeId();
int workId = model.getWorkId();
int backUserId = model.getBackUserId();
String comment = model.getComment();
//查询当前工单
MeteWorkProcessNodeNongHang workProcessNode = Optional
.ofNullable(this.selectById(processNodeId))
.orElseThrow(() -> new RuntimeException("没有找id为" + processNodeId + "工单的流程节点"));
//流程定义
List<WorkFlowProssNodeDefinition> definitions = WorkFlowProssNodeDefinition.workFlowIDCNongHang();
int backIndex = -1,currIndex = -1;
for (int i = 0; i < definitions.size(); i++) {
WorkFlowProssNodeDefinition definition = definitions.get(i);
if (definition.getUserId() == backUserId){
backIndex = i;
}
if (definition.getUserId() == workProcessNode.getAuditUserId()){
currIndex = i;
}
}
if (backIndex == -1 || currIndex == -1 || currIndex <= backIndex){
throw new RuntimeException("驳回节点[" + backUserId + "]不是当前节点[" + workProcessNode.getAuditUserId() + "]的前继节点");
}
//当前节点状态设置为驳回
if (this.mapper.updateStatusByIdAndStatus(processNodeId,WorkFlowProcessNodeStateEnun.REJECT.getId(),comment) == 0){
throw new RuntimeException("设置驳回节点失败");
}
//删除当前工单的所有待审流程
this.mapper.deleteHistoryByStatus(workId,WorkFlowProcessNodeStateEnun.UNDER_REVIEW.getId());
//从驳回节点从新开始流程
boolean isStartNode = false;
int first = 0;
for (WorkFlowProssNodeDefinition definition : definitions) {
if (definition.getUserId() == backUserId){
isStartNode = true;
}
if (isStartNode){
MeteWorkProcessNodeNongHang processNode = new MeteWorkProcessNodeNongHang();
processNode.setWorkId(workId);
processNode.setBusinessId(definition.getBusinessId());
processNode.setAuditUserId(definition.getUserId());
processNode.setAuditUserName(definition.getUserName());
if (first == 0){
processNode.setAuditStatus(WorkFlowProcessNodeStateEnun.CURR_USER_APPROVAL.getId());
}else {
processNode.setAuditStatus(WorkFlowProcessNodeStateEnun.UNDER_REVIEW.getId());
}
this.mapper.insert(processNode);
first++;
}
}
return "当前流程[" + workId + "]驳回到节点[" + backUserId + "]";
}
/**
* @descript 根据id修改流程节点状态
*/
@Override
public int updateStatusById(int id, int status,String comment) {
return this.mapper.updateStatusById(id,status,comment);
}
/**
* @descript 查询当前用户审核通过的流程
*/
@Override
public PageUtils selectAuditPassList(Query query) {
List<MeteWorkProcessNodeNongHang> list = this.mapper.selectAuditPassList(query);
//noRepeatList(list);
List<Map<String,Object>> datas = new ArrayList<>();
for (MeteWorkProcessNodeNongHang l : list) {
try {
Map<String, Object> map = mergerData(l, l.getOneToOne());
datas.add(map);
} catch (IllegalAccessException e) {
log.error("属性合并失败:",e);
}
}
return new PageUtils(datas);
}
/**
* @descript 查询 已办结 的工单
*/
@Override
public PageUtils selectFinishList(Query query) {
List<MeteWorkProcessNodeNongHang> list = this.mapper.selectFinishList(query);
// noRepeatList(list);
List<Map<String,Object>> datas = new ArrayList<>();
for (MeteWorkProcessNodeNongHang l : list) {
try {
Map<String, Object> map = mergerData(l, l.getOneToOne());
datas.add(map);
} catch (IllegalAccessException e) {
log.error("属性合并失败:",e);
}
}
return new PageUtils(datas);
}
/**
* @descript 详情
*/
@Override
public Map<String, Object> workFlowDetails(WorkFlowModel model) {
boolean bindingUserId = false;
for (WorkFlowProssNodeDefinition definition : WorkFlowProssNodeDefinition.workFlowIDCNongHang()) {
if (definition.getUserId() == model.getUserId()){
bindingUserId = true;
}
}
if (!bindingUserId){
model.setUserId(-1);
}
MeteWorkProcessNodeNongHang processNode = this.mapper.workFlowDetails(model);
try {
if (processNode != null && processNode.getOneToOne() != null){
return mergerData(processNode, processNode.getOneToOne());
}
} catch (IllegalAccessException e) {
log.error("合并失败:",e);
}
return new HashMap<>();
}
}
三、流程节点
public enum WorkFlowStateEnun {
/**
* 工单流程-审核中
*/
CHECK_PENDING(1,"待审核"),
/**
* 工单流程-撤销
*/
REVOCATION(2,"撤销"),
/**
* 工单流程-驳回
*/
REJECT(3,"驳回"),
/**
* 工单流程-通过(已办结)
*/
PASS(4,"通过(已办结)");
WorkFlowStateEnun(int id,String name){
this.id = id;
this.name = name;
}
private int id;
private String name;
public static String getNameById(int id){
for (WorkFlowProcessNodeStateEnun value : WorkFlowProcessNodeStateEnun.values()) {
if (value.getId() == id){
return value.getName();
}
}
return null;
}
}
四、 流程实例
public enum WorkFlowProcessNodeStateEnun {
/**
* 流程节点-审核中
*/
UNDER_REVIEW(1,"审核中"),
/**
* 流程节点-待我审核
*/
CURR_USER_APPROVAL(2,"待我审核"),
/**
* 流程节点-通过
*/
PASS(3,"通过"),
/**
* 流程节点-驳回
*/
REJECT(4,"驳回");
WorkFlowProcessNodeStateEnun(int id, String name){
this.id = id;
this.name = name;
}
private int id;
private String name;
public static String getNameById(int id){
for (WorkFlowProcessNodeStateEnun value : WorkFlowProcessNodeStateEnun.values()) {
if (value.getId() == id){
return value.getName();
}
}
return null;
}
}
五、流程定义
public class WorkFlowProssNodeDefinition {
/**
* 编号
*/
private int id;
/**
* 业务类型
*/
private int businessId;
/**
* 用户id
*/
private int userId;
/**
* 用户名称
*/
private String userName;
//流程集合
private static final List<WorkFlowProssNodeDefinition> definitions = new ArrayList<>();
public WorkFlowProssNodeDefinition(int id,int businessId,int userId,String userName){
this.id = id;
this.businessId = businessId;
this.userId = userId;
this.userName = userName;
}
public static List<WorkFlowProssNodeDefinition> workFlowIDCNongHang(){
if (CollectionUtils.isEmpty(definitions)){
//校长
WorkFlowProssNodeDefinition node1 = new WorkFlowProssNodeDefinition(1,1001,1,"校长");
//副校长
WorkFlowProssNodeDefinition node2 = new WorkFlowProssNodeDefinition(2,1002,2,"副校长");
//班主任
WorkFlowProssNodeDefinition node3 = new WorkFlowProssNodeDefinition(2,1002,2,"班主任");
definitions.add(node1);
definitions.add(node2);
definitions.add(node3);
}
return definitions;
}
}
Mapper实现
<?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.idc.mapper.MeteWorkProcessNodeNongHangMapper">
<select id="list" resultType="com.idc.entity.MeteWorkProcessNodeNongHang">
select * from mete_work_process_node_nonghang
</select>
<select id="selectById" resultType="com.idc.entity.MeteWorkProcessNodeNongHang">
select * from mete_work_process_node_nonghang
where id = #{id}
</select>
<insert id="insert">
INSERT INTO `energy`.`mete_work_process_node_nonghang`(
`work_id`, `business_id`, `audit_user_id`,`audit_user_name`,
`audit_user_remark`, `audit_time`,`audit_status`, `delete_state`)
VALUES (
#{workId}, #{businessId}, #{auditUserId}, #{auditUserName},
NULL,NULL, #{auditStatus}, #{deleteState}
);
</insert>
<select id="selectWorkProcessNodeInfo" resultMap="oneToOne">
select * from mete_work_process_node_nonghang
</select>
<!-- 我的待办-->
<select id="selectAgendaList" resultMap="oneToOne">
select
a.id as `process_node_id`,a.work_id,a.business_id ,a.audit_user_id ,a.audit_user_name,
a.audit_user_remark ,a.audit_time,a.audit_status,a.delete_state,b.*
from
mete_work_process_node_nonghang a
left join
mete_work_nonghang b
on
a.work_id = b.id
where
a.audit_user_id = #{userId} and a.audit_status = 2 and a.delete_state = 0
<if test="workId != null and workId != ''">
and b.id = #{workId}
</if>
<if test="startTime != null and startTime != '' and endTime != null and endTime != ''">
and b.create_time between #{startTime} and #{endTime}
</if>
</select>
<!-- 审核历史 -->
<select id="auditHistory" resultType="com.idc.entity.MeteWorkProcessNodeNongHang">
select * from mete_work_process_node_nonghang
where work_id = #{workId} and audit_status != 1
order by id
</select>
<!--审核通过-->
<update id="auditPass">
update mete_work_process_node_nonghang
set audit_status = #{status},audit_user_remark = #{comment}, audit_time = #{auditTime}
where
id = #{processNodeId} and work_id = #{workId} and audit_user_id = #{userId} and audit_status = 2
</update>
<!--流转到下一个节点-->
<update id="flowNext">
update mete_work_process_node_nonghang set audit_status = #{auditStatus}
where id = #{id} and audit_status = 1
</update>
<!--指定工单 查询指定状态的流程节点 -->
<select id="selectWorkProcessNodeInfoByWorkFlowStatus" resultType="com.idc.entity.MeteWorkProcessNodeNongHang">
select * from mete_work_process_node_nonghang
where work_id = #{workId} and audit_status = #{status}
</select>
<update id="updateStatusById">
update mete_work_process_node_nonghang set audit_status = #{status},audit_user_remark = #{comment},audit_time = now()
where id = #{id}
</update>
<update id="updateStatusByIdAndStatus">
update mete_work_process_node_nonghang set audit_status = #{status},audit_user_remark = #{comment},audit_time = now()
where id = #{id} and audit_status = 2
</update>
<delete id="deleteHistoryByStatus">
delete from mete_work_process_node_nonghang
where work_id = #{workId} and audit_status = #{status}
</delete>
</mapper>