考勤系统Java子弹系统架构

SSM员工考勤系统五

2018-08-20  本文已影响82人  shenyoujian

转自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、按照流程图插件


image.png

5、补签表


image.png

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


image.png

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


image.png
image.png
image.png

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


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

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


image.png

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


image.png

接着模拟组长同意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生成,这里有两对多对多需要建立两张第三表,但是这里没有使用外键注意。
接着,在多对多中各自设置"外键",有程序来控制外键。

private List<Role> roleList;
//省略get和set
5.2、shiro的运行流程。
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

5.5、复写Myrealm继承
上一篇 下一篇

猜你喜欢

热点阅读