Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程

开发多级审批流程

请假流程

Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第1张图片

设计约束

每一个请假单对应一个审批流程。
请假单创建后,按业务规则生成部门经理、总经理审批任务。
审批任务的经办人只能审批自己辖区内的请假申请。
所有审批任务“通过”,代表请假已经批准。
任意审批任务“驳回”操作,其余审批任务取消,请假申请驳回。
请假流程中的任意节点产生的操作都要生成对应的系统通知。

工作流程表设计

Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第2张图片
打开navicat,创建adm_leave_form请假单表
Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第3张图片
创建adm_process_flow审批任务流程表
Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第4张图片
创建sys_notice消息通知表
Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第5张图片

实现Dao与数据交互

先将实体类创建好

package com.ql.oa.entity;

import java.util.Date;

public class LeaveForm {
    private Long formId;
    private Long employeeId;
    private Integer formType;
    private Date startTime;
    private Date endTime;
    private String reason;
    private Date createTime;
    private String state;

    public Long getFormId() {
        return formId;
    }

    public void setFormId(Long formId) {
        this.formId = formId;
    }

    public Long getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(Long employeeId) {
        this.employeeId = employeeId;
    }

    public Integer getFormType() {
        return formType;
    }

    public void setFormType(Integer formType) {
        this.formType = formType;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

package com.ql.oa.entity;

import java.util.Date;

public class ProcessFlow {
    private Long processId;
    private Long formId;
    private Long operatorId;
    private String action;
    private String result;
    private String reason;
    private Date createTime;
    private Date auditTime;
    private Integer orderNo;
    private String state;
    private Integer isLast;

    public Long getProcessId() {
        return processId;
    }

    public void setProcessId(Long processId) {
        this.processId = processId;
    }

    public Long getFormId() {
        return formId;
    }

    public void setFormId(Long formId) {
        this.formId = formId;
    }

    public Long getOperatorId() {
        return operatorId;
    }

    public void setOperatorId(Long operatorId) {
        this.operatorId = operatorId;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getAuditTime() {
        return auditTime;
    }

    public void setAuditTime(Date auditTime) {
        this.auditTime = auditTime;
    }

    public Integer getOrderNo() {
        return orderNo;
    }

    public void setOrderNo(Integer orderNo) {
        this.orderNo = orderNo;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public Integer getIsLast() {
        return isLast;
    }

    public void setIsLast(Integer isLast) {
        this.isLast = isLast;
    }
}

package com.ql.oa.entity;

import java.util.Date;

public class Notice {
    private Long noticeId;
    private Long receiverId;
    private String content;
    private Date createTime;
    public Notice(){

    }
    public Notice(Long receiverId , String content){
        this.receiverId = receiverId;
        this.content = content;
        this.createTime = new Date();
    }

    public Long getNoticeId() {
        return noticeId;
    }

    public void setNoticeId(Long noticeId) {
        this.noticeId = noticeId;
    }

    public Long getReceiverId() {
        return receiverId;
    }

    public void setReceiverId(Long receiverId) {
        this.receiverId = receiverId;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

然后把每个实体类的数据交互的接口Dao创建好并填写新增数据的方法:

package com.ql.oa.dao;

import com.ql.oa.entity.LeaveForm;

public interface LeaveFormDao {
    public void insert(LeaveForm form);
}
package com.ql.oa.dao;

import com.ql.oa.entity.ProcessFlow;

public interface ProcessFlowDao {
    public void insert(ProcessFlow processFlow);
}
package com.ql.oa.dao;

import com.ql.oa.entity.Notice;

public interface NoticeDao {
    public void insert(Notice notice);
}

然后在resources\mappers下创建mapper文件并在mybatis-config.xml里配置


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ql.oa.dao.LeaveFormDao">
    <insert id="insert" parameterType="com.ql.oa.entity.LeaveForm"
        useGeneratedKeys="true" keyProperty="formId" keyColumn="form_id">
        INSERT INTO adm_leave_form( employee_id, form_type, start_time, end_time, reason, create_time, state)
        VALUES ( #{employeeId}, #{formType}, #{startTime}, #{endTime}, #{reason}, #{createTime}, #{state})
    insert>
mapper>

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ql.oa.dao.ProcessFlowDao">
    <insert id="insert" parameterType="com.ql.oa.entity.ProcessFlow"
            useGeneratedKeys="true" keyColumn="process_id" keyProperty="processId">
      INSERT INTO adm_process_flow(form_id, operator_id, action, result, reason, create_time , audit_time , order_no , state,is_last)
      VALUES (#{formId}, #{operatorId}, #{action}, #{result}, #{reason}, #{createTime} , #{auditTime} , #{orderNo} , #{state} , #{isLast});
    insert>
mapper>

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ql.oa.dao.NoticeDao">
    <insert id="insert" parameterType="com.ql.oa.entity.Notice"
        useGeneratedKeys="true" keyProperty="noticeId" keyColumn="notice_id">
        INSERT INTO sys_notice( receiver_id, content, create_time) VALUES (#{receiverId}, #{content}, #{createTime})
    insert>
mapper>
    <mappers>
        <mapper resource="mappers/user.xml"/>
        <mapper resource="mappers/rbac.xml"/>
        <mapper resource="mappers/employee.xml"/>
        <mapper resource="mappers/department.xml"/>
        <mapper resource="mappers/leave_form.xml"/>
        <mapper resource="mappers/process_flow.xml"/>
        <mapper resource="mappers/notice.xml"/>
    mappers>

开发请假申请功能

在EmployeeDao接口中新增一个方法,并且在相应的employee.xml中添加实现:

    /**
     * 根据传入员工对象获取上级主管对象
     * @param employee 员工对象
     * @return 上级主管对象
     */
    public Employee selectLeader(@Param("emp") Employee employee);
    <select id="selectLeader" parameterType="com.ql.oa.entity.Employee" resultType="com.ql.oa.entity.Employee">
        select * from adm_employee
        where
        <if test="emp.level < 7">
            level = 7 and department_id = #{emp.departmentId}
        if>
        <if test="emp.level == 7">
            level = 8
        if>
        <if test="emp.level == 8">
            employee_id = #{emp.employeeId}
        if>
    select>

在com.ql.oa.service包下创建LeaveFormService.java请假单流程业务类,并编写创建请假单的方法

/**
 * 请假单流程服务
 */
public class LeaveFormService {
    /**
     * 创建请假单
     * @param form 前端输入的请假单数据
     * @return 持久化后的请假单对象
     */
    public LeaveForm createLeaveForm(LeaveForm form){
        LeaveForm savedForm = (LeaveForm)MyBatisUtils.executeUpdate(sqlSession -> {
            //1.持久化form表单数据,8级以下员工表单状态为processing,8级(总经理)状态为approved
            EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
            Employee employee = employeeDao.selectById(form.getEmployeeId());
            if(employee.getLevel() == 8){
                form.setState("approved");
            }else{
                form.setState("processing");
            }
            LeaveFormDao leaveFormDao = sqlSession.getMapper(LeaveFormDao.class);
            leaveFormDao.insert(form);
            //2.增加第一条流程数据,说明表单已提交,状态为complete
            ProcessFlowDao processFlowDao = sqlSession.getMapper(ProcessFlowDao.class);
            ProcessFlow flow1 = new ProcessFlow();
            flow1.setFormId(form.getFormId());
            flow1.setOperatorId(employee.getEmployeeId());
            flow1.setAction("apply");
            flow1.setCreateTime(new Date());
            flow1.setOrderNo(1);
            flow1.setState("complete");
            flow1.setIsLast(0);
            processFlowDao.insert(flow1);
            //3.分情况创建其余流程数据
            //3.1 7级以下员工,生成部门经理审批任务,请假时间大于72小时,还需生成总经理审批任务
            if(employee.getLevel() < 7){
                Employee dmanager = employeeDao.selectLeader(employee);
                ProcessFlow flow2 = new ProcessFlow();
                flow2.setFormId(form.getFormId());
                flow2.setOperatorId(dmanager.getEmployeeId());
                flow2.setAction("audit");
                flow2.setCreateTime(new Date());
                flow2.setOrderNo(2);
                flow2.setState("process");
                long diff = form.getEndTime().getTime() - form.getStartTime().getTime();
                float hours = diff/(1000*60*60) * 1f;
                if(hours >= BussinessConstants.MANAGER_AUDIT_HOURS){
                    flow2.setIsLast(0);
                    processFlowDao.insert(flow2);
                    Employee manager = employeeDao.selectLeader(dmanager);
                    ProcessFlow flow3 = new ProcessFlow();
                    flow3.setFormId(form.getFormId());
                    flow3.setOperatorId(manager.getEmployeeId());
                    flow3.setAction("audit");
                    flow3.setCreateTime(new Date());
                    flow3.setState("ready");
                    flow3.setOrderNo(3);
                    flow3.setIsLast(1);
                    processFlowDao.insert(flow3);
                }else {
                    flow2.setIsLast(1);
                    processFlowDao.insert(flow2);
                }
            }else if(employee.getLevel() == 7){ //部门经理
                //3.2 7级员工,生成总经理审批任务
                Employee manager = employeeDao.selectLeader(employee);
                ProcessFlow flow = new ProcessFlow();
                flow.setFormId(form.getFormId());
                flow.setOperatorId(manager.getEmployeeId());
                flow.setAction("audit");
                flow.setCreateTime(new Date());
                flow.setState("process");
                flow.setOrderNo(2);
                flow.setIsLast(1);
                processFlowDao.insert(flow);
            }else if(employee.getLevel() == 8){
                //3.3 8级员工,生成总经理审批任务,系统自动通过
                ProcessFlow flow = new ProcessFlow();
                flow.setFormId(form.getFormId());
                flow.setOperatorId(employee.getEmployeeId());
                flow.setAction("audit");
                flow.setResult("approved");
                flow.setReason("自动通过");
                flow.setCreateTime(new Date());
                flow.setAuditTime(new Date());
                flow.setState("complete");
                flow.setOrderNo(2);
                flow.setIsLast(1);
                processFlowDao.insert(flow);
            }
            return form;

        });
        return savedForm;
    }
}

然后在com.ql.oa.controller包下创建LeaveFormServlet并实现请假申请控制器:

@WebServlet(name = "LeaveFormServlet",urlPatterns = "/leave/*")
public class LeaveFormServlet extends HttpServlet {
    private LeaveFormService leaveFormService = new LeaveFormService();
    private Logger logger = LoggerFactory.getLogger(LeaveFormServlet.class);
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        // http://localhost/leave/create
        String uri = request.getRequestURI();
        String methodName = uri.substring(uri.lastIndexOf("/")+1);
        if(methodName.equals("create")){
            this.create(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    /**
     * 创建请假单
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void create(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收各项请假单数据
        HttpSession session = request.getSession();
        User user = (User)session.getAttribute("login_user");
        String formType = request.getParameter("formType");
        String strStartTime = request.getParameter("startTime");
        String strEndTime = request.getParameter("endTime");
        String reason = request.getParameter("reason");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");

        Map result = new HashMap();
        try {
            LeaveForm form = new LeaveForm();
            form.setEmployeeId(user.getEmployeeId());
            form.setStartTime(sdf.parse(strStartTime));
            form.setEndTime(sdf.parse(strEndTime));
            form.setFormType(Integer.parseInt(formType));
            form.setReason(reason);
            form.setCreateTime(new Date());
            //2. 调用业务逻辑方法
            leaveFormService.createLeaveForm(form);
            result.put("code", "0");
            result.put("message", "success");
        } catch (Exception e) {
            logger.error("请假申请异常" ,e);
            result.put("code", e.getClass().getSimpleName());
            result.put("message", e.getMessage());
        }
        //3. 组织响应数据
        String json = JSON.toJSONString(result);
        response.getWriter().println(json);
    }
}

完整实现请假申请功能

在com.ql.oa.controller下新增ForwardServlet.java,实现页面跳转到对应的ftl文件

package com.ql.oa.controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 页面跳转Servlet
 */
@WebServlet(name = "ForwardServlet" , urlPatterns = "/forward/*")
public class ForwardServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        /*
        * /forward/form
        * /forward/a/b/c/form
        */
        String subUri = uri.substring(1);
        String page = subUri.substring(subUri.indexOf("/"));
        request.getRequestDispatcher(page + ".ftl").forward(request,response);
    }
}

然后在webapp\resources路径下引入sweetalert2.all.min.js弹窗JS插件。
Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第6张图片
然后在src/main/webapp/WEB-INF/ftl下创建form.ftl请假表单申请页面

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请假申请title>
    <link rel="stylesheet" href="/resources/layui/css/layui.css">
    <style>
        /*表单容器*/
        .ns-container {
            position: absolute;
            width: 500px;
            height: 450px;
            top: 150px;
            left: 50%;
            margin-left: -250px;
            padding: 20px;
            box-sizing: border-box;
            border: 1px solid #cccccc;
        }
    style>
head>
<body>
<div class="layui-row">
    <blockquote class="layui-elem-quote">
        <h2>请假申请h2>
    blockquote>
    <table id="grdNoticeList" lay-filter="grdNoticeList">table>
div>
<div class="ns-container">
    <h1 style="text-align: center;margin-bottom: 20px">请假申请单h1>
    <form class="layui-form">
        
        <div class="layui-form-item">
            <label class="layui-form-label">部门label>
            <div class="layui-input-block">
                <div class="layui-col-md12" style="padding-top: 10px;">
                    ${current_department.departmentName}
                div>

            div>
        div>
        <div class="layui-form-item">
            <label class="layui-form-label">申请人label>
            <div class="layui-input-block">
                <div class="layui-col-md12" style="padding-top: 10px;">
                    ${current_employee.name}[${current_employee.title}]
                div>

            div>
        div>
        
        <div class="layui-form-item">
            <label class="layui-form-label">请假类别label>
            <div class="layui-input-block layui-col-space5">
                    <select name="formType" lay-verify="required" lay-filter="cityCode">
                        <option value="1">事假option>
                        <option value="2">病假option>
                        <option value="3">工伤假option>
                        <option value="4">婚嫁option>
                        <option value="5">产假option>
                        <option value="6">丧假option>
                    select>
            div>
        div>
        
        
        <div class="layui-form-item">
            <label class="layui-form-label">请假时长label>
            <div class="layui-input-block layui-col-space5">
                    <input name="leaveRange" type="text" class="layui-input" id="daterange" placeholder=" - " autocomplete="off">
                    <input id="startTime" name="startTime" type="hidden">
                    <input id="endTime" name="endTime" type="hidden">
            div>
        div>

        
        <div class="layui-form-item">
            <label class="layui-form-label">请假事由label>
            <div class="layui-input-block layui-col-space5">
                    <input name="reason" type="text"  lay-verify="required|mobile" placeholder="" autocomplete="off" class="layui-input">
            div>
        div>

        
        <div class="layui-form-item " style="text-align: center">
            <button class="layui-btn" type="button" lay-submit lay-filter="sub">立即申请button>
        div>
    form>
div>

<script src="/resources/layui/layui.js">script>

<script src="/resources/sweetalert2.all.min.js">script>

<script>

        var layDate = layui.laydate; //Layui日期选择框JS对象
        var layForm = layui.form; //layui表单对象
        var $ = layui.$; //jQuery对象
        //日期时间范围
        layDate.render({
            elem: '#daterange'
            ,type: 'datetime'
            ,range: true
            ,format: 'yyyy年M月d日H时'
            ,done: function(value, start, end){
                //选择日期后触发事件,设置startTime与endTime隐藏域
                var startTime = start.year + "-" + start.month + "-" + start.date + "-" + start.hours;
                var endTime = end.year + "-" + end.month + "-" + end.date + "-" + end.hours;
                console.info("请假开始时间",startTime);
                $("#startTime").val(startTime);
                console.info("请假结束时间",endTime);
                $("#endTime").val(endTime);
            }
        });

        //表单提交事件
        layForm.on('submit(sub)', function(data){
            console.info("向服务器提交的表单数据",data.field);
            $.post("/leave/create",data.field,function (json) {
                console.info("服务器返回数据",json);
                if(json.code == "0"){
                    /*SweetAlert2确定对话框*/
                    swal({
                        type: 'success',
                        html: "

请假单已提交,等待上级审批

"
, confirmButtonText: "确定" }).then(function (result) { window.location.href="/forward/notice"; }); }else{ swal({ type: 'warning', html: "

" + json.message + "

"
, confirmButtonText: "确定" }); } },"json"); return false; });
script> body> html>

然后打开navicat,打开sys_node表把菜单url填充,并打开index.ftl文件把菜单跳转路径改成动态的。
Java项目(一)--MyBatis实现OA系统项目实战(7)--开发多级审批流程_第7张图片


<dd class="function" data-parent-id="${node.parentId}">
	<a href="${node.url}" target="ifmMain">${node.nodeName}a>
dd>

运行项目,登录后点击请假申请测试。

你可能感兴趣的:(Java项目,java,开发语言,intellij-idea)