9 changed files with 571 additions and 9 deletions
@ -0,0 +1,354 @@ |
|||||
|
package com.zdxt.rest.test.cp; |
||||
|
|
||||
|
import cn.dev33.satoken.context.mock.SaTokenContextMockUtil; |
||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
|
import com.zdxt.enforcementcode.domain.EfcodeCpPlan; |
||||
|
import com.zdxt.enforcementcode.domain.EfcodeCpReserve; |
||||
|
import com.zdxt.enforcementcode.mapper.EfcodeCpPlanMapper; |
||||
|
import com.zdxt.enforcementcode.mapper.EfcodeCpReserveMapper; |
||||
|
import com.zdxt.enforcementcode.service.IEfcodeCpPlanService; |
||||
|
import org.dromara.ZdxtAdminApplication; |
||||
|
import org.junit.jupiter.api.*; |
||||
|
import org.junit.jupiter.api.Tag; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; |
||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
import org.springframework.mock.web.MockHttpServletRequest; |
||||
|
import org.springframework.test.context.ActiveProfiles; |
||||
|
import org.springframework.web.context.request.RequestContextHolder; |
||||
|
import org.springframework.web.context.request.ServletRequestAttributes; |
||||
|
|
||||
|
import java.time.LocalDate; |
||||
|
import java.time.ZoneId; |
||||
|
import java.util.*; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
/** |
||||
|
* 计划自动匹配预约 autoMatchReserve 方法测试 |
||||
|
* <p> |
||||
|
* 测试场景: |
||||
|
* 1. 强制模式下执行自动匹配(绕过日期校验) |
||||
|
* 2. 验证匹配结果:同企业不同单位的计划被联合 |
||||
|
* 3. 验证计划状态更新:system_reserved=1, reserve_status=2 |
||||
|
* 4. 验证预约记录生成:相同 plan_group_id 关联多条记录 |
||||
|
* </p> |
||||
|
*/ |
||||
|
@SpringBootTest(classes = ZdxtAdminApplication.class) |
||||
|
@AutoConfigureMockMvc |
||||
|
@ActiveProfiles({"dev", "dev-cust-zrb"}) |
||||
|
@DisplayName("autoMatchReserve 自动匹配预约测试") |
||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) |
||||
|
@Tag("dev") |
||||
|
public class AutoMatchReserveTest { |
||||
|
|
||||
|
@Autowired |
||||
|
private IEfcodeCpPlanService cpPlanService; |
||||
|
|
||||
|
@Autowired |
||||
|
private EfcodeCpPlanMapper cpPlanMapper; |
||||
|
|
||||
|
@Autowired |
||||
|
private EfcodeCpReserveMapper cpReserveMapper; |
||||
|
|
||||
|
@BeforeEach |
||||
|
public void setUp() { |
||||
|
SaTokenContextMockUtil.setMockContext(); |
||||
|
MockHttpServletRequest mockRequest = new MockHttpServletRequest(); |
||||
|
mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); |
||||
|
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest)); |
||||
|
} |
||||
|
|
||||
|
@AfterEach |
||||
|
public void tearDown() { |
||||
|
try { |
||||
|
StpUtil.logout(); |
||||
|
} catch (Exception ignored) { |
||||
|
} |
||||
|
RequestContextHolder.resetRequestAttributes(); |
||||
|
SaTokenContextMockUtil.clearContext(); |
||||
|
} |
||||
|
|
||||
|
@DisplayName("0. 插入1000条适合匹配的计划数据") |
||||
|
@Test |
||||
|
@Order(0) |
||||
|
public void testInsert1000MatchablePlans() { |
||||
|
// 模拟登录(避免自动填充 createBy 报空)
|
||||
|
StpUtil.login(1); |
||||
|
|
||||
|
// 准备数据:200个企业 × 每企业5个不同执法单位 = 1000条计划
|
||||
|
int enterpriseCount = 200; |
||||
|
int unitsPerEnterprise = 5; |
||||
|
Random random = new Random(); |
||||
|
|
||||
|
// 模拟执法单位ID池(确保不同单位)
|
||||
|
String[] bureauDeptIds = { |
||||
|
"BUREAU_TEST_A", "BUREAU_TEST_B", "BUREAU_TEST_C", |
||||
|
"BUREAU_TEST_D", "BUREAU_TEST_E", "BUREAU_TEST_F", |
||||
|
"BUREAU_TEST_G", "BUREAU_TEST_H" |
||||
|
}; |
||||
|
String[] bureauDeptNames = { |
||||
|
"测试执法局A", "测试执法局B", "测试执法局C", |
||||
|
"测试执法局D", "测试执法局E", "测试执法局F", |
||||
|
"测试执法局G", "测试执法局H" |
||||
|
}; |
||||
|
|
||||
|
// 基准日期:从明天开始
|
||||
|
LocalDate baseDate = LocalDate.now().plusDays(1); |
||||
|
int insertedCount = 0; |
||||
|
|
||||
|
for (int e = 0; e < enterpriseCount; e++) { |
||||
|
String enterpriseNumber = String.format("91440300TEST%06d", e); |
||||
|
String enterpriseName = "测试企业" + e + "号"; |
||||
|
String enterpriseId = "ENT_TEST_" + e; |
||||
|
|
||||
|
// 同一企业的计划日期在基准日期附3天内(确保一周内可匹配)
|
||||
|
Date planDate = Date.from( |
||||
|
baseDate.plusDays(random.nextInt(3)) |
||||
|
.atStartOfDay(ZoneId.systemDefault()).toInstant() |
||||
|
); |
||||
|
|
||||
|
for (int u = 0; u < unitsPerEnterprise; u++) { |
||||
|
int bureauIdx = (e + u) % bureauDeptIds.length; |
||||
|
|
||||
|
EfcodeCpPlan plan = new EfcodeCpPlan(); |
||||
|
plan.setId(UUID.randomUUID().toString().replace("-", "")); |
||||
|
plan.setPlanType(random.nextBoolean() ? 1L : 2L); |
||||
|
plan.setPlanDate(planDate); |
||||
|
plan.setPlanTime((long) (random.nextInt(3) + 1)); // 1=上午,2=下午,3=其他
|
||||
|
plan.setPlanActualDate(plan.getPlanDate()); |
||||
|
plan.setPlanActualTime(plan.getPlanTime()); // 1=上午,2=下午,3=其他
|
||||
|
plan.setEnforceContent("自动匹配测试-执法事项" + e + "-" + u); |
||||
|
plan.setEnterpriseName(enterpriseName); |
||||
|
plan.setEnterpriseNumber(enterpriseNumber); |
||||
|
plan.setEnterpriseId(enterpriseId); |
||||
|
plan.setEnforceUserNames("测试人员" + u); |
||||
|
plan.setEnforceUserIds(String.valueOf(1000 + u)); |
||||
|
plan.setEnforceUserCodes("CODE" + u); |
||||
|
plan.setEnforceUserTels("1380000" + String.format("%04d", u)); |
||||
|
plan.setIsPublic(1L); |
||||
|
plan.setIsUnion(1L); |
||||
|
plan.setReserveStatus(1L); // 待预约
|
||||
|
plan.setSystemReserved(0L); // 未经系统匹配
|
||||
|
plan.setManualReserved(0L); |
||||
|
plan.setBureauDeptId(bureauDeptIds[bureauIdx]); |
||||
|
plan.setBureauDeptName(bureauDeptNames[bureauIdx]); |
||||
|
plan.setOrganId(plan.getBureauDeptId()); |
||||
|
plan.setOrganName(plan.getBureauDeptName()); |
||||
|
plan.setDeptId(plan.getBureauDeptId()); |
||||
|
plan.setDeptName(plan.getBureauDeptName()); |
||||
|
plan.setIsDelete(0L); |
||||
|
plan.setCreateBy(1L); |
||||
|
plan.setCreateTime(new Date()); |
||||
|
plan.setUpdateBy(1L); |
||||
|
plan.setUpdateTime(new Date()); |
||||
|
|
||||
|
cpPlanMapper.insert(plan); |
||||
|
insertedCount++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
System.out.println("========== 插入匹配测试数据完成 =========="); |
||||
|
System.out.println("共插入计划数: " + insertedCount); |
||||
|
System.out.println("涉及企业数: " + enterpriseCount); |
||||
|
System.out.println("每企业不同单位数: " + unitsPerEnterprise); |
||||
|
System.out.println("预期可匹配企业组数: " + enterpriseCount + " (每企业都有>=2个单位)"); |
||||
|
System.out.println("==========================================="); |
||||
|
|
||||
|
Assertions.assertEquals(1000, insertedCount, "应插入恰好1000条计划"); |
||||
|
} |
||||
|
|
||||
|
@DisplayName("1. 查看当前待匹配计划数据概况") |
||||
|
@Test |
||||
|
@Order(1) |
||||
|
public void testCheckPendingPlans() { |
||||
|
// 查询符合自动匹配条件的计划
|
||||
|
LambdaQueryWrapper<EfcodeCpPlan> lqw = Wrappers.lambdaQuery(); |
||||
|
lqw.gt(EfcodeCpPlan::getPlanDate, new Date()); |
||||
|
lqw.eq(EfcodeCpPlan::getSystemReserved, 0L); |
||||
|
lqw.eq(EfcodeCpPlan::getReserveStatus, 1L); |
||||
|
lqw.eq(EfcodeCpPlan::getIsPublic, 1L); |
||||
|
lqw.eq(EfcodeCpPlan::getIsUnion, 1L); |
||||
|
lqw.eq(EfcodeCpPlan::getIsDelete, 0L); |
||||
|
List<EfcodeCpPlan> pendingPlans = cpPlanMapper.selectList(lqw); |
||||
|
|
||||
|
System.out.println("========== 待匹配计划数据概况 =========="); |
||||
|
System.out.println("符合条件的计划总数: " + pendingPlans.size()); |
||||
|
|
||||
|
// 按企业信用代码分组统计
|
||||
|
Map<String, List<EfcodeCpPlan>> groupByEnterprise = pendingPlans.stream() |
||||
|
.filter(p -> p.getEnterpriseNumber() != null) |
||||
|
.collect(Collectors.groupingBy(EfcodeCpPlan::getEnterpriseNumber)); |
||||
|
|
||||
|
int potentialMatchCount = 0; |
||||
|
for (Map.Entry<String, List<EfcodeCpPlan>> entry : groupByEnterprise.entrySet()) { |
||||
|
// 统计不同执法单位数量
|
||||
|
long distinctUnits = entry.getValue().stream() |
||||
|
.map(EfcodeCpPlan::getBureauDeptId) |
||||
|
.distinct() |
||||
|
.count(); |
||||
|
if (distinctUnits >= 2) { |
||||
|
potentialMatchCount++; |
||||
|
System.out.println(" 企业[" + entry.getKey() + "] - 有" + distinctUnits + "个不同单位的计划,可匹配"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
System.out.println("潜在可匹配的企业数: " + potentialMatchCount); |
||||
|
System.out.println("======================================="); |
||||
|
} |
||||
|
|
||||
|
@DisplayName("2. 强制执行 autoMatchReserve 并验证结果") |
||||
|
@Test |
||||
|
@Order(2) |
||||
|
public void testAutoMatchReserveForceMode() { |
||||
|
// 执行前:统计当前系统匹配预约记录数
|
||||
|
long beforeReserveCount = cpReserveMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpReserve>lambdaQuery().eq(EfcodeCpReserve::getResFrom, 1L) |
||||
|
); |
||||
|
System.out.println("执行前系统匹配预约记录数: " + beforeReserveCount); |
||||
|
|
||||
|
// 执行前:统计已被系统预约的计划数
|
||||
|
long beforeMatchedPlanCount = cpPlanMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpPlan>lambdaQuery() |
||||
|
.eq(EfcodeCpPlan::getSystemReserved, 1L) |
||||
|
.eq(EfcodeCpPlan::getIsDelete, 0L) |
||||
|
); |
||||
|
System.out.println("执行前已系统匹配计划数: " + beforeMatchedPlanCount); |
||||
|
|
||||
|
// 执行自动匹配(强制模式,忽略日期校验)
|
||||
|
System.out.println("\n========== 开始执行 autoMatchReserve(true) =========="); |
||||
|
cpPlanService.autoMatchReserve(true, true, true); |
||||
|
System.out.println("========== 执行完毕 ==========\n"); |
||||
|
|
||||
|
// 执行后:统计系统匹配预约记录数
|
||||
|
long afterReserveCount = cpReserveMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpReserve>lambdaQuery().eq(EfcodeCpReserve::getResFrom, 1L) |
||||
|
); |
||||
|
System.out.println("执行后系统匹配预约记录数: " + afterReserveCount); |
||||
|
System.out.println("本次新增预约记录数: " + (afterReserveCount - beforeReserveCount)); |
||||
|
|
||||
|
// 执行后:统计已被系统预约的计划数
|
||||
|
long afterMatchedPlanCount = cpPlanMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpPlan>lambdaQuery() |
||||
|
.eq(EfcodeCpPlan::getSystemReserved, 1L) |
||||
|
.eq(EfcodeCpPlan::getIsDelete, 0L) |
||||
|
); |
||||
|
System.out.println("执行后已系统匹配计划数: " + afterMatchedPlanCount); |
||||
|
System.out.println("本次新增匹配计划数: " + (afterMatchedPlanCount - beforeMatchedPlanCount)); |
||||
|
|
||||
|
// 验证:如果有符合条件的数据,则应产生匹配结果
|
||||
|
if (afterReserveCount > beforeReserveCount) { |
||||
|
System.out.println("\n[验证通过] 自动匹配产生了新的预约记录"); |
||||
|
} else { |
||||
|
System.out.println("\n[提示] 未产生新预约记录(可能没有符合条件的跨单位同企业计划)"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@DisplayName("3. 验证匹配后的 plan_group_id 分组一致性") |
||||
|
@Test |
||||
|
@Order(3) |
||||
|
public void testVerifyPlanGroupConsistency() { |
||||
|
// 查询所有系统匹配生成的预约记录
|
||||
|
LambdaQueryWrapper<EfcodeCpReserve> lqw = Wrappers.lambdaQuery(); |
||||
|
lqw.eq(EfcodeCpReserve::getResFrom, 1L); |
||||
|
lqw.eq(EfcodeCpReserve::getOpStatus, 3L); // 待确认
|
||||
|
lqw.orderByDesc(EfcodeCpReserve::getCreateTime); |
||||
|
List<EfcodeCpReserve> reserves = cpReserveMapper.selectList(lqw); |
||||
|
|
||||
|
if (reserves.isEmpty()) { |
||||
|
System.out.println("[跳过] 无系统匹配的预约记录,无法验证分组一致性"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 按 plan_group_id 分组
|
||||
|
Map<String, List<EfcodeCpReserve>> groupMap = reserves.stream() |
||||
|
.collect(Collectors.groupingBy(EfcodeCpReserve::getPlanGroupId)); |
||||
|
|
||||
|
System.out.println("========== plan_group_id 分组验证 =========="); |
||||
|
System.out.println("共有 " + groupMap.size() + " 组联合预约"); |
||||
|
|
||||
|
int validGroups = 0; |
||||
|
for (Map.Entry<String, List<EfcodeCpReserve>> entry : groupMap.entrySet()) { |
||||
|
List<EfcodeCpReserve> group = entry.getValue(); |
||||
|
// 每组至少2条记录(2个不同单位)
|
||||
|
if (group.size() >= 2) { |
||||
|
validGroups++; |
||||
|
|
||||
|
// 验证同组内关联的计划属于不同执法单位
|
||||
|
Set<String> planIds = group.stream() |
||||
|
.map(EfcodeCpReserve::getPlanId) |
||||
|
.collect(Collectors.toSet()); |
||||
|
List<EfcodeCpPlan> plans = cpPlanMapper.selectByIds(planIds); |
||||
|
Set<String> bureauDeptIds = plans.stream() |
||||
|
.map(EfcodeCpPlan::getBureauDeptId) |
||||
|
.collect(Collectors.toSet()); |
||||
|
|
||||
|
System.out.println(" groupId=" + entry.getKey() |
||||
|
+ " | 记录数=" + group.size() |
||||
|
+ " | 涉及单位数=" + bureauDeptIds.size()); |
||||
|
|
||||
|
// 断言:同组关联的计划应来自不同单位
|
||||
|
Assertions.assertTrue(bureauDeptIds.size() >= 2, |
||||
|
"同一planGroupId下应关联至少2个不同执法单位的计划"); |
||||
|
} |
||||
|
} |
||||
|
System.out.println("有效分组数(>=2条记录): " + validGroups); |
||||
|
System.out.println("============================================="); |
||||
|
} |
||||
|
|
||||
|
@DisplayName("4. 验证匹配后计划状态已更新") |
||||
|
@Test |
||||
|
@Order(4) |
||||
|
public void testVerifyPlanStatusUpdated() { |
||||
|
// 查询已被系统匹配的计划
|
||||
|
LambdaQueryWrapper<EfcodeCpPlan> lqw = Wrappers.lambdaQuery(); |
||||
|
lqw.eq(EfcodeCpPlan::getSystemReserved, 1L); |
||||
|
lqw.eq(EfcodeCpPlan::getReserveStatus, 2L); // 预约中
|
||||
|
lqw.eq(EfcodeCpPlan::getIsDelete, 0L); |
||||
|
List<EfcodeCpPlan> matchedPlans = cpPlanMapper.selectList(lqw); |
||||
|
|
||||
|
System.out.println("========== 计划状态验证 =========="); |
||||
|
System.out.println("system_reserved=1 且 reserve_status=2 的计划数: " + matchedPlans.size()); |
||||
|
|
||||
|
if (!matchedPlans.isEmpty()) { |
||||
|
// 验证每条计划都满足状态要求
|
||||
|
for (EfcodeCpPlan plan : matchedPlans) { |
||||
|
Assertions.assertEquals(1L, plan.getSystemReserved(), |
||||
|
"计划[" + plan.getId() + "]的system_reserved应为1"); |
||||
|
Assertions.assertEquals(2L, plan.getReserveStatus(), |
||||
|
"计划[" + plan.getId() + "]的reserve_status应为2(预约中)"); |
||||
|
} |
||||
|
System.out.println("[验证通过] 所有已匹配计划状态正确"); |
||||
|
} else { |
||||
|
System.out.println("[提示] 无已匹配计划数据"); |
||||
|
} |
||||
|
System.out.println("=================================="); |
||||
|
} |
||||
|
|
||||
|
@DisplayName("5. 再次执行 autoMatchReserve 验证幂等性(不重复匹配)") |
||||
|
@Test |
||||
|
@Order(5) |
||||
|
public void testAutoMatchReserveIdempotent() { |
||||
|
// 先统计当前预约记录数
|
||||
|
long beforeCount = cpReserveMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpReserve>lambdaQuery().eq(EfcodeCpReserve::getResFrom, 1L) |
||||
|
); |
||||
|
|
||||
|
// 再次执行
|
||||
|
System.out.println("========== 二次执行 autoMatchReserve(true) 验证幂等性 =========="); |
||||
|
cpPlanService.autoMatchReserve(true, true, true); |
||||
|
|
||||
|
long afterCount = cpReserveMapper.selectCount( |
||||
|
Wrappers.<EfcodeCpReserve>lambdaQuery().eq(EfcodeCpReserve::getResFrom, 1L) |
||||
|
); |
||||
|
|
||||
|
long newRecords = afterCount - beforeCount; |
||||
|
System.out.println("二次执行新增预约记录数: " + newRecords); |
||||
|
|
||||
|
// 已匹配过的计划不应再次匹配(system_reserved已设为1)
|
||||
|
// 但如果有新的符合条件的数据也可能产生新记录
|
||||
|
System.out.println("[说明] 已匹配的计划(system_reserved=1)不会被重复匹配"); |
||||
|
System.out.println("================================================================"); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue