讲解请假审批UI原型
webapp下新建audit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>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/axios/axios.js"></script>
<style >
.info .el-col,.info .el-select ,.info .el-input{
padding-top: 5px;
padding-bottom: 5px;
}
</style>
</head>
<body>
<div id="app">
<h2>请假审批</h2>
<el-table
ref="singleTable"
:data="tableData"
highlight-current-row
@current-change="handleCurrentChange"
style="width: 100%">
<el-table-column
type="index"
width="50">
</el-table-column>
<el-table-column
property="ctime"
label="申请时间"
width="180">
</el-table-column>
<el-table-column
property="ftype"
label="类型"
width="120">
</el-table-column>
<el-table-column
property="department_name"
label="部门"
width="120">
</el-table-column>
<el-table-column
property="name"
label="员工"
width="120">
</el-table-column>
<el-table-column
property="stime"
label="起始时间"
width="180">
</el-table-column>
<el-table-column
property="etime"
label="结束时间"
width="180">
</el-table-column>
<el-table-column
property="reason"
label="请假原因">
</el-table-column>
</el-table>
<el-dialog title="请假审批" v-model="dialogFormVisible" width="500px" center>
<el-descriptions :column="2" border>
<el-descriptions-item label="部门">{{currentRow.department_name}}</el-descriptions-item>
<el-descriptions-item label="姓名">{{currentRow.name}}</el-descriptions-item>
<el-descriptions-item label="起始时间" >{{currentRow.stime}}</el-descriptions-item>
<el-descriptions-item label="结束时间" >{{currentRow.etime}}</el-descriptions-item>
<el-descriptions-item label="请假原因" :span="2">
{{currentRow.reason}}
</el-descriptions-item>
</el-descriptions>
<div class="info" >
<el-form :model="form" ref="auditForm">
<el-select v-model="form.result" placeholder="是否同意" style="width: 100%">
<el-option label="同意" value="approved"></el-option>
<el-option label="驳回" value="refused"></el-option>
</el-select>
<el-input v-model="form.reason" placeholder="请输入审批意见" autocomplete="off"></el-input>
</el-form>
<span class="dialog-footer">
<el-button type="primary" v-on:click="onSubmit('auditForm')" style="width: 100%">确认提交</el-button>
</span>
</div>
</el-dialog>
</div>
<script>
function formatDate(time){
var newDate = new Date(time);
return newDate.getFullYear() + "-" +
(newDate.getMonth() + 1) + "-" + newDate.getDate()
+ " " + newDate.getHours() + "时";
}
var Main = {
data() {
return {
dialogFormVisible: false,
form: {
result:"approved",
reason:""
},
formLabelWidth: '120px',
tableData: [{
ctime:"2021-5-29 18时",
ftype:"事假",
stime:"2021-5-31 0时",
etime:"2021-6-3 0时",
department_name:"研发部",
name:"王美美",
reason:"测试数据"
}],
currentRow: null
}
}
,methods: {
handleCurrentChange(val) {
this.currentRow = val;
console.info(val);
this.dialogFormVisible = true;
}
}
};
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app")
</script>
</body>
</html>
tableData是我们添加的静态数据,el-table是element-plus封装的
this.dialogFormVisible = true;
表示是否弹出对话框
开发待审批表单Model层
将上面的静态数据改为动态
打开leave_form.xml文件,新增方法
<select id="selectByParams" parameterType="java.util.Map" resultType="java.util.LinkedHashMap">
select f.*,e.name , d.*
from
adm_leave_form f,adm_process_flow pf , adm_employee e , adm_department d
where
f.form_id = pf.form_id
and f.employee_id = e.employee_id
and e.department_id = d.department_id
<if test="pf_state != null">
and pf.state = #{pf_state}
</if>
<if test="pf_operator_id != null">
and pf.operator_id = #{pf_operator_id}
</if>
</select>
请假单表和员工表是主外键关联,和流程表也是主外键关联,与部门表也是主外键关联,相当于多表查询,无法使用一个实体类存储下来,所以使用集合LinkedHashMap,采用键值对方式存储 引入流程表是因为我们需要通过pf_state、pf_operator_id才能拿到当前登陆的经理待审批请假单数据 LeaveFormMapper.java新增接口拓展
/*
map.put("pf_operator_id" , xxxx)
map.put("pf_state" , xxxx)
*/
public List<Map> selectByParams(@Param("pf_state") String pfState
, @Param("pf_operator_id") Long pfOperatorId);
使用@Param简化put的操作 进行单元测试LeaveFormMapperTest.java
@Test
public void testSelectByParams(){
MybatisUtils.executeQuery(sqlSession -> {
LeaveFormMapper mapper = sqlSession.getMapper(LeaveFormMapper.class);
List<Map> list = mapper.selectByParams("process", 2l);
System.out.println(list);
return list;
});
}
向上推进,在LeaveFormService.java中添加
/**
* 获取指定任务状态及指定经办人对应的请假单列表
* @param pfState ProcessFlow任务状态
* @param operatorId 经办人编号
* @return 请假单及相关数据列表
*/
public List<Map> getLeaveFormList(String pfState, Long operatorId){
return (List<Map>) MybatisUtils.executeQuery(sqlSession -> {
LeaveFormMapper mapper = sqlSession.getMapper(LeaveFormMapper.class);
List<Map> maps = mapper.selectByParams(pfState, operatorId);
return maps;
});
}
实现查询待审批请假单
上一节开发了请假单获取工作getLeaveFormList(),向上开发controller层 在LeaveFormServlet.java中添加与查询有关方法,像create方法一样添加list查询方法
// 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());
}
登陆王美美账号t6,密码test,申请原因为事假,申请成功后打开部门经理齐紫陌账号localhost/api/leave/list?eid=2 得到请假单
查看数据库,是同一部门
下一步进行界面绑定输出,在audit.html下进行绑定
,mounted(){
const objApp = this;
const $message = this.$message;
axios.get("/api/leave/list?eid=" + sessionStorage.eid)
.then(function(response){
const json = response.data;
if(json.code == '0'){
objApp.tableData.splice(0, objApp.tableData.length);
const formList = json.data.list;
formList.forEach(function(item){
switch (item.form_type){
case 1:
item.ftype = "事假";
break;
case 2:
item.ftype = "病假";
break;
case 3:
item.ftype = "工伤假";
break;
case 4:
item.ftype = "婚假";
break;
case 5:
item.ftype = "产假";
break;
case 6:
item.ftype = "丧假";
break;
}
item.stime = formatDate(item.start_time);
item.etime = formatDate(item.end_time);
item.ctime = formatDate(item.create_time);
objApp.tableData.push(item);
})
}else{
$message.error({message:json.message,offset:100})
}
})
}
objApp.tableData.splice(0, objApp.tableData.length);
表示删除掉我们的原始静态数据
objApp.tableData.push(item);
将数据重新压入到tableData中
switch是为了将数字类型转化为对应的文本
然后通过调用audit.html里函数完成日期格式化
function formatDate(time){
var newDate = new Date(time);
return newDate.getFullYear() + "-" +
(newDate.getMonth() + 1) + "-" + newDate.getDate()
+ " " + newDate.getHours() + "时";
}
进行格式化
item.stime = formatDate(item.start_time);
item.etime = formatDate(item.end_time);
item.ctime = formatDate(item.create_time);
输入齐紫陌账号t7,密码test登陆 然后修改url为http://localhost/audit.html回车访问
可以看到,上面我们添加的申请原因为事假的数据显示出来了,而静态数据没有显示
点击数据,可以看到数据也回填到弹出窗口上
开发请假审批Mapper层
审核过程就是一个数据更新过程,核心是update语句
在我们申请请假单时,审核流程已经根据时间是否大于72小时创建好流程表了,接下来的审批只是在这流程表上进行更新操作而已
打开leave_form.xml,添加语句,根据id号更新请假单
<update id="update" parameterType="com.imooc.oa.entity.LeaveForm">
UPDATE adm_leave_form SET employee_id = #{employeeId} , form_type = #{formType}, start_time = #{startTime}, end_time = #{endTime}, reason = #{reason}, state = #{state} ,create_time = #{createTime} WHERE form_id = #{formId}
</update>
向上推到接口LeaveFormMapper.java中,继续添加方法语句
public void update(LeaveForm form);
//补充
public LeaveForm selectById(Long formId);
处理流程process_flow..xml也要添加update
<update id="update" parameterType="com.imooc.oa.entity.ProcessFlow">
UPDATE adm_process_flow SET form_id = #{formId}, operator_id = #{operatorId}, action = #{action}, result = #{result}, reason = #{reason}, create_time = #{createTime}, audit_time = #{auditTime}, order_no = #{orderNo}, state = #{state}, is_last = #{isLast}
WHERE process_id = #{processId}
</update>
打开ProcessFlowMapper.java添加对应方法
public void update(ProcessFlow processFlow);
//补充
public List selectByFormId(Long formId);
为了实现请假单审批,还需要添加查询方法 leave_form.xml中
<select id="selectById" parameterType="Long" resultType="com.imooc.oa.entity.LeaveForm">
select * from adm_leave_form where form_id = #{value}
</select>
process_flow.xml中
<select id="selectByFormId" parameterType="Long" resultType="com.imooc.oa.entity.ProcessFlow">
select * from adm_process_flow where form_id = #{value} order by order_no
</select>
剩下mapper接口代码在上面给出代码进行补充
开发请假审批Service层
LeaveFormService.java添加
/**
* 审核请假单
* @param formId 表单编号
* @param operatorId 经办人(当前登录员工)
* @param result 审批结果
* @param reason 审批意见
*/
public void audit(Long formId , Long operatorId , String result , String reason){
MybatisUtils.executeUpdate(sqlSession -> {
//无论同意/驳回,当前任务状态变更为complete
ProcessFlowMapper processFlowMapper = sqlSession.getMapper(ProcessFlowMapper.class);
List<ProcessFlow> flowList = processFlowMapper.selectByFormId(formId);
if(flowList.size() == 0){
throw new LeaveFormException("无效的审批流程");
}
//获取当前任务ProcessFlow对象
List<ProcessFlow> processList = flowList.stream().filter(p -> p.getOperatorId() == operatorId && p.getState().equals("process")).collect(Collectors.toList());
ProcessFlow process = null;
if(processList.size() == 0){
throw new LeaveFormException("未找到待处理任务节点");
}else{
process = processList.get(0);
process.setState("complete");
process.setResult(result);
process.setReason(reason);
process.setAuditTime(new Date());
processFlowMapper.update(process);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH时");
LeaveFormMapper leaveFormMapper = sqlSession.getMapper(LeaveFormMapper.class);
NoticeMapper noticeMapper = sqlSession.getMapper(NoticeMapper.class);
LeaveForm form = leaveFormMapper.selectById(formId);
Employee operator = employeeService.selectById(operatorId);
Employee employee = employeeService.selectById(form.getEmployeeId());
//如果当前任务是最后一个节点,代表流程结束,更新请假单状态为对应的approved/refused
if(process.getIsLast() == 1){
form.setState(result); //approved / refused
leaveFormMapper.update(form);
String strResult = null;
if(result.equals("approved")){
strResult = "批准";
}else if(result.equals("refused")){
strResult = "驳回";
}
}else{
////readyList包含所有后续任务节点
List<ProcessFlow> readyList = flowList.stream().filter(p -> p.getState().equals("ready")).collect(Collectors.toList());
//如果当前任务不是最后一个节点且审批通过,那下一个节点的状态从ready变为process
if(result.equals("approved")){
ProcessFlow readyProcess = readyList.get(0);
readyProcess.setState("process");
processFlowMapper.update(readyProcess);
}else if(result.equals("refused")){
//如果当前任务不是最后一个节点且审批驳回,则后续所有任务状态变为cancel,请假单状态变为refused
for(ProcessFlow p:readyList){
p.setState("cancel");
processFlowMapper.update(p);
}
form.setState("refused");
leaveFormMapper.update(form);
}
}
return null;
});
}
如果表单没有对应流程数据时,processList.size() == 0
需要我们自定义异常,中断流程,在service下的exception文件下新建LeaveFormException.java
package com.imooc.oa.service.exception;
public class LeaveFormException extends RuntimeException{
public LeaveFormException(String message){
super(message);
}
}
进行测试
/**
* 情况1: 72小时以上请假,部门经理同意,总经理同意,流程结束
* @throws ParseException
*/
@Test
public void audit1() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
LeaveForm form = new LeaveForm();
form.setEmployeeId(3l);
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());
leaveFormService.audit(savedForm.getFormId(),2l,"approved","部门经理同意");
leaveFormService.audit(savedForm.getFormId(),1l,"approved","总经理同意");
}
/**
* 情况2: 72小时以上请假,部门经理拒绝,流程结束
* @throws ParseException
*/
@Test
public void audit2() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
LeaveForm form = new LeaveForm();
form.setEmployeeId(3l);
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);
leaveFormService.audit(savedForm.getFormId(),2l,"refused","部门经理拒绝");
}
/**
* 情况3: 72小时以内请假,部门经理同意,流程结束
* @throws ParseException
*/
@Test
public void audit3() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
LeaveForm form = new LeaveForm();
form.setEmployeeId(3l);
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());
leaveFormService.audit(savedForm.getFormId(),2l,"approved","部门经理同意");
}
情况一运行 查看请假单与请假状态
查看审批流程
三种情况运行请假单结果
以及审批流程
实现请假审批功能
完成sevlet层和view层 LeaveFormServlet.java下新增方法
// 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());
}
继续向上推进,打开audit.html页面 实现提交表单按钮,在method里面书写
,onSubmit(formName){
const objApp = this;
this.$refs[formName].validate(function(valid){
if(valid){
const params = new URLSearchParams();
params.append("formId", objApp.currentRow.form_id);
params.append("result", objApp.form.result);
params.append("reason", objApp.form.reason);
params.append("eid", sessionStorage.eid);
axios.post("/api/leave/audit" , params)
.then(function(response){
const json = response.data;
console.info(json);
if(json.code=="0"){
objApp.$alert("请假已审批完毕" , {
callback:function(){
window.location.href = "/notice.html";
}
})
}else{
objApp.$message.error({message:json.message,offset:100})
}
})
}
})
}
运行tomcat,登陆王美美账号创建大于72小时的请假单 然后登陆部门经理齐紫陌账号进行审批
再登陆总经理账号,进行审批即可
查看数据库流程信息
开发系统通知Service功能
我们已将开发了NoticeMapper,系统通知主要用来创建请假单后和审核后
在LeaveFormService,java中,添加创建请假单通知 一条用来通知自己创建成功,一条通知别人等待审核
package com.imooc.oa.service;
import com.imooc.oa.entity.Employee;
import com.imooc.oa.entity.LeaveForm;
import com.imooc.oa.entity.Notice;
import com.imooc.oa.entity.ProcessFlow;
import com.imooc.oa.mapper.EmployeeMapper;
import com.imooc.oa.mapper.LeaveFormMapper;
import com.imooc.oa.mapper.NoticeMapper;
import com.imooc.oa.mapper.ProcessFlowMapper;
import com.imooc.oa.service.exception.LeaveFormException;
import com.imooc.oa.utils.MybatisUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class LeaveFormService {
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;
}
/**
* 获取指定任务状态及指定经办人对应的请假单列表
* @param pfState ProcessFlow任务状态
* @param operatorId 经办人编号
* @return 请假单及相关数据列表
*/
public List<Map> getLeaveFormList(String pfState, Long operatorId){
return (List<Map>) MybatisUtils.executeQuery(sqlSession -> {
LeaveFormMapper mapper = sqlSession.getMapper(LeaveFormMapper.class);
List<Map> maps = mapper.selectByParams(pfState, operatorId);
return maps;
});
}
/**
* 审核请假单
* @param formId 表单编号
* @param operatorId 经办人(当前登录员工)
* @param result 审批结果
* @param reason 审批意见
*/
public void audit(Long formId , Long operatorId , String result , String reason){
MybatisUtils.executeUpdate(sqlSession -> {
//无论同意/驳回,当前任务状态变更为complete
ProcessFlowMapper processFlowMapper = sqlSession.getMapper(ProcessFlowMapper.class);
List<ProcessFlow> flowList = processFlowMapper.selectByFormId(formId);
if(flowList.size() == 0){
throw new LeaveFormException("无效的审批流程");
}
//获取当前任务ProcessFlow对象
List<ProcessFlow> processList = flowList.stream().filter(p -> p.getOperatorId() == operatorId && p.getState().equals("process")).collect(Collectors.toList());
ProcessFlow process = null;
if(processList.size() == 0){
throw new LeaveFormException("未找到待处理任务节点");
}else{
process = processList.get(0);
process.setState("complete");
process.setResult(result);
process.setReason(reason);
process.setAuditTime(new Date());
processFlowMapper.update(process);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH时");
LeaveFormMapper leaveFormMapper = sqlSession.getMapper(LeaveFormMapper.class);
NoticeMapper noticeMapper = sqlSession.getMapper(NoticeMapper.class);
LeaveForm form = leaveFormMapper.selectById(formId);
Employee operator = employeeService.selectById(operatorId);
Employee employee = employeeService.selectById(form.getEmployeeId());
//如果当前任务是最后一个节点,代表流程结束,更新请假单状态为对应的approved/refused
if(process.getIsLast() == 1){
form.setState(result); //approved / refused
leaveFormMapper.update(form);
String strResult = null;
if(result.equals("approved")){
strResult = "批准";
}else if(result.equals("refused")){
strResult = "驳回";
}
String notice1 = String.format("您的请假申请[%s-%s]%s%s已%s,审批意见:%s,审批流程已结束"
,sdf.format(form.getStartTime()),sdf.format(form.getEndTime())
,operator.getTitle(),operator.getName() ,strResult,reason
);
noticeMapper.insert(new Notice(form.getEmployeeId(), notice1));
String notice2 = String.format("%s-%s提起请假申请[%s-%s]您已%s,审批意见:%s,审批流程已结束" ,
employee.getTitle() , employee.getName() , sdf.format( form.getStartTime()) , sdf.format(form.getEndTime()),
strResult , reason);//发给审批人的通知
noticeMapper.insert(new Notice(operator.getEmployeeId(),notice2));
}else{
////readyList包含所有后续任务节点
List<ProcessFlow> readyList = flowList.stream().filter(p -> p.getState().equals("ready")).collect(Collectors.toList());
//如果当前任务不是最后一个节点且审批通过,那下一个节点的状态从ready变为process
if(result.equals("approved")){
ProcessFlow readyProcess = readyList.get(0);
readyProcess.setState("process");
processFlowMapper.update(readyProcess);
//消息1: 通知表单提交人,部门经理已经审批通过,交由上级继续审批
String notice1 = String.format("您的请假申请[%s-%s]%s%s已批准,审批意见:%s ,请继续等待上级审批" ,
sdf.format(form.getStartTime()) , sdf.format(form.getEndTime()),
operator.getTitle() , operator.getName(),reason);
noticeMapper.insert(new Notice(form.getEmployeeId(),notice1));
//消息2: 通知总经理有新的审批任务
String notice2 = String.format("%s-%s提起请假申请[%s-%s],请尽快审批" ,
employee.getTitle() , employee.getName() , sdf.format( form.getStartTime()) , sdf.format(form.getEndTime()));
noticeMapper.insert(new Notice(readyProcess.getOperatorId(),notice2));
//消息3: 通知部门经理(当前经办人),员工的申请单你已批准,交由上级继续审批
String notice3 = String.format("%s-%s提起请假申请[%s-%s]您已批准,审批意见:%s,申请转至上级领导继续审批" ,
employee.getTitle() , employee.getName() , sdf.format( form.getStartTime()) , sdf.format(form.getEndTime()), reason);
noticeMapper.insert(new Notice(operator.getEmployeeId(),notice3));
}else if(result.equals("refused")){
//如果当前任务不是最后一个节点且审批驳回,则后续所有任务状态变为cancel,请假单状态变为refused
for(ProcessFlow p:readyList){
p.setState("cancel");
processFlowMapper.update(p);
}
form.setState("refused");
leaveFormMapper.update(form);
//消息1: 通知申请人表单已被驳回
String notice1 = String.format("您的请假申请[%s-%s]%s%s已驳回,审批意见:%s,审批流程已结束" ,
sdf.format(form.getStartTime()) , sdf.format(form.getEndTime()),
operator.getTitle() , operator.getName(),reason);
noticeMapper.insert(new Notice(form.getEmployeeId(),notice1));
//消息2: 通知经办人表单"您已驳回"
String notice2 = String.format("%s-%s提起请假申请[%s-%s]您已驳回,审批意见:%s,审批流程已结束" ,
employee.getTitle() , employee.getName() , sdf.format( form.getStartTime()) , sdf.format(form.getEndTime()), reason);
noticeMapper.insert(new Notice(operator.getEmployeeId(),notice2));
}
}
return null;
});
}
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH时");
表示对时间进行格式化
然后运行三种情况的测试用例
实现系统通知功能
打开接口NoticeMapper.java,新增方法,根据接收人不同显示通知
public List<Notice> selectByReceiverId(Long receiverId);
打开notice.xml
<select id="selectByReceiverId" parameterType="Long" resultType="com.imooc.oa.entity.Notice">
select * from sys_notice where receiver_id= #{value} order by create_time desc limit 0,30
</select>
显示30条即可,没必要全部显示出来,消息太多 向上推进,新建NoticeService类
package com.imooc.oa.service;
import com.imooc.oa.entity.Notice;
import com.imooc.oa.mapper.NoticeMapper;
import com.imooc.oa.utils.MybatisUtils;
import java.util.List;
public class NoticeService {
public List<Notice> getNoticeList(Long receiverId){
return (List)MybatisUtils.executeQuery(sqlSession -> {
NoticeMapper mapper = sqlSession.getMapper(NoticeMapper.class);
return mapper.selectByReceiverId(receiverId);
});
}
}
继续向上推进,新建NoticeServlet.java文件
package com.imooc.oa.controller;
import com.imooc.oa.entity.Notice;
import com.imooc.oa.service.NoticeService;
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.List;
@WebServlet("/api/notice/list")
public class NoticeServlet extends HttpServlet {
private NoticeService noticeService = new NoticeService();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String employeeId = request.getParameter("eid");
ResponseUtils resp;
try {
List<Notice> noticeList = noticeService.getNoticeList(Long.parseLong(employeeId));
resp = new ResponseUtils().put("list", noticeList);
}catch (Exception e){
e.printStackTrace();
resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
}
response.setContentType("application/json;charset=utf-8");
response.getWriter().println(resp.toJsonString());
}
}
后台工作完成,切换到前台上 webapp下新建notice.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/axios/axios.js"></script>
</head>
<body>
<div id="app">
<h2>系统通知</h2>
<el-table
ref="singleTable"
:data="tableData"
highlight-current-row
style="width: 100%">
<el-table-column
property="index"
label="序号"
width="50">
</el-table-column>
<el-table-column
property="ctime"
label="通知时间"
width="180">
</el-table-column>
<el-table-column
property="content"
label="通知内容">
</el-table-column>
</el-table>
</div>
<script>
var Main = {
data() {
return {
tableData: []
}
}
,mounted() {
const objApp = this;
axios.get("/api/notice/list?eid=" + sessionStorage.eid)
.then(function (response) {
objApp.tableData.splice(0, objApp.tableData.length);
response.data.data.list.forEach(function (item,index) {
var date = new Date(item.createTime);
item.ctime = date.getFullYear() + "-" +
(date.getMonth() + 1) + "-" + date.getDate()
+ " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
item.index = index + 1;
objApp.tableData.push(item);
});
})
.catch(function (error) {
console.log(error);
});
}
};
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app")
</script>
</body>
</html>
记得在index.html下将我们原来添加的哔哩哔哩网页改掉,改成通知页面
运行tomcat 记住点击redeploy,而不是restart server,点击redeploy才能将java代码进行编译输出到target文件夹字节流文件夹下
实现系统登陆拦截功能
输入后台的某一网址,可以不登陆而直接进入 在每个页面添加验证,若没登陆过需要先登陆 在webapp的assets的oa目录下新建security.js文件
if(sessionStorage.eid == null || sessionStorage.uid==null){
window.location.href = "/login.html";
}
然后在每个页面进行引入,记住登陆界面不需要,登陆界面是产生这两个值的地方 每个html界面前进行引入
<script src="/assets/oa/security.js"></script>
关闭浏览器(不是关闭网页,为了清空登陆数据),重新打开输入链接访问
目前还有一个问题,就是设计的接口可以访问到我们的敏感数据,比如localhost/api/leave/list?eid=2 这个现在我们暂时无法解决,需要学习架构层面的东西,后面会有所涉猎,这里暂时不做处理