From cdad9edf576c0b858a2d97784c81397b80fc598b Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 17 Mar 2026 14:57:02 +0800 Subject: [PATCH 1/4] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E6=A0=87=E7=AD=BE=E5=85=A5=E5=8F=A3=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CcdiBankTagController.java | 4 ++ .../impl/CcdiFileUploadServiceImpl.java | 4 ++ .../controller/CcdiBankTagControllerTest.java | 33 +++++++++++ .../impl/CcdiFileUploadServiceImplTest.java | 55 ++++++++++++++++++- 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java index b99a515f..e5498bbc 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankTagController.java @@ -8,6 +8,7 @@ import com.ruoyi.common.utils.SecurityUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController; * 项目流水标签控制器 */ @Tag(name = "项目流水标签") +@Slf4j @RestController @RequestMapping("/ccdi/project/tags") public class CcdiBankTagController extends BaseController { @@ -32,6 +34,8 @@ public class CcdiBankTagController extends BaseController { @PostMapping("/rebuild") public AjaxResult rebuild(@Validated @RequestBody CcdiBankTagRebuildDTO dto) { String operator = SecurityUtils.getUsername(); + log.info("【流水标签】收到手动重算请求: projectId={}, modelCode={}, operator={}", + dto.getProjectId(), dto.getModelCode(), operator); return success(bankTagService.submitRebuild(dto, operator)); } } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java index a5322870..9f1bc5f3 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java @@ -679,7 +679,11 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService { } private void handleTagRebuildAfterBatchCompletion(Long projectId, TriggerType triggerType, Boolean anySuccess) { + log.info("【流水标签】批处理完成,准备触发自动重算: projectId={}, triggerType={}, anySuccess={}", + projectId, triggerType, anySuccess); if (!Boolean.TRUE.equals(anySuccess)) { + log.warn("【流水标签】跳过自动重算: projectId={}, triggerType={}, reason=all_records_failed", + projectId, triggerType); return; } bankTagService.submitAutoRebuild(projectId, triggerType); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java index ee1872de..4c81a159 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankTagControllerTest.java @@ -1,5 +1,8 @@ package com.ruoyi.ccdi.project.controller; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO; import com.ruoyi.ccdi.project.service.ICcdiBankTagService; import com.ruoyi.common.core.domain.AjaxResult; @@ -10,8 +13,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,4 +47,32 @@ class CcdiBankTagControllerTest { verify(bankTagService).submitRebuild(dto, "admin"); } } + + @Test + void rebuild_shouldLogManualRebuildRequest() { + CcdiBankTagRebuildDTO dto = new CcdiBankTagRebuildDTO(); + dto.setProjectId(40L); + dto.setModelCode("LARGE_TRANSACTION"); + + Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagController.class); + ListAppender appender = new ListAppender<>(); + appender.start(); + logger.addAppender(appender); + + when(bankTagService.submitRebuild(dto, "admin")).thenReturn("标签重算任务已提交"); + + try (MockedStatic mocked = mockStatic(SecurityUtils.class)) { + mocked.when(SecurityUtils::getUsername).thenReturn("admin"); + + controller.rebuild(dto); + + assertTrue(appender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("收到手动重算请求") + && message.contains("projectId=40") + && message.contains("modelCode=LARGE_TRANSACTION") + && message.contains("operator=admin"))); + } finally { + logger.detachAppender(appender); + } + } } diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java index 157923b4..725fd073 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImplTest.java @@ -54,8 +54,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -167,6 +167,59 @@ class CcdiFileUploadServiceImplTest { assertFalse(Files.exists(batchLogDir)); } + @Test + void handleTagRebuildAfterBatchCompletion_shouldLogSkipWhenAllRecordsFailed() { + Logger logger = (Logger) LoggerFactory.getLogger(CcdiFileUploadServiceImpl.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + ReflectionTestUtils.invokeMethod( + service, + "handleTagRebuildAfterBatchCompletion", + PROJECT_ID, + TriggerType.AUTO_BATCH_UPLOAD, + Boolean.FALSE + ); + + verify(bankTagService, never()).submitAutoRebuild(any(), any()); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("跳过自动重算") + && message.contains("projectId=100") + && message.contains("AUTO_BATCH_UPLOAD"))); + } finally { + logger.detachAppender(logAppender); + } + } + + @Test + void handleTagRebuildAfterBatchCompletion_shouldLogSubmissionWhenAnyRecordSucceeded() { + Logger logger = (Logger) LoggerFactory.getLogger(CcdiFileUploadServiceImpl.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + ReflectionTestUtils.invokeMethod( + service, + "handleTagRebuildAfterBatchCompletion", + PROJECT_ID, + TriggerType.AUTO_PULL_BANK_INFO, + Boolean.TRUE + ); + + verify(bankTagService).submitAutoRebuild(PROJECT_ID, TriggerType.AUTO_PULL_BANK_INFO); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("准备触发自动重算") + && message.contains("projectId=100") + && message.contains("AUTO_PULL_BANK_INFO") + && message.contains("anySuccess=true"))); + } finally { + logger.detachAppender(logAppender); + } + } + @Test void processFileAsync_shouldKeepParsingUntilBankStatementsSaved() throws IOException { List events = new ArrayList<>(); From 4076097185fc42280f72c3a06dc25cef14909045 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 17 Mar 2026 14:58:35 +0800 Subject: [PATCH 2/4] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E6=A0=87=E7=AD=BE=E5=8D=8F=E8=B0=83=E5=99=A8=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectBankTagRebuildCoordinator.java | 11 ++++ .../ProjectBankTagRebuildCoordinatorTest.java | 55 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java index 8785c48f..a90b3cda 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinator.java @@ -9,10 +9,12 @@ import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import java.util.concurrent.ConcurrentHashMap; +import lombok.extern.slf4j.Slf4j; /** * 项目级流水标签重算协调器 */ +@Slf4j @Component public class ProjectBankTagRebuildCoordinator { @@ -33,7 +35,11 @@ public class ProjectBankTagRebuildCoordinator { * @param operator 操作人 */ public void submitManual(Long projectId, String modelCode, String operator) { + log.info("【流水标签】手动重算开始排队: projectId={}, modelCode={}, operator={}", + projectId, modelCode, operator); if (isProjectRunning(projectId)) { + log.warn("【流水标签】项目已有运行中任务,拒绝手动重算: projectId={}, modelCode={}, operator={}", + projectId, modelCode, operator); throw new ServiceException("当前项目标签正在重算中,请稍后再试"); } @@ -49,6 +55,8 @@ public class ProjectBankTagRebuildCoordinator { public void submitAuto(Long projectId, TriggerType triggerType) { CcdiBankTagTask runningTask = taskMapper.selectRunningTaskByProjectId(projectId); if (runningTask != null || runningProjects.containsKey(projectId)) { + log.warn("【流水标签】项目正在重算,已标记完成后补跑: projectId={}, runningTaskId={}, triggerType={}", + projectId, runningTask != null ? runningTask.getId() : null, triggerType); markNeedRerun(runningTask); return; } @@ -64,12 +72,15 @@ public class ProjectBankTagRebuildCoordinator { private void executeWithLock(Long projectId, Runnable action) { if (runningProjects.putIfAbsent(projectId, Boolean.TRUE) != null) { + log.warn("【流水标签】项目已有运行中任务,拒绝获取项目锁: projectId={}", projectId); throw new ServiceException("当前项目标签正在重算中,请稍后再试"); } + log.info("【流水标签】获取项目重算锁成功: projectId={}", projectId); try { action.run(); } finally { runningProjects.remove(projectId); + log.info("【流水标签】释放项目重算锁: projectId={}", projectId); } } diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java index d0799917..526a2f1e 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/ProjectBankTagRebuildCoordinatorTest.java @@ -1,5 +1,8 @@ package com.ruoyi.ccdi.project.service.impl; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask; import com.ruoyi.ccdi.project.domain.enums.TriggerType; import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper; @@ -9,8 +12,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -54,4 +59,54 @@ class ProjectBankTagRebuildCoordinatorTest { verify(taskMapper).updateTask(any(CcdiBankTagTask.class)); verify(bankTagService, never()).rebuildProject(40L, null, "system", TriggerType.AUTO_BATCH_UPLOAD); } + + @Test + void submitManual_shouldLogRejectWhenProjectAlreadyRunning() { + CcdiBankTagTask runningTask = new CcdiBankTagTask(); + runningTask.setId(1L); + runningTask.setProjectId(40L); + runningTask.setStatus("RUNNING"); + when(taskMapper.selectRunningTaskByProjectId(40L)).thenReturn(runningTask); + + Logger logger = (Logger) LoggerFactory.getLogger(ProjectBankTagRebuildCoordinator.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + assertThrows(ServiceException.class, () -> coordinator.submitManual(40L, null, "admin")); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("拒绝手动重算") + && message.contains("projectId=40") + && message.contains("operator=admin"))); + } finally { + logger.detachAppender(logAppender); + } + } + + @Test + void submitAuto_shouldLogNeedRerunWhenProjectAlreadyRunning() { + CcdiBankTagTask runningTask = new CcdiBankTagTask(); + runningTask.setId(1L); + runningTask.setProjectId(40L); + runningTask.setStatus("RUNNING"); + runningTask.setNeedRerun(0); + when(taskMapper.selectRunningTaskByProjectId(40L)).thenReturn(runningTask); + + Logger logger = (Logger) LoggerFactory.getLogger(ProjectBankTagRebuildCoordinator.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + coordinator.submitAuto(40L, TriggerType.AUTO_BATCH_UPLOAD); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("已标记完成后补跑") + && message.contains("projectId=40") + && message.contains("runningTaskId=1") + && message.contains("AUTO_BATCH_UPLOAD"))); + } finally { + logger.detachAppender(logAppender); + } + } } From e94e5398eb511b99281ffe80aa3480818b559a08 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 17 Mar 2026 15:00:41 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E6=A0=87=E7=AD=BE=E6=89=A7=E8=A1=8C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/CcdiBankTagServiceImpl.java | 44 ++++++++- .../impl/CcdiBankTagServiceImplTest.java | 94 +++++++++++++++++++ 2 files changed, 134 insertions(+), 4 deletions(-) diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java index 200274a5..485de68f 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java @@ -24,10 +24,12 @@ import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import lombok.extern.slf4j.Slf4j; /** * 流水标签服务实现 */ +@Slf4j @Service public class CcdiBankTagServiceImpl implements ICcdiBankTagService { @@ -85,16 +87,23 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService { * @param triggerType 触发方式 */ public Long rebuildProject(Long projectId, String modelCode, String operator, TriggerType triggerType) { + long taskStartTime = System.currentTimeMillis(); CcdiBankTagTask task = buildRunningTask(projectId, modelCode, operator, triggerType); taskMapper.insertTask(task); + log.info("【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}", + task.getId(), projectId, modelCode, triggerType, operator); try { List rules = ruleMapper.selectEnabledRules(modelCode); + log.info("【流水标签】加载启用规则完成: taskId={}, projectId={}, modelCode={}, ruleCount={}", + task.getId(), projectId, modelCode, rules.size()); + log.info("【流水标签】开始清理历史结果: taskId={}, projectId={}, modelCode={}", + task.getId(), projectId, modelCode); resultMapper.deleteByProjectAndModel(projectId, modelCode); List>> futures = rules.stream() .map(rule -> CompletableFuture.supplyAsync( - () -> executeRule(projectId, rule, operator), + () -> executeRule(task.getId(), projectId, rule, operator), tagRuleExecutor )) .toList(); @@ -105,6 +114,8 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService { .toList(); if (!allResults.isEmpty()) { + log.info("【流水标签】批量写入标签结果: taskId={}, projectId={}, resultCount={}", + task.getId(), projectId, allResults.size()); resultMapper.insertBatch(allResults); } @@ -117,6 +128,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService { task.setUpdateBy(operator); task.setUpdateTime(new Date()); taskMapper.updateTask(task); + log.info("【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}", + task.getId(), projectId, modelCode, triggerType, rules.size(), allResults.size(), + System.currentTimeMillis() - taskStartTime); return task.getId(); } catch (Exception ex) { task.setStatus(STATUS_FAILED); @@ -126,6 +140,8 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService { task.setUpdateBy(operator); task.setUpdateTime(new Date()); taskMapper.updateTask(task); + log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}", + task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex); throw ex; } } @@ -149,15 +165,35 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService { return task; } - private List executeRule(Long projectId, CcdiBankTagRule rule, String operator) { + private List executeRule(Long taskId, Long projectId, CcdiBankTagRule rule, String operator) { + long startTime = System.currentTimeMillis(); + log.info("【流水标签】规则开始执行: taskId={}, projectId={}, ruleCode={}, resultType={}", + taskId, projectId, rule.getRuleCode(), rule.getResultType()); BankTagRuleExecutionConfig config = configResolver.resolve(projectId, rule); + log.debug("【流水标签】规则执行参数: taskId={}, ruleCode={}, thresholds={}", + taskId, rule.getRuleCode(), config.getThresholdValues()); if (RESULT_TYPE_STATEMENT.equals(rule.getResultType())) { List hits = executeStatementRule(projectId, rule, config); - return buildStatementResults(projectId, rule, hits, operator); + List results = buildStatementResults(projectId, rule, hits, operator); + logRuleCompletion(taskId, projectId, rule.getRuleCode(), results.size(), startTime); + return results; } List hits = executeObjectRule(projectId, rule, config); - return buildObjectResults(projectId, rule, hits, operator); + List results = buildObjectResults(projectId, rule, hits, operator); + logRuleCompletion(taskId, projectId, rule.getRuleCode(), results.size(), startTime); + return results; + } + + private void logRuleCompletion(Long taskId, Long projectId, String ruleCode, int hitCount, long startTime) { + long costMs = System.currentTimeMillis() - startTime; + if (hitCount == 0) { + log.warn("【流水标签】规则无命中: taskId={}, projectId={}, ruleCode={}, costMs={}", + taskId, projectId, ruleCode, costMs); + return; + } + log.info("【流水标签】规则执行完成: taskId={}, projectId={}, ruleCode={}, hitCount={}, costMs={}", + taskId, projectId, ruleCode, hitCount, costMs); } private List executeStatementRule(Long projectId, diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java index 1f98a951..f92485a9 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImplTest.java @@ -1,5 +1,8 @@ package com.ruoyi.ccdi.project.service.impl; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule; import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask; import com.ruoyi.ccdi.project.domain.enums.TriggerType; @@ -15,13 +18,17 @@ import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import org.springframework.test.util.ReflectionTestUtils; import java.util.List; import java.util.concurrent.Executor; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -81,4 +88,91 @@ class CcdiBankTagServiceImplTest { inOrder.verify(resultMapper).insertBatch(anyList()); verify(taskMapper).insertTask(any(CcdiBankTagTask.class)); } + + @Test + void rebuildProject_shouldLogTaskLifecycleAndRuleSummary() { + ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run); + + CcdiBankTagRule rule = new CcdiBankTagRule(); + rule.setModelCode("LARGE_TRANSACTION"); + rule.setModelName("大额交易"); + rule.setRuleCode("HOUSE_OR_CAR_EXPENSE"); + rule.setRuleName("房车消费支出交易"); + rule.setResultType("STATEMENT"); + + BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig(); + config.setProjectId(40L); + config.setRuleMeta(rule); + + BankTagStatementHitVO hit = new BankTagStatementHitVO(); + hit.setBankStatementId(10L); + hit.setGroupId(40); + hit.setLogId(40001); + hit.setReasonDetail("命中房车消费支出"); + + doAnswer(invocation -> { + CcdiBankTagTask task = invocation.getArgument(0); + task.setId(88L); + return 1; + }).when(taskMapper).insertTask(any(CcdiBankTagTask.class)); + + when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule)); + when(configResolver.resolve(40L, rule)).thenReturn(config); + when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of(hit)); + + Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagServiceImpl.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + service.rebuildProject(40L, null, "admin", TriggerType.MANUAL); + + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("任务创建成功") + && message.contains("taskId=88") + && message.contains("projectId=40"))); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("规则执行完成") + && message.contains("ruleCode=HOUSE_OR_CAR_EXPENSE") + && message.contains("hitCount=1"))); + } finally { + logger.detachAppender(logAppender); + } + } + + @Test + void rebuildProject_shouldLogFailureSummaryWhenRuleExecutionFails() { + ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run); + + CcdiBankTagRule rule = new CcdiBankTagRule(); + rule.setModelCode("LARGE_TRANSACTION"); + rule.setRuleCode("HOUSE_OR_CAR_EXPENSE"); + rule.setResultType("STATEMENT"); + + doAnswer(invocation -> { + CcdiBankTagTask task = invocation.getArgument(0); + task.setId(89L); + return 1; + }).when(taskMapper).insertTask(any(CcdiBankTagTask.class)); + + when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule)); + when(configResolver.resolve(40L, rule)).thenThrow(new RuntimeException("threshold missing")); + + Logger logger = (Logger) LoggerFactory.getLogger(CcdiBankTagServiceImpl.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + assertThrows(RuntimeException.class, + () -> service.rebuildProject(40L, null, "admin", TriggerType.MANUAL)); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("任务执行失败") + && message.contains("taskId=89") + && message.contains("threshold missing"))); + } finally { + logger.detachAppender(logAppender); + } + } } From 56d4fe5b841d60b836b420c555cb98d2bd1e22b2 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 17 Mar 2026 15:01:42 +0800 Subject: [PATCH 4/4] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E6=A0=87=E7=AD=BE=E5=8F=82=E6=95=B0=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/BankTagRuleConfigResolver.java | 15 +++++++ .../impl/BankTagRuleConfigResolverTest.java | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java index 059826d5..c07543a6 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolver.java @@ -14,10 +14,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; /** * 流水标签规则执行参数解析器 */ +@Slf4j @Component public class BankTagRuleConfigResolver { @@ -61,11 +64,23 @@ public class BankTagRuleConfigResolver { Map thresholdValues = new LinkedHashMap<>(); Set requiredParamCodes = RULE_PARAM_MAPPING.getOrDefault(ruleMeta.getRuleCode(), Set.of()); + log.info("【流水标签】解析规则参数: projectId={}, effectiveProjectId={}, ruleCode={}, requiredParams={}", + projectId, effectiveProjectId, ruleMeta.getRuleCode(), requiredParamCodes); for (CcdiModelParam param : params) { if (requiredParamCodes.contains(param.getParamCode())) { thresholdValues.put(param.getParamCode(), param.getParamValue()); } } + log.debug("【流水标签】规则参数解析结果: projectId={}, ruleCode={}, thresholdValues={}", + projectId, ruleMeta.getRuleCode(), thresholdValues); + + Set missingParamCodes = requiredParamCodes.stream() + .filter(code -> !thresholdValues.containsKey(code)) + .collect(Collectors.toSet()); + if (!missingParamCodes.isEmpty()) { + log.warn("【流水标签】规则参数缺失: projectId={}, ruleCode={}, missingParams={}", + projectId, ruleMeta.getRuleCode(), missingParamCodes); + } BankTagRuleExecutionConfig config = new BankTagRuleExecutionConfig(); config.setProjectId(projectId); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java index b2d51553..619eb8ad 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/BankTagRuleConfigResolverTest.java @@ -1,5 +1,8 @@ package com.ruoyi.ccdi.project.service.impl; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.ruoyi.ccdi.project.domain.CcdiModelParam; import com.ruoyi.ccdi.project.domain.CcdiProject; import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule; @@ -11,10 +14,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -49,6 +54,42 @@ class BankTagRuleConfigResolverTest { assertEquals("1111", config.getThresholdValue("SINGLE_TRANSACTION_AMOUNT")); } + @Test + void resolve_shouldLogThresholdSourceAndMissingParams() { + CcdiProject project = new CcdiProject(); + project.setProjectId(40L); + project.setConfigType("default"); + when(projectMapper.selectById(40L)).thenReturn(project); + when(modelParamMapper.selectByProjectAndModel(0L, "LARGE_TRANSACTION")).thenReturn(List.of( + buildParam("LARGE_CASH_DEPOSIT", "50000") + )); + + CcdiBankTagRule ruleMeta = new CcdiBankTagRule(); + ruleMeta.setModelCode("LARGE_TRANSACTION"); + ruleMeta.setRuleCode("FREQUENT_CASH_DEPOSIT"); + + Logger logger = (Logger) LoggerFactory.getLogger(BankTagRuleConfigResolver.class); + ListAppender logAppender = new ListAppender<>(); + logAppender.start(); + logger.addAppender(logAppender); + + try { + resolver.resolve(40L, ruleMeta); + + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("解析规则参数") + && message.contains("projectId=40") + && message.contains("effectiveProjectId=0") + && message.contains("FREQUENT_CASH_DEPOSIT"))); + assertTrue(logAppender.list.stream().map(ILoggingEvent::getFormattedMessage) + .anyMatch(message -> message.contains("规则参数缺失") + && message.contains("projectId=40") + && message.contains("FREQUENT_CASH_DEPOSIT"))); + } finally { + logger.detachAppender(logAppender); + } + } + private CcdiModelParam buildParam(String paramCode, String paramValue) { CcdiModelParam param = new CcdiModelParam(); param.setProjectId(0L);