SSM员工考勤系统五
转自http://coder520.com/
添加定时器
Quartz学习
1、加入pom配置
2、创建配置文件spring-job
3、在spring-cfg配置文件里引入spring-job
4、corn在线生成表达式
打开异常处理,主要有两种情况,
1、一种是今天一天没上班,我们就先查出今天谁没打卡(说明没上班),然后给他插入打卡记录(异常)。
2、第二种是今天早上打卡,晚上没打卡,这是有打卡记录的所以不用先查用户列表,查evening为空就行,然后你晚上没打卡,你也可能是早上打卡之后就溜了,所以缺勤时间还是设置为480.
/**
* 缺勤一整天
**/
private static final Integer ABSENCE_DAY = 480;
/**
* 考勤异常状态
**/
private static final Byte ATTEND_STATUS_ABNORMAL = 2;
private static final Byte ATTEND_STATUS_NORMAL = 1;
@Override
@Transactional
public void checkAttend() {
//1、查询缺勤用户ID,插入打卡记录 并且设置为异常 缺勤480分钟
List<Long> userIdList = attendMapper.selectTodayAbsence();
// 健壮性非空判断,使用这个apache的这个工具类可以判断非空,美观一点。。。
if (CollectionUtils.isNotEmpty(userIdList)) {
List<Attend> attendList = new ArrayList<Attend>();
for (Long userId : userIdList) {
Attend attend = new Attend();
attend.setUserId(userId);
attend.setAttendDate(new Date());
attend.setAttendWeek((byte) DateUtil.getTodayWeek());
attend.setAttendStatus(ATTEND_STATUS_ABNORMAL);
attend.setAbsence(ABSENCE_DAY);
attendList.add(attend);
}
attendMapper.batchInsert(attendList);
}
//2、检查晚打卡 将下班未打卡设置为异常
List<Attend> absenceList = attendMapper.selectTodayEveningAbsence();
if (CollectionUtils.isNotEmpty(absenceList)) {
for (Attend attend : absenceList) {
attend.setAbsence(ABSENCE_DAY);
attend.setAttendStatus(ATTEND_STATUS_ABNORMAL);
attendMapper.updateByPrimaryKeySelective(attend);
}
}
}
接着实现selectTodayAbsence(),batchInsert(attendList),selectTodayEveningAbsence()这三个方法
<select id="selectTodayAbsence" resultType="Long">
select
u.id
from `user` u
where u.id NOT IN
(
SELECT a.user_id FROM
attend a
WHERE a.attend_date = CURDATE()
)
</select>
选择批量插入,不要循环一次就插入一次,性能不好,但是更新就没办法
<insert id="batchInsert" parameterType="list" >
insert into attend (user_id, attend_date,
attend_week, absence, attend_status)
VALUE
<foreach collection="list" item="item" index="index" separator=",">
(#{item.userId}, #{item.attendDate}, #{item.attendWeek},
#{item.absence}, #{item.attendStatus})
</foreach>
</insert>
我们更新要更新那些晚上没打卡而且状态不是异常的,并且不是星期6日的
<select id="selectTodayEveningAbsence" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from attend
where attend_date = CURDATE()
AND attend_evening IS NULL
AND attend_status = 1
AND attend_week NOT IN (6,7)
</select>
之前的打卡方法也需要修改,当没有记录并且早上的打卡的时候,要判断是否9点半之前,之后就设置为异常,还有早退。
/**
* 中午十二点 用于判定上下午
* 早上9点半 用于判定是否迟早
* 下午6点半 用于判定是否早退
**/
private static final int NOON_HOUR = 12;
private static final int NOON_MINUTE = 00;
private static final int MORNING_HOUR = 9;
private static final int MORNING_MINUTE = 30;
private static final int EVENING_HOUR = 18;
private static final int EVENING_MINUTE = 30;
/**
* Author ljs
* Description 模拟签到
* Date 2018/8/18 19:36
**/
@Override
public void signAttend(Attend attend) throws Exception {
try {
Date today = new Date();
attend.setAttendDate(today);
attend.setAttendWeek((byte) DateUtil.getTodayWeek());
Date noon = DateUtil.getDate(NOON_HOUR, NOON_MINUTE);
Date monringAttend = DateUtil.getDate(MORNING_HOUR, MORNING_MINUTE);
Date eveningAttend = DateUtil.getDate(EVENING_HOUR, EVENING_MINUTE);
// 查询当天是否有打卡记录
Attend todayRecord = attendMapper.selectTodaySignRecord(attend.getUserId());
if (todayRecord != null) {
if (today.compareTo(noon) <= 0) {
//有记录并且now打卡时间早于12点,不用再存进去
return;
} else {
//有记录并且now打卡时间晚于12点,需要更新
//注意是更新todayRecord,不是today
todayRecord.setAttendEvening(today);
attendMapper.insertSelective(todayRecord);
}
} else {
if (today.compareTo(noon) <= 0) {
//没有记录并且now打卡时间早于12点,需要存进去
attend.setAttendMorning(today);
//计算打卡时间是否迟到
if (today.compareTo(monringAttend) > 0) {
//大于9点半迟到了
attend.setAttendStatus(ATTEND_STATUS_ABNORMAL);
attend.setAbsence(DateUtil.getMinutes(monringAttend, today));
} else {
//没有迟早
attend.setAttendStatus(ATTEND_STATUS_NORMAL);
attend.setAbsence(0);
}
} else {
//没有记录并且now打卡时间晚于12点,需要存进去
attend.setAttendEvening(today);
//计算打卡时间是否早退
if (today.compareTo(eveningAttend) < 0) {
//早于下午6点半早退了
attend.setAttendStatus(ATTEND_STATUS_ABNORMAL);
attend.setAbsence(DateUtil.getMinutes(today, eveningAttend));
} else {
//没有早退
attend.setAttendStatus(ATTEND_STATUS_NORMAL);
attend.setAbsence(0);
}
}
attendMapper.insertSelective(attend);
}
//1、中午十二点以前打卡都算上午打卡,过9:30以后打卡,直接异常算迟到
//2、十二点以后都算下午打卡,超过6:00以后打卡直接异常,算早退
//3、选择最后一个下午的打卡时间与上午打卡进行比较获取时间差
//4、时间差不足8小时算异常,并且缺席多长时间要存进去
} catch (Exception e) {
log.error("用户签到异常", e);
throw e;
}
}
补签工作流
1、pom
2、和spring整合
3、生成23张表就整合成功
4、按照流程图插件

5、补签表

attend_id 关联考勤attend表
re_attend_starter 补签发起人
mor,eve 早晚不签时间
current_handler 补签处理人
status 该补签的状态 1 处理中 2 通过 3 不通过,你发起补签任务就为1,组长处理过后就为2或3
comments 补签理由
6、generator
7、组员想要补签提交任务
controller接口,当页面上点击补签就会弹出一个窗口,填写数据,这里我们先把用户写死,原因是我们要在postman发送数据测试。
**
* Author ljs
* Description 用户发起工作流补签Task
* Date 2018/8/20 8:47
**/
@RequestMapping("/start")
public void startReAttendFlow(@RequestBody ReAttend reAttend, HttpSession session) {
// User user = (User) session.getAttribute("userinfo");
// reAttend.setReAttendStarter(user.getUsername());
reAttend.setReAttendStarter("ljs"); //补签发起人
reAttendService.startReAttendFlow(reAttend);
service
7.1、设置补签实例的下一个处理人和状态
7.2、把该条记录插入到数据库
7.3、创建一个键值对,把补签实例和处理人存进去,方便后面组长在处理补签的时候根据用户名获取补签实例
7.4、启动补签实例
7.5、提交补签任务
/**
* Author ljs
* Description 发起补签工作流
* Date 2018/8/21 14:08
**/
@Override
@Transactional
public void startReAttendFlow(ReAttend reAttend) {
//laowang是ljs的组长
reAttend.setCurrentHandler("laowang");
reAttend.setStatus(RE_ATTEND_STATUS_ONGOING); //处理中
//插入数据库补签表
reAttendMapper.insertSelective(reAttend);
Map<String, Object> map = new HashMap();
map.put(RE_ATTEND_SIGN, reAttend);
map.put(NEXT_HANDLER, reAttend.getCurrentHandler());
//启动补签流程实例
ProcessInstance instance = runtimeService.startProcessInstanceByKey(RE_ATTEND_FLOW_ID, map);
//提交用户补签任务
Task task = taskService.createTaskQuery().processInstanceId(instance.getId()).singleResult();
taskService.complete(task.getId(), map);
}
8、提交之后就存到数据库里了,现在组长登录要处理任务controller
/**
* Author ljs
* Description 查看需要处理的工作流列表
* Date 2018/8/20 8:49
**/
@RequestMapping("/list")
public List<ReAttend> listReAttendFlow() {
// User user = session.getAttribute("userinfo");
String userName = "laowang";
List<ReAttend> tasks = reAttendService.listTasks(userName);
return tasks;
}
service,这里只是查询就不要事务
/**
* Author ljs
* Description 查询需要处理的工作流列表
* Date 2018/8/21 14:37
**/
@Override
public List<ReAttend> listTasks(String username) {
List<ReAttend> reAttendList = new ArrayList<ReAttend>();
//获取taskService组长需要处理的工作流列表
List<Task> taskList = taskService.createTaskQuery().processVariableValueEquals(username).list();
//转换成页面实体,在页面显示出来
if (CollectionUtils.isNotEmpty(taskList)) {
for (Task task : taskList) {
Map<String, Object> variable = taskService.getVariables(task.getId());
ReAttend reAttend = (ReAttend) variable.get(RE_ATTEND_SIGN);
reAttend.setTaskId(task.getId());
reAttendList.add(reAttend);
}
}
return reAttendList;
}
9、最后开始审批审批结束后修改页面状态,在这之前要给补签实体类添加两个字段,因为在当在任务列表中选择了某个任务的时候要获取它的任务id和任务状态,然后才能知道是审批的是哪个任务和在页面上修改审批的状态,也可以创建vo增强类,这些就没有再创建一个vo类,直接在实体类里。
//选择需要审批任务的id
private String taskId;
//用于审批结束后判断页面的显示
private String approveFlag;
//省略get和set
controller
/**
* Author ljs
* Description 选择某个任务进行审批
* Date 2018/8/20 8:52
**/
@RequestMapping("/approve")
public void approveReAttendFlow(@RequestBody ReAttend reAttend) {
reAttendService.approve(reAttend);
}
/**
* Author ljs
* Description 开始进行审批,审批结束修改页面状态
* Date 2018/8/21 14:39
**/
@Override
@Transactional
public void approve(ReAttend reAttend) {
//获取选中的工作流
Task task = taskService.createTaskQuery().taskId(reAttend.getTaskId()).singleResult();
if (RE_ATTEND_STATUS_PSSS.toString().equals(reAttend.getApproveFlag())) {
// 审批通过,修改补签数据状态
reAttend.setStatus(RE_ATTEND_STATUS_PSSS);
reAttendMapper.updateByPrimaryKeySelective(reAttend);
}else if(RE_ATTEND_STATUS_REFUSE.toString().equals(reAttend.getApproveFlag())){
// 审批不通过,修改补签数据状态
reAttend.setStatus(RE_ATTEND_STATUS_REFUSE);
reAttendMapper.updateByPrimaryKeySelective(reAttend);
}
taskService.complete(reAttend.getTaskId());
}
测试,给各个controller方法设置断点,postman选择一个需要补签的id

我们就来补签下面这条数据



调试到这里记住taskid808
在发送一条需要补签的

模拟组长登录查看任务列表


就可以看到之前提交的任务了,这里好像不需要记得taskId。。。因为我们组长laowang之前已经写死了。

可以看到发起人都是ljs,处理人都是laowang

接着模拟组长同意id为3的任务,然后调用approve需要传递taskid等等。
工作流面试
https://blog.csdn.net/u013456370/article/details/50144849
五、权限shiro权限控制
5.1、需要五张表
用户表user,权限表permission,角色表role,角色与权限的中间表,角色和用户的中间表。
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`permissionid` bigint(20) NOT NULL,
`permission` varchar(50) NOT NULL,
`description` varchar(50) NOT NULL,
PRIMARY KEY (`permissionid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名称',
`password` varchar(50) NOT NULL COMMENT '密码',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号码',
`real_name` varchar(20) NOT NULL COMMENT '真实姓名',
`head_image` varchar(100) DEFAULT NULL COMMENT '头像',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`roleid` bigint(20) NOT NULL,
`role` varchar(50) NOT NULL,
`description` varchar(50) NOT NULL COMMENT '角色描述',
PRIMARY KEY (`roleid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`role_id` bigint(20) NOT NULL,
`permission_id` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后generator生成,这里有两对多对多需要建立两张第三表,但是这里没有使用外键注意。
接着,在多对多中各自设置"外键",有程序来控制外键。
- user实体类
private List<Role> roleList;
//省略get和set
5.2、shiro的运行流程。
- subject代表你当前的用户
-
subject经过securitymanager安全验证
-实现realm类告诉框架给哪些人哪些权限
image.png
5.3、和web整合
web.xml配置权限过滤器
<!--shiro权限过滤器-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5.4、和spring整合
创建spring-shiro.xml
在spring-cfg.xml里引入spring-shiro.xml