diff --git a/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/FeedbackExceptionCheckJob.java b/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/FeedbackExceptionCheckJob.java index b3e2242..2249634 100644 --- a/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/FeedbackExceptionCheckJob.java +++ b/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/FeedbackExceptionCheckJob.java @@ -10,7 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** - * 安全责任清单 - 定时任务2:反馈异常检测 + * 安全责任清单 - 定时任务:反馈异常检测 *
* 执行周期:每月1日0时(cron = "0 0 0 1 * ?") *
@@ -35,9 +35,6 @@ public class FeedbackExceptionCheckJob implements Job { *
* 由XXL-Job调度框架调用,执行反馈异常检测逻辑。 * 执行成功返回SUCCESS,异常时返回FAIL及错误信息。 - * - * @param param XXL-Job传入的参数(当前任务未使用) - * @return 执行结果,成功返回ReturnT.SUCCESS,失败返回ReturnT.FAIL_CODE及异常信息 */ @Override @JobRegister(cron = "0 55 10 * * ?", jobDesc = "安全责任清单-反馈异常检测", triggerStatus = 1) diff --git a/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/TaskStatusUpdateJob.java b/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/TaskStatusUpdateJob.java index c1ffa02..eff8110 100644 --- a/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/TaskStatusUpdateJob.java +++ b/web-adapter/src/main/java/com/zcloud/safetyDutyList/job/TaskStatusUpdateJob.java @@ -10,7 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** - * 安全责任清单 - 定时任务1:更新清单状态和任务状态 + * 安全责任清单 - 定时任务:更新清单状态和任务状态 *
* 执行周期:每月1日0时(cron = "0 0 0 1 * ?") *
@@ -43,12 +43,12 @@ public class TaskStatusUpdateJob implements Job {
@JobRegister(cron = "0 0 0 1 * ?", jobDesc = "安全责任清单-更新清单和任务状态", triggerStatus = 1)
@XxlJob("com.zcloud.safetyDutyList.job.TaskStatusUpdateJob")
public ReturnT
- * 提供反馈提交、反馈列表查询、反馈周期分组查询、反馈异常记录查询等接口,
+ * 提供反馈提交、反馈列表查询、反馈周期分组查询、反馈详情等接口,
* 委托TaskListServiceI处理业务逻辑。
*/
@Api(tags = "执行反馈")
diff --git a/web-adapter/src/main/java/com/zcloud/safetyDutyList/web/tasklist/StatisticsController.java b/web-adapter/src/main/java/com/zcloud/safetyDutyList/web/tasklist/StatisticsController.java
index b2c028b..938d8a6 100644
--- a/web-adapter/src/main/java/com/zcloud/safetyDutyList/web/tasklist/StatisticsController.java
+++ b/web-adapter/src/main/java/com/zcloud/safetyDutyList/web/tasklist/StatisticsController.java
@@ -17,11 +17,11 @@ import org.springframework.web.bind.annotation.*;
public class StatisticsController {
private final TaskListServiceI taskListService;
- @ApiOperation("获取统计信息")
- @PostMapping("/list")
- public MultiResponse
- * 处理定时任务2的业务逻辑:检测所有进行中任务的反馈异常情况。
+ * 处理定时任务2的业务逻辑:检测已下发清单下所有任务的反馈异常情况。
*
* 处理流程:
- * 1. 查询所有进行中的任务(task_status=1)
- * 2. 根据每个任务的反馈周期类型(feedback_cycle_type)计算上一阶段的周期标识
- * 3. 检查上一阶段是否已存在异常记录,避免重复添加
- * 4. 检查上一阶段是否有反馈记录,若无则判定为异常
- * 5. 为异常任务创建反馈异常记录,写入safety_accountability_feedback_exception表
+ * 1. 查询所有已下发且周期时间完整的清单(issue_status=1, period_start_time/end_time不为空)
+ * 2. 遍历每个清单,获取其下所有任务
+ * 3. 对每个任务,以清单的周期起始时间判断是否仍处于首个反馈周期内
+ * 4. 若仍处于首周期,跳过(首周期尚未结束,不应判定异常)
+ * 5. 若已过首周期,计算上一阶段周期标识,检查是否有反馈记录
+ * 6. 若上一阶段无反馈且无异常记录,则创建反馈异常记录
*
- * 反馈周期判定规则:
- * feedback_cycle_type=1(每月):每个月都需要反馈至少一次,上一阶段=上个月
- * feedback_cycle_type=2(每季度):每个季度都需要反馈至少一次,上一阶段=上个季度
- * feedback_cycle_type=3(每半年):每半年都需要反馈至少一次,上一阶段=上个半年
- * feedback_cycle_type=4(年):一年内必须反馈至少一次,上一阶段=去年
+ * 首周期判断逻辑(以清单periodStartTime为准):
+ * 将periodStartTime和当前时间分别生成周期标识,若相同则仍在首周期内。
+ * 例如:清单起始时间2026年7月,当前2026年7月12日:
+ * 每月→首周期=2026-07=当前周期→跳过
+ * 每季度→首周期=2026-Q3=当前周期→跳过
+ * 每半年→首周期=2026-H2=当前周期→跳过
+ * 每年→首周期=2026-Y=当前周期→跳过
*
* 事务控制:整个检测过程在同一事务中执行,确保异常记录写入的原子性
*/
@@ -41,79 +46,101 @@ import java.util.List;
@AllArgsConstructor
public class FeedbackExceptionCheckExe {
- /** 任务详情网关,用于查询进行中的任务列表 */
+ private final TaskListGateway taskListGateway;
+
private final TaskDetailGateway taskDetailGateway;
- /** 反馈网关,用于查询任务的反馈记录 */
private final FeedbackGateway feedbackGateway;
- /** 反馈异常网关,用于查询已有异常记录和新增异常记录 */
private final FeedbackExceptionGateway feedbackExceptionGateway;
/**
* 执行反馈异常检测
*
- * 遍历所有进行中的任务,根据反馈周期类型判断上一阶段是否有反馈记录,
- * 若无反馈则创建反馈异常记录。
+ * 遍历所有已下发且周期时间完整的清单,对每个清单下的任务进行反馈异常检测。
*/
@Transactional(rollbackFor = Exception.class)
public void execute() {
- // 步骤1:获取所有进行中的任务
- List
- * 查询该任务的所有异常记录,筛选出与上一阶段周期标识匹配的记录,
- * 若存在则说明该阶段的异常已被记录,无需重复添加。
*
* @param taskDetailId 任务详情ID
- * @param previousPeriodFlag 上一阶段的周期标识(如"2026-04"、"2026-Q1"等)
+ * @param previousPeriodFlag 上一阶段的周期标识
* @return true-已存在异常记录,false-不存在异常记录
*/
private boolean checkExistingException(String taskDetailId, String previousPeriodFlag) {
@@ -121,19 +148,15 @@ public class FeedbackExceptionCheckExe {
if (exceptionList == null || exceptionList.isEmpty()) {
return false;
}
- // 遍历异常记录,判断是否存在与上一阶段周期标识匹配的记录
return exceptionList.stream()
.anyMatch(e -> previousPeriodFlag.equals(e.getExceptionPeriodFlag()));
}
/**
* 检查上一阶段是否有反馈记录
- *
- * 查询该任务的所有反馈记录,筛选出与上一阶段周期标识匹配的记录,
- * 若存在至少一条则说明该阶段已正常反馈。
*
* @param taskDetailId 任务详情ID
- * @param previousPeriodFlag 上一阶段的周期标识(如"2026-04"、"2026-Q1"等)
+ * @param previousPeriodFlag 上一阶段的周期标识
* @return true-存在反馈记录,false-不存在反馈记录
*/
private boolean checkPreviousPeriodFeedback(String taskDetailId, String previousPeriodFlag) {
@@ -141,23 +164,12 @@ public class FeedbackExceptionCheckExe {
if (feedbackList == null || feedbackList.isEmpty()) {
return false;
}
- // 遍历反馈记录,判断是否存在与上一阶段周期标识匹配的反馈
return feedbackList.stream()
.anyMatch(f -> previousPeriodFlag.equals(f.getFeedbackPeriodFlag()));
}
/**
* 创建反馈异常记录
- *
- * 当检测到任务在上一阶段未提交反馈时,创建一条反馈异常记录,
- * 写入safety_accountability_feedback_exception表。
- *
- * 记录内容包括:
- * - 任务详情ID和清单ID:关联到具体的任务和清单
- * - 异常周期标识:标识哪个周期未反馈
- * - 异常周期起止时间:该周期的起止时间范围
- * - 异常类型:未按时反馈(1)
- * - 异常时间:当前检测时间
*
* @param taskDetail 未按时反馈的任务详情
* @param previousPeriodFlag 上一阶段的周期标识
@@ -166,21 +178,17 @@ public class FeedbackExceptionCheckExe {
FeedbackExceptionE exceptionE = new FeedbackExceptionE();
exceptionE.init();
- // 设置关联的任务详情ID和清单ID
exceptionE.setTaskDetailId(taskDetail.getTaskDetailId());
exceptionE.setTaskListId(taskDetail.getTaskListId());
- // 设置异常周期标识(如"2026-04"、"2026-Q1"等)
exceptionE.setExceptionPeriodFlag(previousPeriodFlag);
- // 根据周期标识计算该周期的起止时间,用于记录异常的时间范围
LocalDateTime[] periodTime = FeedbackCycleUtil.getPeriodTime(previousPeriodFlag);
- if (periodTime != null) {
+ if (periodTime != null && periodTime.length == 2) {
exceptionE.setExceptionPeriodStartTime(periodTime[0]);
exceptionE.setExceptionPeriodEndTime(periodTime[1]);
}
- // 设置异常类型为"未按时反馈",异常时间为当前时间
exceptionE.setExceptionType(ExceptionTypeEnum.NOT_FEEDBACK.getCode());
exceptionE.setExceptionTime(LocalDateTime.now());
diff --git a/web-app/src/main/java/com/zcloud/safetyDutyList/command/tasklist/TaskListStatusUpdateExe.java b/web-app/src/main/java/com/zcloud/safetyDutyList/command/tasklist/TaskListStatusUpdateExe.java
index a15f948..fbd43b2 100644
--- a/web-app/src/main/java/com/zcloud/safetyDutyList/command/tasklist/TaskListStatusUpdateExe.java
+++ b/web-app/src/main/java/com/zcloud/safetyDutyList/command/tasklist/TaskListStatusUpdateExe.java
@@ -47,16 +47,17 @@ public class TaskListStatusUpdateExe {
// 步骤1:查询所有状态为进行中(status=1)且周期结束时间(period_end_time)小于当前时间的清单
List
+ * 查询条件:issue_status=1(已下发)且 period_start_time IS NOT NULL 且 period_end_time IS NOT NULL
+ * 且 period_end_time不超过当前时间1个月(超过1个月不再检测异常)
+ * 用于定时任务2:反馈异常检测,以清单周期起始时间判断是否跳过检测
+ *
+ * @return 已下发且周期时间完整的清单列表
+ */
+ List
- * 例如:"2026" -> [2026-01-01 00:00:00, 2026-12-31 23:59:59]
+ * 例如:"2026-Y" -> [2026-01-01 00:00:00, 2026-12-31 23:59:59]
*
- * @param periodFlag 年度周期标识(格式:"yyyy")
+ * @param periodFlag 年度周期标识(格式:"yyyy-Y")
* @return 起止时间数组
*/
private static LocalDateTime[] getYearPeriod(String periodFlag) {
- int year = Integer.parseInt(periodFlag);
- // 起始时间:1月1日00:00:00
+ String yearStr = periodFlag.replace("-Y", "");
+ int year = Integer.parseInt(yearStr);
LocalDateTime start = LocalDateTime.of(year, 1, 1, 0, 0, 0);
- // 结束时间:12月31日23:59:59
LocalDateTime end = LocalDateTime.of(year, 12, 31, 23, 59, 59);
return new LocalDateTime[]{start, end};
}
@@ -143,7 +129,7 @@ public class FeedbackCycleUtil {
* feedbackCycle=1(每月): "2026-05"
* feedbackCycle=2(每季度): "2026-Q1"
* feedbackCycle=3(每半年): "2026-H1"
- * feedbackCycle=4(每年): "2026"
+ * feedbackCycle=4(每年): "2026-Y"
*
* @param feedbackCycle 反馈周期类型(1-每月,2-每季度,3-每半年,4-每年)
* @param time 参考时间,若为null则使用当前时间
@@ -156,21 +142,15 @@ public class FeedbackCycleUtil {
int year = time.getYear();
switch (feedbackCycle) {
case 1:
- // 每月:格式为"年-月",如"2026-05"
return String.format("%d-%02d", year, time.getMonthValue());
case 2:
- // 每季度:格式为"年-Q季度",如"2026-Q1"
- // 季度计算:(月份-1)/3 + 1,如5月=(5-1)/3+1=Q2
int quarter = (time.getMonthValue() - 1) / 3 + 1;
return String.format("%d-Q%d", year, quarter);
case 3:
- // 每半年:格式为"年-H半年",如"2026-H1"
- // 1-6月为H1,7-12月为H2
int half = time.getMonthValue() <= 6 ? 1 : 2;
return String.format("%d-H%d", year, half);
case 4:
- // 每年:格式为"年",如"2026"
- return String.valueOf(year);
+ return String.format("%d-Y", year);
default:
return null;
}
@@ -185,7 +165,7 @@ public class FeedbackCycleUtil {
* feedbackCycle=1(每月): 上个月,如当前2026-05则返回"2026-04"
* feedbackCycle=2(每季度): 上个季度,如当前Q1则返回上年Q4,当前Q2则返回当年Q1
* feedbackCycle=3(每半年): 上个半年,如当前H1则返回上年H2,当前H2则返回当年H1
- * feedbackCycle=4(每年): 去年,如当前2026则返回"2025"
+ * feedbackCycle=4(每年): 去年,如当前2026-Y则返回"2025-Y"
*
* @param feedbackCycle 反馈周期类型(1-每月,2-每季度,3-每半年,4-每年)
* @return 上一阶段周期标识,若周期类型不合法则返回null
@@ -194,12 +174,9 @@ public class FeedbackCycleUtil {
LocalDateTime now = LocalDateTime.now();
switch (feedbackCycle) {
case 1:
- // 每月:回退一个月,如2026-05 -> 2026-04
LocalDateTime lastMonth = now.minusMonths(1);
return String.format("%d-%02d", lastMonth.getYear(), lastMonth.getMonthValue());
case 2:
- // 每季度:回退一个季度
- // 若当前为Q1(1-3月),则上一季度为上年的Q4;否则为当年的上一季度
int currentQuarter = (now.getMonthValue() - 1) / 3 + 1;
if (currentQuarter == 1) {
return String.format("%d-Q4", now.getYear() - 1);
@@ -207,8 +184,6 @@ public class FeedbackCycleUtil {
return String.format("%d-Q%d", now.getYear(), currentQuarter - 1);
}
case 3:
- // 每半年:回退一个半年
- // 若当前为H1(1-6月),则上一半年为上年的H2;否则为当年的H1
int currentHalf = now.getMonthValue() <= 6 ? 1 : 2;
if (currentHalf == 1) {
return String.format("%d-H2", now.getYear() - 1);
@@ -216,13 +191,47 @@ public class FeedbackCycleUtil {
return String.format("%d-H1", now.getYear());
}
case 4:
- // 每年:回退一年,如2026 -> 2025
- return String.valueOf(now.getYear() - 1);
+ return String.format("%d-Y", now.getYear() - 1);
default:
return null;
}
}
+ /**
+ * 判断当前时间是否仍处于清单的首个反馈周期内
+ *
+ * 用于反馈异常检测定时任务,判断是否应该检查上一周期的反馈情况。
+ * 如果当前时间仍处于清单的首个反馈周期内,说明该周期尚未结束,不应检测异常;
+ * 只有当首个反馈周期已结束,进入第二个或之后的周期时,才需要检测上一周期。
+ *
+ * 判断方式:根据清单周期起始时间生成"首周期标识",根据当前时间生成"当前周期标识",
+ * 若两者相同则说明仍在首周期内,应跳过检测。
+ *
+ * 示例(清单周期起始时间为2026年7月,当前时间为2026年7月12日):
+ * 每月→首周期=2026-07,当前=2026-07 → 相同 → 跳过
+ * 每季度→首周期=2026-Q3,当前=2026-Q3 → 相同 → 跳过
+ * 每半年→首周期=2026-H2,当前=2026-H2 → 相同 → 跳过
+ * 每年→首周期=2026-Y,当前=2026-Y → 相同 → 跳过
+ *
+ * 示例(清单周期起始时间为2026年7月,当前时间为2026年8月1日):
+ * 每月→首周期=2026-07,当前=2026-08 → 不同 → 检查2026-07
+ * 每季度→首周期=2026-Q3,当前=2026-Q3 → 相同 → 跳过
+ * 每半年→首周期=2026-H2,当前=2026-H2 → 相同 → 跳过
+ * 每年→首周期=2026-Y,当前=2026-Y → 相同 → 跳过
+ *
+ * @param feedbackCycle 反馈周期类型(1-每月,2-每季度,3-每半年,4-每年)
+ * @param periodStartTime 清单周期起始时间
+ * @return true-仍在首周期内应跳过检测,false-首周期已结束应检测上一周期
+ */
+ public static boolean isFirstCycle(Integer feedbackCycle, LocalDateTime periodStartTime) {
+ if (periodStartTime == null) {
+ return true;
+ }
+ String firstCycleFlag = generatePeriodFlag(feedbackCycle, periodStartTime);
+ String currentCycleFlag = generatePeriodFlag(feedbackCycle, LocalDateTime.now());
+ return firstCycleFlag != null && firstCycleFlag.equals(currentCycleFlag);
+ }
+
/**
* 判断给定周期标识是否为当前周期
*
diff --git a/web-infrastructure/src/main/java/com/zcloud/safetyDutyList/gatewayimpl/tasklist/TaskListGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/safetyDutyList/gatewayimpl/tasklist/TaskListGatewayImpl.java
index 94ff3ca..f887909 100644
--- a/web-infrastructure/src/main/java/com/zcloud/safetyDutyList/gatewayimpl/tasklist/TaskListGatewayImpl.java
+++ b/web-infrastructure/src/main/java/com/zcloud/safetyDutyList/gatewayimpl/tasklist/TaskListGatewayImpl.java
@@ -144,4 +144,18 @@ public class TaskListGatewayImpl implements TaskListGateway {
}
return eList;
}
+
+ @Override
+ public List