|
@@ -324,9 +324,12 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
lqw.orderByDesc(RiskInspectionTask::getPlanStartTime);
|
|
lqw.orderByDesc(RiskInspectionTask::getPlanStartTime);
|
|
|
Page<RiskInspectionTask> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
|
Page<RiskInspectionTask> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
|
|
// 2. 实体 → 基础 vo + 按 time_slot 拆分为多个"待办"(每日任务多时段 → 移动端 N 条)
|
|
// 2. 实体 → 基础 vo + 按 time_slot 拆分为多个"待办"(每日任务多时段 → 移动端 N 条)
|
|
|
|
|
+ // 再按 status 内存层按 slot.punchStatus 二次过滤(每日多时段任务下,
|
|
|
|
|
+ // task 整体已打卡 ≠ 所有 slot 都打卡,必须按 slot 状态细筛)
|
|
|
List<MobileInspectionTaskVo> rows = new ArrayList<>();
|
|
List<MobileInspectionTaskVo> rows = new ArrayList<>();
|
|
|
for (RiskInspectionTask task : page.getRecords()) {
|
|
for (RiskInspectionTask task : page.getRecords()) {
|
|
|
- rows.addAll(expandByTimeSlots(task));
|
|
|
|
|
|
|
+ List<MobileInspectionTaskVo> expanded = expandByTimeSlots(task);
|
|
|
|
|
+ rows.addAll(filterByPunchStatus(expanded, status));
|
|
|
}
|
|
}
|
|
|
return TableDataInfo.build(new Page<MobileInspectionTaskVo>()
|
|
return TableDataInfo.build(new Page<MobileInspectionTaskVo>()
|
|
|
.setRecords(rows)
|
|
.setRecords(rows)
|
|
@@ -351,10 +354,11 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
applyStatusFilter(lqw, status);
|
|
applyStatusFilter(lqw, status);
|
|
|
lqw.orderByDesc(RiskInspectionTask::getPlanStartTime);
|
|
lqw.orderByDesc(RiskInspectionTask::getPlanStartTime);
|
|
|
Page<RiskInspectionTask> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
|
Page<RiskInspectionTask> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
|
|
- // 同 queryMobileList:按 time_slot 拆分
|
|
|
|
|
|
|
+ // 同 queryMobileList:按 time_slot 拆分 + 内存层按 slot.punchStatus 过滤
|
|
|
List<MobileInspectionTaskVo> rows = new ArrayList<>();
|
|
List<MobileInspectionTaskVo> rows = new ArrayList<>();
|
|
|
for (RiskInspectionTask task : page.getRecords()) {
|
|
for (RiskInspectionTask task : page.getRecords()) {
|
|
|
- rows.addAll(expandByTimeSlots(task));
|
|
|
|
|
|
|
+ List<MobileInspectionTaskVo> expanded = expandByTimeSlots(task);
|
|
|
|
|
+ rows.addAll(filterByPunchStatus(expanded, status));
|
|
|
}
|
|
}
|
|
|
return TableDataInfo.build(new Page<MobileInspectionTaskVo>()
|
|
return TableDataInfo.build(new Page<MobileInspectionTaskVo>()
|
|
|
.setRecords(rows)
|
|
.setRecords(rows)
|
|
@@ -363,6 +367,34 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
.setCurrent(1L));
|
|
.setCurrent(1L));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按 vo.punchStatus 内存层二次过滤(每日多时段任务下,SQL 层 task.task_status 粗筛
|
|
|
|
|
+ * 不能区分哪些 slot 打过卡,必须按每条 vo 自己的 punchStatus 细筛)
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * - ALL: 不过滤
|
|
|
|
|
+ * - DONE: 只保留 PUNCHED(真正打过卡的 slot)
|
|
|
|
|
+ * - PENDING: 只保留 PENDING(待打卡)
|
|
|
|
|
+ * - OVERDUE/EXPIRED: 只保留 OVERDUE(已过期未打卡)
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<MobileInspectionTaskVo> filterByPunchStatus(List<MobileInspectionTaskVo> rows, String status) {
|
|
|
|
|
+ if (rows == null || rows.isEmpty()) {
|
|
|
|
|
+ return rows;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isBlank(status) || "ALL".equalsIgnoreCase(status)) {
|
|
|
|
|
+ return rows;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("DONE".equalsIgnoreCase(status)) {
|
|
|
|
|
+ return rows.stream().filter(v -> "PUNCHED".equals(v.getPunchStatus())).toList();
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("PENDING".equalsIgnoreCase(status)) {
|
|
|
|
|
+ return rows.stream().filter(v -> "PENDING".equals(v.getPunchStatus())).toList();
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("OVERDUE".equalsIgnoreCase(status) || "EXPIRED".equalsIgnoreCase(status)) {
|
|
|
|
|
+ return rows.stream().filter(v -> "OVERDUE".equals(v.getPunchStatus())).toList();
|
|
|
|
|
+ }
|
|
|
|
|
+ return rows;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 按 task.time_slot 把单条 task 拆成 N 个移动端 vo(每日多时段场景)
|
|
* 按 task.time_slot 把单条 task 拆成 N 个移动端 vo(每日多时段场景)
|
|
|
* <p>
|
|
* <p>
|
|
@@ -371,36 +403,38 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
* <li>time_slot 为空 / 解析不出 slot → 原样返回 1 个 vo(非每日任务或单时段任务)</li>
|
|
* <li>time_slot 为空 / 解析不出 slot → 原样返回 1 个 vo(非每日任务或单时段任务)</li>
|
|
|
* <li>time_slot = "08:00-08:30,10:00-10:30,12:00-12:30" → 返回 3 个 vo</li>
|
|
* <li>time_slot = "08:00-08:30,10:00-10:30,12:00-12:30" → 返回 3 个 vo</li>
|
|
|
* <li>每个 vo 的 planStartTime/planEndTime = 今天 + slot 起止时间(独立时段)</li>
|
|
* <li>每个 vo 的 planStartTime/planEndTime = 今天 + slot 起止时间(独立时段)</li>
|
|
|
- * <li>每个 vo 的 punchStatus 共享 task 级别(同一 task 一次打卡所有 slot 都视为已打卡)</li>
|
|
|
|
|
- * <li>若 task 还没打卡且当前时间已过 slot.planEndTime → 单独把该 slot 标记为 OVERDUE</li>
|
|
|
|
|
|
|
+ * <li>每个 vo 的 punchStatus <b>独立判定</b>:按 (taskId, slot) 查最新 record,
|
|
|
|
|
+ * 打过卡的 slot 单独标 PUNCHED,其他 slot 保持 PENDING / OVERDUE</li>
|
|
|
|
|
+ * <li>未打卡 slot 且当前时间已过 slot.planEndTime → 单独标 OVERDUE</li>
|
|
|
* </ul>
|
|
* </ul>
|
|
|
*/
|
|
*/
|
|
|
private List<MobileInspectionTaskVo> expandByTimeSlots(RiskInspectionTask task) {
|
|
private List<MobileInspectionTaskVo> expandByTimeSlots(RiskInspectionTask task) {
|
|
|
List<MobileInspectionTaskVo> result = new ArrayList<>();
|
|
List<MobileInspectionTaskVo> result = new ArrayList<>();
|
|
|
- // 1) 基础 vo(含 task 级别 punchStatus/打卡时间等)
|
|
|
|
|
- MobileInspectionTaskVo baseVo = convertToMobileVo(task);
|
|
|
|
|
- // 2) 解析 slot 列表
|
|
|
|
|
|
|
+ // 1) 解析 slot 列表
|
|
|
List<String> slots = parseTimeSlots(task.getTimeSlot());
|
|
List<String> slots = parseTimeSlots(task.getTimeSlot());
|
|
|
- baseVo.setAllTimeSlots(task.getTimeSlot());
|
|
|
|
|
if (slots.isEmpty()) {
|
|
if (slots.isEmpty()) {
|
|
|
- // 无 time_slot(每周/每月/单时段)→ 原样返回
|
|
|
|
|
- result.add(baseVo);
|
|
|
|
|
|
|
+ // 无 time_slot(每周/每月/单时段)→ 按 task 整体查最新 record
|
|
|
|
|
+ MobileInspectionTaskVo vo = convertToMobileVo(task, null);
|
|
|
|
|
+ vo.setAllTimeSlots(null);
|
|
|
|
|
+ result.add(vo);
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
- // 3) 每个 slot 复制一份,planStartTime/planEndTime 改为今天+slot 时间
|
|
|
|
|
|
|
+ // 2) 每个 slot 独立生成 vo(按 (taskId, slot) 查 record)
|
|
|
LocalDate today = LocalDate.now();
|
|
LocalDate today = LocalDate.now();
|
|
|
Date now = new Date();
|
|
Date now = new Date();
|
|
|
- boolean isPunchable = "PENDING".equals(baseVo.getPunchStatus());
|
|
|
|
|
for (String slot : slots) {
|
|
for (String slot : slots) {
|
|
|
- MobileInspectionTaskVo vo = new MobileInspectionTaskVo();
|
|
|
|
|
- BeanUtil.copyProperties(baseVo, vo);
|
|
|
|
|
|
|
+ MobileInspectionTaskVo vo = convertToMobileVo(task, slot);
|
|
|
|
|
+ vo.setAllTimeSlots(task.getTimeSlot());
|
|
|
vo.setSlot(slot);
|
|
vo.setSlot(slot);
|
|
|
String[] parts = slot.split("-");
|
|
String[] parts = slot.split("-");
|
|
|
if (parts.length == 2) {
|
|
if (parts.length == 2) {
|
|
|
vo.setPlanStartTime(mergeDateTime(today, parts[0].trim()));
|
|
vo.setPlanStartTime(mergeDateTime(today, parts[0].trim()));
|
|
|
vo.setPlanEndTime(mergeDateTime(today, parts[1].trim()));
|
|
vo.setPlanEndTime(mergeDateTime(today, parts[1].trim()));
|
|
|
- // 未打卡的 slot:如果当前时间已过 slot.planEndTime → 标 OVERDUE
|
|
|
|
|
- if (isPunchable && vo.getPlanEndTime() != null && vo.getPlanEndTime().before(now)) {
|
|
|
|
|
|
|
+ // 兜底:未打卡 + 已过 slot.planEndTime → OVERDUE
|
|
|
|
|
+ // (convertToMobileVo 用的是 task.planEndTime,不够精确)
|
|
|
|
|
+ if ("PENDING".equals(vo.getPunchStatus())
|
|
|
|
|
+ && vo.getPlanEndTime() != null
|
|
|
|
|
+ && vo.getPlanEndTime().before(now)) {
|
|
|
vo.setPunchStatus("OVERDUE");
|
|
vo.setPunchStatus("OVERDUE");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -448,18 +482,45 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
*/
|
|
*/
|
|
|
@Override
|
|
@Override
|
|
|
public MobileInspectionTaskVo queryMobileDetail(Long id) {
|
|
public MobileInspectionTaskVo queryMobileDetail(Long id) {
|
|
|
|
|
+ return queryMobileDetail(id, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 移动端-查询任务详情(按 slot 区分打卡状态)
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 每日多时段任务:传 slot 即可按 (taskId, slot) 查 record 状态
|
|
|
|
|
+ * 单时段/无 slot 任务:slot 传 null,按 task 整体查(向后兼容)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public MobileInspectionTaskVo queryMobileDetail(Long id, String slot) {
|
|
|
RiskInspectionTask task = baseMapper.selectById(id);
|
|
RiskInspectionTask task = baseMapper.selectById(id);
|
|
|
Assert.notNull(task, "巡检任务不存在: " + id);
|
|
Assert.notNull(task, "巡检任务不存在: " + id);
|
|
|
- MobileInspectionTaskVo vo = convertToMobileVo(task);
|
|
|
|
|
- // 加载巡检项目列表(供详情页渲染;needPhoto 字符串"0"/"1" 留到前端按需转 boolean)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 1) 基础 vo(按 slot 维度查 record;无 slot 时用 task 整体)
|
|
|
|
|
+ MobileInspectionTaskVo vo = convertToMobileVo(task, slot);
|
|
|
|
|
+ if (StringUtils.isNotBlank(slot)) {
|
|
|
|
|
+ vo.setAllTimeSlots(task.getTimeSlot());
|
|
|
|
|
+ vo.setSlot(slot);
|
|
|
|
|
+ // 时间窗口改为 slot 区间
|
|
|
|
|
+ String[] parts = slot.split("-");
|
|
|
|
|
+ if (parts.length == 2) {
|
|
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
|
|
+ vo.setPlanStartTime(mergeDateTime(today, parts[0].trim()));
|
|
|
|
|
+ vo.setPlanEndTime(mergeDateTime(today, parts[1].trim()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2) 加载巡检项目列表
|
|
|
List<RiskInspectionTaskItemVo> items = taskItemMapper.selectVoListByTaskId(id);
|
|
List<RiskInspectionTaskItemVo> items = taskItemMapper.selectVoListByTaskId(id);
|
|
|
vo.setItems(items);
|
|
vo.setItems(items);
|
|
|
vo.setItemCount(items == null ? 0 : items.size());
|
|
vo.setItemCount(items == null ? 0 : items.size());
|
|
|
|
|
|
|
|
- // 打卡地点:已打卡任务取实际打卡位置(record.check_in_location),未打卡则用任务预设位置
|
|
|
|
|
|
|
+ // 3) 打卡地点:已打卡取实际位置(record.check_in_location),未打卡则用任务预设位置
|
|
|
|
|
+ // 按 (taskId, slot) 取 record(若传 slot)
|
|
|
RiskInspectionRecord latest = recordMapper.selectOne(
|
|
RiskInspectionRecord latest = recordMapper.selectOne(
|
|
|
Wrappers.<RiskInspectionRecord>lambdaQuery()
|
|
Wrappers.<RiskInspectionRecord>lambdaQuery()
|
|
|
.eq(RiskInspectionRecord::getTaskId, id)
|
|
.eq(RiskInspectionRecord::getTaskId, id)
|
|
|
|
|
+ .eq(StringUtils.isNotBlank(slot), RiskInspectionRecord::getSlot, slot)
|
|
|
.orderByDesc(RiskInspectionRecord::getCheckInTime)
|
|
.orderByDesc(RiskInspectionRecord::getCheckInTime)
|
|
|
.last("LIMIT 1")
|
|
.last("LIMIT 1")
|
|
|
);
|
|
);
|
|
@@ -542,8 +603,15 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
* - punchType : 1-扫码 0-常规(基于 scan_enabled/ check_method)
|
|
* - punchType : 1-扫码 0-常规(基于 scan_enabled/ check_method)
|
|
|
* - punchStatus: PENDING-待打卡 / PUNCHED-已打卡 / OVERDUE-已逾期 / FINISHED-已完成
|
|
* - punchStatus: PENDING-待打卡 / PUNCHED-已打卡 / OVERDUE-已逾期 / FINISHED-已完成
|
|
|
* - punchResult: 0-正常 / 1-异常 / null-未打卡 (基于最新一条 record)
|
|
* - punchResult: 0-正常 / 1-异常 / null-未打卡 (基于最新一条 record)
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * slot 维度:每日多时段任务按 (taskId, slot) 查最新 record,每个 slot 独立判定
|
|
|
|
|
+ * (不传 slot 时按 taskId 查最新 record,向后兼容)
|
|
|
*/
|
|
*/
|
|
|
private MobileInspectionTaskVo convertToMobileVo(RiskInspectionTask task) {
|
|
private MobileInspectionTaskVo convertToMobileVo(RiskInspectionTask task) {
|
|
|
|
|
+ return convertToMobileVo(task, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private MobileInspectionTaskVo convertToMobileVo(RiskInspectionTask task, String slot) {
|
|
|
MobileInspectionTaskVo vo = new MobileInspectionTaskVo();
|
|
MobileInspectionTaskVo vo = new MobileInspectionTaskVo();
|
|
|
BeanUtil.copyProperties(task, vo);
|
|
BeanUtil.copyProperties(task, vo);
|
|
|
|
|
|
|
@@ -552,10 +620,11 @@ public class RiskInspectionTaskServiceImpl implements IRiskInspectionTaskService
|
|
|
|| "扫码".equals(task.getCheckMethod());
|
|
|| "扫码".equals(task.getCheckMethod());
|
|
|
vo.setPunchType(isScan ? "SCAN" : "NORMAL");
|
|
vo.setPunchType(isScan ? "SCAN" : "NORMAL");
|
|
|
|
|
|
|
|
- // 2. 查最近一条记录
|
|
|
|
|
|
|
+ // 2. 查最近一条记录(每日多时段按 (taskId, slot) 查;单时段/无 slot 按 taskId 查)
|
|
|
RiskInspectionRecord latest = recordMapper.selectOne(
|
|
RiskInspectionRecord latest = recordMapper.selectOne(
|
|
|
Wrappers.<RiskInspectionRecord>lambdaQuery()
|
|
Wrappers.<RiskInspectionRecord>lambdaQuery()
|
|
|
.eq(RiskInspectionRecord::getTaskId, task.getId())
|
|
.eq(RiskInspectionRecord::getTaskId, task.getId())
|
|
|
|
|
+ .eq(StringUtils.isNotBlank(slot), RiskInspectionRecord::getSlot, slot)
|
|
|
.orderByDesc(RiskInspectionRecord::getCheckInTime)
|
|
.orderByDesc(RiskInspectionRecord::getCheckInTime)
|
|
|
.last("LIMIT 1")
|
|
.last("LIMIT 1")
|
|
|
);
|
|
);
|