请假流程数据库设计

设计约束

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

执行流程表:

执行过程: 员工申请部门同意(请假时间超过三天)经理审批

消息通知表:

通知过程:

开发请假申请UI界面

webapp下新建页面leave_form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请假申请单</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
    <!-- 引入组件库 -->
    <script src="/assets/vue/vue.global.js"></script>
    <script src="/assets/element-plus/index.full.js"></script>
    <script src="/assets/element-plus/locale/zh-cn.js"></script>
    <script src="/assets/axios/axios.js"></script>
 
    <style>
        .el-form {
            border: 1px solid #DCDFE6;
            width: 600px;
            margin: 180px auto;
            padding: 35px 35px 15px 35px;
            border-radius: 5px;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            box-shadow: 0 0 25px #909399;
        }
    </style>
 
</head>
<body>
<div id="app">
    <el-form ref="leaveForm" :model="form" :rules="rules" label-width="80px">
        <el-descriptions title="请假申请单" :column="1" border>
            <el-descriptions-item label="部门">研发部</el-descriptions-item>
            <el-descriptions-item label="申请人">王美美[高级研发工程师]
            </el-descriptions-item>
            <el-descriptions-item label="请假类型">
 
                <el-select v-model="form.formType" style="width: 100%">
                    <el-option label="事假" value="1"></el-option>
                    <el-option label="病假" value="2"></el-option>
                    <el-option label="工伤假" value="3"></el-option>
                    <el-option label="婚嫁" value="4"></el-option>
                    <el-option label="产假" value="5"></el-option>
                    <el-option label="丧假" value="6"></el-option>
                </el-select>
 
            </el-descriptions-item>
            <el-descriptions-item label="请假时间">
                <el-form-item prop="timeRange" label-width="0px">
                    <div class="block">
                        <el-date-picker
                                v-model="form.timeRange"
                                type="datetimerange"
                                range-separator="至"
                                start-placeholder="开始日期"
                                end-placeholder="结束日期">
                        </el-date-picker>
                    </div>
                </el-form-item>
            </el-descriptions-item>
            <el-descriptions-item label="请假原因">
                <el-form-item prop="reason" label-width="0px">
                    <el-input type="text" placeholder="请输入请假原因" v-model="form.reason"/>
                </el-form-item>
            </el-descriptions-item>
 
        </el-descriptions>
        <div style="text-align: center;padding-top: 30px">
            <el-button type="primary" >立即申请</el-button>
        </div>
    </el-form>
 
</div>
 
<script>
 
    var Main = {
        data() {
            return {
                form: {
                    formType: "1",
                    timeRange: "",
                    startTime: "",
                    endTime: "",
                    reason: "",
                    eid: "",
                },
                // 表单验证,需要在 el-form-item 元素中增加 prop 属性
                rules: {
                    timeRange: [
                        {required: true, message: '请选择请假时间', trigger: 'blur'}
                    ],
                    reason: [
                        {required: true, message: '请填写请假原因', trigger: 'blur'}
                    ]
                }
            }
        }
    };
    ElementPlus.locale(ElementPlus.lang.zhCn);
    const app = Vue.createApp(Main);
    app.use(ElementPlus, ElementPlus.lang.zhCn);
    app.mount("#app")
</script>
</body>
</html>

引入zh-cn.js文件是因为日期格式改变为中文格式 rules="rules"表示表单校验规则,一个是日期申请的,一个是请假原因,默认规则不能为空 formType: "1"表示默认选择值为1的选项,在e-select标签中对应是事假 element-plus默认提供日期选择器即el-date-picker app.use(ElementPlus, ElementPlus.lang.zhCn);使用zhCn语言包

开发请假申请Mapper层

创建实体类LeaveForm.java

package com.imooc.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;
 
    @Override
    public String toString() {
        return "LeaveForm{" +
                "formId=" + formId +
                ", employeeId=" + employeeId +
                ", formType=" + formType +
                ", startTime=" + startTime +
                ", endTime=" + endTime +
                ", reason='" + reason + '\'' +
                ", createTime=" + createTime +
                ", state='" + 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;
    }
}

继续向前推进,mapper下新建接口LeaveFormMapper.java

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.LeaveForm;
import org.apache.ibatis.annotations.Param;
 
import java.util.List;
import java.util.Map;
 
public interface LeaveFormMapper {
    public void insert(LeaveForm form);
 
}

资源目录下mappers新建leave_form.xml文件

<?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.imooc.oa.mapper.LeaveFormMapper">
    <insert id="insert" parameterType="com.imooc.oa.entity.LeaveForm"
    useGeneratedKeys="true" keyProperty="formId" keyColumn="form_id">
        INSERT INTO `imooc_oa`.`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>

可以在navicat自动生成sql语句,form_id字段因为设置为主键自增,所以不需要在insert语句写入,进行标签里绑定 useGeneratedKeys="true" keyProperty="formId" keyColumn="form_id"

然后就是进行mybatis-config.xml注册

<mapper resource="mappers/leave_form.xml"/>

我们快速生成测试

@Test
    public void insert() {
        MybatisUtils.executeUpdate(sqlSession -> {
            LeaveFormMapper mapper = sqlSession.getMapper(LeaveFormMapper.class);
            LeaveForm form = new LeaveForm();
            form.setEmployeeId(4l); //员工编号
            form.setFormType(1); //事假
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date startTime = null;//起始时间
            Date endTime = null;//结束时间
            try {
                startTime = sdf.parse("2020-03-25 08:00:00");
                endTime = sdf.parse("2020-04-01 18:00:00");
            } catch (ParseException e) {
                e.printStackTrace();
            }
            form.setStartTime(startTime);
            form.setEndTime(endTime);
            form.setReason("回家探亲");//请假事由
            form.setCreateTime(new Date());//创建时间
            form.setState("processing");//当前状态
            mapper.insert(form);
            return null;
        });
    }

不用返回对象,直接return NULL

查看数据库,生成成功

请假单完成,接下来是请假流程 新建实体类ProcessFlow.java

package com.imooc.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;
 
    @Override
    public String toString() {
        return "ProcessFlow{" +
                "processId=" + processId +
                ", formId=" + formId +
                ", operatorId=" + operatorId +
                ", action='" + action + '\'' +
                ", result='" + result + '\'' +
                ", reason='" + reason + '\'' +
                ", createTime=" + createTime +
                ", auditTime=" + auditTime +
                ", orderNo=" + orderNo +
                ", state='" + state + '\'' +
                ", isLast=" + 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;
    }
}

记得重写toString方法

mapper下新增接口ProcessFlowMapper.java

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.ProcessFlow;
 
import java.util.List;
 
public interface ProcessFlowMapper {
    public void insert(ProcessFlow processFlow);
}

然后配置sql语句,mappers新增process_flow.xml

<?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.imooc.oa.mapper.ProcessFlowMapper">
    <insert id="insert" parameterType="com.imooc.oa.entity.ProcessFlow"
            useGeneratedKeys="true" keyProperty="processId" keyColumn="process_id">
        INSERT INTO `imooc_oa`.`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>

书写一条垃圾数据,然后右键生成insert语句,即可直接粘贴到xml,同理上面的主键自增处理方法 进行注册

右键函数名生成单元测试用例

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.ProcessFlow;
import com.imooc.oa.utils.MybatisUtils;
import org.junit.Test;
 
import java.util.Date;
 
import static org.junit.Assert.*;
 
public class ProcessFlowMapperTest {
 
    @Test
    public void insert() {
        MybatisUtils.executeUpdate(sqlSession -> {
            ProcessFlowMapper mapper = sqlSession.getMapper(ProcessFlowMapper.class);
            ProcessFlow processFlow = new ProcessFlow();
            processFlow.setFormId(3l);
            processFlow.setOperatorId(2l);
            processFlow.setAction("audit");
            processFlow.setResult("approved");
            processFlow.setReason("同意");
            processFlow.setCreateTime(new Date());
            processFlow.setAuditTime(new Date());
            processFlow.setOrderNo(1);
            processFlow.setState("ready");
            processFlow.setIsLast(1);
            mapper.insert(processFlow);
            return null;
        });
    }
}

右键运行,测试成功

接下来同样方法快速处理系统通知表 新建实体Notice.java

package com.imooc.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();
    }
 
    @Override
    public String toString() {
        return "Notice{" +
                "noticeId=" + noticeId +
                ", receiverId=" + receiverId +
                ", content='" + content + '\'' +
                ", createTime=" + createTime +
                '}';
    }
 
    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;
    }
 
}

为了实例化方便,额外添加一个带参构造方法 然后回到mapper上 继续新建NoticeMapper.java

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.Notice;
 
import java.util.List;
 
public interface NoticeMapper {
    public void insert(Notice notice);
}

配置sql.新建notice.xml

<?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.imooc.oa.mapper.NoticeMapper">
    <insert id="insert" parameterType="com.imooc.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>

进行注册

<mapper resource="mappers/notice.xml"/>

进行测试用例

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.Notice;
import com.imooc.oa.utils.MybatisUtils;
import org.junit.Test;
 
import static org.junit.Assert.*;
 
public class NoticeMapperTest {
 
    @Test
    public void insert() {
        MybatisUtils.executeUpdate(sqlSession -> {
            NoticeMapper mapper = sqlSession.getMapper(NoticeMapper.class);
            mapper.insert(new Notice(2l, "测试消息"));
            return null;
        });
    }
}

成功执行

我们完成了上述三张表的插入操作,接下来执行业务处理

开发请假申请的service层

如何将上面的逻辑联系在一起形成完整的业务逻辑? service文件夹下创建LeaveFormService.java文件

private EmployeeService employeeService = new EmployeeService();
    /**
     * 创建请假单
     * @param form 前端输入的请假单数据
     * @return 持久化后的请假单对象
     */
    public LeaveForm createLeaveForm(LeaveForm form){
        LeaveForm f = (LeaveForm) MybatisUtils.executeUpdate(sqlSession -> {
            //1.持久化form表单数据,8级以下员工表单状态为processing,8级(总经理)状态为approved
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.selectById(form.getEmployeeId());
            if(employee.getLevel() == 8){
                form.setState("approved");
            }else{
                form.setState("processing");
            }
            LeaveFormMapper leaveFormMapper = sqlSession.getMapper(LeaveFormMapper.class);
            leaveFormMapper.insert(form);
            NoticeMapper noticeMapper = sqlSession.getMapper(NoticeMapper.class);
            //2.增加第一条流程数据,说明表单已提交,状态为complete
            ProcessFlowMapper processFlowMapper = sqlSession.getMapper(ProcessFlowMapper.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);
            processFlowMapper.insert(flow1);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH时");
            //3.分情况创建其余流程数据
            //3.1 7级以下员工,生成部门经理审批任务,请假时间大于等于72小时,还需生成总经理审批任务
            if(employee.getLevel() < 7){
                Employee dmanager = employeeService.selectLeader(employee.getEmployeeId());
                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 >= 72){
                    flow2.setIsLast(0);
                    processFlowMapper.insert(flow2);
                    Employee manager = employeeService.selectLeader(dmanager.getEmployeeId());
                    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);
                    processFlowMapper.insert(flow3);
                }else {
                    flow2.setIsLast(1);
                    processFlowMapper.insert(flow2);
                }
                String notice1 = String.format("您的请假申请[%s-%s]已提交,请等待上级审批.", sdf.format(form.getStartTime()), sdf.format(form.getEndTime()));
                noticeMapper.insert(new Notice(employee.getEmployeeId(),notice1));
                String notice2 = String.format("%s-%s提起请假申请[%s-%s],请尽快审批", employee.getTitle(), employee.getName(), sdf.format(form.getStartTime()), sdf.format(form.getEndTime()));
                noticeMapper.insert(new Notice(dmanager.getEmployeeId(),notice2));
            }else if(employee.getLevel() == 7){
                //3.2 7级员工,仅生成总经理审批任务
                Employee manager = employeeService.selectLeader(employee.getEmployeeId());
                ProcessFlow flow2 = new ProcessFlow();
                flow2.setFormId(form.getFormId());
                flow2.setOperatorId(manager.getEmployeeId());
                flow2.setAction("audit");
                flow2.setCreateTime(new Date());
                flow2.setState("process");
                flow2.setOrderNo(2);
                flow2.setIsLast(1);
                processFlowMapper.insert(flow2);
                //请假单已提交消息
                String notice1 = String.format("您的请假申请[%s-%s]已提交,请等待上级审批."
                        , sdf.format(form.getStartTime()), sdf.format(form.getEndTime()));
                noticeMapper.insert(new Notice(employee.getEmployeeId(),notice1));
 
                //通知总经理审批消息
                String notice2 = String.format("%s-%s提起请假申请[%s-%s],请尽快审批",
                        employee.getTitle() , employee.getName() ,sdf.format(form.getStartTime()),sdf.format(form.getEndTime()));
                noticeMapper.insert(new Notice(manager.getEmployeeId(),notice2));
            }else if(employee.getLevel() == 8){
                //3.3 8级员工,生成总经理审批任务,系统自动通过
                ProcessFlow flow2 = new ProcessFlow();
                flow2.setFormId(form.getFormId());
                flow2.setOperatorId(employee.getEmployeeId());
                flow2.setAction("audit");
                flow2.setResult("approved");
                flow2.setReason("自动通过");
                flow2.setCreateTime(new Date());
                flow2.setAuditTime(new Date());
                flow2.setState("complete");
                flow2.setOrderNo(2);
                flow2.setIsLast(1);
                processFlowMapper.insert(flow2);
                String noticeContent = String.format("您的请假申请[%s-%s]系统已自动批准通过." ,
                        sdf.format(form.getStartTime()) , sdf.format(form.getEndTime()));
                noticeMapper.insert(new Notice(employee.getEmployeeId(),noticeContent));
            }
 
 
            return form;
        });
        return f;
    }

setIsLast表示是否是最后一位审批?大于72小时,总经理是最后一个审批,小于72小时,部门经理是最后一位审批

第三部分分情况时需要获取到上级部门操作,与员工相关,所以在EployeeService.java下,新增代码

public Employee selectLeader(Long employeeId){
        Employee l = (Employee)MybatisUtils.executeQuery(sqlSession -> {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = mapper.selectById(employeeId);
            Map params = new HashMap<>();
            Employee leader = null;
            if(employee.getLevel() < 7 ){
                //查询部门经理
                params.put("level", 7);
                params.put("departmentId", employee.getDepartmentId());
                List<Employee> employees = mapper.selectByParams(params);
                leader = employees.get(0);
            }else if(employee.getLevel() == 7 ){
                //查询总经理
                params.put("level", 8);
                List<Employee> employees = mapper.selectByParams(params);
                leader = employees.get(0);
            }else if(employee.getLevel() == 8){
                //返回自己
                leader = employee;
            }
            return leader;
        });
        return l;
    }

小于7级员工查询部门经理,等于7级员工查询总经理,等于8级员工就是总经理自己,自动通过 后台要动态执行sql语句 回到EployeeMapper.java接口上,额外增加方法

public List<Employee> selectByParams(Map params);

根据params不同动态返回Eployee集合结果 然后打开employee.xml文件进行处理

<select id="selectByParams" parameterType="java.util.Map" resultType="com.imooc.oa.entity.Employee">
        select * from adm_employee
        where
            1=1
        <if test="level != null">
            and level = #{level}
        </if>
        <if test="departmentId != null">
            and department_id = #{departmentId}
        </if>
        <if test="title != null">
            and title = #{title}
        </if>
 
    </select>

对selectByParams方法进行测试用例

@Test
    public void selectByParams1() {
        Map params = new HashMap<>();
        params.put("level", 7);
        params.put("departmentId", 2);
        MybatisUtils.executeQuery(sqlSession -> {
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            List<Employee> employees = employeeMapper.selectByParams(params);
            System.out.println(employees);
            return employees;
        });
    }
 
    @Test
    public void selectByParams2() {
        Map params = new HashMap<>();
        params.put("level", 8);
        MybatisUtils.executeQuery(sqlSession -> {
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            List<Employee> employees = employeeMapper.selectByParams(params);
            System.out.println(employees);
            return employees;
        });
    }

进行测试

package com.imooc.oa.service;
 
import com.imooc.oa.entity.LeaveForm;
import org.junit.Test;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import static org.junit.Assert.*;
 
public class LeaveFormServiceTest {
    LeaveFormService leaveFormService = new LeaveFormService();
    /**
     * 市场部员工请假单(72小时以上)测试用例
     * @throws ParseException
     */
    @Test
    public void createLeaveForm1() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
        LeaveForm form = new LeaveForm();
        form.setEmployeeId(8l);
        form.setStartTime(sdf.parse("2020032608"));
        form.setEndTime(sdf.parse("2020040118"));
        form.setFormType(1);
        form.setReason("市场部员工请假单(72小时以上)");
        form.setCreateTime(new Date());
        LeaveForm savedForm = leaveFormService.createLeaveForm(form);
        System.out.println(savedForm.getFormId());
    }
 
    /**
     * 市场部员工请假单(72小时内)测试用例
     * @throws ParseException
     */
    @Test
    public void createLeaveForm2() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
        LeaveForm form = new LeaveForm();
        form.setEmployeeId(8l);
        form.setStartTime(sdf.parse("2020032608"));
        form.setEndTime(sdf.parse("2020032718"));
        form.setFormType(1);
        form.setReason("市场部员工请假单(72小时内)");
        form.setCreateTime(new Date());
        LeaveForm savedForm = leaveFormService.createLeaveForm(form);
        System.out.println(savedForm.getFormId());
    }
 
    /**
     * 研发部部门经理请假单测试用例
     * @throws ParseException
     */
    @Test
    public void createLeaveForm3() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
        LeaveForm form = new LeaveForm();
        form.setEmployeeId(2l);
        form.setStartTime(sdf.parse("2020032608"));
        form.setEndTime(sdf.parse("2020040118"));
        form.setFormType(1);
        form.setReason("研发部部门经理请假单");
        form.setCreateTime(new Date());
        LeaveForm savedForm = leaveFormService.createLeaveForm(form);
        System.out.println(savedForm.getFormId());
    }
 
    /**
     * 总经理请假单测试用例
     * @throws ParseException
     */
    @Test
    public void createLeaveForm4() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
        LeaveForm form = new LeaveForm();
        form.setEmployeeId(1l);
        form.setStartTime(sdf.parse("2020032608"));
        form.setEndTime(sdf.parse("2020040118"));
        form.setFormType(1);
        form.setReason("总经理请假单");
        form.setCreateTime(new Date());
        LeaveForm savedForm = leaveFormService.createLeaveForm(form);
        System.out.println(savedForm.getFormId());
    }
 
 
}

情况一测试成功,请假单完成申请

审核流程也完成

部门经理处理中,最后一步由总经理完成,setlast为1,处于ready状态,等部门经理通过,ready状态变processing

开发请假Controller层

向上推演,开发contronller contronller下创建LeaveFormServlet.java

package com.imooc.oa.controller;
 
import com.imooc.oa.entity.LeaveForm;
import com.imooc.oa.service.LeaveFormService;
import com.imooc.oa.utils.ResponseUtils;
 
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;
import java.util.Date;
import java.util.List;
import java.util.Map;
//*为通配符,匹配后面的create/list/audit
@WebServlet("/api/leave/*")
public class LeaveFormServlet extends HttpServlet {
    private LeaveFormService leaveFormService = new LeaveFormService();
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        //http://localhost/api/leave/create 截取URI
        String uri = request.getRequestURI();
        String methodName = uri.substring(uri.lastIndexOf("/") + 1);
        if (methodName.equals("create")) {
            this.create(request, response);
        } else if (methodName.equals("list")) {
            this.list(request, response);
        } else if (methodName.equals("audit")) {
            this.audit(request, response);
        }
    }
//    URI带有create时执行
    private void create(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String strEmployeeId = request.getParameter("eid");
        String formType = request.getParameter("formType");
        //从1970年到现在的毫秒数
        String startTime = request.getParameter("startTime");
        String endTime = request.getParameter("endTime");
        String reason = request.getParameter("reason");
        LeaveForm form = new LeaveForm();
        form.setEmployeeId(Long.parseLong(strEmployeeId));
        form.setStartTime(new Date(Long.parseLong(startTime)));
        form.setEndTime(new Date(Long.parseLong(endTime)));
        form.setFormType(Integer.parseInt(formType));
        form.setReason(reason);
        form.setCreateTime(new Date());
        ResponseUtils resp = null;
        try {
            leaveFormService.createLeaveForm(form);
            resp = new ResponseUtils();
        } catch (Exception e) {
            e.printStackTrace();
            resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
        }
 
        response.getWriter().println(resp.toJsonString());
    }
    //    URI带有list时执行
    private void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String employeeId = request.getParameter("eid");
        ResponseUtils resp = null;
        try {
            List<Map> formList = leaveFormService.getLeaveFormList("process", Long.parseLong(employeeId));
            resp = new ResponseUtils().put("list", formList);
        } catch (Exception e) {
            e.printStackTrace();
            resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
        }
        response.getWriter().println(resp.toJsonString());
    }
    //    URI带有audit时执行
    private void audit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String formId = request.getParameter("formId");
        String result = request.getParameter("result");
        String reason = request.getParameter("reason");
        String eid = request.getParameter("eid");
        ResponseUtils resp = null;
        try {
            leaveFormService.audit(Long.parseLong(formId), Long.parseLong(eid), result, reason);
            resp = new ResponseUtils();
        }catch (Exception e){
            e.printStackTrace();
            resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
        }
        response.getWriter().println(resp.toJsonString());
    }
}

输入localhost/api/leave/create?eid=2&formType=1&startTime=1623940149&endTime=1624026549&reason=测试

查看数据库

开发请假申请View层

前端页面如何与Servlet进行交互 回到我们的leave_form.html页面

我们的逻辑是通过获取员工的id,从而得到部门和名字岗位等信息,所以需要和登陆界面login.html进行绑定 输入总经理账号m8,密码test 可以看出我们需要将其进行动态改变

修改leave_form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请假申请单</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
    <!-- 引入组件库 -->
    <script src="/assets/vue/vue.global.js"></script>
    <script src="/assets/element-plus/index.full.js"></script>
    <script src="/assets/element-plus/locale/zh-cn.js"></script>
    <script src="/assets/axios/axios.js"></script>
 
    <style>
        .el-form {
            border: 1px solid #DCDFE6;
            width: 600px;
            margin: 180px auto;
            padding: 35px 35px 15px 35px;
            border-radius: 5px;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            box-shadow: 0 0 25px #909399;
        }
    </style>
 
</head>
<body>
<div id="app">
    <el-form ref="leaveForm" :model="form" :rules="rules" label-width="80px">
        <el-descriptions title="请假申请单" :column="1" border>
            <el-descriptions-item label="部门">{{department.departmentName}}</el-descriptions-item>
            <el-descriptions-item label="申请人">{{employee.name}}[{{employee.title}}]
            </el-descriptions-item>
            <el-descriptions-item label="请假类型">
 
                <el-select v-model="form.formType" style="width: 100%">
                    <el-option label="事假" value="1"></el-option>
                    <el-option label="病假" value="2"></el-option>
                    <el-option label="工伤假" value="3"></el-option>
                    <el-option label="婚嫁" value="4"></el-option>
                    <el-option label="产假" value="5"></el-option>
                    <el-option label="丧假" value="6"></el-option>
                </el-select>
 
            </el-descriptions-item>
            <el-descriptions-item label="请假时间">
                <el-form-item prop="timeRange" label-width="0px">
                    <div class="block">
                        <el-date-picker
                                v-model="form.timeRange"
                                type="datetimerange"
                                range-separator="至"
                                start-placeholder="开始日期"
                                end-placeholder="结束日期">
                        </el-date-picker>
                    </div>
                </el-form-item>
            </el-descriptions-item>
            <el-descriptions-item label="请假原因">
                <el-form-item prop="reason" label-width="0px">
                    <el-input type="text" placeholder="请输入请假原因" v-model="form.reason"/>
                </el-form-item>
            </el-descriptions-item>
 
        </el-descriptions>
        <div style="text-align: center;padding-top: 30px">
            <el-button type="primary" >立即申请</el-button>
        </div>
    </el-form>
 
</div>
 
<script>
 
    var Main = {
        data() {
            return {
                employee:{},
                department:{},
                form: {
                    formType: "1",
                    timeRange: "",
                    startTime: "",
                    endTime: "",
                    reason: "",
                    eid: "",
                },
                // 表单验证,需要在 el-form-item 元素中增加 prop 属性
                rules: {
                    timeRange: [
                        {required: true, message: '请选择请假时间', trigger: 'blur'}
                    ],
                    reason: [
                        {required: true, message: '请填写请假原因', trigger: 'blur'}
                    ]
                }
            }
        }
        ,mounted(){
            const objApp = this;
            axios.get("/api/user_info?uid=" + sessionStorage.uid + "&eid=" + sessionStorage.eid)
                .then(function(response){
                    console.info(response);
                    objApp.employee = response.data.data.employee;
                    objApp.department = response.data.data.department;
                })
        }
    };
    ElementPlus.locale(ElementPlus.lang.zhCn);
    const app = Vue.createApp(Main);
    app.use(ElementPlus, ElementPlus.lang.zhCn);
    app.mount("#app");
</script>
</body>
</html>

运行后部门应该也要改变,现在快速开发部门功能 创建实体类department.java

package com.imooc.oa.entity;
 
public class Department {
    private Long departmentId;
    private String departmentName;
 
    public Long getDepartmentId() {
        return departmentId;
    }
 
    public void setDepartmentId(Long departmentId) {
        this.departmentId = departmentId;
    }
 
    public String getDepartmentName() {
        return departmentName;
    }
 
    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }
}

创建DepartmentMapper.java文件

package com.imooc.oa.mapper;
 
import com.imooc.oa.entity.Department;
 
public interface DepartmentMapper {
    public Department selectById(Long departmentId);
}

xml里进行sql书写

<?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">
<!--namespace与包名类名一致-->
<mapper namespace="com.imooc.oa.mapper.DepartmentMapper">
    <select id="selectById" parameterType="Long" resultType="com.imooc.oa.entity.Department">
        select * from adm_department where department_id = #{value}
    </select>
</mapper>

注册

<mapper resource="mappers/department.xml"/>

从service对mapper调用,不要在servlet进行调用,不要跨层访问,违规 创建DepartmentService.java文件

package com.imooc.oa.service;
 
import com.imooc.oa.entity.Department;
import com.imooc.oa.mapper.DepartmentMapper;
import com.imooc.oa.utils.MybatisUtils;
 
public class DepartmentService {
    public Department selectById(Long departmentId){
        return (Department)MybatisUtils.executeQuery(sqlSession -> sqlSession.getMapper(DepartmentMapper.class).selectById(departmentId));
    }
}

继续向上推演,我们在UseInfoServlet.java中书写过员工编号,我们应该在里面继续书写部门编号

package com.imooc.oa.controller;
 
import com.imooc.oa.entity.Department;
import com.imooc.oa.entity.Employee;
import com.imooc.oa.entity.Node;
import com.imooc.oa.service.DepartmentService;
import com.imooc.oa.service.EmployeeService;
import com.imooc.oa.service.RbacService;
import com.imooc.oa.utils.ResponseUtils;
 
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;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
@WebServlet("/api/user_info")
public class UserInfoServlet extends HttpServlet {
    private RbacService rbacService = new RbacService();
    private EmployeeService employeeService = new EmployeeService();
    private DepartmentService departmentService = new DepartmentService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uid = request.getParameter("uid");
        String eid = request.getParameter("eid");
        List<Node> nodes = rbacService.selectNodeByUserId(Long.parseLong(uid));
        List<Map> treeList = new ArrayList<>();
        Map module = null;
        for(Node node : nodes){
            if(node.getNodeType() == 1){
                module = new LinkedHashMap();
                module.put("node", node);
                module.put("children", new ArrayList());
                treeList.add(module);
            }else if(node.getNodeType() == 2){
                List children = (List)module.get("children");
                children.add(node);
            }
        }
        Employee employee = employeeService.selectById(Long.parseLong(eid));
        Department department = departmentService.selectById(employee.getDepartmentId());
        String json = new ResponseUtils()
                .put("nodeList", treeList).put("employee",employee).put("department",department).toJsonString();
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(json);
    }
 
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

添加.put("department",department) 修改leave_form.html 绑定mounted

objApp.employee = response.data.data.employee;

传入数据

department:{}

进行绑定

<el-descriptions-item label="部门">{{department.departmentName}}</el-descriptions-item>

重启tomcat,运行并登陆

继续开发leave_form.html文件,完成表单数据和申请按钮处理 捕捉日期,在el-date-picker标签中

@change="changeTimeRange">

在methods增加方法

,methods:{
            changeTimeRange : function(){
                console.info(this.form.timeRange);
                this.form.startTime = this.form.timeRange[0].getTime();
                this.form.endTime = this.form.timeRange[1].getTime();
            }
            ,onSubmit(formName){
                const objApp = this;
                const formData = this.form;
                const $message = this.$message;
                this.$refs[formName].validate(function(valid){
                    if(valid){
                        const params = new URLSearchParams();
                        params.append("formType", formData.formType);
                        params.append("startTime", formData.startTime);
                        params.append("endTime", formData.endTime);
                        params.append("reason", formData.reason);
                        params.append("eid", sessionStorage.eid);
                        axios.post("/api/leave/create",params)
                            .then(function(response){
                                console.info(response);
                                const json = response.data;
                                if(json.code == "0"){
                                    objApp.$alert("请假单已提交,等待上级审批",{
                                        callback:function(){
                                            window.location.href = "/notice.html";
                                        }
                                    })
                                }else{
                                    $message.error({message:json.message,offset:100})
                                }
                            })
                    }
                })
            }
        }

登陆并运行

完整leave_form代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请假申请单</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
    <!-- 引入组件库 -->
    <script src="/assets/vue/vue.global.js"></script>
    <script src="/assets/element-plus/index.full.js"></script>
    <script src="/assets/element-plus/locale/zh-cn.js"></script>
    <script src="/assets/axios/axios.js"></script>
    <script src="/assets/oa/security.js"></script>
 
    <style>
        .el-form {
            border: 1px solid #DCDFE6;
            width: 600px;
            margin: 180px auto;
            padding: 35px 35px 15px 35px;
            border-radius: 5px;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            box-shadow: 0 0 25px #909399;
        }
    </style>
 
</head>
<body>
<div id="app">
    <el-form ref="leaveForm" :model="form" :rules="rules" label-width="80px">
        <el-descriptions title="请假申请单" :column="1" border>
            <el-descriptions-item label="部门">{{department.departmentName}}</el-descriptions-item>
            <el-descriptions-item label="申请人">{{employee.name}}[{{employee.title}}]
            </el-descriptions-item>
            <el-descriptions-item label="请假类型">
 
                <el-select v-model="form.formType" style="width: 100%">
                    <el-option label="事假" value="1"></el-option>
                    <el-option label="病假" value="2"></el-option>
                    <el-option label="工伤假" value="3"></el-option>
                    <el-option label="婚嫁" value="4"></el-option>
                    <el-option label="产假" value="5"></el-option>
                    <el-option label="丧假" value="6"></el-option>
                </el-select>
 
            </el-descriptions-item>
            <el-descriptions-item label="请假时间">
                <el-form-item prop="timeRange" label-width="0px">
                    <div class="block">
                        <el-date-picker
                                v-model="form.timeRange"
                                type="datetimerange"
                                range-separator="至"
                                start-placeholder="开始日期"
                                end-placeholder="结束日期"
                                @change="changeTimeRange">
                        </el-date-picker>
                    </div>
                </el-form-item>
            </el-descriptions-item>
            <el-descriptions-item label="请假原因">
                <el-form-item prop="reason" label-width="0px">
                    <el-input type="text" placeholder="请输入请假原因" v-model="form.reason"/>
                </el-form-item>
            </el-descriptions-item>
 
        </el-descriptions>
        <div style="text-align: center;padding-top: 30px">
            <el-button type="primary" v-on:click="onSubmit('leaveForm')" >立即申请</el-button>
        </div>
    </el-form>
 
</div>
 
<script>
 
    var Main = {
        data() {
            return {
                employee:{},
                department:{},
                form: {
                    formType: "1",
                    timeRange: "",
                    startTime: "",
                    endTime: "",
                    reason: "",
                    eid: ""
                },
                // 表单验证,需要在 el-form-item 元素中增加 prop 属性
                rules: {
                    timeRange: [
                        {required: true, message: '请选择请假时间', trigger: 'blur'}
                    ],
                    reason: [
                        {required: true, message: '请填写请假原因', trigger: 'blur'}
                    ]
                }
            }
        }
        ,methods:{
            changeTimeRange : function(){
                console.info(this.form.timeRange);
                this.form.startTime = this.form.timeRange[0].getTime();
                this.form.endTime = this.form.timeRange[1].getTime();
            }
            ,onSubmit(formName){
                const objApp = this;
                const formData = this.form;
                const $message = this.$message;
                this.$refs[formName].validate(function(valid){
                    if(valid){
                        const params = new URLSearchParams();
                        params.append("formType", formData.formType);
                        params.append("startTime", formData.startTime);
                        params.append("endTime", formData.endTime);
                        params.append("reason", formData.reason);
                        params.append("eid", sessionStorage.eid);
                        axios.post("/api/leave/create",params)
                            .then(function(response){
                                console.info(response);
                                const json = response.data;
                                if(json.code == "0"){
                                    objApp.$alert("请假单已提交,等待上级审批",{
                                        callback:function(){
                                            window.location.href = "/notice.html";
                                        }
                                    })
                                }else{
                                    $message.error({message:json.message,offset:100})
                                }
                            })
                    }
                })
            }
        }
        ,mounted(){
            const objApp = this;
            axios.get("/api/user_info?uid=" + sessionStorage.uid + "&eid=" + sessionStorage.eid)
                .then(function(response){
                    console.info(response);
                    objApp.employee = response.data.data.employee;
                    objApp.department = response.data.data.department;
                })
        }
    };
    ElementPlus.locale(ElementPlus.lang.zhCn);
    const app = Vue.createApp(Main);
    app.use(ElementPlus, ElementPlus.lang.zhCn);
    app.mount("#app");
</script>
</body>
</html>