|
@@ -330,12 +330,23 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
}
|
|
}
|
|
|
// 新增记录
|
|
// 新增记录
|
|
|
po.setDaKaShiJian1(now.format(DATETIME_FORMATTER));
|
|
po.setDaKaShiJian1(now.format(DATETIME_FORMATTER));
|
|
|
- if (!now.isAfter(banCiKaiShi)) {
|
|
|
|
|
|
|
+ /* if (!now.isAfter(banCiKaiShi)) {
|
|
|
po.setZhuangTai1("正常");
|
|
po.setZhuangTai1("正常");
|
|
|
} else {
|
|
} else {
|
|
|
po.setZhuangTai1("异常");
|
|
po.setZhuangTai1("异常");
|
|
|
long chiDao = Duration.between(banCiKaiShi, now).toMinutes();
|
|
long chiDao = Duration.between(banCiKaiShi, now).toMinutes();
|
|
|
po.setChiDaoShiChang(chiDao);//迟到的时候计算迟到时长
|
|
po.setChiDaoShiChang(chiDao);//迟到的时候计算迟到时长
|
|
|
|
|
+ }*/
|
|
|
|
|
+ // 计算迟到分钟数(向上取整或直接使用分钟差)
|
|
|
|
|
+ long lateMinutes = Duration.between(banCiKaiShi, now).toMinutes();
|
|
|
|
|
+ if (lateMinutes <= 1) {
|
|
|
|
|
+ // 未迟到或迟到1分钟以内(含) -> 正常
|
|
|
|
|
+ po.setZhuangTai1("正常");
|
|
|
|
|
+ po.setChiDaoShiChang(0L);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 迟到超过1分钟 -> 异常,记录实际迟到时长
|
|
|
|
|
+ po.setZhuangTai1("异常");
|
|
|
|
|
+ po.setChiDaoShiChang(lateMinutes);
|
|
|
}
|
|
}
|
|
|
// 如果上班异常,考勤状态先设为异常(下班打卡后会重新计算)
|
|
// 如果上班异常,考勤状态先设为异常(下班打卡后会重新计算)
|
|
|
if ("异常".equals(po.getZhuangTai1())) {
|
|
if ("异常".equals(po.getZhuangTai1())) {
|
|
@@ -397,7 +408,7 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
* 2.下班迟到,修改正常逻辑同上
|
|
* 2.下班迟到,修改正常逻辑同上
|
|
|
* 3.上班和下班卡时间不允许修改为异常,即上班卡不能大于配置上班时间,下班卡不能小于配置时间(已经考虑过跨日情况)
|
|
* 3.上班和下班卡时间不允许修改为异常,即上班卡不能大于配置上班时间,下班卡不能小于配置时间(已经考虑过跨日情况)
|
|
|
* 4.考虑到配置可能被修改,每次修改打卡时间都会重新读取配置,重新计算工作时长并更新
|
|
* 4.考虑到配置可能被修改,每次修改打卡时间都会重新读取配置,重新计算工作时长并更新
|
|
|
- * @param po 传入参数,必须包含:yongHuId, buMen, paiBanMingChen, banCiMing
|
|
|
|
|
|
|
+ * @param po 传入参数 记录id 上班时间,下班时间
|
|
|
* @return 填充后的考勤记录对象
|
|
* @return 填充后的考勤记录对象
|
|
|
* @throws Exception 校验或业务异常
|
|
* @throws Exception 校验或业务异常
|
|
|
*/
|
|
*/
|
|
@@ -437,25 +448,30 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATE);
|
|
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATE);
|
|
|
LocalDate recordDate = LocalDate.parse(riQi, dateFormatter);
|
|
LocalDate recordDate = LocalDate.parse(riQi, dateFormatter);
|
|
|
|
|
|
|
|
- // 5. 处理传入的上班打卡时间
|
|
|
|
|
|
|
+ // 标记哪些字段被修改了
|
|
|
|
|
+ boolean isDaKa1Modified = false;
|
|
|
|
|
+ boolean isDaKa2Modified = false;
|
|
|
String newDaKa1 = po.getDaKaShiJian1();
|
|
String newDaKa1 = po.getDaKaShiJian1();
|
|
|
|
|
+ String newDaKa2 = po.getDaKaShiJian2();
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 处理传入的上班打卡时间
|
|
|
LocalDateTime newDaKa1Time = null;
|
|
LocalDateTime newDaKa1Time = null;
|
|
|
if (BeanUtils.isNotEmpty(newDaKa1)) {
|
|
if (BeanUtils.isNotEmpty(newDaKa1)) {
|
|
|
newDaKa1Time = LocalDateTime.parse(newDaKa1, DATETIME_FORMATTER);
|
|
newDaKa1Time = LocalDateTime.parse(newDaKa1, DATETIME_FORMATTER);
|
|
|
// 构建配置上班时间
|
|
// 构建配置上班时间
|
|
|
LocalDateTime configStart = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
LocalDateTime configStart = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
- // 校验:配置上班时间不能小于传入时间(即传入时间不能晚于配置时间)
|
|
|
|
|
|
|
+ // 校验:传入时间不能晚于配置上班时间
|
|
|
if (configStart.isBefore(newDaKa1Time)) {
|
|
if (configStart.isBefore(newDaKa1Time)) {
|
|
|
throw new Exception("上班卡不允许修改比配置的上班时间晚");
|
|
throw new Exception("上班卡不允许修改比配置的上班时间晚");
|
|
|
}
|
|
}
|
|
|
- // 更新上班打卡信息,考勤状态在后面修改,这里只修改打上班卡涉及的内容
|
|
|
|
|
|
|
+ // 更新上班打卡信息
|
|
|
records.setDaKaShiJian1(newDaKa1);
|
|
records.setDaKaShiJian1(newDaKa1);
|
|
|
records.setZhuangTai1("正常");
|
|
records.setZhuangTai1("正常");
|
|
|
records.setChiDaoShiChang(null); // 清空迟到时长
|
|
records.setChiDaoShiChang(null); // 清空迟到时长
|
|
|
|
|
+ isDaKa1Modified = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 6. 处理传入的下班打卡时间
|
|
// 6. 处理传入的下班打卡时间
|
|
|
- String newDaKa2 = po.getDaKaShiJian2();
|
|
|
|
|
LocalDateTime newDaKa2Time = null;
|
|
LocalDateTime newDaKa2Time = null;
|
|
|
LocalDateTime configEnd = null;
|
|
LocalDateTime configEnd = null;
|
|
|
if (BeanUtils.isNotEmpty(newDaKa2)) {
|
|
if (BeanUtils.isNotEmpty(newDaKa2)) {
|
|
@@ -467,7 +483,7 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
}
|
|
}
|
|
|
String endDateStr = endDate.format(dateFormatter);
|
|
String endDateStr = endDate.format(dateFormatter);
|
|
|
configEnd = LocalDateTime.parse(endDateStr + " " + xiaBanShiJian + ":00", DATETIME_FORMATTER);
|
|
configEnd = LocalDateTime.parse(endDateStr + " " + xiaBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
- // 校验:配置下班时间不能大于传入时间(即传入时间不能早于配置时间)
|
|
|
|
|
|
|
+ // 校验:传入时间不能早于配置下班时间
|
|
|
if (configEnd.isAfter(newDaKa2Time)) {
|
|
if (configEnd.isAfter(newDaKa2Time)) {
|
|
|
throw new Exception("下班卡不允许修改的比配置的下班时间早");
|
|
throw new Exception("下班卡不允许修改的比配置的下班时间早");
|
|
|
}
|
|
}
|
|
@@ -477,58 +493,95 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
// 计算加班时长
|
|
// 计算加班时长
|
|
|
long jiaBan = Duration.between(configEnd, newDaKa2Time).toMinutes();
|
|
long jiaBan = Duration.between(configEnd, newDaKa2Time).toMinutes();
|
|
|
records.setJiaBanShiChang(jiaBan);
|
|
records.setJiaBanShiChang(jiaBan);
|
|
|
|
|
+ isDaKa2Modified = true;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 7. 计算工作时长(配置下班时间 - 配置上班时间)
|
|
|
|
|
|
|
+ // 7.0 修改上下班时间,需要重新计算班次时长(配置下班时间 - 配置上班时间)
|
|
|
// 注意:需要重新获取配置上下班时间的完整 LocalDateTime,因为可能跨日
|
|
// 注意:需要重新获取配置上下班时间的完整 LocalDateTime,因为可能跨日
|
|
|
LocalDateTime configStartFullTime = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
LocalDateTime configStartFullTime = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
LocalDate endDateForConfigTime = recordDate;//2026-03-26 17:30:00
|
|
LocalDate endDateForConfigTime = recordDate;//2026-03-26 17:30:00
|
|
|
- if ("Y".equals(shiFouKuaRi)) {
|
|
|
|
|
|
|
+ if ("Y".equals(shiFouKuaRi)) {//如果跨日,上班日期是2026-03-01,下班日期应该是2026-03-02
|
|
|
endDateForConfigTime = recordDate.plusDays(1);
|
|
endDateForConfigTime = recordDate.plusDays(1);
|
|
|
}
|
|
}
|
|
|
LocalDateTime configEndFullTime = LocalDateTime.parse(endDateForConfigTime.format(dateFormatter) + " " + xiaBanShiJian + ":00", DATETIME_FORMATTER);
|
|
LocalDateTime configEndFullTime = LocalDateTime.parse(endDateForConfigTime.format(dateFormatter) + " " + xiaBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
//这里目的的防止有人修改了配置之后在修改考勤打卡时间导致后续统计考勤模块出问题
|
|
//这里目的的防止有人修改了配置之后在修改考勤打卡时间导致后续统计考勤模块出问题
|
|
|
- long gongZuoShiChang = Duration.between(configStartFullTime, configEndFullTime).toMinutes();
|
|
|
|
|
- if (!Long.valueOf(gongZuoShiChang).equals(records.getGongZuoShiChan())) {
|
|
|
|
|
- records.setGongZuoShiChan(gongZuoShiChang);
|
|
|
|
|
|
|
+ long banCiShiChang = Duration.between(configStartFullTime, configEndFullTime).toMinutes();
|
|
|
|
|
+ if (!Long.valueOf(banCiShiChang).equals(records.getBanCiShiChang())) {
|
|
|
|
|
+ records.setBanCiShiChang(banCiShiChang);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 7.1 计算工作时长(根据修改情况)
|
|
|
|
|
+ long gongZuoShiChang = 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (isDaKa1Modified && isDaKa2Modified) {
|
|
|
|
|
+ // 两个都修改:直接使用po中两个时间计算
|
|
|
|
|
+ LocalDateTime startTime = LocalDateTime.parse(newDaKa1, DATETIME_FORMATTER);
|
|
|
|
|
+ LocalDateTime endTime = LocalDateTime.parse(newDaKa2, DATETIME_FORMATTER);
|
|
|
|
|
+ gongZuoShiChang = Duration.between(startTime, endTime).toMinutes();
|
|
|
|
|
+ } else if (isDaKa1Modified) {
|
|
|
|
|
+ // 只修改上班时间:使用新上班时间 + 数据库中的下班时间
|
|
|
|
|
+ LocalDateTime startTime = newDaKa1Time;
|
|
|
|
|
+ String existingDaKa2 = records.getDaKaShiJian2();
|
|
|
|
|
+ if (BeanUtils.isNotEmpty(existingDaKa2)) {
|
|
|
|
|
+ LocalDateTime endTime = LocalDateTime.parse(existingDaKa2, DATETIME_FORMATTER);
|
|
|
|
|
+ gongZuoShiChang = Duration.between(startTime, endTime).toMinutes();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 下班时间不存在,工作时长暂时为0
|
|
|
|
|
+ gongZuoShiChang = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (isDaKa2Modified) {
|
|
|
|
|
+ // 只修改下班时间:使用数据库中的上班时间 + 新下班时间
|
|
|
|
|
+ String existingDaKa1 = records.getDaKaShiJian1();
|
|
|
|
|
+ if (BeanUtils.isNotEmpty(existingDaKa1)) {
|
|
|
|
|
+ LocalDateTime startTime = LocalDateTime.parse(existingDaKa1, DATETIME_FORMATTER);
|
|
|
|
|
+ LocalDateTime endTime = newDaKa2Time;
|
|
|
|
|
+ gongZuoShiChang = Duration.between(startTime, endTime).toMinutes();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 上班时间不存在,工作时长暂时为0
|
|
|
|
|
+ gongZuoShiChang = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 都没有修改,保持原工作时长
|
|
|
|
|
+ gongZuoShiChang = records.getGongZuoShiChan() != null ? records.getGongZuoShiChan() : 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 工作时长不能为负数
|
|
|
|
|
+ if (gongZuoShiChang < 0) {
|
|
|
|
|
+ gongZuoShiChang = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ records.setGongZuoShiChan(gongZuoShiChang);
|
|
|
|
|
+
|
|
|
// 8. 综合考勤状态
|
|
// 8. 综合考勤状态
|
|
|
String zhuangTai1 = records.getZhuangTai1();
|
|
String zhuangTai1 = records.getZhuangTai1();
|
|
|
String zhuangTai2 = records.getZhuangTai2();
|
|
String zhuangTai2 = records.getZhuangTai2();
|
|
|
- if ("正常".equals(zhuangTai1) && "正常".equals(zhuangTai2)) {//想了下,还是这样:只有都正常才处理否则不处理,统计那边考勤状态为空时会自动统计为异常天数
|
|
|
|
|
|
|
+ if ("正常".equals(zhuangTai1) && "正常".equals(zhuangTai2)) {
|
|
|
records.setKaoQinZhuangTa("正常");
|
|
records.setKaoQinZhuangTa("正常");
|
|
|
- }/* else if(BeanUtils.isEmpty(zhuangTai2)){//这里考虑的是还没到下班时间,用户就修改上班考勤,这时候下班状态是null
|
|
|
|
|
- LOGGER.info("记录{[]}没到下班时间或者下班卡没打,考勤状态不做处理",po.getId());
|
|
|
|
|
}
|
|
}
|
|
|
- else {
|
|
|
|
|
- records.setKaoQinZhuangTa("异常");
|
|
|
|
|
- }*/
|
|
|
|
|
|
|
|
|
|
// 9. 构建需要更新的字段映射(只更新指定字段)
|
|
// 9. 构建需要更新的字段映射(只更新指定字段)
|
|
|
Map<String, String> updateCond = new HashMap<>();
|
|
Map<String, String> updateCond = new HashMap<>();
|
|
|
// 上班相关
|
|
// 上班相关
|
|
|
- if (BeanUtils.isNotEmpty(newDaKa1)) {
|
|
|
|
|
|
|
+ if (isDaKa1Modified) {
|
|
|
updateCond.put("da_ka_shi_jian_1_", records.getDaKaShiJian1());
|
|
updateCond.put("da_ka_shi_jian_1_", records.getDaKaShiJian1());
|
|
|
updateCond.put("zhuang_tai_1_", records.getZhuangTai1());
|
|
updateCond.put("zhuang_tai_1_", records.getZhuangTai1());
|
|
|
updateCond.put("chi_dao_shi_chang", null); // 清空
|
|
updateCond.put("chi_dao_shi_chang", null); // 清空
|
|
|
}
|
|
}
|
|
|
// 下班相关
|
|
// 下班相关
|
|
|
- if (BeanUtils.isNotEmpty(newDaKa2)) {
|
|
|
|
|
|
|
+ if (isDaKa2Modified) {
|
|
|
updateCond.put("da_ka_shi_jian_2_", records.getDaKaShiJian2());
|
|
updateCond.put("da_ka_shi_jian_2_", records.getDaKaShiJian2());
|
|
|
updateCond.put("zhuang_tai_2_", records.getZhuangTai2());
|
|
updateCond.put("zhuang_tai_2_", records.getZhuangTai2());
|
|
|
updateCond.put("jia_ban_shi_chang", String.valueOf(records.getJiaBanShiChang()));
|
|
updateCond.put("jia_ban_shi_chang", String.valueOf(records.getJiaBanShiChang()));
|
|
|
}
|
|
}
|
|
|
- // 工作时长(可能因配置改变而更新)
|
|
|
|
|
|
|
+ // 工作时长(总是更新,因为可能因修改而变化)
|
|
|
updateCond.put("gong_zuo_shi_chan", String.valueOf(records.getGongZuoShiChan()));
|
|
updateCond.put("gong_zuo_shi_chan", String.valueOf(records.getGongZuoShiChan()));
|
|
|
// 考勤状态
|
|
// 考勤状态
|
|
|
updateCond.put("kao_qin_zhuang_ta", records.getKaoQinZhuangTa());
|
|
updateCond.put("kao_qin_zhuang_ta", records.getKaoQinZhuangTa());
|
|
|
- //修改成功过的数据需要做一下标记
|
|
|
|
|
|
|
+ // 修改成功过的数据需要做一下标记
|
|
|
updateCond.put("tenant_id_", "999999");
|
|
updateCond.put("tenant_id_", "999999");
|
|
|
updateCond.put("update_time_", LocalDateTime.now().format(DATETIME_FORMATTER));
|
|
updateCond.put("update_time_", LocalDateTime.now().format(DATETIME_FORMATTER));
|
|
|
|
|
+
|
|
|
Map<String, String> whereCond = Collections.singletonMap("id_", id);
|
|
Map<String, String> whereCond = Collections.singletonMap("id_", id);
|
|
|
int updated = updateDataTableDao.updateDataTable("t_attendance_detail", updateCond, whereCond);
|
|
int updated = updateDataTableDao.updateDataTable("t_attendance_detail", updateCond, whereCond);
|
|
|
- LOGGER.info("记录{[]}updateCond-更新内容为:{}",po.getId(),updateCond);
|
|
|
|
|
|
|
+ LOGGER.info("记录{} updateCond-更新内容为:{}", po.getId(), updateCond);
|
|
|
if (updated != 1) {
|
|
if (updated != 1) {
|
|
|
throw new Exception("更新考勤记录失败");
|
|
throw new Exception("更新考勤记录失败");
|
|
|
}
|
|
}
|