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