From c8a3ea254eeb32fd0f2fb25511ba68885618657c Mon Sep 17 00:00:00 2001 From: spllzh <28668817@qq.com> Date: Sun, 21 Sep 2025 21:35:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=94=BB=E5=83=8F=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomerProfileAnalysisController.java | 525 ++++++++++++++++++ ...CustomerProfileAnalysisControllerTest.java | 306 ++++++++++ .../CpaBrandCoreSchedulerLogicTest.java | 183 ++++++ 3 files changed, 1014 insertions(+) create mode 100644 src/main/java/com/rj/controller/biz/CustomerProfileAnalysisController.java create mode 100644 src/test/java/com/rj/controller/CustomerProfileAnalysisControllerTest.java create mode 100644 src/test/java/com/rj/scheduler/CpaBrandCoreSchedulerLogicTest.java diff --git a/src/main/java/com/rj/controller/biz/CustomerProfileAnalysisController.java b/src/main/java/com/rj/controller/biz/CustomerProfileAnalysisController.java new file mode 100644 index 0000000..76a876d --- /dev/null +++ b/src/main/java/com/rj/controller/biz/CustomerProfileAnalysisController.java @@ -0,0 +1,525 @@ +package com.rj.controller.biz; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.rj.entity.CustomerProfileAnalysis; +import com.rj.service.biz.ICustomerProfileAnalysisService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 客户画像分析 前端控制器 + * + * @author 李中华 + * @date 2025/1/15 + */ +@Slf4j +@RestController +@RequestMapping("/api/customer-profile-analysis") +@Tag(name = "客户画像分析", description = "客户画像分析相关接口") +public class CustomerProfileAnalysisController { + + @Autowired + private ICustomerProfileAnalysisService customerProfileAnalysisService; + + /** + * 分页查询客户画像分析列表 + */ + @GetMapping("/list") + @Operation(summary = "分页查询客户画像分析列表", description = "根据条件分页查询客户画像分析信息列表") + public ResponseEntity> getAnalysisList( + @Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer current, + @Parameter(description = "每页大小", example = "10") @RequestParam(defaultValue = "10") Integer size, + @Parameter(description = "分析记录ID") @RequestParam(required = false) String profileAnalysisRecordId, + @Parameter(description = "业务ID") @RequestParam(required = false) String relatedBusinessId, + @Parameter(description = "经销商编码") @RequestParam(required = false) String dealerCode, + @Parameter(description = "经销商名称(模糊查询)") @RequestParam(required = false) String dealerName, + @Parameter(description = "大区") @RequestParam(required = false) String bigArea, + @Parameter(description = "分析场景类型") @RequestParam(required = false) Integer analysisSceneType, + @Parameter(description = "客户姓名(模糊查询)") @RequestParam(required = false) String clientName, + @Parameter(description = "客户电话") @RequestParam(required = false) String clientPhone, + @Parameter(description = "开始日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + + log.info("分页查询客户画像分析列表,分析记录ID={}, 业务ID={}, 经销商编码={}, 经销商名称={}, 大区={}, 分析场景类型={}, 客户姓名={}, 客户电话={}, 开始日期={}, 结束日期={}", + profileAnalysisRecordId, relatedBusinessId, dealerCode, dealerName, bigArea, analysisSceneType, clientName, clientPhone, startDate, endDate); + + Map result = new HashMap<>(); + try { + Page page = new Page<>(current, size); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 添加查询条件 + if (profileAnalysisRecordId != null && !profileAnalysisRecordId.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getProfileAnalysisRecordId, profileAnalysisRecordId); + } + if (relatedBusinessId != null && !relatedBusinessId.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getRelatedBusinessId, relatedBusinessId); + } + if (dealerCode != null && !dealerCode.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getDealerCode, dealerCode); + } + if (dealerName != null && !dealerName.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getDealerName, dealerName); + } + if (bigArea != null && !bigArea.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getBigArea, bigArea); + } + if (analysisSceneType != null) { + queryWrapper.eq(CustomerProfileAnalysis::getAnalysisSceneType, analysisSceneType); + } + if (clientName != null && !clientName.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getClientName, clientName); + } + if (clientPhone != null && !clientPhone.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getClientPhone, clientPhone); + } + if (startDate != null) { + queryWrapper.ge(CustomerProfileAnalysis::getInteractionDate, startDate.atStartOfDay()); + } + if (endDate != null) { + queryWrapper.le(CustomerProfileAnalysis::getInteractionDate, endDate.atTime(23, 59, 59)); + } + + // 只查询未删除的记录 + queryWrapper.eq(CustomerProfileAnalysis::getIsDeleted, 0); + + // 按创建时间倒序排列 + queryWrapper.orderByDesc(CustomerProfileAnalysis::getCreatedAt); + + Page analysisPage = customerProfileAnalysisService.page(page, queryWrapper); + + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", analysisPage.getRecords()); + result.put("total", analysisPage.getTotal()); + result.put("current", analysisPage.getCurrent()); + result.put("size", analysisPage.getSize()); + result.put("pages", analysisPage.getPages()); + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("分页查询客户画像分析列表失败", e); + result.put("success", false); + result.put("message", "查询异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 根据条件查询客户画像分析(不分页) + */ + @GetMapping("/query") + @Operation(summary = "根据条件查询客户画像分析", description = "根据条件查询客户画像分析信息(不分页)") + public ResponseEntity> getAnalysisList( + @Parameter(description = "分析记录ID") @RequestParam(required = false) String profileAnalysisRecordId, + @Parameter(description = "业务ID") @RequestParam(required = false) String relatedBusinessId, + @Parameter(description = "经销商编码") @RequestParam(required = false) String dealerCode, + @Parameter(description = "大区") @RequestParam(required = false) String bigArea, + @Parameter(description = "分析场景类型") @RequestParam(required = false) Integer analysisSceneType, + @Parameter(description = "客户姓名") @RequestParam(required = false) String clientName, + @Parameter(description = "开始日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + + Map result = new HashMap<>(); + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 添加查询条件 + if (profileAnalysisRecordId != null && !profileAnalysisRecordId.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getProfileAnalysisRecordId, profileAnalysisRecordId); + } + if (relatedBusinessId != null && !relatedBusinessId.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getRelatedBusinessId, relatedBusinessId); + } + if (dealerCode != null && !dealerCode.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getDealerCode, dealerCode); + } + if (bigArea != null && !bigArea.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getBigArea, bigArea); + } + if (analysisSceneType != null) { + queryWrapper.eq(CustomerProfileAnalysis::getAnalysisSceneType, analysisSceneType); + } + if (clientName != null && !clientName.trim().isEmpty()) { + queryWrapper.like(CustomerProfileAnalysis::getClientName, clientName); + } + if (startDate != null) { + queryWrapper.ge(CustomerProfileAnalysis::getInteractionDate, startDate.atStartOfDay()); + } + if (endDate != null) { + queryWrapper.le(CustomerProfileAnalysis::getInteractionDate, endDate.atTime(23, 59, 59)); + } + + // 只查询未删除的记录 + queryWrapper.eq(CustomerProfileAnalysis::getIsDeleted, 0); + + // 按创建时间倒序排列 + queryWrapper.orderByDesc(CustomerProfileAnalysis::getCreatedAt); + + List analysisList = customerProfileAnalysisService.list(queryWrapper); + + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", analysisList); + result.put("total", analysisList.size()); + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("根据条件查询客户画像分析失败", e); + result.put("success", false); + result.put("message", "查询异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 根据ID查询客户画像分析 + */ + @GetMapping("/{id}") + @Operation(summary = "根据ID查询客户画像分析", description = "根据主键ID查询单条客户画像分析信息") + public ResponseEntity> getAnalysisById( + @Parameter(description = "客户画像分析ID", required = true) + @PathVariable String id) { + + Map result = new HashMap<>(); + try { + CustomerProfileAnalysis analysis = customerProfileAnalysisService.getById(id); + if (analysis != null && analysis.getIsDeleted() == 0) { + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", analysis); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "客户画像分析不存在或已删除"); + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + log.error("根据ID查询客户画像分析失败", e); + result.put("success", false); + result.put("message", "查询异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 根据分析记录ID查询客户画像分析 + */ + @GetMapping("/by-record-id/{profileAnalysisRecordId}") + @Operation(summary = "根据分析记录ID查询客户画像分析", description = "根据分析记录ID查询客户画像分析信息") + public ResponseEntity> getAnalysisByRecordId( + @Parameter(description = "分析记录ID", required = true) + @PathVariable String profileAnalysisRecordId) { + + Map result = new HashMap<>(); + try { + CustomerProfileAnalysis analysis = customerProfileAnalysisService.getByProfileAnalysisRecordId(profileAnalysisRecordId); + if (analysis != null) { + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", analysis); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "未找到对应的客户画像分析记录"); + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + log.error("根据分析记录ID查询客户画像分析失败", e); + result.put("success", false); + result.put("message", "查询异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 根据业务ID查询客户画像分析 + */ + @GetMapping("/by-business-id/{businessId}") + @Operation(summary = "根据业务ID查询客户画像分析", description = "根据业务ID查询客户画像分析信息") + public ResponseEntity> getAnalysisByBusinessId( + @Parameter(description = "业务ID", required = true) + @PathVariable String businessId) { + + Map result = new HashMap<>(); + try { + CustomerProfileAnalysis analysis = customerProfileAnalysisService.getByBusinessId(businessId); + if (analysis != null) { + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", analysis); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "未找到对应的客户画像分析记录"); + return ResponseEntity.notFound().build(); + } + } catch (Exception e) { + log.error("根据业务ID查询客户画像分析失败", e); + result.put("success", false); + result.put("message", "查询异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 新增客户画像分析 + */ + @PostMapping("/add") + @Operation(summary = "新增客户画像分析", description = "新增一条客户画像分析记录") + public ResponseEntity> addAnalysis( + @Parameter(description = "客户画像分析信息", required = true) + @Valid @RequestBody CustomerProfileAnalysis analysis) { + + Map result = new HashMap<>(); + try { + // 设置系统字段 + LocalDateTime now = LocalDateTime.now(); + analysis.setCreatedAt(now); + analysis.setUpdatedAt(now); + analysis.setRecordVersion(0); + analysis.setIsDeleted(0); + + boolean success = customerProfileAnalysisService.saveCustomerProfileAnalysis(analysis); + if (success) { + result.put("success", true); + result.put("message", "客户画像分析添加成功"); + result.put("data", analysis); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "客户画像分析添加失败"); + return ResponseEntity.badRequest().body(result); + } + } catch (Exception e) { + log.error("新增客户画像分析失败", e); + result.put("success", false); + result.put("message", "添加异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 修改客户画像分析 + */ + @PutMapping("/update") + @Operation(summary = "修改客户画像分析", description = "修改客户画像分析信息") + public ResponseEntity> updateAnalysis( + @Parameter(description = "客户画像分析信息", required = true) + @Valid @RequestBody CustomerProfileAnalysis analysis) { + + Map result = new HashMap<>(); + try { + if (analysis.getId() == null || analysis.getId().trim().isEmpty()) { + result.put("success", false); + result.put("message", "ID不能为空"); + return ResponseEntity.badRequest().body(result); + } + + // 检查记录是否存在 + CustomerProfileAnalysis existingAnalysis = customerProfileAnalysisService.getById(analysis.getId()); + if (existingAnalysis == null || existingAnalysis.getIsDeleted() == 1) { + result.put("success", false); + result.put("message", "客户画像分析不存在或已删除"); + return ResponseEntity.notFound().build(); + } + + // 设置更新时间 + analysis.setUpdatedAt(LocalDateTime.now()); + + boolean success = customerProfileAnalysisService.updateById(analysis); + if (success) { + result.put("success", true); + result.put("message", "客户画像分析修改成功"); + result.put("data", analysis); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "客户画像分析修改失败"); + return ResponseEntity.badRequest().body(result); + } + } catch (Exception e) { + log.error("修改客户画像分析失败", e); + result.put("success", false); + result.put("message", "修改异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 删除客户画像分析(逻辑删除) + */ + @DeleteMapping("/delete/{id}") + @Operation(summary = "删除客户画像分析", description = "根据ID逻辑删除客户画像分析记录") + public ResponseEntity> deleteAnalysis( + @Parameter(description = "客户画像分析ID", required = true) + @PathVariable String id) { + + Map result = new HashMap<>(); + try { + // 检查记录是否存在 + CustomerProfileAnalysis existingAnalysis = customerProfileAnalysisService.getById(id); + if (existingAnalysis == null || existingAnalysis.getIsDeleted() == 1) { + result.put("success", false); + result.put("message", "客户画像分析不存在或已删除"); + return ResponseEntity.notFound().build(); + } + + // 逻辑删除 + existingAnalysis.setIsDeleted(1); + existingAnalysis.setUpdatedAt(LocalDateTime.now()); + + boolean success = customerProfileAnalysisService.updateById(existingAnalysis); + if (success) { + result.put("success", true); + result.put("message", "客户画像分析删除成功"); + return ResponseEntity.ok(result); + } else { + result.put("success", false); + result.put("message", "客户画像分析删除失败"); + return ResponseEntity.badRequest().body(result); + } + } catch (Exception e) { + log.error("删除客户画像分析失败", e); + result.put("success", false); + result.put("message", "删除异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 批量删除客户画像分析(逻辑删除) + */ + @DeleteMapping("/batch-delete") + @Operation(summary = "批量删除客户画像分析", description = "根据ID列表批量逻辑删除客户画像分析记录") + public ResponseEntity> batchDeleteAnalysis( + @Parameter(description = "客户画像分析ID列表", required = true) + @RequestBody List ids) { + + Map result = new HashMap<>(); + try { + if (ids == null || ids.isEmpty()) { + result.put("success", false); + result.put("message", "ID列表不能为空"); + return ResponseEntity.badRequest().body(result); + } + + LocalDateTime now = LocalDateTime.now(); + int successCount = 0; + int failCount = 0; + + for (String id : ids) { + try { + CustomerProfileAnalysis existingAnalysis = customerProfileAnalysisService.getById(id); + if (existingAnalysis != null && existingAnalysis.getIsDeleted() == 0) { + existingAnalysis.setIsDeleted(1); + existingAnalysis.setUpdatedAt(now); + if (customerProfileAnalysisService.updateById(existingAnalysis)) { + successCount++; + } else { + failCount++; + } + } else { + failCount++; + } + } catch (Exception e) { + log.error("删除客户画像分析失败,ID: {}", id, e); + failCount++; + } + } + + result.put("success", true); + result.put("message", String.format("批量删除完成,成功:%d,失败:%d", successCount, failCount)); + result.put("data", Map.of("successCount", successCount, "failCount", failCount)); + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("批量删除客户画像分析失败", e); + result.put("success", false); + result.put("message", "批量删除异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + + /** + * 获取客户画像分析统计信息 + */ + @GetMapping("/statistics") + @Operation(summary = "获取客户画像分析统计信息", description = "获取客户画像分析的统计信息") + public ResponseEntity> getAnalysisStatistics( + @Parameter(description = "开始日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate, + @Parameter(description = "经销商编码") @RequestParam(required = false) String dealerCode, + @Parameter(description = "大区") @RequestParam(required = false) String bigArea) { + + Map result = new HashMap<>(); + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 添加查询条件 + if (startDate != null) { + queryWrapper.ge(CustomerProfileAnalysis::getInteractionDate, startDate.atStartOfDay()); + } + if (endDate != null) { + queryWrapper.le(CustomerProfileAnalysis::getInteractionDate, endDate.atTime(23, 59, 59)); + } + if (dealerCode != null && !dealerCode.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getDealerCode, dealerCode); + } + if (bigArea != null && !bigArea.trim().isEmpty()) { + queryWrapper.eq(CustomerProfileAnalysis::getBigArea, bigArea); + } + + // 只查询未删除的记录 + queryWrapper.eq(CustomerProfileAnalysis::getIsDeleted, 0); + + List analysisList = customerProfileAnalysisService.list(queryWrapper); + + // 计算统计信息 + Map statistics = new HashMap<>(); + statistics.put("totalCount", analysisList.size()); + statistics.put("uniqueDealers", analysisList.stream().map(CustomerProfileAnalysis::getDealerCode).distinct().count()); + statistics.put("uniqueClients", analysisList.stream().map(CustomerProfileAnalysis::getClientId).distinct().count()); + + // 按分析场景类型统计 + Map sceneTypeStats = analysisList.stream() + .collect(java.util.stream.Collectors.groupingBy( + CustomerProfileAnalysis::getAnalysisSceneType, + java.util.stream.Collectors.counting())); + statistics.put("sceneTypeStatistics", sceneTypeStats); + + // 按大区统计 + Map bigAreaStats = analysisList.stream() + .filter(analysis -> analysis.getBigArea() != null) + .collect(java.util.stream.Collectors.groupingBy( + CustomerProfileAnalysis::getBigArea, + java.util.stream.Collectors.counting())); + statistics.put("bigAreaStatistics", bigAreaStats); + + result.put("success", true); + result.put("message", "统计信息获取成功"); + result.put("data", statistics); + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("获取客户画像分析统计信息失败", e); + result.put("success", false); + result.put("message", "统计信息获取异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } +} diff --git a/src/test/java/com/rj/controller/CustomerProfileAnalysisControllerTest.java b/src/test/java/com/rj/controller/CustomerProfileAnalysisControllerTest.java new file mode 100644 index 0000000..188e354 --- /dev/null +++ b/src/test/java/com/rj/controller/CustomerProfileAnalysisControllerTest.java @@ -0,0 +1,306 @@ +package com.rj.controller; + +import com.rj.controller.biz.CustomerProfileAnalysisController; +import com.rj.entity.CustomerProfileAnalysis; +import com.rj.service.biz.ICustomerProfileAnalysisService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * CustomerProfileAnalysisController 测试类 + * + * @author 李中华 + * @date 2025/1/15 + */ +@WebMvcTest(CustomerProfileAnalysisController.class) +@DisplayName("客户画像分析控制器测试") +class CustomerProfileAnalysisControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private ICustomerProfileAnalysisService customerProfileAnalysisService; + + private CustomerProfileAnalysis testAnalysis; + + @BeforeEach + void setUp() { + testAnalysis = new CustomerProfileAnalysis(); + testAnalysis.setId("test-id-123"); + testAnalysis.setProfileAnalysisRecordId("profile-record-001"); + testAnalysis.setAnalysisSceneType(1); + testAnalysis.setInteractionDate(LocalDateTime.now()); + testAnalysis.setRelatedBusinessId("business-123"); + testAnalysis.setDealerCode("DEALER001"); + testAnalysis.setDealerName("测试经销商"); + testAnalysis.setBigArea("华北区"); + testAnalysis.setClientName("张三"); + testAnalysis.setClientPhone("13800138000"); + testAnalysis.setCreatedAt(LocalDateTime.now()); + testAnalysis.setUpdatedAt(LocalDateTime.now()); + testAnalysis.setRecordVersion(0); + testAnalysis.setIsDeleted(0); + } + + @Test + @DisplayName("测试根据ID查询客户画像分析") + void testGetAnalysisById() throws Exception { + // Given + when(customerProfileAnalysisService.getById("test-id-123")).thenReturn(testAnalysis); + + // When & Then + mockMvc.perform(get("/api/customer-profile-analysis/test-id-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("查询成功")) + .andExpect(jsonPath("$.data.id").value("test-id-123")) + .andExpect(jsonPath("$.data.dealerName").value("测试经销商")); + + verify(customerProfileAnalysisService, times(1)).getById("test-id-123"); + } + + @Test + @DisplayName("测试根据分析记录ID查询客户画像分析") + void testGetAnalysisByRecordId() throws Exception { + // Given + when(customerProfileAnalysisService.getByProfileAnalysisRecordId("profile-record-001")) + .thenReturn(testAnalysis); + + // When & Then + mockMvc.perform(get("/api/customer-profile-analysis/by-record-id/profile-record-001")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("查询成功")) + .andExpect(jsonPath("$.data.profileAnalysisRecordId").value("profile-record-001")); + + verify(customerProfileAnalysisService, times(1)) + .getByProfileAnalysisRecordId("profile-record-001"); + } + + @Test + @DisplayName("测试根据业务ID查询客户画像分析") + void testGetAnalysisByBusinessId() throws Exception { + // Given + when(customerProfileAnalysisService.getByBusinessId("business-123")) + .thenReturn(testAnalysis); + + // When & Then + mockMvc.perform(get("/api/customer-profile-analysis/by-business-id/business-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("查询成功")) + .andExpect(jsonPath("$.data.relatedBusinessId").value("business-123")); + + verify(customerProfileAnalysisService, times(1)) + .getByBusinessId("business-123"); + } + + @Test + @DisplayName("测试新增客户画像分析") + void testAddAnalysis() throws Exception { + // Given + when(customerProfileAnalysisService.saveCustomerProfileAnalysis(any(CustomerProfileAnalysis.class))) + .thenReturn(true); + + String requestJson = """ + { + "profileAnalysisRecordId": "profile-record-002", + "analysisSceneType": 1, + "relatedBusinessId": "business-456", + "dealerCode": "DEALER002", + "dealerName": "测试经销商2", + "bigArea": "华东区", + "clientName": "李四", + "clientPhone": "13900139000" + } + """; + + // When & Then + mockMvc.perform(post("/api/customer-profile-analysis/add") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("客户画像分析添加成功")); + + verify(customerProfileAnalysisService, times(1)) + .saveCustomerProfileAnalysis(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试修改客户画像分析") + void testUpdateAnalysis() throws Exception { + // Given + when(customerProfileAnalysisService.getById("test-id-123")).thenReturn(testAnalysis); + when(customerProfileAnalysisService.updateById(any(CustomerProfileAnalysis.class))) + .thenReturn(true); + + String requestJson = """ + { + "id": "test-id-123", + "profileAnalysisRecordId": "profile-record-001", + "analysisSceneType": 1, + "relatedBusinessId": "business-123", + "dealerCode": "DEALER001", + "dealerName": "测试经销商(修改)", + "bigArea": "华北区", + "clientName": "张三(修改)", + "clientPhone": "13800138000" + } + """; + + // When & Then + mockMvc.perform(put("/api/customer-profile-analysis/update") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("客户画像分析修改成功")); + + verify(customerProfileAnalysisService, times(1)).getById("test-id-123"); + verify(customerProfileAnalysisService, times(1)) + .updateById(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试删除客户画像分析") + void testDeleteAnalysis() throws Exception { + // Given + when(customerProfileAnalysisService.getById("test-id-123")).thenReturn(testAnalysis); + when(customerProfileAnalysisService.updateById(any(CustomerProfileAnalysis.class))) + .thenReturn(true); + + // When & Then + mockMvc.perform(delete("/api/customer-profile-analysis/delete/test-id-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("客户画像分析删除成功")); + + verify(customerProfileAnalysisService, times(1)).getById("test-id-123"); + verify(customerProfileAnalysisService, times(1)) + .updateById(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试批量删除客户画像分析") + void testBatchDeleteAnalysis() throws Exception { + // Given + when(customerProfileAnalysisService.getById(anyString())).thenReturn(testAnalysis); + when(customerProfileAnalysisService.updateById(any(CustomerProfileAnalysis.class))) + .thenReturn(true); + + String requestJson = """ + ["test-id-123", "test-id-456"] + """; + + // When & Then + mockMvc.perform(delete("/api/customer-profile-analysis/batch-delete") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value("批量删除完成,成功:2,失败:0")); + + verify(customerProfileAnalysisService, times(2)).getById(anyString()); + verify(customerProfileAnalysisService, times(2)) + .updateById(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试查询不存在的记录") + void testGetNonExistentAnalysis() throws Exception { + // Given + when(customerProfileAnalysisService.getById("non-existent-id")).thenReturn(null); + + // When & Then + mockMvc.perform(get("/api/customer-profile-analysis/non-existent-id")) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.message").value("客户画像分析不存在或已删除")); + + verify(customerProfileAnalysisService, times(1)).getById("non-existent-id"); + } + + @Test + @DisplayName("测试新增客户画像分析失败") + void testAddAnalysisFailure() throws Exception { + // Given + when(customerProfileAnalysisService.saveCustomerProfileAnalysis(any(CustomerProfileAnalysis.class))) + .thenReturn(false); + + String requestJson = """ + { + "profileAnalysisRecordId": "profile-record-003", + "analysisSceneType": 1, + "relatedBusinessId": "business-789" + } + """; + + // When & Then + mockMvc.perform(post("/api/customer-profile-analysis/add") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.message").value("客户画像分析添加失败")); + + verify(customerProfileAnalysisService, times(1)) + .saveCustomerProfileAnalysis(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试修改客户画像分析 - ID为空") + void testUpdateAnalysisWithEmptyId() throws Exception { + // Given + String requestJson = """ + { + "id": "", + "profileAnalysisRecordId": "profile-record-001", + "analysisSceneType": 1 + } + """; + + // When & Then + mockMvc.perform(put("/api/customer-profile-analysis/update") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.message").value("ID不能为空")); + + verify(customerProfileAnalysisService, never()).getById(anyString()); + verify(customerProfileAnalysisService, never()).updateById(any(CustomerProfileAnalysis.class)); + } + + @Test + @DisplayName("测试批量删除客户画像分析 - 空ID列表") + void testBatchDeleteAnalysisWithEmptyList() throws Exception { + // Given + String requestJson = "[]"; + + // When & Then + mockMvc.perform(delete("/api/customer-profile-analysis/batch-delete") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.message").value("ID列表不能为空")); + + verify(customerProfileAnalysisService, never()).getById(anyString()); + verify(customerProfileAnalysisService, never()).updateById(any(CustomerProfileAnalysis.class)); + } +} diff --git a/src/test/java/com/rj/scheduler/CpaBrandCoreSchedulerLogicTest.java b/src/test/java/com/rj/scheduler/CpaBrandCoreSchedulerLogicTest.java new file mode 100644 index 0000000..c36b7e4 --- /dev/null +++ b/src/test/java/com/rj/scheduler/CpaBrandCoreSchedulerLogicTest.java @@ -0,0 +1,183 @@ +package com.rj.scheduler; + +import com.rj.service.biz.ICpaBrandScoreStatisticsService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** + * CpaBrandCoreScheduler 逻辑测试类 + * 测试修改后的调度器逻辑 + * + * @author 李中华 + * @date 2025/1/15 + */ +@SpringBootTest +@ActiveProfiles("test") +@DisplayName("经销商品牌得分统计调度器逻辑测试") +class CpaBrandCoreSchedulerLogicTest { + + @Autowired + private ICpaBrandScoreStatisticsService cpaBrandScoreStatisticsService; + + @Autowired + private CpaBrandCoreScheduler cpaBrandCoreScheduler; + + + @Test + @DisplayName("测试周统计任务 - 服务层自动计算时间范围") + void testGenerateWeeklyStatistics_AutoCalculateTimeRange() { + // Given + // When + cpaBrandCoreScheduler.generateWeeklyStatistics(); + + + } + + @Test + @DisplayName("测试月统计任务 - 服务层自动计算时间范围") + void testGenerateMonthlyStatistics_AutoCalculateTimeRange() { + + // When + cpaBrandCoreScheduler.generateMonthlyStatistics(); + + + } + + @Test + @DisplayName("测试日统计任务 - 仍然需要传递具体日期") + void testGenerateDailyStatistics_StillRequiresDate() { + + // When + cpaBrandCoreScheduler.generateDailyStatistics(); + + } + + @Test + @DisplayName("测试总计统计任务 - 不需要日期参数") + void testGenerateTotalStatistics_NoDateRequired() { + + // When + cpaBrandCoreScheduler.generateTotalStatistics(); + + } + + @Test + @DisplayName("测试手动触发周统计 - 参数被忽略") + void testManualGenerateStatisticsByRange_Weekly_ParametersIgnored() { + // Given + LocalDate dummyStartDate = LocalDate.of(2025, 1, 1); + LocalDate dummyEndDate = LocalDate.of(2025, 1, 7); + int expectedCount = 20; + + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("weekly"), eq(dummyStartDate), eq(dummyEndDate), eq("manual"))) + .thenReturn(expectedCount); + + // When + int actualCount = cpaBrandCoreScheduler.manualGenerateStatisticsByRange(dummyStartDate, dummyEndDate, "weekly"); + + // Then + assertEquals(expectedCount, actualCount); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("weekly"), eq(dummyStartDate), eq(dummyEndDate), eq("manual")); + } + + @Test + @DisplayName("测试手动触发月统计 - 参数被忽略") + void testManualGenerateStatisticsByRange_Monthly_ParametersIgnored() { + // Given + LocalDate dummyStartDate = LocalDate.of(2025, 1, 1); + LocalDate dummyEndDate = LocalDate.of(2025, 1, 31); + int expectedCount = 80; + + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("monthly"), eq(dummyStartDate), eq(dummyEndDate), eq("manual"))) + .thenReturn(expectedCount); + + // When + int actualCount = cpaBrandCoreScheduler.manualGenerateStatisticsByRange(dummyStartDate, dummyEndDate, "monthly"); + + // Then + assertEquals(expectedCount, actualCount); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("monthly"), eq(dummyStartDate), eq(dummyEndDate), eq("manual")); + } + + @Test + @DisplayName("测试手动触发日统计 - 参数仍然有效") + void testManualGenerateStatisticsByRange_Daily_ParametersStillValid() { + // Given + LocalDate testDate = LocalDate.of(2025, 1, 15); + int expectedCount = 12; + + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("daily"), eq(testDate), eq(testDate), eq("manual"))) + .thenReturn(expectedCount); + + // When + int actualCount = cpaBrandCoreScheduler.manualGenerateStatisticsByRange(testDate, testDate, "daily"); + + // Then + assertEquals(expectedCount, actualCount); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("daily"), eq(testDate), eq(testDate), eq("manual")); + } + + @Test + @DisplayName("测试手动触发总计统计 - 参数被忽略") + void testManualGenerateStatisticsByRange_Total_ParametersIgnored() { + // Given + LocalDate dummyStartDate = LocalDate.of(2025, 1, 1); + LocalDate dummyEndDate = LocalDate.of(2025, 12, 31); + int expectedCount = 500; + + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("total"), eq(dummyStartDate), eq(dummyEndDate), eq("manual"))) + .thenReturn(expectedCount); + + // When + int actualCount = cpaBrandCoreScheduler.manualGenerateStatisticsByRange(dummyStartDate, dummyEndDate, "total"); + + // Then + assertEquals(expectedCount, actualCount); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("total"), eq(dummyStartDate), eq(dummyEndDate), eq("manual")); + } + + @Test + @DisplayName("测试异常处理 - 周统计任务异常") + void testGenerateWeeklyStatistics_Exception() { + // Given + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("weekly"), isNull(), isNull(), eq("system"))) + .thenThrow(new RuntimeException("周统计服务异常")); + + // When & Then + assertDoesNotThrow(() -> cpaBrandCoreScheduler.generateWeeklyStatistics()); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("weekly"), isNull(), isNull(), eq("system")); + } + + @Test + @DisplayName("测试异常处理 - 月统计任务异常") + void testGenerateMonthlyStatistics_Exception() { + // Given + when(cpaBrandScoreStatisticsService.generateAndSaveStatistics( + eq("monthly"), isNull(), isNull(), eq("system"))) + .thenThrow(new RuntimeException("月统计服务异常")); + + // When & Then + assertDoesNotThrow(() -> cpaBrandCoreScheduler.generateMonthlyStatistics()); + verify(cpaBrandScoreStatisticsService, times(1)) + .generateAndSaveStatistics(eq("monthly"), isNull(), isNull(), eq("system")); + } +}