请假流程数据库设计
设计约束
根据请假流程设计约束 ◆每一个请假单对应一个审批流程 ◆请假单创建后按业务规则生成部门经理、总经理审批任务 ◆审批任务的经办人只能审批自己辖区内的请假申请 ◆所有审批任务”通过”,代表请假已经批准 ◆任意审批任务”驳回”操作,其余审批任务取消,请假申请被驳回 ◆请假流程中任意节点产生的操作都要生成对应的系统通知 请假申请表:
执行流程表:
执行过程:
员工申请→部门同意(请假时间超过三天)→经理审批
消息通知表:
通知过程:
开发请假申请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>