diff --git a/klp-wms/src/main/java/com/klp/controller/WmsAttendanceCheckController.java b/klp-wms/src/main/java/com/klp/controller/WmsAttendanceCheckController.java new file mode 100644 index 00000000..bce2b9b0 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/controller/WmsAttendanceCheckController.java @@ -0,0 +1,58 @@ +package com.klp.controller; + +import com.klp.common.annotation.Log; +import com.klp.common.core.controller.BaseController; +import com.klp.common.core.domain.PageQuery; +import com.klp.common.core.domain.R; +import com.klp.common.core.page.TableDataInfo; +import com.klp.common.enums.BusinessType; +import com.klp.domain.bo.AttendanceCheckBo; +import com.klp.domain.bo.WmsAttendanceCheckBo; +import com.klp.domain.vo.WmsAttendanceCheckVo; +import com.klp.service.IWmsAttendanceCheckService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotEmpty; +import java.util.Arrays; +import java.util.List; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/wms/attendanceCheck") +public class WmsAttendanceCheckController extends BaseController { + + private final IWmsAttendanceCheckService iWmsAttendanceCheckService; + + @GetMapping("/list") + public TableDataInfo list(WmsAttendanceCheckBo bo, PageQuery pageQuery) { + return iWmsAttendanceCheckService.queryPageList(bo, pageQuery); + } + + @GetMapping("/{checkId}") + public R getInfo(@PathVariable Long checkId) { + return R.ok(iWmsAttendanceCheckService.queryById(checkId)); + } + + @Log(title = "考勤比对", businessType = BusinessType.DELETE) + @DeleteMapping("/{checkIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] checkIds) { + return toAjax(iWmsAttendanceCheckService.deleteWithValidByIds(Arrays.asList(checkIds), true)); + } + + @Log(title = "考勤比对", businessType = BusinessType.INSERT) + @PostMapping("/check") + public R checkAttendance(@Validated @RequestBody AttendanceCheckBo bo) { + iWmsAttendanceCheckService.checkAttendance(bo); + return R.ok(); + } +} diff --git a/klp-wms/src/main/java/com/klp/domain/WmsAttendanceCheck.java b/klp-wms/src/main/java/com/klp/domain/WmsAttendanceCheck.java new file mode 100644 index 00000000..4a7e58d1 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/WmsAttendanceCheck.java @@ -0,0 +1,54 @@ +package com.klp.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.klp.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wms_attendance_check") +public class WmsAttendanceCheck extends BaseEntity { + + @TableId + private Long checkId; + private Long scheduleId; + private Long userId; + private String employeeName; + private Date workDate; + private Long shiftId; + private String shiftName; + private String shiftType; + + private Date p1StartTime; + private Date p1EndTime; + private Date p1FirstCheck; + private Date p1LastCheck; + private Integer p1LateMinutes; + private Integer p1EarlyMinutes; + private String p1Status; + private BigDecimal p1Deduct; + + private Date p2StartTime; + private Date p2EndTime; + private Date p2FirstCheck; + private Date p2LastCheck; + private Integer p2LateMinutes; + private Integer p2EarlyMinutes; + private String p2Status; + private BigDecimal p2Deduct; + + private String absentType; + private Integer continuousAbsentDays; + private BigDecimal totalDeduct; + private String overallStatus; + private String remark; + + @TableLogic + private Integer delFlag; +} diff --git a/klp-wms/src/main/java/com/klp/domain/bo/AttendanceCheckBo.java b/klp-wms/src/main/java/com/klp/domain/bo/AttendanceCheckBo.java new file mode 100644 index 00000000..7837ce1d --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/bo/AttendanceCheckBo.java @@ -0,0 +1,19 @@ +package com.klp.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +@Data +public class AttendanceCheckBo { + + @NotNull(message = "开始日期不能为空") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + @NotNull(message = "结束日期不能为空") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; +} diff --git a/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceCheckBo.java b/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceCheckBo.java new file mode 100644 index 00000000..5e3a95fb --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/bo/WmsAttendanceCheckBo.java @@ -0,0 +1,20 @@ +package com.klp.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class WmsAttendanceCheckBo { + + private Long userId; + private String employeeName; + private Long shiftId; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; +} diff --git a/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceCheckVo.java b/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceCheckVo.java new file mode 100644 index 00000000..e9adc685 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/domain/vo/WmsAttendanceCheckVo.java @@ -0,0 +1,71 @@ +package com.klp.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@ExcelIgnoreUnannotated +public class WmsAttendanceCheckVo { + + private Long checkId; + private Long scheduleId; + private Long userId; + + @ExcelProperty(value = "员工姓名") + private String employeeName; + + @JsonFormat(pattern = "yyyy-MM-dd") + @ExcelProperty(value = "日期") + private Date workDate; + + @ExcelProperty(value = "班次") + private String shiftName; + + @JsonFormat(pattern = "HH:mm") + private Date p1StartTime; + @JsonFormat(pattern = "HH:mm") + private Date p1EndTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private Date p1FirstCheck; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private Date p1LastCheck; + @ExcelProperty(value = "上午迟到(分钟)") + private Integer p1LateMinutes; + @ExcelProperty(value = "上午早退(分钟)") + private Integer p1EarlyMinutes; + @ExcelProperty(value = "上午状态") + private String p1Status; + @ExcelProperty(value = "上午扣款") + private BigDecimal p1Deduct; + + @JsonFormat(pattern = "HH:mm") + private Date p2StartTime; + @JsonFormat(pattern = "HH:mm") + private Date p2EndTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private Date p2FirstCheck; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm") + private Date p2LastCheck; + @ExcelProperty(value = "下午迟到(分钟)") + private Integer p2LateMinutes; + @ExcelProperty(value = "下午早退(分钟)") + private Integer p2EarlyMinutes; + @ExcelProperty(value = "下午状态") + private String p2Status; + @ExcelProperty(value = "下午扣款") + private BigDecimal p2Deduct; + + @ExcelProperty(value = "旷工类型") + private String absentType; + @ExcelProperty(value = "连续旷工天数") + private Integer continuousAbsentDays; + @ExcelProperty(value = "总扣款") + private BigDecimal totalDeduct; + @ExcelProperty(value = "总体状态") + private String overallStatus; +} diff --git a/klp-wms/src/main/java/com/klp/mapper/WmsAttendanceCheckMapper.java b/klp-wms/src/main/java/com/klp/mapper/WmsAttendanceCheckMapper.java new file mode 100644 index 00000000..ee3ae402 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/mapper/WmsAttendanceCheckMapper.java @@ -0,0 +1,8 @@ +package com.klp.mapper; + +import com.klp.common.core.mapper.BaseMapperPlus; +import com.klp.domain.WmsAttendanceCheck; +import com.klp.domain.vo.WmsAttendanceCheckVo; + +public interface WmsAttendanceCheckMapper extends BaseMapperPlus { +} diff --git a/klp-wms/src/main/java/com/klp/service/IWmsAttendanceCheckService.java b/klp-wms/src/main/java/com/klp/service/IWmsAttendanceCheckService.java new file mode 100644 index 00000000..f392c3f2 --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/IWmsAttendanceCheckService.java @@ -0,0 +1,23 @@ +package com.klp.service; + +import com.klp.common.core.page.TableDataInfo; +import com.klp.common.core.domain.PageQuery; +import com.klp.domain.bo.AttendanceCheckBo; +import com.klp.domain.bo.WmsAttendanceCheckBo; +import com.klp.domain.vo.WmsAttendanceCheckVo; + +import java.util.Collection; +import java.util.List; + +public interface IWmsAttendanceCheckService { + + WmsAttendanceCheckVo queryById(Long checkId); + + TableDataInfo queryPageList(WmsAttendanceCheckBo bo, PageQuery pageQuery); + + List queryList(WmsAttendanceCheckBo bo); + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + void checkAttendance(AttendanceCheckBo bo); +} diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java new file mode 100644 index 00000000..5c48a5ca --- /dev/null +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java @@ -0,0 +1,398 @@ +package com.klp.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.klp.common.core.domain.PageQuery; +import com.klp.common.core.page.TableDataInfo; +import com.klp.common.utils.StringUtils; +import com.klp.domain.AttendanceRecords; +import com.klp.domain.WmsAttendanceCheck; +import com.klp.domain.WmsAttendanceRule; +import com.klp.domain.bo.AttendanceCheckBo; +import com.klp.domain.bo.AttendanceRecordsBo; +import com.klp.domain.bo.WmsAttendanceCheckBo; +import com.klp.domain.bo.WmsAttendanceScheduleBo; +import com.klp.domain.vo.AttendanceRecordsVo; +import com.klp.domain.vo.WmsAttendanceCheckVo; +import com.klp.domain.vo.WmsAttendanceScheduleVo; +import com.klp.mapper.WmsAttendanceCheckMapper; +import com.klp.mapper.WmsAttendanceRuleMapper; +import com.klp.service.IAttendanceRecordsService; +import com.klp.service.IWmsAttendanceCheckService; +import com.klp.service.IWmsAttendanceScheduleService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService { + + private final WmsAttendanceCheckMapper baseMapper; + private final IWmsAttendanceScheduleService scheduleService; + private final IAttendanceRecordsService attendanceRecordsService; + private final WmsAttendanceRuleMapper ruleMapper; + + @Override + public WmsAttendanceCheckVo queryById(Long checkId) { + return baseMapper.selectVoById(checkId); + } + + @Override + public TableDataInfo queryPageList(WmsAttendanceCheckBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(WmsAttendanceCheckBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + return baseMapper.deleteBatchIds(ids) > 0; + } + + private LambdaQueryWrapper buildQueryWrapper(WmsAttendanceCheckBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getUserId() != null, WmsAttendanceCheck::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getEmployeeName()), WmsAttendanceCheck::getEmployeeName, bo.getEmployeeName()); + lqw.eq(bo.getShiftId() != null, WmsAttendanceCheck::getShiftId, bo.getShiftId()); + lqw.ge(bo.getStartDate() != null, WmsAttendanceCheck::getWorkDate, bo.getStartDate()); + lqw.le(bo.getEndDate() != null, WmsAttendanceCheck::getWorkDate, bo.getEndDate()); + lqw.orderByDesc(WmsAttendanceCheck::getWorkDate); + return lqw; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void checkAttendance(AttendanceCheckBo bo) { + WmsAttendanceScheduleBo scheduleBo = new WmsAttendanceScheduleBo(); + scheduleBo.setStartDate(bo.getStartDate()); + scheduleBo.setEndDate(bo.getEndDate()); + List schedules = scheduleService.queryList(scheduleBo); + + if (schedules.isEmpty()) { + return; + } + + WmsAttendanceRule rule = getActiveRule(); + + LocalDate startLocal = toLocalDate(bo.getStartDate()); + LocalDate endLocal = toLocalDate(bo.getEndDate()); + + for (WmsAttendanceScheduleVo schedule : schedules) { + if (schedule.getEmployeeName() == null || schedule.getShiftStartTime() == null) { + continue; + } + + // 获取该员工该日期的所有打卡记录 + List records = getRecords(schedule.getEmployeeName(), schedule.getWorkDate()); + + WmsAttendanceCheck check = buildCheck(schedule, rule, records); + + // 删除已有的比对结果 + baseMapper.delete(Wrappers.lambdaQuery() + .eq(WmsAttendanceCheck::getScheduleId, schedule.getScheduleId())); + + baseMapper.insert(check); + } + + // 更新连续旷工天数 + updateContinuousAbsent(startLocal, endLocal); + } + + private WmsAttendanceRule getActiveRule() { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(WmsAttendanceRule::getDelFlag, 0); + wrapper.last("LIMIT 1"); + WmsAttendanceRule rule = ruleMapper.selectOne(wrapper); + if (rule == null) { + rule = new WmsAttendanceRule(); + rule.setLateWarn(3L); + rule.setLate1(5L); + rule.setLate2(15L); + rule.setDeduct1(new BigDecimal("10")); + rule.setDeduct2(new BigDecimal("30")); + rule.setAbsentHalfDay(15L); + rule.setContinuousAbsentDays(3L); + } + return rule; + } + + private List getRecords(String employeeName, Date workDate) { + AttendanceRecordsBo recordsBo = new AttendanceRecordsBo(); + recordsBo.setEname(employeeName); + LocalDate ld = toLocalDate(workDate); + recordsBo.setChecktimeStart(toDate(ld.atStartOfDay())); + recordsBo.setChecktimeEnd(toDate(ld.atTime(LocalTime.of(23, 59, 59)))); + List voList = attendanceRecordsService.queryList(recordsBo); + return voList.stream() + .map(v -> { + AttendanceRecords r = new AttendanceRecords(); + r.setId(v.getId()); + r.setEname(v.getEname()); + r.setChecktime(v.getChecktime()); + return r; + }) + .sorted(Comparator.comparing(AttendanceRecords::getChecktime)) + .collect(Collectors.toList()); + } + + private WmsAttendanceCheck buildCheck(WmsAttendanceScheduleVo schedule, WmsAttendanceRule rule, List records) { + WmsAttendanceCheck check = new WmsAttendanceCheck(); + check.setScheduleId(schedule.getScheduleId()); + check.setUserId(schedule.getUserId()); + check.setEmployeeName(schedule.getEmployeeName()); + check.setWorkDate(schedule.getWorkDate()); + check.setShiftId(schedule.getShiftId()); + check.setShiftName(schedule.getShiftName()); + check.setShiftType(schedule.getShiftType()); + + boolean hasPeriod2 = schedule.getShiftStartTime2() != null && schedule.getShiftEndTime2() != null; + + if (records.isEmpty()) { + check.setOverallStatus("absent_full"); + check.setAbsentType("full_day"); + return check; + } + + List checkTimes = records.stream() + .map(r -> toLocalDateTime(r.getChecktime()).toLocalTime()) + .collect(Collectors.toList()); + + if (hasPeriod2) { + // 正常班次:按时间中点划分上下午 + LocalTime p1End = toLocalTime(schedule.getShiftEndTime()); + LocalTime p2Start = toLocalTime(schedule.getShiftStartTime2()); + LocalTime split = LocalTime.of( + (p1End.getHour() + p2Start.getHour()) / 2, + (p1End.getMinute() + p2Start.getMinute()) / 2); + + List period1Times = new ArrayList<>(); + List period2Times = new ArrayList<>(); + for (LocalTime t : checkTimes) { + if (t.isBefore(split)) { + period1Times.add(t); + } else { + period2Times.add(t); + } + } + + check.setP1StartTime(schedule.getShiftStartTime()); + check.setP1EndTime(schedule.getShiftEndTime()); + checkPeriod(check, rule, 1, period1Times, schedule.getShiftStartTime(), schedule.getShiftEndTime(), records); + + check.setP2StartTime(schedule.getShiftStartTime2()); + check.setP2EndTime(schedule.getShiftEndTime2()); + checkPeriod(check, rule, 2, period2Times, schedule.getShiftStartTime2(), schedule.getShiftEndTime2(), records); + } else { + // 倒班:全天一个时段 + check.setP1StartTime(schedule.getShiftStartTime()); + check.setP1EndTime(schedule.getShiftEndTime()); + checkPeriod(check, rule, 1, checkTimes, schedule.getShiftStartTime(), schedule.getShiftEndTime(), records); + } + + // 总体判定 + calculateOverall(check, rule); + return check; + } + + private void checkPeriod(WmsAttendanceCheck check, WmsAttendanceRule rule, int period, + List periodTimes, Date expectedStart, Date expectedEnd, + List allRecords) { + if (periodTimes.isEmpty()) { + if (period == 1) { + check.setP1Status("missed"); + } else { + check.setP2Status("missed"); + } + return; + } + + LocalTime expStart = toLocalTime(expectedStart); + LocalTime expEnd = toLocalTime(expectedEnd); + LocalTime firstCheck = periodTimes.get(0); + LocalTime lastCheck = periodTimes.get(periodTimes.size() - 1); + + int lateMinutes = 0; + int earlyMinutes = 0; + BigDecimal deduct = BigDecimal.ZERO; + String status = "normal"; + + // 迟到判定:最早打卡 vs 理论上班时间 + if (firstCheck.isAfter(expStart)) { + lateMinutes = (int) Duration.between(expStart, firstCheck).toMinutes(); + if (lateMinutes > rule.getAbsentHalfDay()) { + status = "absent_half"; + } else if (lateMinutes > rule.getLate2()) { + status = "absent_half"; + } else if (lateMinutes > rule.getLate1()) { + status = "late_2"; + deduct = deduct.add(rule.getDeduct2()); + } else if (lateMinutes > rule.getLateWarn()) { + status = "late_1"; + deduct = deduct.add(rule.getDeduct1()); + } else { + status = "late_warn"; + } + } + + // 早退判定:最晚打卡 vs 理论下班时间 + if (lastCheck.isBefore(expEnd)) { + int min = (int) Duration.between(lastCheck, expEnd).toMinutes(); + if (min > rule.getAbsentHalfDay()) { + status = maxSeverity(status, "absent_half"); + earlyMinutes = min; + } else if (min > rule.getLate2()) { + status = maxSeverity(status, "absent_half"); + earlyMinutes = min; + } else if (min > rule.getLate1()) { + status = maxSeverity(status, "early_2"); + deduct = deduct.add(rule.getDeduct2()); + earlyMinutes = min; + } else if (min > rule.getLateWarn()) { + status = maxSeverity(status, "early_1"); + deduct = deduct.add(rule.getDeduct1()); + earlyMinutes = min; + } else { + if ("normal".equals(status)) { + status = "early_warn"; + } + earlyMinutes = min; + } + } + + // 找到对应的打卡时间 + for (AttendanceRecords r : allRecords) { + LocalTime ct = toLocalDateTime(r.getChecktime()).toLocalTime(); + if (ct.equals(firstCheck)) { + if (period == 1) check.setP1FirstCheck(r.getChecktime()); + else check.setP2FirstCheck(r.getChecktime()); + } + if (ct.equals(lastCheck)) { + if (period == 1) check.setP1LastCheck(r.getChecktime()); + else check.setP2LastCheck(r.getChecktime()); + } + } + + if (period == 1) { + check.setP1LateMinutes(lateMinutes); + check.setP1EarlyMinutes(earlyMinutes); + check.setP1Status(status); + check.setP1Deduct(deduct); + } else { + check.setP2LateMinutes(lateMinutes); + check.setP2EarlyMinutes(earlyMinutes); + check.setP2Status(status); + check.setP2Deduct(deduct); + } + } + + private String maxSeverity(String a, String b) { + List severity = java.util.Arrays.asList("normal", "late_warn", "early_warn", "late_1", "early_1", "late_2", "early_2", "absent_half"); + int ai = severity.indexOf(a); + int bi = severity.indexOf(b); + return severity.get(Math.max(ai, bi)); + } + + private void calculateOverall(WmsAttendanceCheck check, WmsAttendanceRule rule) { + BigDecimal total = BigDecimal.ZERO; + boolean hasAbsentHalf = false; + + if (check.getP1Deduct() != null) total = total.add(check.getP1Deduct()); + if (check.getP2Deduct() != null) total = total.add(check.getP2Deduct()); + + check.setTotalDeduct(total); + + if ("absent_half".equals(check.getP1Status()) || "absent_half".equals(check.getP2Status())) { + hasAbsentHalf = true; + } + if ("missed".equals(check.getP1Status()) && check.getP2StartTime() != null && "missed".equals(check.getP2Status())) { + check.setAbsentType("full_day"); + check.setOverallStatus("absent_full"); + return; + } + if (check.getP1StartTime() == null && check.getP2StartTime() == null) { + check.setAbsentType("full_day"); + check.setOverallStatus("absent_full"); + return; + } + if (hasAbsentHalf) { + check.setAbsentType("half_day"); + check.setOverallStatus("absent_half"); + return; + } + + boolean abnormal = !"normal".equals(check.getP1Status()) || (check.getP2Status() != null && !"normal".equals(check.getP2Status())); + check.setOverallStatus(abnormal ? "abnormal" : "normal"); + } + + private void updateContinuousAbsent(LocalDate startDate, LocalDate endDate) { + List checks = baseMapper.selectList(Wrappers.lambdaQuery() + .ge(WmsAttendanceCheck::getWorkDate, toDate(startDate.atStartOfDay())) + .le(WmsAttendanceCheck::getWorkDate, toDate(endDate.atTime(LocalTime.of(23, 59, 59)))) + .eq(WmsAttendanceCheck::getDelFlag, 0)); + + for (WmsAttendanceCheck check : checks) { + if (check.getAbsentType() != null) { + int continuous = countContinuousAbsent(check.getUserId(), toLocalDate(check.getWorkDate())); + check.setContinuousAbsentDays(continuous); + baseMapper.updateById(check); + } + } + } + + private int countContinuousAbsent(Long userId, LocalDate workDate) { + int count = 0; + LocalDate date = workDate.minusDays(1); + while (true) { + WmsAttendanceCheck prev = baseMapper.selectOne(Wrappers.lambdaQuery() + .eq(WmsAttendanceCheck::getUserId, userId) + .eq(WmsAttendanceCheck::getWorkDate, toDate(date.atStartOfDay())) + .eq(WmsAttendanceCheck::getDelFlag, 0)); + if (prev != null && prev.getAbsentType() != null) { + count++; + date = date.minusDays(1); + } else { + break; + } + } + return count; + } + + private static LocalDate toLocalDate(Date date) { + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + private static LocalTime toLocalTime(Date date) { + if (date == null) return null; + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); + } + + private static LocalDateTime toLocalDateTime(Date date) { + if (date == null) return null; + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + } + + private static Date toDate(LocalDateTime ldt) { + return Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + } +} diff --git a/klp-wms/src/main/resources/mapper/klp/WmsAttendanceCheckMapper.xml b/klp-wms/src/main/resources/mapper/klp/WmsAttendanceCheckMapper.xml new file mode 100644 index 00000000..e0267684 --- /dev/null +++ b/klp-wms/src/main/resources/mapper/klp/WmsAttendanceCheckMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +