每月满勤天数如何计算
1 原始版本
1.1 需求
- 通过系统判断某月份的满勤天数,然后继续后续操作
1.2 解决方案
- 创建一张表(setting_month_rest_day),将每个月上班天数提前设置好即可(每年的假期不同,所以一年需要设置一次)
2 变更版本二
2.1 需求
- 出现了特殊情况,每个月的满勤天数各不相同 有的人是周日休,有的人是双休还有的是周六休
2.2 解决方案
- 在原有表的基础上新增一张表(setting_rest_type),主要对休班方式的不同进行保存
- 原有表(setting_month_rest_day)新增一个字段用于关联setting_rest_type,因为每个人只可能有一种休班方式,因此不需要一对多关系
3 变更版本三
3.1 需求
- 在每个月会出现一个人在某个时间段周日休,某个时间段双休的情况,需要根据实际情况来获得满勤天数
3.2 处理难点
- 每年都会有调休的情况发生(法定休假)
- 如果把周日,周六和节假日都存到表里,感觉录入的工作量会很大,一旦临时发生变故(例如过年不按照法定的来),还要进行修改,本来人力就不够,还好耗费人力,最后活还是自己的
- 满勤天数主要用于计算工资
3.3 解决方案
- 将满勤天数去掉,将工资计算从按月计算改为按天计算,上一天班发一天工资(有日报和考勤),最后相加之和就是当月工资
3.4 逻辑遗漏
- 没有考虑加班的情况,每个月工资=当月应发/当月满勤*当月出勤天数,当满勤小于出勤时,就会出现多发工资的情况(出勤大于满勤,工资不应该继续发放了)
4 变更版本四
4.1 需求
- 加班情况出现,且需要考虑真实的出勤天数
4.2 场景
4.2.1 2023年1月满勤天数
4.2.1.1 先单后双
-
已知条件
- 张三 1号到12号周日休息
- 张三 13号到月底都是双休
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-12号 9 周日休 2个周日+1天休假 13号-31号 9 双休 2个周末(去掉1个周末)+7天休假 共计18天满勤
4.2.1.2 先双后单
-
已知条件
- 张三 1号到12号双休
- 张三 13号到月底都是周日休息
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-12号 8 双休 2个周日(3天)+1天休假 13号-31号 10 周日休 三个周日(去掉1个周日)+7天休假 共计18天满勤
2.2.1.3 结论
- 主要判断的日期
- 28,29是否上班-------对于双休的来说
- 29是否上班-------------对于周日休班的来说
- 28是否上班-------------对于周六休班的来说
- 周末上班表和节假日表,并且休班类型的都要区分开
4.2.2 2023年4月满勤天数
4.2.2.1 先单后双
-
已知条件
- 张三 1号到12号周日休息
- 张三 13号到月底都是双休
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-12号 9 周日休 2个周日+1天休假 13号-31号 13 双休 3个周末(去掉1个周日)+ 2天休假(重合不算) 共计21天满勤
4.2.2.2 先双后单
-
已知条件
- 张三 1号到12号双休
- 张三 13号到月底都是周日休息
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-12号 7 双休 2个周日(4天)+1天休假 13号-31号 15 周日休 三个周日(去掉1个周日)+2天的假期(只算一天) 共计22天满勤
4.2.2.3 结论
- 主要判断的日期
- 23是否上班-------------对于双休的来说
- 29是否上班-------------对于周日休班的来说
- 28是否上班-------------对于周六休班的来说
4.2.3 2023年5月份满勤天数
4.2.3.1 先单后双
-
已知条件
- 张三 1号到15号周日休息
- 张三 16号到月底都是双休
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-15号 10 周日休 2个周日+3天休假 16号-31号 12 双休 2个周末 共计22天满勤
4.2.3.2 先双后单
-
已知条件
- 张三 1号到15号双休
- 张三 16号到月底都是周日休息
-
获得当月满勤天数
起止日期 满勤天数 休班方式 说明 1号-15号 9 双休 2个周末(去掉1个周六)+3天休假 16号-31号 14 周日休 2个周日 共计23天满勤
4.2.3.3 结论
- 主要判断的日期
- 6号是否上班-------------对于双休的来说
- 6号是否上班-------------对于周六休班的来说
- 休假会有重合的地方
4.3 思路想法
- 还是不想过多的录入信息
- 先通过代码计算出某个时间段有几个周六或周日,然后通过查询上班和加班的天数来计算出勤天数
- 目前不考虑周一到周五休假的可能性(暂时不验证)
4.4 设计实现
4.4.1 前期准备
- 在变更版本三的前提添加一个表setting_month_day_calculate,主要用于存储休班日期和加班日期
其中use_model保存setting_rest_type中唯一标识的内容,多个可按照逗号分隔
-
通过java代码获得周六或者周日的个数
/** * 查询日期间有几天一周中的某一天 * 日期格式 yyyy-MM-dd yyyy-MM-dd 1-7(表示周一到周日) * @param startDate 准备查询的起始日期 * @param endDate 准备查询的结束日期 * @param dayOfWeek 准备查的一周中的某一天(准备查周几?) * @return 包含所查周几的天数 * @throws ParseException 不支持跨年查询、不支持结束日期早于起始日期、周几输入错误等 */ public static int getMondayNumber(Date startDate,Date endDate,int dayOfWeek) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); int differenceDay = 0; //实例化起始和结束Calendar对象 Calendar startCalendar = Calendar.getInstance(); Calendar endCalendar = Calendar.getInstance(); //分别设置Calendar对象的时间 startCalendar.setTime(startDate); endCalendar.setTime(endDate); //定义起始日期和结束日期分别属于第几周 int startWeek = startCalendar.get(Calendar.WEEK_OF_YEAR); int endWeek = endCalendar.get(Calendar.WEEK_OF_YEAR); //拿到起始日期是星期几 int startDayOfWeek = startCalendar.get(Calendar.DAY_OF_WEEK); if(startDayOfWeek == 1) { startDayOfWeek = 7; startWeek--; }else startDayOfWeek--; //拿到结束日期是星期几 int endDayOfWeek = endCalendar.get(Calendar.DAY_OF_WEEK); if(endDayOfWeek == 1) { endDayOfWeek = 7; endWeek--; }else endDayOfWeek--; //计算相差的周数 int differenceWeek = endWeek - startWeek; //开始计算 if(startDayOfWeek <= dayOfWeek) { if(endDayOfWeek >= dayOfWeek) differenceDay = differenceWeek + 1; }else if(startDayOfWeek > dayOfWeek) { if(endDayOfWeek < dayOfWeek) differenceDay = differenceWeek-1; }else { differenceDay = differenceWeek; } return differenceDay; }
4.4.2 实现(以2023年1月为例)
4.4.2.1 模拟场景
- 张三 1号到12号周日休息
- 张三 13号到月底都是双休
4.4.2.2 数据准备(根据模拟场景设置)
-
setting_month_day_calculate (id等非关键字段忽略)数据
select_date/name use_model calculate 2023-01-02 双休,周日休,周六休 1 2023-01-23 双休,周日休,周六休 5 2023-01-28 双休,周六休 -1 2023-01-29 双休,周日休 -1 -
setting_rest_type (id等非关键字段忽略)数据
name 双休 周六休 周日休 -
setting_month_rest_day (id等非关键字段忽略)数据
person_name use_begin_time use_end_time rest_type_name 张三 2022-12-20 2023-01-12 周日休 张三 2023-01-13 null 双休
4.4.2.3 获得1月份满勤天数
-
通过sql获得当月的setting_month_rest_day的条数(当大于两条才进行计算,并适用map存放,判断map的key值如果不存在,则证明setting_month_rest_day中最多只有一条,可直接查询出来)。
-- start_day: 2023-01-01 -- end_day : 2023-01-31 SELECT t1.person_name, t1.use_begin_time, t1.use_end_time, t1.rest_type_name FROM setting_month_rest_day t1 INNER JOIN ( SELECT person_name FROM setting_month_rest_day WHERE ( use_begin_time BETWEEN #{start_day} AND #{end_day} OR use_end_time BETWEEN #{start_day} AND #{end_day} ) GROUP BY rest_type_name HAVING count( rest_type_name ) > 1 ) t2 ON t1.person_name=t2.person_name WHERE (t1.use_begin_time BETWEEN #{start_day} AND #{end_day} OR t1.use_end_time BETWEEN #{start_day} AND #{end_day} ) ORDER BY t1.use_begin_time DESC
-
通过在循环判断rest_type_name值,如果是双休则需要加周六和周日的天数,如果是单休只需要添加周日或周六即可
if(rest_type_name.index("双") != -1){ return getMondayNumber(use_begin_time, use_end_time, 7)+getMondayNumber(use_begin_time, use_end_time, 6); }else if(rest_type_name.index("日") != -1){ return getMondayNumber(use_begin_time, use_end_time, 7); }else if(rest_type_name.index("六") != -1){ return getMondayNumber(use_begin_time, use_end_time, 6); }
-
通过获得setting_month_day_calculate的天数来加上法定的休班和调休
-- startDate setting_month_rest_day的use_begin_time字段 -- endDate setting_month_rest_day的use_end_time字段 -- model setting_month_rest_day的rest_type_name字段 select SUM(t1.calculate) from wr_month_day_statistics_calculate t1 where (t1.select_date BETWEEN #{startDate} and #{endDate}) and FIND_IN_SET(#{model}',t1.use_model)
-
通过2和3的数值相加,即可获得当月满勤天数