Sfoglia il codice sorgente

港大 考勤排班改造 打卡和修改接口提交

xiexh 3 settimane fa
parent
commit
4a3ff43296

+ 17 - 0
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/employee/api/IAttendanceDetailService.java

@@ -89,4 +89,21 @@ public interface IAttendanceDetailService {
 	@RequestMapping(value = "/clockIn", method = { RequestMethod.POST })
 	public APIResult<AttendanceDetailPo> clockIn(
 			@RequestParam(name = "id", required = true) String id);
+
+	/**
+	 * 港大打卡
+	 * @param
+	 * @return
+	 */
+	@RequestMapping(value = "/gdClockIn", method = { RequestMethod.POST })
+	public APIResult<AttendanceDetailPo> gdClockIn(
+			@RequestBody AttendanceDetailPo po);
+	/**
+	 * 港大打卡
+	 * @param
+	 * @return
+	 */
+	@RequestMapping(value = "/gdUpdateAttendce", method = { RequestMethod.POST })
+	public APIResult<AttendanceDetailPo> gdUpdateAttendce(
+			@RequestBody AttendanceDetailPo po);
 }

+ 403 - 1
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/employee/domain/AttendanceDetail.java

@@ -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;
+	}
 	
 }

+ 34 - 1
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/components/employee/provider/AttendanceDetailProvider.java

@@ -43,6 +43,8 @@ public class AttendanceDetailProvider extends GenericProvider implements IAttend
 
 	@Resource
 	private AttendanceDetailRepository attendanceDetailRepository;
+	@Resource
+	private AttendanceDetail attendanceDetail;
 
 	@ApiOperation(value = "考勤明细表列表(分页条件查询)数据", notes = "考勤明细表列表(分页条件查询)数据")
 	@Override
@@ -136,6 +138,37 @@ public class AttendanceDetailProvider extends GenericProvider implements IAttend
 		return result;
 	}
 
+	@Override
+	public APIResult<AttendanceDetailPo> gdClockIn(AttendanceDetailPo po) {
+		APIResult<AttendanceDetailPo> result = new APIResult<AttendanceDetailPo>();
+		try {
+			logger.info(" com.lc.ibps.components.provider.AttendanceDetailProvider.gdClockIn()--->attendanceDetailPo: {}", po.toString());
+			//打卡逻辑
+			AttendanceDetailPo data = attendanceDetail.gdClockInDeal(po);
+			//调用保存接口
+			AttendanceDetail domain = attendanceDetailRepository.newInstance(data);
+			domain.save();
+			result.setMessage("打卡成功");
+		} catch (Exception e) {
+			result.setMessage("打卡失败");
+			setExceptionResult(result, StateEnum.ERROR.getCode(), StateEnum.ERROR.getText(), e);
+		}
+		return result;
+	}
+
+	@Override
+	public APIResult<AttendanceDetailPo> gdUpdateAttendce(AttendanceDetailPo po) {
+		APIResult<AttendanceDetailPo> result = new APIResult<AttendanceDetailPo>();
+		try {
+			logger.info(" com.lc.ibps.components.provider.AttendanceDetailProvider.gdUpdateAttendce()--->attendanceDetailPo: {}", po.toString());
+			//修改
+			AttendanceDetailPo data = attendanceDetail.gdUpdateAttendce(po);
+			result.setMessage("更新考勤打卡时间成功");
+		} catch (Exception e) {
+			result.setMessage("更新考勤打卡时间失败");
+			setExceptionResult(result, StateEnum.ERROR.getCode(), StateEnum.ERROR.getText(), e);
+		}
+		return null;
+	}
 
-	
 }

+ 12 - 3
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/sysdata/controller/GangDaPaiBanController.java

@@ -66,7 +66,7 @@ public class GangDaPaiBanController extends GenericProvider {
 
     /**
      * 批量删除
-     * @param ids ID列表(逗号分隔
+     * @param  map(string,ids)
      * @return 操作结果
      */
     @PostMapping("/delete")
@@ -90,11 +90,13 @@ public class GangDaPaiBanController extends GenericProvider {
      * @return 操作结果
      */
     @PostMapping("/update")
-    public void update(@RequestBody GangDaPaiBanPo po) {
+    public void update(@RequestBody GangDaPaiBanPo po) throws Exception {
+        //增加校验
+        service.checkParam(po,"U");
         service.updateSelective(po);
     }
 
-    /**撒旦阿萨的撒旦阿斯顿撒旦阿萨打赏asd
+    /**
      * 批量插入配置
      * @param list 配置对象列表
      * @return 操作结果(包含插入行数)
@@ -103,6 +105,13 @@ public class GangDaPaiBanController extends GenericProvider {
     public APIResult<Integer> insertBatch(@RequestBody List<GangDaPaiBanPo> list) {
         APIResult<Integer> result = new APIResult<>();
         try {
+            //增加校验
+            if (list == null || list.isEmpty()) {
+                throw new Exception("上送参数配置不允许为空");
+            }
+            for (GangDaPaiBanPo po : list) {
+                service.checkParam(po,"I"); // 复用单个对象的校验
+            }
             int rows = service.insertBatch(list);
             result.setData(rows);
             result.setMessage("成功插入"+rows+"条数据");

+ 8 - 1
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/sysdata/dao/GangDaPaiBanDao.java

@@ -11,7 +11,7 @@ import java.util.Map;
 public interface GangDaPaiBanDao {
 
     /**
-     * 根据部门查询所有配置
+     * 根据部门查询所有配置 分页
      * @param  入参
      * @return 配置列表
      */
@@ -50,4 +50,11 @@ public interface GangDaPaiBanDao {
      * @return 插入行数
      */
     int insertBatch(@Param("list") List<GangDaPaiBanPo> list);
+
+    /**
+     * 根据部门,ID等参数查询所有配置
+     * @param  入参
+     * @return 配置列表
+     */
+    List<GangDaPaiBanPo> selectInfo(@Param("map") Map<String,Object> map);
 }

+ 16 - 0
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/sysdata/entity/GangDaPaiBanPo.java

@@ -30,8 +30,24 @@ public class GangDaPaiBanPo {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date jieShuShiJian;           // 结束时间
     private String paiBanBuMen;           // 排班部门
+    private String shangBanShiJian;        // 上班时间
+    private String xiaBanShiJian;           // 下班时间
+    private String shiFouKuaRi;           //是否跨日
+    private String shengXiaoBiaoZhi;      //生效标志
 
     // Getters and Setters
+    public String getShengXiaoBiaoZhi() { return shengXiaoBiaoZhi; }
+    public void setShengXiaoBiaoZhi(String shengXiaoBiaoZhi) { this.shengXiaoBiaoZhi = shengXiaoBiaoZhi; }
+
+    public String getShangBanShiJian() { return shangBanShiJian; }
+    public void setShangBanShiJian(String shangBanShiJian) { this.shangBanShiJian = shangBanShiJian; }
+
+    public String getXiaBanShiJian() { return xiaBanShiJian; }
+    public void setXiaBanShiJian(String xiaBanShiJian) { this.xiaBanShiJian = xiaBanShiJian; }
+
+    public String getShiFouKuaRi() { return shiFouKuaRi; }
+    public void setShiFouKuaRi(String shiFouKuaRi) { this.shiFouKuaRi = shiFouKuaRi; }
+
     public String getId() { return id; }
     public void setId(String id) { this.id = id; }
 

+ 4 - 0
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/sysdata/services/GangDaPaiBanService.java

@@ -36,4 +36,8 @@ public interface GangDaPaiBanService {
 
 
     int insertBatch(List<GangDaPaiBanPo> list);
+
+    void checkParam(GangDaPaiBanPo po,String flag) throws Exception;
+
+    List<GangDaPaiBanPo> selectInfo(Map map);
 }

+ 111 - 1
ibps-provider-root/modules/provider-business/src/main/java/com/lc/ibps/sysdata/services/impl/GangDaPaiBanServiceImpl.java

@@ -11,12 +11,16 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.*;
 
 @Service
 @Slf4j
 public class GangDaPaiBanServiceImpl extends GenericProvider implements GangDaPaiBanService {
 
+    private static final String TIME_PATTERN = "HH:mm";
     @Autowired
     private GangDaPaiBanDao mapper;
 
@@ -24,7 +28,10 @@ public class GangDaPaiBanServiceImpl extends GenericProvider implements GangDaPa
     public List<GangDaPaiBanPo> queryByBuMen(Map map) {
         return mapper.selectListByBuMen(map);
     }
-
+    @Override
+    public List<GangDaPaiBanPo> selectInfo(Map map) {
+        return mapper.selectInfo(map);
+    }
     @Override
     @Transactional
     public Integer deleteByIds(List<String> ids) {
@@ -85,6 +92,12 @@ public class GangDaPaiBanServiceImpl extends GenericProvider implements GangDaPa
                 if(BeanUtils.isNotEmpty(param.get("peiZhiMingChen"))){
                     stringObjectHashMap.put("peiZhiMingChen",param.get("peiZhiMingChen"));
                 }
+                if(BeanUtils.isNotEmpty(param.get("id"))){
+                    stringObjectHashMap.put("id",param.get("id"));
+                }
+                if(BeanUtils.isNotEmpty(param.get("shengXiaoBiaoZhi"))){
+                    stringObjectHashMap.put("shengXiaoBiaoZhi",param.get("shengXiaoBiaoZhi"));
+                }
             }
         }
         return stringObjectHashMap;
@@ -132,4 +145,101 @@ public class GangDaPaiBanServiceImpl extends GenericProvider implements GangDaPa
         return mapper.insertBatch(list);
     }
 
+    @Override
+    public void checkParam(GangDaPaiBanPo po,String flag) throws Exception {
+        // 1. 参数检查
+        if (po == null) {
+            throw new Exception("参数不能为空");
+        }
+
+        //String bianZhiBuMen = po.getBianZhiBuMen();
+        String paiBanBuMen=po.getPaiBanBuMen();
+        String peiZhiMingChen = po.getPeiZhiMingChen();
+        String paiBanLeiXing = po.getPaiBanLeiXing();
+        String shiFouKuaRi = po.getShiFouKuaRi();
+        String shangBanShiJian = po.getShangBanShiJian();
+        String xiaBanShiJian = po.getXiaBanShiJian();
+        String shengXiaoBiaoZhi = po.getShengXiaoBiaoZhi();
+        String id = po.getId();
+
+        if("U".equals(flag)){//更新操作检查
+            if (BeanUtils.isEmpty(id)) {
+                throw new Exception("上送id不能为空");
+            }
+            Map<String, Object> param = new HashMap<>();
+            Map<String, Object> paramId = new HashMap<>();
+
+            paramId.put("id",id);
+            List<GangDaPaiBanPo> RecordByIdGet = selectInfo(paramId);
+            if(RecordByIdGet == null || RecordByIdGet.isEmpty()){
+                throw new Exception("查询配置不存在");
+            }else{
+                //配置存在就需要确定修改后的配置是否已经存在,如果存在则报错
+                param.put("buMen",paiBanBuMen);
+                param.put("paiBanLeiXing",paiBanLeiXing);
+                param.put("peiZhiMingChen",peiZhiMingChen);//这三个参数是唯一索引,如果后续有改造需要同步修改
+                List<GangDaPaiBanPo> data = selectInfo(param);
+                if (data != null && !data.isEmpty() &&
+                        !data.get(0).getId().equals(id)) {//这个是为了避免前端直接上送旧数据导致判断错误
+                    throw new Exception("配置已经存在,不允许修改");
+                }
+            }
+            //校验时间 上班时间和下班时间一般不会同时修改,所以需要取出数据库中和是否跨日一起校验
+            shiFouKuaRi = BeanUtil.isNotEmpty(po.getShiFouKuaRi())?po.getShiFouKuaRi():RecordByIdGet.get(0).getShiFouKuaRi();
+            shangBanShiJian = BeanUtil.isNotEmpty(po.getShangBanShiJian())?po.getShangBanShiJian():RecordByIdGet.get(0).getShangBanShiJian();
+            xiaBanShiJian = BeanUtil.isNotEmpty(po.getXiaBanShiJian())?po.getXiaBanShiJian():RecordByIdGet.get(0).getXiaBanShiJian();
+            checkTime(shiFouKuaRi,shangBanShiJian,xiaBanShiJian);
+            return ;
+        }
+        if (BeanUtils.isEmpty(paiBanBuMen)) {
+            throw new Exception("排班部门不能为空");
+        }
+        if (BeanUtils.isEmpty(peiZhiMingChen)) {
+            throw new Exception("配置名称不能为空");
+        }
+        if (BeanUtils.isEmpty(paiBanLeiXing)) {
+            throw new Exception("排班类型不能为空");
+        }
+        if (BeanUtils.isEmpty(shiFouKuaRi)) {
+            throw new Exception("是否跨日不能为空");
+        }
+        if (BeanUtils.isEmpty(shangBanShiJian)) {
+            throw new Exception("上班时间不能为空");
+        }
+        if (BeanUtils.isEmpty(xiaBanShiJian)) {
+            throw new Exception("下班时间不能为空");
+        }
+        if (BeanUtils.isEmpty(shengXiaoBiaoZhi)) {//给默认值
+            po.setShengXiaoBiaoZhi("Y");
+        }
+        po.setCreateTime(new Date());
+        //po.setBianZhiShiJian();
+        checkTime(shiFouKuaRi,shangBanShiJian,xiaBanShiJian);
+    }
+
+
+    private void checkTime(String shiFouKuaRi, String shangBanShiJian, String xiaBanShiJian) throws Exception{
+        if(BeanUtils.isEmpty(shiFouKuaRi)
+                ||BeanUtils.isEmpty(shangBanShiJian)
+                ||BeanUtils.isEmpty(xiaBanShiJian)){
+            return ;
+        }
+        // 检查时间格式
+        try {
+            LocalTime.parse(shangBanShiJian, DateTimeFormatter.ofPattern(TIME_PATTERN));
+            LocalTime.parse(xiaBanShiJian, DateTimeFormatter.ofPattern(TIME_PATTERN));
+        } catch (DateTimeParseException e) {
+            throw new Exception("排班配置中的时间格式不正确,应为HH:mm");
+        }
+
+        // 检查跨日逻辑(暂时先不控制一个班次最长时间,默认是最大24小时,等客户有提再说)
+        if (xiaBanShiJian.compareTo(shangBanShiJian) <= 0 && "N".equals(shiFouKuaRi)) {
+            throw new Exception("非跨日配置中,下班时间不能小于等于上班时间");
+        }
+        if (xiaBanShiJian.compareTo(shangBanShiJian) >= 0 && "Y".equals(shiFouKuaRi)) {
+            throw new Exception("跨日配置中,下班时间不能大于等于上班时间");
+        }
+    }
+
+
 }

+ 47 - 5
ibps-provider-root/modules/provider-business/src/main/resources/com/lc/ibps/klimsibps/mapping/GangDaPaiBanMapper.xml

@@ -23,6 +23,10 @@
         <result column="kai_shi_shi_jian_" property="kaiShiShiJian" jdbcType="TIMESTAMP"/>
         <result column="jie_shu_shi_jian_" property="jieShuShiJian" jdbcType="TIMESTAMP"/>
         <result column="pai_ban_bu_men_" property="paiBanBuMen" jdbcType="VARCHAR"/>
+        <result column="shi_fou_kua_ri_" property="shiFouKuaRi" jdbcType="VARCHAR"/>
+        <result column="shang_ban_shi_jia" property="shangBanShiJian" jdbcType="VARCHAR"/>
+        <result column="xia_ban_shi_jian_" property="xiaBanShiJian" jdbcType="VARCHAR"/>
+        <result column="sheng_xiao_biao_z" property="shengXiaoBiaoZhi" jdbcType="VARCHAR"/>
     </resultMap>
 
     <!-- 通用查询列 -->
@@ -30,7 +34,7 @@
         id_, tenant_id_, ip_, create_by_, create_time_, update_by_, update_time_,
         shi_fou_guo_shen_, di_dian_, bian_zhi_ren_, bian_zhi_bu_men_, bian_zhi_shi_jian,
         kuai_zhao_, pei_zhi_ming_chen, pai_ban_lei_xing_, pai_ban_ren_yuan_,
-        kai_shi_shi_jian_, jie_shu_shi_jian_, pai_ban_bu_men_
+        kai_shi_shi_jian_, jie_shu_shi_jian_, pai_ban_bu_men_, shi_fou_kua_ri_, shang_ban_shi_jia, xia_ban_shi_jian_,sheng_xiao_biao_z
     </sql>
 
     <!-- 根据部门,配置名称,排班类型等查询 -->
@@ -46,6 +50,12 @@
         <if test="@o.Ognl@isNotEmpty(map.peiZhiMingChen)">
             AND pei_zhi_ming_chen like CONCAT('%', #{map.peiZhiMingChen}, '%')
         </if>
+        <if test="@o.Ognl@isNotEmpty(map.id)">
+            AND id_ = #{map.id}
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.shengXiaoBiaoZhi)">
+            AND sheng_xiao_biao_z = #{map.shengXiaoBiaoZhi}
+        </if>
         order by create_time_ desc
         limit #{map.startPage},#{map.limit}
     </select>
@@ -62,6 +72,12 @@
         <if test="@o.Ognl@isNotEmpty(map.peiZhiMingChen)">
             AND pei_zhi_ming_chen like CONCAT('%', #{map.peiZhiMingChen}, '%')
         </if>
+        <if test="@o.Ognl@isNotEmpty(map.id)">
+            AND id_ = #{map.id}
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.shengXiaoBiaoZhi)">
+            AND sheng_xiao_biao_z = #{map.shengXiaoBiaoZhi}
+        </if>
         order by create_time_ desc
     </select>
 
@@ -104,6 +120,10 @@
             <if test="updateTime != null">update_time_ = #{updateTime},</if>
             <if test="kaiShiShiJian != null">kai_shi_shi_jian_ = #{kaiShiShiJian},</if>
             <if test="jieShuShiJian != null">jie_shu_shi_jian_ = #{jieShuShiJian},</if>
+            <if test="shiFouKuaRi != null">shi_fou_kua_ri_ = #{shiFouKuaRi},</if>
+            <if test="shangBanShiJian != null">shang_ban_shi_jia = #{shangBanShiJian},</if>
+            <if test="xiaBanShiJian != null">xia_ban_shi_jian_ = #{xiaBanShiJian},</if>
+            <if test="shengXiaoBiaoZhi != null">sheng_xiao_biao_z = #{shengXiaoBiaoZhi},</if>
         </set>
         WHERE id_ = #{id}
     </update>
@@ -111,9 +131,9 @@
     <insert id="insertBatch" parameterType="list">
         INSERT INTO t_gdpbpzb (
         id_, tenant_id_, ip_, create_by_, create_time_, update_by_, update_time_,
-        shi_fou_guo_shen_, di_dian_, bian_zhi_ren_, bian_zhi_bu_men_, bian_zhi_shi_jian,
-        kuai_zhao_, pei_zhi_ming_chen, pai_ban_lei_xing_, pai_ban_ren_yuan_,
-        kai_shi_shi_jian_, jie_shu_shi_jian_, pai_ban_bu_men_
+        shi_fou_guo_shen_, di_dian_, bian_zhi_ren_, bian_zhi_bu_men_, bian_zhi_shi_jian,kuai_zhao_,
+        pei_zhi_ming_chen, pai_ban_lei_xing_, pai_ban_ren_yuan_,kai_shi_shi_jian_, jie_shu_shi_jian_,
+        pai_ban_bu_men_, shi_fou_kua_ri_, shang_ban_shi_jia, xia_ban_shi_jian_, sheng_xiao_biao_z
         ) VALUES
         <foreach collection="list" item="item" separator=",">
             (
@@ -122,8 +142,30 @@
             #{item.bianZhiRen}, #{item.bianZhiBuMen}, #{item.bianZhiShiJian},
             #{item.kuaiZhao}, #{item.peiZhiMingChen}, #{item.paiBanLeiXing},
             #{item.paiBanRenYuan}, #{item.kaiShiShiJian}, #{item.jieShuShiJian},
-            #{item.paiBanBuMen}
+            #{item.paiBanBuMen}, #{item.shiFouKuaRi}, #{item.shangBanShiJian},
+            #{item.xiaBanShiJian}, #{item.shengXiaoBiaoZhi}
             )
         </foreach>
     </insert>
+    <!-- 根据部门,配置名称,排班类型等查询 -->
+    <select id="selectInfo" resultMap="BaseResultMap" parameterType="java.util.Map">
+        SELECT <include refid="Base_Column_List"/>
+        FROM t_gdpbpzb WHERE 1=1
+        <if test="@o.Ognl@isNotEmpty(map.buMen)">
+            AND pai_ban_bu_men_ like CONCAT('%', #{map.buMen}, '%')
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.paiBanLeiXing)">
+            AND pai_ban_lei_xing_ like CONCAT('%', #{map.paiBanLeiXing}, '%')
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.peiZhiMingChen)">
+            AND pei_zhi_ming_chen like CONCAT('%', #{map.peiZhiMingChen}, '%')
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.id)">
+            AND id_ = #{map.id}
+        </if>
+        <if test="@o.Ognl@isNotEmpty(map.shengXiaoBiaoZhi)">
+            AND sheng_xiao_biao_z = #{map.shengXiaoBiaoZhi}
+        </if>
+        order by create_time_ desc
+    </select>
 </mapper>