当前位置: 首页 > 工具软件 > JFinal OA > 使用案例 >

OA工作流实现支持多级审批、驳回

丘友樵
2023-12-01

一、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>

 类似资料: