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] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=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); + } + } }