perf(attendance): 优化考勤对比逻辑以及性能
- 实现批量删除和插入操作,减少数据库交互次数 - 预加载员工打卡记录,避免按排班逐条查询的性能问题 - 添加跨天班次标识字段,简化跨天判断逻辑 - 使用缓存机制计算连续旷工天数,替代逐天查询 - 优化过滤窗口逻辑,提高数据处理效率 - 添加批处理大小限制,防止单次操作数据量过大
This commit is contained in:
@@ -121,4 +121,9 @@ public class WmsAttendanceScheduleVo {
|
|||||||
@ExcelProperty(value = "工时")
|
@ExcelProperty(value = "工时")
|
||||||
private java.math.BigDecimal workHours;
|
private java.math.BigDecimal workHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否跨天
|
||||||
|
*/
|
||||||
|
private Integer shiftIsCrossDay;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,20 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Service
|
@Service
|
||||||
public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService {
|
public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService {
|
||||||
|
|
||||||
|
private static final List<String> STATUS_SEVERITY = java.util.Arrays.asList("normal", "late_warn", "early_warn",
|
||||||
|
"late_one", "early_one", "late_two", "early_two", "absent_half");
|
||||||
|
|
||||||
|
private static final int BATCH_SIZE = 500;
|
||||||
|
|
||||||
private final WmsAttendanceCheckMapper baseMapper;
|
private final WmsAttendanceCheckMapper baseMapper;
|
||||||
private final IWmsAttendanceScheduleService scheduleService;
|
private final IWmsAttendanceScheduleService scheduleService;
|
||||||
private final IAttendanceRecordsService attendanceRecordsService;
|
private final IAttendanceRecordsService attendanceRecordsService;
|
||||||
@@ -98,36 +105,45 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
LocalDate startLocal = toLocalDate(bo.getStartDate());
|
LocalDate startLocal = toLocalDate(bo.getStartDate());
|
||||||
LocalDate endLocal = toLocalDate(bo.getEndDate());
|
LocalDate endLocal = toLocalDate(bo.getEndDate());
|
||||||
|
|
||||||
|
List<WmsAttendanceScheduleVo> toProcess = new ArrayList<>();
|
||||||
for (WmsAttendanceScheduleVo schedule : schedules) {
|
for (WmsAttendanceScheduleVo schedule : schedules) {
|
||||||
if (schedule.getEmployeeName() == null || schedule.getShiftStartTime() == null) {
|
if (schedule.getEmployeeName() == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (schedule.getShiftStartTime() == null && schedule.getShiftEndTime() == null) {
|
||||||
boolean crossDay = isCrossDayShift(schedule);
|
continue;
|
||||||
List<AttendanceRecords> records = getRecords(schedule.getEmployeeName(), schedule.getWorkDate(), crossDay);
|
|
||||||
|
|
||||||
WmsAttendanceCheck check = buildCheck(schedule, rule, records);
|
|
||||||
|
|
||||||
baseMapper.delete(Wrappers.<WmsAttendanceCheck>lambdaQuery()
|
|
||||||
.eq(WmsAttendanceCheck::getScheduleId, schedule.getScheduleId()));
|
|
||||||
|
|
||||||
baseMapper.insert(check);
|
|
||||||
}
|
}
|
||||||
|
toProcess.add(schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toProcess.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<AttendanceRecords>> recordsByEmployee = prefetchAttendanceRecords(toProcess);
|
||||||
|
|
||||||
|
List<Long> scheduleIds = toProcess.stream()
|
||||||
|
.map(WmsAttendanceScheduleVo::getScheduleId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
batchDeleteByScheduleIds(scheduleIds);
|
||||||
|
|
||||||
|
List<WmsAttendanceCheck> checksToInsert = new ArrayList<>(toProcess.size());
|
||||||
|
for (WmsAttendanceScheduleVo schedule : toProcess) {
|
||||||
|
boolean crossDay = isCrossDayShift(schedule);
|
||||||
|
List<AttendanceRecords> records = sliceRecordsForDay(
|
||||||
|
recordsByEmployee.get(schedule.getEmployeeName()),
|
||||||
|
schedule.getWorkDate(),
|
||||||
|
crossDay);
|
||||||
|
checksToInsert.add(buildCheck(schedule, rule, records));
|
||||||
|
}
|
||||||
|
|
||||||
|
baseMapper.insertBatch(checksToInsert, BATCH_SIZE);
|
||||||
|
|
||||||
updateContinuousAbsent(startLocal, endLocal);
|
updateContinuousAbsent(startLocal, endLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCrossDayShift(WmsAttendanceScheduleVo schedule) {
|
private boolean isCrossDayShift(WmsAttendanceScheduleVo schedule) {
|
||||||
if (schedule.getShiftStartTime() == null || schedule.getShiftEndTime() == null) {
|
return schedule.getShiftIsCrossDay() != null && schedule.getShiftIsCrossDay() == 1;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean hasPeriod2 = schedule.getShiftStartTime2() != null && schedule.getShiftEndTime2() != null;
|
|
||||||
if (hasPeriod2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LocalTime start = toLocalTime(schedule.getShiftStartTime());
|
|
||||||
LocalTime end = toLocalTime(schedule.getShiftEndTime());
|
|
||||||
return end.isBefore(start);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private WmsAttendanceRule getActiveRule() {
|
private WmsAttendanceRule getActiveRule() {
|
||||||
@@ -148,16 +164,33 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AttendanceRecords> getRecords(String employeeName, Date workDate, boolean crossDay) {
|
/**
|
||||||
|
* 按员工姓名合并时间范围,每人只查一次打卡(与原先按排班逐条查询结果一致)。
|
||||||
|
*/
|
||||||
|
private Map<String, List<AttendanceRecords>> prefetchAttendanceRecords(List<WmsAttendanceScheduleVo> toProcess) {
|
||||||
|
Map<String, LocalDate> minByName = new HashMap<>();
|
||||||
|
Map<String, LocalDate> maxByName = new HashMap<>();
|
||||||
|
for (WmsAttendanceScheduleVo s : toProcess) {
|
||||||
|
String name = s.getEmployeeName();
|
||||||
|
LocalDate ld = toLocalDate(s.getWorkDate());
|
||||||
|
minByName.merge(name, ld, (a, b) -> a.isBefore(b) ? a : b);
|
||||||
|
maxByName.merge(name, ld, (a, b) -> a.isAfter(b) ? a : b);
|
||||||
|
}
|
||||||
|
Map<String, List<AttendanceRecords>> out = new HashMap<>(minByName.size() * 2);
|
||||||
|
for (Map.Entry<String, LocalDate> e : minByName.entrySet()) {
|
||||||
|
String name = e.getKey();
|
||||||
|
LocalDate minLd = e.getValue();
|
||||||
|
LocalDate maxLd = maxByName.get(name);
|
||||||
|
out.put(name, fetchRecordsForNameRange(name, minLd, maxLd));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AttendanceRecords> fetchRecordsForNameRange(String employeeName, LocalDate minLd, LocalDate maxLd) {
|
||||||
AttendanceRecordsBo recordsBo = new AttendanceRecordsBo();
|
AttendanceRecordsBo recordsBo = new AttendanceRecordsBo();
|
||||||
recordsBo.setEname(employeeName);
|
recordsBo.setEname(employeeName);
|
||||||
LocalDate ld = toLocalDate(workDate);
|
recordsBo.setChecktimeStart(toDate(minLd.atStartOfDay()));
|
||||||
recordsBo.setChecktimeStart(toDate(ld.atStartOfDay()));
|
recordsBo.setChecktimeEnd(toDate(maxLd.plusDays(1).atTime(LocalTime.of(23, 59, 59))));
|
||||||
if (crossDay) {
|
|
||||||
recordsBo.setChecktimeEnd(toDate(ld.plusDays(1).atTime(LocalTime.of(23, 59, 59))));
|
|
||||||
} else {
|
|
||||||
recordsBo.setChecktimeEnd(toDate(ld.atTime(LocalTime.of(23, 59, 59))));
|
|
||||||
}
|
|
||||||
List<AttendanceRecordsVo> voList = attendanceRecordsService.queryList(recordsBo);
|
List<AttendanceRecordsVo> voList = attendanceRecordsService.queryList(recordsBo);
|
||||||
return voList.stream()
|
return voList.stream()
|
||||||
.map(v -> {
|
.map(v -> {
|
||||||
@@ -171,6 +204,39 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与原先 {@code getRecords(ename, workDate, crossDay)} 的时间窗口一致。
|
||||||
|
*/
|
||||||
|
private List<AttendanceRecords> sliceRecordsForDay(List<AttendanceRecords> prefetched, Date workDate, boolean crossDay) {
|
||||||
|
if (prefetched == null || prefetched.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
LocalDate ld = toLocalDate(workDate);
|
||||||
|
LocalDateTime rangeStart = ld.atStartOfDay();
|
||||||
|
LocalDateTime rangeEnd = crossDay
|
||||||
|
? ld.plusDays(1).atTime(LocalTime.of(23, 59, 59))
|
||||||
|
: ld.atTime(LocalTime.of(23, 59, 59));
|
||||||
|
return prefetched.stream()
|
||||||
|
.filter(r -> {
|
||||||
|
LocalDateTime ct = toLocalDateTime(r.getChecktime());
|
||||||
|
return ct != null && !ct.isBefore(rangeStart) && !ct.isAfter(rangeEnd);
|
||||||
|
})
|
||||||
|
.sorted(Comparator.comparing(AttendanceRecords::getChecktime))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void batchDeleteByScheduleIds(List<Long> scheduleIds) {
|
||||||
|
if (scheduleIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < scheduleIds.size(); i += BATCH_SIZE) {
|
||||||
|
int to = Math.min(i + BATCH_SIZE, scheduleIds.size());
|
||||||
|
List<Long> chunk = scheduleIds.subList(i, to);
|
||||||
|
baseMapper.delete(Wrappers.<WmsAttendanceCheck>lambdaQuery()
|
||||||
|
.in(WmsAttendanceCheck::getScheduleId, chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private WmsAttendanceCheck buildCheck(WmsAttendanceScheduleVo schedule, WmsAttendanceRule rule,
|
private WmsAttendanceCheck buildCheck(WmsAttendanceScheduleVo schedule, WmsAttendanceRule rule,
|
||||||
List<AttendanceRecords> records) {
|
List<AttendanceRecords> records) {
|
||||||
WmsAttendanceCheck check = new WmsAttendanceCheck();
|
WmsAttendanceCheck check = new WmsAttendanceCheck();
|
||||||
@@ -191,13 +257,7 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasPeriod2) {
|
if (hasPeriod2) {
|
||||||
List<AttendanceRecords> filtered = filterWindow(records, schedule.getWorkDate(),
|
|
||||||
schedule.getShiftStartTime(), schedule.getShiftEndTime(), crossDay);
|
|
||||||
check.setP1StartTime(schedule.getShiftStartTime());
|
|
||||||
check.setP1EndTime(schedule.getShiftEndTime());
|
|
||||||
checkPeriod(check, rule, 1, filtered, schedule.getShiftStartTime(), schedule.getShiftEndTime());
|
|
||||||
} else {
|
|
||||||
LocalTime p1End = toLocalTime(schedule.getShiftEndTime());
|
LocalTime p1End = toLocalTime(schedule.getShiftEndTime());
|
||||||
LocalTime p2Start = toLocalTime(schedule.getShiftStartTime2());
|
LocalTime p2Start = toLocalTime(schedule.getShiftStartTime2());
|
||||||
LocalTime split = LocalTime.of(
|
LocalTime split = LocalTime.of(
|
||||||
@@ -222,6 +282,12 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
check.setP2StartTime(schedule.getShiftStartTime2());
|
check.setP2StartTime(schedule.getShiftStartTime2());
|
||||||
check.setP2EndTime(schedule.getShiftEndTime2());
|
check.setP2EndTime(schedule.getShiftEndTime2());
|
||||||
checkPeriod(check, rule, 2, p2Records, schedule.getShiftStartTime2(), schedule.getShiftEndTime2());
|
checkPeriod(check, rule, 2, p2Records, schedule.getShiftStartTime2(), schedule.getShiftEndTime2());
|
||||||
|
} else {
|
||||||
|
List<AttendanceRecords> filtered = filterWindow(records, schedule.getWorkDate(),
|
||||||
|
schedule.getShiftStartTime(), schedule.getShiftEndTime(), crossDay);
|
||||||
|
check.setP1StartTime(schedule.getShiftStartTime());
|
||||||
|
check.setP1EndTime(schedule.getShiftEndTime());
|
||||||
|
checkPeriod(check, rule, 1, filtered, schedule.getShiftStartTime(), schedule.getShiftEndTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateOverall(check, rule);
|
calculateOverall(check, rule);
|
||||||
@@ -240,15 +306,23 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
if (crossDay) {
|
if (crossDay) {
|
||||||
windowStart = LocalDateTime.of(ld, st).minusHours(2);
|
windowStart = LocalDateTime.of(ld, st).minusHours(2);
|
||||||
windowEnd = LocalDateTime.of(ld.plusDays(1), et).plusHours(2);
|
windowEnd = LocalDateTime.of(ld.plusDays(1), et).plusHours(2);
|
||||||
} else {
|
} else if (st != null && et != null) {
|
||||||
windowStart = LocalDateTime.of(ld, st).minusHours(2);
|
windowStart = LocalDateTime.of(ld, st).minusHours(2);
|
||||||
windowEnd = LocalDateTime.of(ld, et).plusHours(2);
|
windowEnd = LocalDateTime.of(ld, et).plusHours(2);
|
||||||
|
} else if (st != null) {
|
||||||
|
windowStart = LocalDateTime.of(ld, st).minusHours(2);
|
||||||
|
windowEnd = LocalDateTime.of(ld, st).plusHours(2);
|
||||||
|
} else {
|
||||||
|
windowStart = LocalDateTime.of(ld, et).minusHours(2);
|
||||||
|
windowEnd = LocalDateTime.of(ld, et).plusHours(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalDateTime finalStart = windowStart;
|
||||||
|
LocalDateTime finalEnd = windowEnd;
|
||||||
return records.stream()
|
return records.stream()
|
||||||
.filter(r -> {
|
.filter(r -> {
|
||||||
LocalDateTime ct = toLocalDateTime(r.getChecktime());
|
LocalDateTime ct = toLocalDateTime(r.getChecktime());
|
||||||
return !ct.isBefore(windowStart) && !ct.isAfter(windowEnd);
|
return !ct.isBefore(finalStart) && !ct.isAfter(finalEnd);
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
@@ -276,7 +350,7 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
BigDecimal deduct = BigDecimal.ZERO;
|
BigDecimal deduct = BigDecimal.ZERO;
|
||||||
String status = "normal";
|
String status = "normal";
|
||||||
|
|
||||||
if (firstCheck.isAfter(expStart)) {
|
if (expStart != null && firstCheck.isAfter(expStart)) {
|
||||||
lateMinutes = (int) Duration.between(expStart, firstCheck).toMinutes();
|
lateMinutes = (int) Duration.between(expStart, firstCheck).toMinutes();
|
||||||
if (lateMinutes > rule.getAbsentHalfDay()) {
|
if (lateMinutes > rule.getAbsentHalfDay()) {
|
||||||
status = "absent_half";
|
status = "absent_half";
|
||||||
@@ -291,7 +365,7 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastCheck.isBefore(expEnd)) {
|
if (expEnd != null && lastCheck.isBefore(expEnd)) {
|
||||||
int min = (int) Duration.between(lastCheck, expEnd).toMinutes();
|
int min = (int) Duration.between(lastCheck, expEnd).toMinutes();
|
||||||
if (min > rule.getAbsentHalfDay()) {
|
if (min > rule.getAbsentHalfDay()) {
|
||||||
status = maxSeverity(status, "absent_half");
|
status = maxSeverity(status, "absent_half");
|
||||||
@@ -330,11 +404,9 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String maxSeverity(String a, String b) {
|
private String maxSeverity(String a, String b) {
|
||||||
List<String> severity = java.util.Arrays.asList("normal", "late_warn", "early_warn",
|
int ai = STATUS_SEVERITY.indexOf(a);
|
||||||
"late_one", "early_one", "late_two", "early_two", "absent_half");
|
int bi = STATUS_SEVERITY.indexOf(b);
|
||||||
int ai = severity.indexOf(a);
|
return STATUS_SEVERITY.get(Math.max(ai, bi));
|
||||||
int bi = severity.indexOf(b);
|
|
||||||
return severity.get(Math.max(ai, bi));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateOverall(WmsAttendanceCheck check, WmsAttendanceRule rule) {
|
private void calculateOverall(WmsAttendanceCheck check, WmsAttendanceRule rule) {
|
||||||
@@ -375,23 +447,64 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
|
|||||||
.le(WmsAttendanceCheck::getWorkDate, toDate(endDate.atTime(LocalTime.of(23, 59, 59))))
|
.le(WmsAttendanceCheck::getWorkDate, toDate(endDate.atTime(LocalTime.of(23, 59, 59))))
|
||||||
.eq(WmsAttendanceCheck::getDelFlag, 0));
|
.eq(WmsAttendanceCheck::getDelFlag, 0));
|
||||||
|
|
||||||
|
if (checks.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> userIds = checks.stream()
|
||||||
|
.map(WmsAttendanceCheck::getUserId)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<Long, Map<LocalDate, WmsAttendanceCheck>> historyByUserDate = loadAbsentHistoryByUser(userIds, endDate);
|
||||||
|
|
||||||
|
List<WmsAttendanceCheck> toUpdate = new ArrayList<>();
|
||||||
for (WmsAttendanceCheck check : checks) {
|
for (WmsAttendanceCheck check : checks) {
|
||||||
if (check.getAbsentType() != null) {
|
if (check.getAbsentType() != null) {
|
||||||
int continuous = countContinuousAbsent(check.getUserId(), toLocalDate(check.getWorkDate()));
|
int continuous = countContinuousAbsentCached(
|
||||||
|
check.getUserId(), toLocalDate(check.getWorkDate()), historyByUserDate);
|
||||||
check.setContinuousAbsentDays(continuous);
|
check.setContinuousAbsentDays(continuous);
|
||||||
baseMapper.updateById(check);
|
toUpdate.add(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!toUpdate.isEmpty()) {
|
||||||
|
baseMapper.updateBatchById(toUpdate, BATCH_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countContinuousAbsent(Long userId, LocalDate workDate) {
|
/**
|
||||||
|
* 加载各员工在 endDate 及之前的考勤结果,用于内存计算连续旷工(与按天 selectOne 结果一致)。
|
||||||
|
*/
|
||||||
|
private Map<Long, Map<LocalDate, WmsAttendanceCheck>> loadAbsentHistoryByUser(List<Long> userIds, LocalDate endDate) {
|
||||||
|
Map<Long, Map<LocalDate, WmsAttendanceCheck>> out = new HashMap<>();
|
||||||
|
Date end = toDate(endDate.atTime(LocalTime.of(23, 59, 59)));
|
||||||
|
for (int i = 0; i < userIds.size(); i += BATCH_SIZE) {
|
||||||
|
int to = Math.min(i + BATCH_SIZE, userIds.size());
|
||||||
|
List<Long> chunk = userIds.subList(i, to);
|
||||||
|
List<WmsAttendanceCheck> rows = baseMapper.selectList(Wrappers.<WmsAttendanceCheck>lambdaQuery()
|
||||||
|
.select(WmsAttendanceCheck::getUserId, WmsAttendanceCheck::getWorkDate, WmsAttendanceCheck::getAbsentType,
|
||||||
|
WmsAttendanceCheck::getCheckId, WmsAttendanceCheck::getDelFlag)
|
||||||
|
.in(WmsAttendanceCheck::getUserId, chunk)
|
||||||
|
.le(WmsAttendanceCheck::getWorkDate, end)
|
||||||
|
.eq(WmsAttendanceCheck::getDelFlag, 0));
|
||||||
|
for (WmsAttendanceCheck row : rows) {
|
||||||
|
LocalDate wd = toLocalDate(row.getWorkDate());
|
||||||
|
out.computeIfAbsent(row.getUserId(), k -> new HashMap<>()).put(wd, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countContinuousAbsentCached(Long userId, LocalDate workDate,
|
||||||
|
Map<Long, Map<LocalDate, WmsAttendanceCheck>> historyByUserDate) {
|
||||||
|
Map<LocalDate, WmsAttendanceCheck> userMap = historyByUserDate.get(userId);
|
||||||
|
if (userMap == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int count = 0;
|
int count = 0;
|
||||||
LocalDate date = workDate.minusDays(1);
|
LocalDate date = workDate.minusDays(1);
|
||||||
while (true) {
|
while (true) {
|
||||||
WmsAttendanceCheck prev = baseMapper.selectOne(Wrappers.<WmsAttendanceCheck>lambdaQuery()
|
WmsAttendanceCheck prev = userMap.get(date);
|
||||||
.eq(WmsAttendanceCheck::getUserId, userId)
|
|
||||||
.eq(WmsAttendanceCheck::getWorkDate, toDate(date.atStartOfDay()))
|
|
||||||
.eq(WmsAttendanceCheck::getDelFlag, 0));
|
|
||||||
if (prev != null && prev.getAbsentType() != null) {
|
if (prev != null && prev.getAbsentType() != null) {
|
||||||
count++;
|
count++;
|
||||||
date = date.minusDays(1);
|
date = date.minusDays(1);
|
||||||
|
|||||||
@@ -36,12 +36,13 @@
|
|||||||
<result property="shiftStartTime2" column="shift_start_time2"/>
|
<result property="shiftStartTime2" column="shift_start_time2"/>
|
||||||
<result property="shiftEndTime2" column="shift_end_time2"/>
|
<result property="shiftEndTime2" column="shift_end_time2"/>
|
||||||
<result property="workHours" column="work_hours"/>
|
<result property="workHours" column="work_hours"/>
|
||||||
|
<result property="shiftIsCrossDay" column="shift_is_cross_day"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectScheduleWithDetailsVo">
|
<sql id="selectScheduleWithDetailsVo">
|
||||||
SELECT s.schedule_id, s.user_id, s.work_date, s.shift_id, s.shift_name, s.shift_group, s.remark,
|
SELECT s.schedule_id, s.user_id, s.work_date, s.shift_id, s.shift_name, s.shift_group, s.remark,
|
||||||
e.name as employee_name, e.dept as employee_dept, e.job_type as employee_job_type,
|
e.name as employee_name, e.dept as employee_dept, e.job_type as employee_job_type,
|
||||||
sh.shift_type, sh.start_time as shift_start_time, sh.end_time as shift_end_time, sh.start_time2 as shift_start_time2, sh.end_time2 as shift_end_time2, sh.work_hours
|
sh.shift_type, sh.start_time as shift_start_time, sh.end_time as shift_end_time, sh.start_time2 as shift_start_time2, sh.end_time2 as shift_end_time2, sh.work_hours, sh.is_cross_day as shift_is_cross_day
|
||||||
FROM wms_attendance_schedule s
|
FROM wms_attendance_schedule s
|
||||||
LEFT JOIN wms_employee_info e ON s.user_id = e.info_id AND e.del_flag = 0
|
LEFT JOIN wms_employee_info e ON s.user_id = e.info_id AND e.del_flag = 0
|
||||||
LEFT JOIN wms_attendance_shift sh ON s.shift_id = sh.shift_id AND sh.del_flag = 0
|
LEFT JOIN wms_attendance_shift sh ON s.shift_id = sh.shift_id AND sh.del_flag = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user