|
|
@@ -4,7 +4,16 @@ package com.lc.ibps.components.employee.domain;
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
import com.lc.ibps.base.core.constants.StringPool;
|
|
|
+import com.lc.ibps.base.core.util.AppUtil;
|
|
|
import com.lc.ibps.base.web.context.ContextUtil;
|
|
|
+import com.lc.ibps.cloud.entity.APIResult;
|
|
|
+import com.lc.ibps.cloud.provider.GenericProvider;
|
|
|
+import com.lc.ibps.org.api.IPartyPositionService;
|
|
|
+import com.lc.ibps.org.party.persistence.entity.PartyPositionPo;
|
|
|
+import com.lc.ibps.sysdata.dao.UpdateDataTableDao;
|
|
|
+import oracle.sql.DATE;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.context.annotation.Scope;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
@@ -19,9 +28,15 @@ import com.lc.ibps.components.employee.repository.AttendanceDetailRepository;
|
|
|
import com.lc.ibps.components.employee.persistence.entity.AttendanceDetailPo;
|
|
|
|
|
|
import java.time.Duration;
|
|
|
+import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
|
|
|
/**
|
|
|
* 考勤明细表 领域对象实体
|
|
|
@@ -44,9 +59,13 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
private AttendanceDetailQueryDao attendanceDetailQueryDao;
|
|
|
@Resource
|
|
|
private AttendanceDetailRepository attendanceDetailRepository;
|
|
|
+ @Resource
|
|
|
+ private UpdateDataTableDao updateDataTableDao;
|
|
|
|
|
|
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATETIME);
|
|
|
-
|
|
|
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATE);
|
|
|
+ private static final String TIME_PATTERN = "HH:mm";
|
|
|
+ private static Logger LOGGER = LoggerFactory.getLogger(AttendanceDetail.class);
|
|
|
|
|
|
protected void init(){
|
|
|
//
|
|
|
@@ -132,5 +151,388 @@ public class AttendanceDetail extends AbstractDomain<String, AttendanceDetailPo>
|
|
|
delete(po.getId());
|
|
|
}
|
|
|
}
|
|
|
+ /**
|
|
|
+ * 改造后的打卡逻辑(供gdClockIn接口使用)
|
|
|
+ *
|
|
|
+ * @param po 传入参数,必须包含:yongHuId, buMen, paiBanMingChen, banCiMing
|
|
|
+ * @return 填充后的考勤记录对象
|
|
|
+ * @throws Exception 校验或业务异常
|
|
|
+ */
|
|
|
+ public AttendanceDetailPo gdClockInDeal(AttendanceDetailPo po) throws Exception {
|
|
|
+ // 1. 参数检查
|
|
|
+ if (po == null) {
|
|
|
+ throw new Exception("参数不能为空");
|
|
|
+ }
|
|
|
+ String yongHuId = po.getYongHuId();
|
|
|
+ String buMen = po.getBuMen();
|
|
|
+ String paiBanMingChen = po.getPaiBanMingChen();
|
|
|
+ String banCiMing = po.getBanCiMing();
|
|
|
+ if (BeanUtils.isEmpty(yongHuId)) {
|
|
|
+ throw new Exception("用户ID不能为空");
|
|
|
+ }
|
|
|
+ if (BeanUtils.isEmpty(buMen)) {
|
|
|
+ throw new Exception("考勤部门不能为空");
|
|
|
+ }
|
|
|
+ if (BeanUtils.isEmpty(paiBanMingChen)) {
|
|
|
+ throw new Exception("配置名称不能为空");
|
|
|
+ }
|
|
|
+ if (BeanUtils.isEmpty(banCiMing)) {
|
|
|
+ throw new Exception("考勤类型不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 查询用户信息(检查用户是否存在,并获取工号)
|
|
|
+ Map<String, String> userParam = new HashMap<>();
|
|
|
+ userParam.put("ID_", yongHuId);
|
|
|
+ List<Map<String, Object>> userList = updateDataTableDao.selectDataTable("ibps_party_employee", userParam, null);
|
|
|
+ if (BeanUtils.isEmpty(userList)) {
|
|
|
+ throw new Exception("用户不存在");
|
|
|
+ }
|
|
|
+ Map<String, Object> userMap = userList.get(0);
|
|
|
+ // 设置工号
|
|
|
+ if (userMap.containsKey("jian_ding_zi_ge_z")) {
|
|
|
+ po.setGongHao(String.valueOf(userMap.get("jian_ding_zi_ge_z")));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 查询考勤配置表 t_gdpbpzb
|
|
|
+ Map<String, String> configParam = new HashMap<>();
|
|
|
+ configParam.put("pai_ban_bu_men_", buMen);
|
|
|
+ configParam.put("pei_zhi_ming_chen", paiBanMingChen);
|
|
|
+ configParam.put("pai_ban_lei_xing_", banCiMing);
|
|
|
+ List<Map<String, Object>> configList = updateDataTableDao.selectDataTable("t_gdpbpzb", configParam, null);
|
|
|
+ if (BeanUtils.isEmpty(configList)) {
|
|
|
+ throw new Exception("考勤配置不存在");
|
|
|
+ }
|
|
|
+ if (configList.size() > 1) {
|
|
|
+ throw new Exception("考勤配置存在多条记录,请联系管理员");
|
|
|
+ }
|
|
|
+ Map<String, Object> configMap = configList.get(0);
|
|
|
+ if ("N".equals(configMap.get("sheng_xiao_biao_z"))) {
|
|
|
+ throw new Exception("考勤配置已失效");
|
|
|
+ }
|
|
|
+ // 获取配置字段
|
|
|
+ String shangBanShiJianPz = (String) configMap.get("shang_ban_shi_jia");
|
|
|
+ String xiaBanShiJianPz = (String) configMap.get("xia_ban_shi_jian_");
|
|
|
+ String shiFouKuaRiPz = (String) configMap.get("shi_fou_kua_ri_");
|
|
|
+
|
|
|
+ // 检查时间格式(配置那边也要同步这段配置)
|
|
|
+ try {
|
|
|
+ LocalTime.parse(shangBanShiJianPz, DateTimeFormatter.ofPattern(TIME_PATTERN));
|
|
|
+ LocalTime.parse(xiaBanShiJianPz, DateTimeFormatter.ofPattern(TIME_PATTERN));
|
|
|
+ } catch (DateTimeParseException e) {
|
|
|
+ throw new Exception("考勤配置中的时间格式不正确,应为HH:mm");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查跨日逻辑
|
|
|
+ if (xiaBanShiJianPz.compareTo(shangBanShiJianPz) <= 0 && "N".equals(shiFouKuaRiPz)) {
|
|
|
+ throw new Exception("非跨日配置中,下班时间不能小于等于上班时间");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 构建基础信息
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+ String riQi = today.format(DATE_FORMATTER);
|
|
|
+ po.setRiQi(riQi);//这个后续在统计考勤中有用到必须保证有值且格式为2026-03-18
|
|
|
+ po.setShiFouKuaRi(shiFouKuaRiPz);
|
|
|
+
|
|
|
+ // 构建班次开始时间
|
|
|
+ String banCiKaiShiStr = riQi + " " + shangBanShiJianPz + ":00";
|
|
|
+ LocalDateTime banCiKaiShi = LocalDateTime.parse(banCiKaiShiStr, DATETIME_FORMATTER);
|
|
|
+ po.setBanCiKaiShi(banCiKaiShi.format(DATETIME_FORMATTER));
|
|
|
+
|
|
|
+ // 构建班次结束时间(考虑跨日)
|
|
|
+ LocalDate endDate = today;
|
|
|
+ if ("Y".equals(shiFouKuaRiPz)) {
|
|
|
+ endDate = today.plusDays(1);
|
|
|
+ }
|
|
|
+ String banCiJieShuStr = endDate.format(DATE_FORMATTER) + " " + xiaBanShiJianPz + ":00";
|
|
|
+ LocalDateTime banCiJieShu = LocalDateTime.parse(banCiJieShuStr, DATETIME_FORMATTER);
|
|
|
+ po.setBanCiJieShu(banCiJieShu.format(DATETIME_FORMATTER));
|
|
|
+
|
|
|
+ // 计算班次时长(分钟)配置中开始结束时间一定在登记配置时做非空控制,这里就不做非空校验
|
|
|
+ long banCiShiChang = Duration.between(banCiKaiShi, banCiJieShu).toMinutes();
|
|
|
+ po.setBanCiShiChang(banCiShiChang);
|
|
|
+
|
|
|
+ // 设置其他字段(可从上下文获取)
|
|
|
+ po.setTenantId(ContextUtil.getCurrentTenantId());
|
|
|
+ po.setIp(ContextUtil.getCurrentUserIp());
|
|
|
+ String diDian ="";
|
|
|
+ IPartyPositionService partyPositionService = AppUtil.getBean(IPartyPositionService.class);
|
|
|
+ APIResult<List<PartyPositionPo>> result = partyPositionService.findByUserId(ContextUtil.getCurrentUserId());
|
|
|
+ try {
|
|
|
+ diDian = result.getData().get(0).getPath().split(StringPool.BACK_SLASH + StringPool.DOT)[1];
|
|
|
+ po.setDiDian(diDian);
|
|
|
+ }catch (Exception ex){
|
|
|
+ LOGGER.error("Can't get didian information",ex);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 5. 查询当天是否存在考勤记录
|
|
|
+ Map<String, String> recordParam = new HashMap<>();
|
|
|
+ recordParam.put("yong_hu_id_", yongHuId);
|
|
|
+ recordParam.put("bu_men_", buMen);
|
|
|
+ recordParam.put("pai_ban_ming_chen", paiBanMingChen);
|
|
|
+ recordParam.put("ban_ci_ming_", banCiMing);
|
|
|
+ recordParam.put("ri_qi_", riQi);
|
|
|
+ List<Map<String, Object>> recordList = updateDataTableDao.selectDataTable("t_attendance_detail", recordParam, "999");
|
|
|
+ LOGGER.info("记录{[]},recordParam查询当日考勤参数{}",po.getId(),recordParam);
|
|
|
+ AttendanceDetailPo existingPo = null;
|
|
|
+ if (!BeanUtils.isEmpty(recordList)) {
|
|
|
+ if (recordList.size() > 1) {
|
|
|
+ throw new Exception("考勤记录存在多条,请联系管理员排查");
|
|
|
+ }
|
|
|
+ // 更新对象,存储了用户当日打卡记录id
|
|
|
+ existingPo = new AttendanceDetailPo();
|
|
|
+ Map<String, Object> recordMap = recordList.get(0);
|
|
|
+ // 手动设置部分字段(保存没设置值会把原来的值置空,必须要旧值设置回去)
|
|
|
+ if (recordMap.containsKey("id_")) existingPo.setId((String) recordMap.get("id_"));
|
|
|
+ if (recordMap.containsKey("da_ka_shi_jian_1_")) existingPo.setDaKaShiJian1((String) recordMap.get("da_ka_shi_jian_1_"));
|
|
|
+ if (recordMap.containsKey("zhuang_tai_1_")) existingPo.setZhuangTai1((String) recordMap.get("zhuang_tai_1_"));
|
|
|
+ if (recordMap.containsKey("da_ka_shi_jian_2_")) existingPo.setDaKaShiJian2((String) recordMap.get("da_ka_shi_jian_2_"));
|
|
|
+ if (recordMap.containsKey("zhuang_tai_2_")) existingPo.setZhuangTai2((String) recordMap.get("zhuang_tai_2_"));
|
|
|
+ if (recordMap.containsKey("da_ka_ci_shu_")) existingPo.setDaKaCiShu(Long.valueOf(recordMap.get("da_ka_ci_shu_").toString()));
|
|
|
+ if (recordMap.containsKey("kao_qin_zhuang_ta")) existingPo.setKaoQinZhuangTa((String) recordMap.get("kao_qin_zhuang_ta"));
|
|
|
+ if (recordMap.containsKey("jia_ban_shi_chang")) existingPo.setJiaBanShiChang(Long.valueOf(recordMap.get("jia_ban_shi_chang").toString()));
|
|
|
+ if (recordMap.containsKey("chi_dao_shi_chang")) existingPo.setChiDaoShiChang(Long.valueOf(recordMap.get("chi_dao_shi_chang").toString()));
|
|
|
+ if (recordMap.containsKey("gong_zuo_shi_chan")) existingPo.setGongZuoShiChan(Long.valueOf(recordMap.get("gong_zuo_shi_chan").toString()));
|
|
|
+ if (recordMap.containsKey("ban_ci_shi_chang_")) existingPo.setBanCiShiChang(Long.valueOf(recordMap.get("ban_ci_shi_chang_").toString()));
|
|
|
+
|
|
|
+ //确定唯一一条记录的几个字段,因为是从原来逻辑改造的,不想动表结构,所以没有设置为唯一索引控制,只是通过业务逻辑控制唯一
|
|
|
+ if (recordMap.containsKey("ri_qi_")) existingPo.setRiQi((String) recordMap.get("ri_qi_"));
|
|
|
+ if (recordMap.containsKey("yong_hu_id_")) existingPo.setYongHuId((String) recordMap.get("yong_hu_id_"));
|
|
|
+ if (recordMap.containsKey("bu_men_")) existingPo.setBuMen((String) recordMap.get("bu_men_"));
|
|
|
+ if (recordMap.containsKey("pai_ban_ming_chen")) existingPo.setPaiBanMingChen((String) recordMap.get("pai_ban_ming_chen"));
|
|
|
+ if (recordMap.containsKey("ban_ci_ming_")) existingPo.setBanCiMing((String) recordMap.get("ban_ci_ming_"));
|
|
|
+ // 其他字段
|
|
|
+ if (recordMap.containsKey("tenant_id_")) existingPo.setTenantId((String) recordMap.get("tenant_id_"));
|
|
|
+ if (recordMap.containsKey("di_dian_")) existingPo.setDiDian((String) recordMap.get("di_dian_"));
|
|
|
+ if (recordMap.containsKey("shi_fou_kua_ri_")) existingPo.setShiFouKuaRi((String) recordMap.get("shi_fou_kua_ri_"));
|
|
|
+ if (recordMap.containsKey("ban_ci_kai_shi_")) existingPo.setBanCiKaiShi((String) recordMap.get("ban_ci_kai_shi_"));
|
|
|
+ if (recordMap.containsKey("ban_ci_jie_shu_")) existingPo.setBanCiJieShu((String) recordMap.get("ban_ci_jie_shu_"));
|
|
|
+ if (recordMap.containsKey("gong_hao_")) existingPo.setGongHao((String) recordMap.get("gong_hao_"));
|
|
|
+ if (recordMap.containsKey("ban_ci_bie_ming_")) existingPo.setBanCiBieMing((String) recordMap.get("ban_ci_bie_ming_"));
|
|
|
+ LOGGER.info("记录{[]}读取到的existingPo{}",po.getId(),existingPo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 判断是上班打卡还是下班打卡
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ long timeInterval = banCiShiChang / 2; // 班次时长的一半
|
|
|
+
|
|
|
+ boolean isMorningClock;
|
|
|
+ if (now.toLocalDate().equals(banCiKaiShi.toLocalDate()) &&
|
|
|
+ banCiKaiShi.plusMinutes(timeInterval).isAfter(now)) {
|
|
|
+ isMorningClock = true; // 上班打卡
|
|
|
+ } else {
|
|
|
+ isMorningClock = false; // 下班打卡
|
|
|
+ }
|
|
|
+ LOGGER.info("记录{[]}上班or下班打卡标志:{}",po.getId(),isMorningClock);
|
|
|
+ if (isMorningClock) {
|
|
|
+ // 上班打卡
|
|
|
+ if (existingPo != null) {
|
|
|
+ throw new Exception("上班已经打卡,无需重复打卡");
|
|
|
+ }
|
|
|
+ // 新增记录
|
|
|
+ po.setDaKaShiJian1(now.format(DATETIME_FORMATTER));
|
|
|
+ if (!now.isAfter(banCiKaiShi)) {
|
|
|
+ po.setZhuangTai1("正常");
|
|
|
+ } else {
|
|
|
+ po.setZhuangTai1("异常");
|
|
|
+ long chiDao = Duration.between(banCiKaiShi, now).toMinutes();
|
|
|
+ po.setChiDaoShiChang(chiDao);//迟到的时候计算迟到时长
|
|
|
+ }
|
|
|
+ // 如果上班异常,考勤状态先设为异常(下班打卡后会重新计算)
|
|
|
+ if ("异常".equals(po.getZhuangTai1())) {
|
|
|
+ po.setKaoQinZhuangTa("异常");
|
|
|
+ }
|
|
|
+ po.setDaKaCiShu(1L);
|
|
|
+ } else {
|
|
|
+ // 下班打卡
|
|
|
+ if (existingPo != null) {
|
|
|
+ // 更新现有记录
|
|
|
+ existingPo.setDaKaShiJian2(now.format(DATETIME_FORMATTER));
|
|
|
+ if (now.isAfter(banCiJieShu) || now.isEqual(banCiJieShu)) {
|
|
|
+ existingPo.setZhuangTai2("正常");
|
|
|
+ } else {
|
|
|
+ existingPo.setZhuangTai2("异常");
|
|
|
+ }
|
|
|
+ // 如果上班状态为空,设为异常
|
|
|
+ if (BeanUtils.isEmpty(existingPo.getZhuangTai1())) {
|
|
|
+ existingPo.setZhuangTai1("异常");
|
|
|
+ }
|
|
|
+ // 更新打卡次数
|
|
|
+ Long currentCount = existingPo.getDaKaCiShu();
|
|
|
+ existingPo.setDaKaCiShu(currentCount == null ? 1L : currentCount + 1);
|
|
|
+ // 计算工作时长(如果上下班时间都存在)
|
|
|
+ if (BeanUtils.isNotEmpty(existingPo.getDaKaShiJian1()) && BeanUtils.isNotEmpty(existingPo.getDaKaShiJian2())) {
|
|
|
+ LocalDateTime daka1 = LocalDateTime.parse(existingPo.getDaKaShiJian1(), DATETIME_FORMATTER);
|
|
|
+ LocalDateTime daka2 = LocalDateTime.parse(existingPo.getDaKaShiJian2(), DATETIME_FORMATTER);
|
|
|
+ long gongZuo = Duration.between(daka1, daka2).toMinutes();
|
|
|
+ existingPo.setGongZuoShiChan(gongZuo);
|
|
|
+ }
|
|
|
+ // 综合考勤状态
|
|
|
+ if ("正常".equals(existingPo.getZhuangTai1()) && "正常".equals(existingPo.getZhuangTai2())) {
|
|
|
+ existingPo.setKaoQinZhuangTa("正常");
|
|
|
+ } else {
|
|
|
+ existingPo.setKaoQinZhuangTa("异常");
|
|
|
+ }
|
|
|
+ // 将更新后的对象返回
|
|
|
+ return existingPo;
|
|
|
+ } else {
|
|
|
+ // 新增记录(只有下班打卡)
|
|
|
+ po.setDaKaShiJian2(now.format(DATETIME_FORMATTER));
|
|
|
+ if (now.isAfter(banCiJieShu) || now.isEqual(banCiJieShu)) {
|
|
|
+ po.setZhuangTai2("正常");
|
|
|
+ } else {
|
|
|
+ po.setZhuangTai2("异常");
|
|
|
+ }
|
|
|
+ po.setZhuangTai1("异常");
|
|
|
+ po.setDaKaCiShu(1L);
|
|
|
+ po.setKaoQinZhuangTa("异常");
|
|
|
+ //po中已经设置了班次信息,直接返回
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return po;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 改造后的修改考勤记录逻辑:
|
|
|
+ * 目前只允许修改上班时间和下班时间,考勤状态和其他统计工作自动根据打卡时间修改
|
|
|
+ * 1.上班迟到,修改上班打卡时间为正常后,上班考勤状态自动更新为正常,迟到时间清空,如果下班卡正常则考勤状态自动设置为正常
|
|
|
+ * 2.下班迟到,修改正常逻辑同上
|
|
|
+ * 3.上班和下班卡时间不允许修改为异常,即上班卡不能大于配置上班时间,下班卡不能小于配置时间(已经考虑过跨日情况)
|
|
|
+ * 4.考虑到配置可能被修改,每次修改打卡时间都会重新读取配置,重新计算工作时长并更新
|
|
|
+ * @param po 传入参数,必须包含:yongHuId, buMen, paiBanMingChen, banCiMing
|
|
|
+ * @return 填充后的考勤记录对象
|
|
|
+ * @throws Exception 校验或业务异常
|
|
|
+ */
|
|
|
+ public AttendanceDetailPo gdUpdateAttendce(AttendanceDetailPo po) throws Exception {
|
|
|
+ // 1. 参数校验
|
|
|
+ if (po == null || BeanUtils.isEmpty(po.getId())) {
|
|
|
+ throw new Exception("ID不能为空");
|
|
|
+ }
|
|
|
+ String id = po.getId();
|
|
|
+
|
|
|
+ // 2.查询考勤记录
|
|
|
+ AttendanceDetailPo records = attendanceDetailRepository.get(id);
|
|
|
+ if (BeanUtils.isEmpty(records)) {
|
|
|
+ throw new Exception("考勤记录不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 查询考勤配置
|
|
|
+ Map<String, String> configWhere = new HashMap<>();
|
|
|
+ configWhere.put("pai_ban_bu_men_", records.getBuMen());
|
|
|
+ configWhere.put("pei_zhi_ming_chen", records.getPaiBanMingChen());
|
|
|
+ configWhere.put("pai_ban_lei_xing_", records.getBanCiMing());
|
|
|
+ configWhere.put("sheng_xiao_biao_z", "Y");
|
|
|
+ List<Map<String, Object>> configList = updateDataTableDao.selectDataTable("t_gdpbpzb", configWhere, null);
|
|
|
+ if (BeanUtils.isEmpty(configList)) {
|
|
|
+ throw new Exception("未找到生效的考勤配置");
|
|
|
+ }
|
|
|
+ if (configList.size() > 1) {
|
|
|
+ throw new Exception("考勤配置存在多条记录,请联系管理员");
|
|
|
+ }
|
|
|
+ Map<String, Object> configMap = configList.get(0);
|
|
|
+ String shangBanShiJian = (String) configMap.get("shang_ban_shi_jia");
|
|
|
+ String xiaBanShiJian = (String) configMap.get("xia_ban_shi_jian_");
|
|
|
+ String shiFouKuaRi = (String) configMap.get("shi_fou_kua_ri_");
|
|
|
+
|
|
|
+ // 4. 解析日期
|
|
|
+ String riQi = records.getRiQi(); // 格式 yyyy-MM-dd
|
|
|
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATE);
|
|
|
+ LocalDate recordDate = LocalDate.parse(riQi, dateFormatter);
|
|
|
+
|
|
|
+ // 5. 处理传入的上班打卡时间
|
|
|
+ String newDaKa1 = po.getDaKaShiJian1();
|
|
|
+ LocalDateTime newDaKa1Time = null;
|
|
|
+ if (BeanUtils.isNotEmpty(newDaKa1)) {
|
|
|
+ newDaKa1Time = LocalDateTime.parse(newDaKa1, DATETIME_FORMATTER);
|
|
|
+ // 构建配置上班时间
|
|
|
+ LocalDateTime configStart = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
+ // 校验:配置上班时间不能小于传入时间(即传入时间不能晚于配置时间)
|
|
|
+ if (configStart.isBefore(newDaKa1Time)) {
|
|
|
+ throw new Exception("上班卡不允许修改比配置的上班时间晚");
|
|
|
+ }
|
|
|
+ // 更新上班打卡信息,考勤状态在后面修改,这里只修改打上班卡涉及的内容
|
|
|
+ records.setDaKaShiJian1(newDaKa1);
|
|
|
+ records.setZhuangTai1("正常");
|
|
|
+ records.setChiDaoShiChang(null); // 清空迟到时长
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 处理传入的下班打卡时间
|
|
|
+ String newDaKa2 = po.getDaKaShiJian2();
|
|
|
+ LocalDateTime newDaKa2Time = null;
|
|
|
+ LocalDateTime configEnd = null;
|
|
|
+ if (BeanUtils.isNotEmpty(newDaKa2)) {
|
|
|
+ newDaKa2Time = LocalDateTime.parse(newDaKa2, DATETIME_FORMATTER);
|
|
|
+ // 确定下班日期(是否跨日)
|
|
|
+ LocalDate endDate = recordDate;
|
|
|
+ if ("Y".equals(shiFouKuaRi)) {
|
|
|
+ endDate = recordDate.plusDays(1);
|
|
|
+ }
|
|
|
+ String endDateStr = endDate.format(dateFormatter);
|
|
|
+ configEnd = LocalDateTime.parse(endDateStr + " " + xiaBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
+ // 校验:配置下班时间不能大于传入时间(即传入时间不能早于配置时间)
|
|
|
+ if (configEnd.isAfter(newDaKa2Time)) {
|
|
|
+ throw new Exception("下班卡不允许修改的比配置的下班时间早");
|
|
|
+ }
|
|
|
+ // 更新下班打卡信息
|
|
|
+ records.setDaKaShiJian2(newDaKa2);
|
|
|
+ records.setZhuangTai2("正常");
|
|
|
+ // 计算加班时长
|
|
|
+ long jiaBan = Duration.between(configEnd, newDaKa2Time).toMinutes();
|
|
|
+ records.setJiaBanShiChang(jiaBan);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 计算工作时长(配置下班时间 - 配置上班时间)
|
|
|
+ // 注意:需要重新获取配置上下班时间的完整 LocalDateTime,因为可能跨日
|
|
|
+ LocalDateTime configStartFullTime = LocalDateTime.parse(riQi + " " + shangBanShiJian + ":00", DATETIME_FORMATTER);
|
|
|
+ LocalDate endDateForConfigTime = recordDate;//2026-03-26 17:30:00
|
|
|
+ if ("Y".equals(shiFouKuaRi)) {
|
|
|
+ endDateForConfigTime = recordDate.plusDays(1);
|
|
|
+ }
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 综合考勤状态
|
|
|
+ String zhuangTai1 = records.getZhuangTai1();
|
|
|
+ String zhuangTai2 = records.getZhuangTai2();
|
|
|
+ if ("正常".equals(zhuangTai1) && "正常".equals(zhuangTai2)) {//想了下,还是这样:只有都正常才处理否则不处理,统计那边考勤状态为空时会自动统计为异常天数
|
|
|
+ records.setKaoQinZhuangTa("正常");
|
|
|
+ }/* else if(BeanUtils.isEmpty(zhuangTai2)){//这里考虑的是还没到下班时间,用户就修改上班考勤,这时候下班状态是null
|
|
|
+ LOGGER.info("记录{[]}没到下班时间或者下班卡没打,考勤状态不做处理",po.getId());
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ records.setKaoQinZhuangTa("异常");
|
|
|
+ }*/
|
|
|
+
|
|
|
+ // 9. 构建需要更新的字段映射(只更新指定字段)
|
|
|
+ Map<String, String> updateCond = new HashMap<>();
|
|
|
+ // 上班相关
|
|
|
+ if (BeanUtils.isNotEmpty(newDaKa1)) {
|
|
|
+ updateCond.put("da_ka_shi_jian_1_", records.getDaKaShiJian1());
|
|
|
+ updateCond.put("zhuang_tai_1_", records.getZhuangTai1());
|
|
|
+ updateCond.put("chi_dao_shi_chang", null); // 清空
|
|
|
+ }
|
|
|
+ // 下班相关
|
|
|
+ if (BeanUtils.isNotEmpty(newDaKa2)) {
|
|
|
+ updateCond.put("da_ka_shi_jian_2_", records.getDaKaShiJian2());
|
|
|
+ updateCond.put("zhuang_tai_2_", records.getZhuangTai2());
|
|
|
+ updateCond.put("jia_ban_shi_chang", String.valueOf(records.getJiaBanShiChang()));
|
|
|
+ }
|
|
|
+ // 工作时长(可能因配置改变而更新)
|
|
|
+ updateCond.put("gong_zuo_shi_chan", String.valueOf(records.getGongZuoShiChan()));
|
|
|
+ // 考勤状态
|
|
|
+ updateCond.put("kao_qin_zhuang_ta", records.getKaoQinZhuangTa());
|
|
|
+ //修改成功过的数据需要做一下标记
|
|
|
+ updateCond.put("tenant_id_", "999999");
|
|
|
+ updateCond.put("update_time_", LocalDateTime.now().format(DATETIME_FORMATTER));
|
|
|
+ Map<String, String> whereCond = Collections.singletonMap("id_", id);
|
|
|
+ int updated = updateDataTableDao.updateDataTable("t_attendance_detail", updateCond, whereCond);
|
|
|
+ LOGGER.info("记录{[]}updateCond-更新内容为:{}",po.getId(),updateCond);
|
|
|
+ if (updated != 1) {
|
|
|
+ throw new Exception("更新考勤记录失败");
|
|
|
+ }
|
|
|
+ return records;
|
|
|
+ }
|
|
|
|
|
|
}
|