diff --git a/pom.xml b/pom.xml index 4786dcb2..a0233c13 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 3.0.33 + 3.0.34 17 ${java.version} diff --git a/sql/dm/部门添加编码、简称字段.sql b/sql/dm/部门添加编码、简称字段.sql new file mode 100644 index 00000000..d2c065df --- /dev/null +++ b/sql/dm/部门添加编码、简称字段.sql @@ -0,0 +1,14 @@ +-- 达梦8数据库DDL脚本 +-- 为 system_dept 表添加 code 和 short_name 字段 + +-- 添加部门编码字段 +ALTER TABLE system_dept ADD COLUMN code VARCHAR(50); + +-- 添加部门简称字段 +ALTER TABLE system_dept ADD COLUMN short_name VARCHAR(20); + +-- 添加字段注释 +COMMENT ON COLUMN system_dept.code IS '部门编码'; +COMMENT ON COLUMN system_dept.short_name IS '部门简称'; + + diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index c9f860f3..da063337 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -26,7 +26,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 3.0.33 + 3.0.34 1.6.0 3.4.5 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java index 83c6db80..dc9487e5 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java @@ -13,6 +13,9 @@ public class DeptRespDTO { @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") private String name; + @Schema(description = "部门编码", example = "XXXXXXX") + private String code; + @Schema(description = "父部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long parentId; diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java index 6e65ad3f..c5e1b031 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -105,18 +105,18 @@ public class DeptController { @GetMapping("/top-level-list") @Operation(summary = "获取顶级部门列表", description = "用于懒加载,只返回没有父部门的顶级部门") @PreAuthorize("@ss.hasPermission('system:dept:query')") - public CommonResult> getTopLevelDeptList() { + public CommonResult> getTopLevelDeptList() { List list = deptService.getTopLevelDeptList(); - return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + return success(BeanUtils.toBean(list, DeptRespVO.class)); } @GetMapping("/children") @Operation(summary = "根据父部门ID获取子部门列表", description = "用于懒加载,根据父部门ID返回直接子部门") @Parameter(name = "parentId", description = "父部门ID", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('system:dept:query')") - public CommonResult> getChildrenDeptList(@RequestParam("parentId") Long parentId) { + public CommonResult> getChildrenDeptList(@RequestParam("parentId") Long parentId) { List list = deptService.getDirectChildDeptList(parentId); - return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + return success(BeanUtils.toBean(list, DeptRespVO.class)); } @GetMapping("/get") diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java index 3f9f0f46..7d74aab6 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java @@ -20,7 +20,6 @@ public class DeptSaveReqVO { private Long id; @Schema(description = "部门编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "DEPT_001") - @NotBlank(message = "部门编码不能为空") @Size(max = 50, message = "部门编码长度不能超过 50 个字符") private String code; diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java index 2bf7c52e..8bd9027c 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sync/SyncLogController.java @@ -16,6 +16,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.Map; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** @@ -58,4 +60,36 @@ public class SyncLogController { return success(success); } + @PostMapping("/batch-rerun") + @Operation(summary = "根据服务类型批量重跑异常的同步接口") + @PreAuthorize("@ss.hasPermission('system:sync-log:batch-rerun')") + public CommonResult batchRerunByServiceName( + @RequestParam("serviceName") String serviceName, + @RequestParam(value = "batchSize", defaultValue = "200") Integer batchSize) { + // 参数校验 + if (batchSize > 500) { + throw new IllegalArgumentException("批次大小不能超过500"); + } + + Integer count = syncLogService.markForBatchRerun(serviceName, batchSize); + return success(count); + } + + @GetMapping("/batch-rerun/progress") + @Operation(summary = "查询批量重跑进度") + @PreAuthorize("@ss.hasPermission('system:sync-log:query')") + public CommonResult> getBatchRerunProgress( + @RequestParam("serviceName") String serviceName) { + Map progress = syncLogService.getBatchRerunProgress(serviceName); + return success(progress); + } + + @PostMapping("/batch-rerun/pause") + @Operation(summary = "暂停批量重跑(将重试中状态重置为原失败状态)") + @PreAuthorize("@ss.hasPermission('system:sync-log:batch-rerun')") + public CommonResult pauseBatchRerun(@RequestParam("serviceName") String serviceName) { + Integer count = syncLogService.pauseBatchRerun(serviceName); + return success(count); + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/enums/sync/SyncLogStatusEnum.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/enums/sync/SyncLogStatusEnum.java index d124f74f..f9c9ff80 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/enums/sync/SyncLogStatusEnum.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/enums/sync/SyncLogStatusEnum.java @@ -17,7 +17,9 @@ public enum SyncLogStatusEnum { SIGNATURE_VERIFY_FAILED(2, "签名验证失败"), AUTH_FAILED(3, "认证失败"), BUSINESS_FAILED(4, "业务处理失败"), - SYSTEM_ERROR(5, "系统异常"); + SYSTEM_ERROR(5, "系统异常"), + RETRYING(6, "重试中"), + RETRY_FAILED(7, "重试失败"); /** * 状态码 diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogAutoMarkRetryJob.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogAutoMarkRetryJob.java new file mode 100644 index 00000000..bdad32b1 --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogAutoMarkRetryJob.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.system.job.sync; + +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.system.service.sync.SyncLogService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 同步日志自动标记重试任务 + * + * 该任务定期扫描失败状态的同步日志记录,自动标记为"重试中"状态 + * 实现完全自动化的重试闭环 + * + * @author ZT + */ +@Component +@Slf4j +public class SyncLogAutoMarkRetryJob { + + @Resource + private SyncLogService syncLogService; + + /** + * 自动标记重试任务 + * + * 建议配置执行频率:每5分钟执行一次 + * cron表达式:0 0/5 * * * ? + * + * 该任务会按服务类型自动标记失败记录为重试状态: + * 1. 优先处理SYSTEM_ERROR状态的记录 + * 2. 然后处理其他失败状态的记录 + * 3. 每次每种服务类型最多标记100条记录 + */ + @XxlJob("syncLogAutoMarkRetryJob") + @TenantJob + public void execute() { + log.info("[syncLogAutoMarkRetryJob][开始执行同步日志自动标记重试任务]"); + + try { + // 定义需要处理的服务类型 + String[] serviceNames = { + "OrgCreateService", + "OrgUpdateService", + "OrgDeleteService", + "UserCreateService", + "UserUpdateService", + "UserDeleteService" + }; + + int totalMarkedCount = 0; + + // 为每种服务类型自动标记重试 + for (String serviceName : serviceNames) { + try { + // 每种服务类型最多标记100条记录,避免一次性处理过多 + Integer markedCount = syncLogService.autoMarkForRetry(serviceName, 100); + totalMarkedCount += markedCount; + + if (markedCount > 0) { + log.info("[syncLogAutoMarkRetryJob][服务类型 {} 自动标记了 {} 条记录为重试状态]", + serviceName, markedCount); + } + } catch (Exception e) { + log.error("[syncLogAutoMarkRetryJob][处理服务类型 {} 时发生异常]", serviceName, e); + } + } + + if (totalMarkedCount > 0) { + log.info("[syncLogAutoMarkRetryJob][自动标记重试任务执行完成,总共标记了 {} 条记录]", totalMarkedCount); + } + } catch (Exception e) { + log.error("[syncLogAutoMarkRetryJob][自动标记重试任务执行异常]", e); + } + } +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogBatchRerunJob.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogBatchRerunJob.java new file mode 100644 index 00000000..4b6a9f4a --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/job/sync/SyncLogBatchRerunJob.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.job.sync; + +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.system.service.sync.SyncLogService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 同步日志批量重跑任务 + * + * 该任务定期扫描状态为"重试中"的同步日志记录,进行分批重跑处理 + * + * @author ZT + */ +@Component +@Slf4j +public class SyncLogBatchRerunJob { + + @Resource + private SyncLogService syncLogService; + + /** + * 执行批量重跑任务 + * + * 建议配置执行频率:每30秒执行一次 + * cron表达式:0/30 * * * * ? + */ + @XxlJob("syncLogBatchRerunJob") + @TenantJob + public void execute() { + log.info("[syncLogBatchRerunJob][开始执行同步日志批量重跑任务]"); + + try { + // 执行批量重跑处理,每次处理50条记录 + int processedCount = syncLogService.processBatchRerun(50); + + if (processedCount > 0) { + log.info("[syncLogBatchRerunJob][批量重跑任务执行完成,处理了 {} 条记录]", processedCount); + } + } catch (Exception e) { + log.error("[syncLogBatchRerunJob][批量重跑任务执行异常]", e); + } + } +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 91368b61..e084d2a6 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.userdept.UserDeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; import cn.iocoder.yudao.module.system.dal.mysql.userdept.UserDeptMapper; import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.dept.DeptSourceEnum; import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -31,8 +32,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import cn.iocoder.yudao.module.system.enums.dept.DeptSourceEnum; - /** * 部门 Service 实现类 * @@ -172,7 +171,7 @@ public class DeptServiceImpl implements DeptService { @VisibleForTesting void validateDeptCodeUnique(Long id, String code) { if (StrUtil.isBlank(code)) { - throw exception(DEPT_CODE_NOT_NULL); + return; } DeptDO dept = deptMapper.selectByCode(code); if (dept == null) { diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java index 900c076e..07953ec9 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogService.java @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.sync.vo.SyncLogPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.sync.SyncLogDO; +import java.util.Map; + /** * 同步接口日志 Service 接口 * @@ -107,4 +109,46 @@ public interface SyncLogService { */ boolean rerunSyncLog(Long logId); + /** + * 根据服务类型批量标记为重试状态 + * + * @param serviceName 服务名称 + * @param batchSize 批次大小(限制单次标记的最大数量) + * @return 标记的记录数量 + */ + Integer markForBatchRerun(String serviceName, Integer batchSize); + + /** + * 自动标记失败记录为重试状态(由XXL-Job调用) + * + * @param serviceName 服务名称 + * @param batchSize 批次大小 + * @return 标记的记录数量 + */ + Integer autoMarkForRetry(String serviceName, Integer batchSize); + + /** + * 处理批量重跑(由XXL-Job调用) + * + * @param batchSize 每次处理的记录数量 + * @return 实际处理的记录数量 + */ + int processBatchRerun(int batchSize); + + /** + * 获取批量重跑进度 + * + * @param serviceName 服务名称 + * @return 进度信息 + */ + Map getBatchRerunProgress(String serviceName); + + /** + * 暂停批量重跑(将重试中状态重置为系统异常状态) + * + * @param serviceName 服务名称 + * @return 暂停的记录数量 + */ + Integer pauseBatchRerun(String serviceName); + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java index 85005b33..493ab831 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/sync/SyncLogServiceImpl.java @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sync.SyncLogDO; import cn.iocoder.yudao.module.system.dal.mysql.sync.SyncLogMapper; import cn.iocoder.yudao.module.system.enums.sync.SyncLogStatusEnum; import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -20,6 +22,11 @@ import org.springframework.validation.annotation.Validated; import java.io.PrintWriter; import java.io.StringWriter; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * 同步接口日志 Service 实现类 @@ -304,4 +311,172 @@ public class SyncLogServiceImpl implements SyncLogService { } } + @Override + public Integer markForBatchRerun(String serviceName, Integer batchSize) { + log.info("开始批量标记重试,serviceName: {}, batchSize: {}", serviceName, batchSize); + + // 安全检查 + if (batchSize > 500) { + throw new IllegalArgumentException("批次大小不能超过500"); + } + + // 查询失败状态的记录(排除成功、重试中、重试失败的状态) + List failedStatuses = Arrays.asList( + SyncLogStatusEnum.DECRYPT_FAILED.getStatus(), + SyncLogStatusEnum.SIGNATURE_VERIFY_FAILED.getStatus(), + SyncLogStatusEnum.AUTH_FAILED.getStatus(), + SyncLogStatusEnum.BUSINESS_FAILED.getStatus(), + SyncLogStatusEnum.SYSTEM_ERROR.getStatus() + ); + + // 使用 MyBatis-Plus 的条件构造器进行批量更新 + int updateCount = syncLogMapper.update(null, + new LambdaUpdateWrapper() + .eq(SyncLogDO::getServiceName, serviceName) + .in(SyncLogDO::getStatus, failedStatuses) + .isNotNull(SyncLogDO::getDecryptedRequest) // 只处理有解密数据的记录 + .set(SyncLogDO::getStatus, SyncLogStatusEnum.RETRYING.getStatus()) + .set(SyncLogDO::getErrorCode, null) + .set(SyncLogDO::getErrorMessage, null) + .set(SyncLogDO::getExceptionStack, null) + .last("LIMIT " + batchSize)); + + log.info("批量标记重试完成,serviceName: {}, 标记数量: {}", serviceName, updateCount); + return updateCount; + } + + @Override + public Integer autoMarkForRetry(String serviceName, Integer batchSize) { + log.debug("开始自动标记重试,serviceName: {}, batchSize: {}", serviceName, batchSize); + + // 自动标记的策略: + // 1. 优先处理"重试失败"状态的记录(给第二次机会) + // 2. 然后处理其他失败状态的记录 + // 3. 排除"重试中"状态的记录(避免重复处理) + // 4. 只处理24小时前的失败记录(避免处理刚刚失败的记录) + + List retryableStatuses = Arrays.asList( + SyncLogStatusEnum.RETRY_FAILED.getStatus(), // 优先重试失败的记录 + SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), // 系统异常可能是临时问题 + SyncLogStatusEnum.BUSINESS_FAILED.getStatus(), // 业务失败也可能是临时问题 + SyncLogStatusEnum.AUTH_FAILED.getStatus() // 认证失败可能是临时问题 + ); + + // 使用 MyBatis-Plus 进行批量更新 + int updateCount = syncLogMapper.update(null, + new LambdaUpdateWrapper() + .eq(SyncLogDO::getServiceName, serviceName) + .in(SyncLogDO::getStatus, retryableStatuses) + .isNotNull(SyncLogDO::getDecryptedRequest) // 只处理有解密数据的记录 + .lt(SyncLogDO::getResponseTime, LocalDateTime.now().minusHours(1)) // 只处理1小时前的记录 + .set(SyncLogDO::getStatus, SyncLogStatusEnum.RETRYING.getStatus()) + .set(SyncLogDO::getErrorCode, null) + .set(SyncLogDO::getErrorMessage, null) + .set(SyncLogDO::getExceptionStack, null) + .last("LIMIT " + batchSize)); + + if (updateCount > 0) { + log.info("自动标记重试完成,serviceName: {}, 标记数量: {}", serviceName, updateCount); + } + return updateCount; + } + + @Override + public int processBatchRerun(int batchSize) { + // 查询状态为"重试中"的记录 + List retryingLogs = syncLogMapper.selectList( + new LambdaQueryWrapper() + .eq(SyncLogDO::getStatus, SyncLogStatusEnum.RETRYING.getStatus()) + .orderByAsc(SyncLogDO::getId) + .last("LIMIT " + batchSize)); + + if (retryingLogs.isEmpty()) { + return 0; + } + + log.info("开始处理批量重跑,本次处理 {} 条记录", retryingLogs.size()); + + int successCount = 0; + int failedCount = 0; + + for (SyncLogDO log : retryingLogs) { + try { + // 调用原有的重跑业务逻辑 + boolean success = rerunBusinessLogic(log.getServiceName(), log.getDecryptedRequest(), log.getId()); + if (success) { + successCount++; + } else { + failedCount++; + // 标记为重试失败 + updateSyncLogStatus(log.getId(), SyncLogStatusEnum.RETRY_FAILED.getStatus(), + "RETRY_FAILED", "重试失败"); + } + } catch (Exception e) { + failedCount++; + // 标记为重试失败 + updateSyncLogStatus(log.getId(), SyncLogStatusEnum.RETRY_FAILED.getStatus(), + "RETRY_ERROR", "重试异常: " + e.getMessage()); + } + } + + log.info("批量重跑处理完成,成功: {}, 失败: {}", successCount, failedCount); + return retryingLogs.size(); + } + + @Override + public Map getBatchRerunProgress(String serviceName) { + // 统计各状态的记录数量 + Map statusCounts = syncLogMapper.selectList( + new LambdaQueryWrapper() + .eq(SyncLogDO::getServiceName, serviceName) + .groupBy(SyncLogDO::getStatus)) + .stream() + .collect(Collectors.groupingBy(SyncLogDO::getStatus, Collectors.counting())); + + Map progress = new HashMap<>(); + progress.put("serviceName", serviceName); + progress.put("totalCount", statusCounts.values().stream().mapToLong(Long::longValue).sum()); + progress.put("successCount", statusCounts.getOrDefault(SyncLogStatusEnum.SUCCESS.getStatus(), 0L)); + progress.put("retryingCount", statusCounts.getOrDefault(SyncLogStatusEnum.RETRYING.getStatus(), 0L)); + progress.put("retryFailedCount", statusCounts.getOrDefault(SyncLogStatusEnum.RETRY_FAILED.getStatus(), 0L)); + progress.put("failedCount", + statusCounts.getOrDefault(SyncLogStatusEnum.DECRYPT_FAILED.getStatus(), 0L) + + statusCounts.getOrDefault(SyncLogStatusEnum.SIGNATURE_VERIFY_FAILED.getStatus(), 0L) + + statusCounts.getOrDefault(SyncLogStatusEnum.AUTH_FAILED.getStatus(), 0L) + + statusCounts.getOrDefault(SyncLogStatusEnum.BUSINESS_FAILED.getStatus(), 0L) + + statusCounts.getOrDefault(SyncLogStatusEnum.SYSTEM_ERROR.getStatus(), 0L)); + + return progress; + } + + /** + * 更新同步日志状态 + */ + private void updateSyncLogStatus(Long logId, Integer status, String errorCode, String errorMessage) { + SyncLogDO updateLog = new SyncLogDO(); + updateLog.setId(logId); + updateLog.setStatus(status); + updateLog.setErrorCode(errorCode); + updateLog.setErrorMessage(errorMessage); + updateLog.setResponseTime(LocalDateTime.now()); + updateSyncLog(updateLog); + } + + @Override + public Integer pauseBatchRerun(String serviceName) { + log.info("暂停批量重跑,serviceName: {}", serviceName); + + // 将"重试中"状态重置为"系统异常"状态 + int updateCount = syncLogMapper.update(null, + new LambdaUpdateWrapper() + .eq(SyncLogDO::getServiceName, serviceName) + .eq(SyncLogDO::getStatus, SyncLogStatusEnum.RETRYING.getStatus()) + .set(SyncLogDO::getStatus, SyncLogStatusEnum.SYSTEM_ERROR.getStatus()) + .set(SyncLogDO::getErrorCode, "PAUSED") + .set(SyncLogDO::getErrorMessage, "批量重跑已暂停")); + + log.info("批量重跑暂停完成,serviceName: {}, 暂停数量: {}", serviceName, updateCount); + return updateCount; + } + }