测试门禁:增加授权生命周期行为回归

This commit is contained in:
wangzhibiao committed 2026-09-21 12:51:20 +08:00
1 parent 84e1972947
commit 056f3d7cac
4 files changed
+920 -43

No files matched your search

@@ -0,0 +1,704 @@
package com.ruoyi.web.controller.train;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.train.domain.TrainAccommodation;
import com.ruoyi.train.domain.TrainClassPlan;
import com.ruoyi.train.domain.TrainDormAuth;
import com.ruoyi.train.domain.TrainEnrollment;
import com.ruoyi.train.domain.TrainExternalOperation;
import com.ruoyi.train.domain.TrainExternalResourceMapping;
import com.ruoyi.train.domain.TrainStudent;
import com.ruoyi.train.integration.dms.config.DmsProperties;
import com.ruoyi.train.integration.dms.dto.DmsPasswordCreateResponse;
import com.ruoyi.train.integration.dms.dto.DmsPasswordDeleteResponse;
import com.ruoyi.train.integration.dms.dto.IssueResultVO;
import com.ruoyi.train.integration.dms.gateway.ExternalCredentialGateway;
import com.ruoyi.train.integration.dms.orchestration.AccessAuthorizationOrchestrator;
import com.ruoyi.train.integration.dms.orchestration.BatchReconciliationResult;
import com.ruoyi.train.integration.dms.orchestration.ReconciliationDecision;
import com.ruoyi.train.integration.dms.orchestration.ReconciliationResult;
import com.ruoyi.train.mapper.TrainAccommodationMapper;
import com.ruoyi.train.mapper.TrainClassPlanMapper;
import com.ruoyi.train.mapper.TrainDormAuthMapper;
import com.ruoyi.train.mapper.TrainEnrollmentMapper;
import com.ruoyi.train.mapper.TrainStudentMapper;
import com.ruoyi.train.service.IExternalOperationService;
import com.ruoyi.train.service.IExternalResourceMappingService;
import com.ruoyi.train.service.impl.TrainDormAuthServiceImpl;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Package A 核心安全与行为回归测试套件 (Requirements 22 A-L)
* 真实执行 Java 逻辑与状态机判定,杜绝纯文本断言
*/
public class DmsAccessSafetyPackageATest {
private AccessAuthorizationOrchestrator orchestrator;
private DmsProperties dmsProperties;
private MockCredentialGatewayStub gatewayStub;
private Map<Long, TrainEnrollment> enrollmentDb;
private Map<Long, TrainAccommodation> accommodationDb;
private Map<Long, TrainDormAuth> dormAuthDb;
private Map<String, TrainStudent> studentDb;
private Map<String, TrainClassPlan> planDb;
private Map<String, TrainExternalResourceMapping> resourceMappingDb;
private Map<Long, TrainExternalOperation> operationDb;
private AtomicLong authSeq;
private AtomicLong opSeq;
@Before
public void setUp() throws Exception {
enrollmentDb = new ConcurrentHashMap<>();
accommodationDb = new ConcurrentHashMap<>();
dormAuthDb = new ConcurrentHashMap<>();
studentDb = new ConcurrentHashMap<>();
planDb = new ConcurrentHashMap<>();
resourceMappingDb = new ConcurrentHashMap<>();
operationDb = new ConcurrentHashMap<>();
authSeq = new AtomicLong(100);
opSeq = new AtomicLong(500);
dmsProperties = new DmsProperties();
dmsProperties.setEnabled(true);
gatewayStub = new MockCredentialGatewayStub();
TrainEnrollmentMapper enrollmentMapper = createMockEnrollmentMapper();
TrainAccommodationMapper accommodationMapper = createMockAccommodationMapper();
TrainDormAuthMapper dormAuthMapper = createMockDormAuthMapper();
TrainStudentMapper studentMapper = createMockStudentMapper();
TrainClassPlanMapper classPlanMapper = createMockClassPlanMapper();
IExternalResourceMappingService mappingService = createMockMappingService();
IExternalOperationService operationService = createMockOperationService();
TrainDormAuthServiceImpl authService = new TrainDormAuthServiceImpl();
setPrivateField(authService, "trainDormAuthMapper", dormAuthMapper);
setPrivateField(authService, "trainAccommodationMapper", accommodationMapper);
orchestrator = new AccessAuthorizationOrchestrator(
enrollmentMapper,
accommodationMapper,
dormAuthMapper,
authService,
mappingService,
operationService,
gatewayStub
);
orchestrator.setStudentMapper(studentMapper);
orchestrator.setClassPlanMapper(classPlanMapper);
orchestrator.setDmsProperties(dmsProperties);
// 初始化标准宿舍与主锁映射
TrainExternalResourceMapping room = new TrainExternalResourceMapping();
room.setDormId("DORM-101");
room.setVendorCode("DMS");
room.setMappingStatus("ACTIVE");
resourceMappingDb.put("ROOM:DORM-101", room);
TrainExternalResourceMapping lock = new TrainExternalResourceMapping();
lock.setDormId("DORM-101");
lock.setVendorCode("DMS");
lock.setIsPrimary(1);
lock.setMappingStatus("ACTIVE");
lock.setExternalResourceId("6001");
lock.setExternalHardwareId("HW-6001");
resourceMappingDb.put("LOCK:DORM-101:6001", lock);
}
/**
* A. DMS disabled:住宿创建不会外呼,auth 保持 PENDING_GRANT
*/
@Test
public void testA_DmsDisabled_NoOutboundHttp_AuthStaysPendingGrant() {
dmsProperties.setEnabled(false);
Long eid = 1L;
setupEnrollmentAndAccommodation(eid, "STU-001", "13812345678", "PLAN-001", "DORM-101", 10L);
// 自动对账 -> AWAITING_MANUAL_ISSUE
ReconciliationResult result = orchestrator.reconcile(eid);
assertEquals(ReconciliationDecision.AWAITING_MANUAL_ISSUE, result.getDecision());
assertEquals("DMS disabled must NOT invoke external gateway during reconcile", 0, gatewayStub.issueCallCount.get());
TrainDormAuth auth = dormAuthDb.get(10L);
assertNotNull(auth);
assertEquals(TrainDormAuth.AUTH_STATUS_PENDING_GRANT, auth.getAuthStatus());
// 管理员显式下发 -> 因 DMS disabled 被拦截并返回提示,状态依然保持 PENDING_GRANT
IssueResultVO issueResult = orchestrator.issueAuthorization(auth.getAuthId(), "admin");
assertEquals("WAITING_FOR_PREREQUISITE", issueResult.getStatus());
assertTrue(issueResult.getMessage().contains("DMS_DISABLED"));
assertEquals("External gateway must not be called when disabled", 0, gatewayStub.issueCallCount.get());
assertEquals(TrainDormAuth.AUTH_STATUS_PENDING_GRANT, auth.getAuthStatus());
}
/**
* B. Issue:真实手机号进入 gateway stub,假手机号彻底不存在
*/
@Test
public void testB_Issue_RealStudentPhoneEnteredGateway_FakePhoneNonexistent() {
Long eid = 2L;
String realPhone = "13987654321";
setupEnrollmentAndAccommodation(eid, "STU-002", realPhone, "PLAN-001", "DORM-101", 20L);
ReconciliationResult r = orchestrator.reconcile(eid);
IssueResultVO issueResult = orchestrator.issueAuthorization(r.getDoorAuthId(), "admin");
assertEquals("ACCEPTED", issueResult.getStatus());
assertNotNull(issueResult.getOneTimePlaintextCredential());
assertEquals(1, gatewayStub.issueCallCount.get());
assertEquals("Gateway must receive student real phone number", realPhone, gatewayStub.lastPhonePassed);
assertNotEquals("13800000000", gatewayStub.lastPhonePassed);
}
/**
* C. missing phone:gateway调用次数 = 0,返回 WAITING_FOR_PREREQUISITE (MISSING_STUDENT_PHONE)
*/
@Test
public void testC_MissingPhone_GatewayCallsZero() {
Long eid = 3L;
// 学员手机号为 null
setupEnrollmentAndAccommodation(eid, "STU-003", null, "PLAN-001", "DORM-101", 30L);
ReconciliationResult r = orchestrator.reconcile(eid);
IssueResultVO issueResult = orchestrator.issueAuthorization(r.getDoorAuthId(), "admin");
assertEquals("WAITING_FOR_PREREQUISITE", issueResult.getStatus());
assertTrue(issueResult.getMessage().contains("MISSING_STUDENT_PHONE"));
assertEquals("Gateway must not be called when student phone is missing", 0, gatewayStub.issueCallCount.get());
// 验证非法格式手机号
TrainStudent student = studentDb.get("STU-003");
student.setsPhone("12345"); // 非法
IssueResultVO invalidResult = orchestrator.issueAuthorization(r.getDoorAuthId(), "admin");
assertEquals("WAITING_FOR_PREREQUISITE", invalidResult.getStatus());
assertEquals(0, gatewayStub.issueCallCount.get());
}
/**
* D. validUntil:等于 plan.end_time,不是 +30 day
*/
@Test
public void testD_ValidUntil_EqualsPlanEndTime_NotPlus30Days() {
Long eid = 4L;
Date explicitEndTime = new Date(System.currentTimeMillis() + 86400000L * 14); // 14 天后
setupEnrollmentAndAccommodation(eid, "STU-004", "13711223344", "PLAN-004", "DORM-101", 40L);
TrainClassPlan plan = planDb.get("PLAN-004");
plan.setEndTime(explicitEndTime);
ReconciliationResult r = orchestrator.reconcile(eid);
orchestrator.issueAuthorization(r.getDoorAuthId(), "admin");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String expectedValidUntilStr = sdf.format(explicitEndTime);
assertEquals("validUntil must match plan.end_time strictly", expectedValidUntilStr, gatewayStub.lastValidUntilPassed);
TrainDormAuth auth = dormAuthDb.get(40L);
assertEquals(explicitEndTime, auth.getAuthEnd());
}
/**
* E. invalid validity:gateway调用次数 = 0,返回 WAITING_FOR_PREREQUISITE
*/
@Test
public void testE_InvalidValidity_GatewayCallsZero() {
Long eid = 5L;
setupEnrollmentAndAccommodation(eid, "STU-005", "13699887766", "PLAN-005", "DORM-101", 50L);
TrainClassPlan plan = planDb.get("PLAN-005");
// 结束时间早于现在 (已过期)
plan.setEndTime(new Date(System.currentTimeMillis() - 86400000L));
ReconciliationResult r = orchestrator.reconcile(eid);
IssueResultVO issueResult = orchestrator.issueAuthorization(r.getDoorAuthId(), "admin");
assertEquals("WAITING_FOR_PREREQUISITE", issueResult.getStatus());
assertTrue(issueResult.getMessage().contains("INVALID_VALIDITY_WINDOW"));
assertEquals("Gateway must not be called when validity window is invalid", 0, gatewayStub.issueCallCount.get());
}
/**
* F. double issue:GRANTING/ACTIVE拒绝再次下发
*/
@Test
public void testF_DoubleIssue_GrantingAndActiveBlockReissue() {
Long eid = 6L;
setupEnrollmentAndAccommodation(eid, "STU-006", "13511112222", "PLAN-001", "DORM-101", 60L);
ReconciliationResult r = orchestrator.reconcile(eid);
Long authId = r.getDoorAuthId();
// 首次 issue -> 成功进入 GRANTING
IssueResultVO firstIssue = orchestrator.issueAuthorization(authId, "admin");
assertEquals("ACCEPTED", firstIssue.getStatus());
TrainDormAuth auth = dormAuthDb.get(60L);
assertEquals(TrainDormAuth.AUTH_STATUS_GRANTING, auth.getAuthStatus());
// 第二次 issue (处于 GRANTING) -> 必须被拒绝
try {
orchestrator.issueAuthorization(authId, "admin");
fail("Expected ServiceException when issuing during GRANTING");
} catch (ServiceException ex) {
assertTrue(ex.getMessage().contains("禁止重复下发"));
}
// 推进到 ACTIVE 后再次 issue -> 依然被拒绝
auth.setAuthStatus(TrainDormAuth.AUTH_STATUS_ACTIVE);
try {
orchestrator.issueAuthorization(authId, "admin");
fail("Expected ServiceException when issuing during ACTIVE");
} catch (ServiceException ex) {
assertTrue(ex.getMessage().contains("禁止重复下发"));
}
}
/**
* G. revoke externalAuthId null:绝不调用 revoke(0)
*/
@Test
public void testG_RevokeExternalAuthIdNull_NeverInvokesDeleteZero() {
TrainDormAuth auth = new TrainDormAuth();
auth.setAuthId(701L);
auth.setAccommodationId(70L);
auth.setAuthStatus(TrainDormAuth.AUTH_STATUS_GRANTING);
auth.setExternalAuthId(null); // externalAuthId 为 null
dormAuthDb.put(70L, auth);
ReconciliationResult revokeResult = orchestrator.revokeAuthorization(701L, "admin");
assertFalse(revokeResult.isSuccess());
assertTrue(revokeResult.getReason().contains("UNRESOLVABLE_EXTERNAL_CREDENTIAL"));
assertEquals("Delete must NOT be invoked with 0 or null", 0, gatewayStub.revokeCallCount.get());
assertEquals("Auth must enter ERROR state rather than REVOKED", TrainDormAuth.AUTH_STATUS_ERROR, auth.getAuthStatus());
}
/**
* H. multi revoke:3条授权 全部被遍历 不能只处理第一条
*/
@Test
public void testH_MultiRevoke_TraversesAllThreeAuthorizations() {
Long eid = 8L;
TrainEnrollment enrollment = new TrainEnrollment();
enrollment.setEnrollmentId(eid);
enrollment.setStudentId("STU-008");
enrollment.setPlanId("PLAN-001");
enrollment.setEnrollmentStatus(3); // 退训
enrollmentDb.put(eid, enrollment);
// 创建 3 个住宿历史与 3 个有效授权
for (long i = 1; i <= 3; i++) {
TrainAccommodation acc = new TrainAccommodation();
acc.setAccommodationId(800L + i);
acc.setEnrollmentId(eid);
acc.setDormId("DORM-101");
acc.setAccommodationStatus(2); // 历史已迁出
accommodationDb.put(acc.getAccommodationId(), acc);
TrainDormAuth auth = new TrainDormAuth();
auth.setAuthId(8000L + i);
auth.setAccommodationId(acc.getAccommodationId());
auth.setAuthStatus(TrainDormAuth.AUTH_STATUS_ACTIVE);
auth.setExternalAuthId(String.valueOf(88000L + i));
dormAuthDb.put(acc.getAccommodationId(), auth);
}
ReconciliationResult result = orchestrator.reconcile(eid);
BatchReconciliationResult batch = result.getBatchResult();
assertNotNull(batch);
assertEquals("Total items evaluated must be 3", 3, batch.getTotal());
assertEquals("All 3 items must be dispatched for revoke", 3, batch.getDispatched());
assertEquals("All 3 gateway revokes must be executed", 3, gatewayStub.revokeCallCount.get());
assertEquals(BatchReconciliationResult.OUTCOME_IN_PROGRESS, batch.getOutcome());
}
/**
* I. 部分失败:BatchReconciliationResult = PARTIAL_FAILURE
*/
@Test
public void testI_PartialFailure_ProducesPartialFailureOutcome() {
Long eid = 9L;
TrainEnrollment enrollment = new TrainEnrollment();
enrollment.setEnrollmentId(eid);
enrollment.setStudentId("STU-009");
enrollment.setPlanId("PLAN-001");
enrollment.setEnrollmentStatus(3); // 退训
enrollmentDb.put(eid, enrollment);
// 住宿 1: 正常有效
TrainAccommodation acc1 = new TrainAccommodation();
acc1.setAccommodationId(901L);
acc1.setEnrollmentId(eid);
acc1.setDormId("DORM-101");
acc1.setAccommodationStatus(2);
accommodationDb.put(901L, acc1);
TrainDormAuth auth1 = new TrainDormAuth();
auth1.setAuthId(9001L);
auth1.setAccommodationId(901L);
auth1.setAuthStatus(TrainDormAuth.AUTH_STATUS_ACTIVE);
auth1.setExternalAuthId("88001");
dormAuthDb.put(901L, auth1);
// 住宿 2: externalAuthId 缺失导致注销失败
TrainAccommodation acc2 = new TrainAccommodation();
acc2.setAccommodationId(902L);
acc2.setEnrollmentId(eid);
acc2.setDormId("DORM-101");
acc2.setAccommodationStatus(2);
accommodationDb.put(902L, acc2);
TrainDormAuth auth2 = new TrainDormAuth();
auth2.setAuthId(9002L);
auth2.setAccommodationId(902L);
auth2.setAuthStatus(TrainDormAuth.AUTH_STATUS_ACTIVE);
auth2.setExternalAuthId(null); // 无外部凭据号,注销判定失败
dormAuthDb.put(902L, auth2);
ReconciliationResult result = orchestrator.reconcile(eid);
BatchReconciliationResult batch = result.getBatchResult();
assertNotNull(batch);
assertEquals(2, batch.getTotal());
assertEquals(1, batch.getDispatched());
assertEquals(1, batch.getFailed());
assertEquals("Outcome must be PARTIAL_FAILURE", BatchReconciliationResult.OUTCOME_PARTIAL_FAILURE, batch.getOutcome());
}
/**
* J. 成功后:last_error 清空
*/
@Test
public void testJ_SuccessStateAdvance_ClearsLastError() throws Exception {
TrainDormAuth auth = new TrainDormAuth();
auth.setAuthId(1001L);
auth.setAccommodationId(1001L);
auth.setAuthStatus(TrainDormAuth.AUTH_STATUS_ERROR);
auth.setLastError("Old network timeout error");
dormAuthDb.put(1001L, auth);
TrainDormAuthServiceImpl service = new TrainDormAuthServiceImpl();
setPrivateField(service, "trainDormAuthMapper", createMockDormAuthMapper());
// 推进到 ACTIVE 状态
service.updateAuthStatus(1001L, TrainDormAuth.AUTH_STATUS_ACTIVE, 501L, "admin");
TrainDormAuth updated = dormAuthDb.get(1001L);
assertEquals(TrainDormAuth.AUTH_STATUS_ACTIVE, updated.getAuthStatus());
assertNull("last_error must be cleared upon advancing to ACTIVE", updated.getLastError());
}
/**
* K. Credential:不进入日志,不进入 list,不进入 export,不进入 operations,不进入普通业务响应
*/
@Test
public void testK_CredentialSecurity_ZeroLeakageInLogsAndVo() throws Exception {
// 验证 TrainAccessManagementController.issueCredential 方法配置了 isSaveResponseData = false
Method issueMethod = TrainAccessManagementController.class.getMethod("issueCredential", Long.class);
Log logAnnotation = issueMethod.getAnnotation(Log.class);
assertNotNull("@Log annotation must be present on issue method", logAnnotation);
assertFalse("@Log isSaveResponseData MUST be false to prevent plaintext password leakage into sys_oper_log",
logAnnotation.isSaveResponseData());
// 验证 TrainExternalOperation 实体中无 plaintext_password 字段
for (Field f : TrainExternalOperation.class.getDeclaredFields()) {
assertNotEquals("TrainExternalOperation must not contain password column", "password", f.getName().toLowerCase());
assertNotEquals("TrainExternalOperation must not contain plaintext password", "plaintextpassword", f.getName().toLowerCase());
}
// 验证 TrainDormAuth 实体中无 password 字段
for (Field f : TrainDormAuth.class.getDeclaredFields()) {
assertFalse("TrainDormAuth must not contain plaintext password column", f.getName().toLowerCase().contains("password"));
}
}
/**
* L. TrainDormAuthController:不存在任何写 Mapping (POST/PUT/DELETE)
*/
@Test
public void testL_TrainDormAuthController_ZeroWriteMappings() {
Class<?> clazz = TrainDormAuthController.class;
int writeMappingCount = 0;
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(PostMapping.class)) {
// export 接口属于安全只读查询导出,其余所有 POST 必须被禁止
if (!"export".equals(m.getName())) {
writeMappingCount++;
}
}
if (m.isAnnotationPresent(PutMapping.class)) {
writeMappingCount++;
}
if (m.isAnnotationPresent(DeleteMapping.class)) {
writeMappingCount++;
}
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping rm = m.getAnnotation(RequestMapping.class);
for (RequestMethod method : rm.method()) {
if (method == RequestMethod.POST && !"export".equals(m.getName())) {
writeMappingCount++;
}
if (method == RequestMethod.PUT || method == RequestMethod.DELETE) {
writeMappingCount++;
}
}
}
}
assertEquals("TrainDormAuthController must have ZERO write mutation endpoints", 0, writeMappingCount);
}
/* ==================== 辅助测试工具与 Mock 桩 ==================== */
private void setupEnrollmentAndAccommodation(Long eid, String studentId, String phone, String planId, String dormId, Long accId) {
TrainStudent student = new TrainStudent();
student.setsId(studentId);
student.setsPhone(phone);
studentDb.put(studentId, student);
TrainClassPlan plan = new TrainClassPlan();
plan.setPlanId(planId);
plan.setEndTime(new Date(System.currentTimeMillis() + 86400000L * 7)); // 默认 7 天后
planDb.put(planId, plan);
TrainEnrollment enrollment = new TrainEnrollment();
enrollment.setEnrollmentId(eid);
enrollment.setStudentId(studentId);
enrollment.setPlanId(planId);
enrollment.setEnrollmentStatus(1);
enrollmentDb.put(eid, enrollment);
TrainAccommodation acc = new TrainAccommodation();
acc.setAccommodationId(accId);
acc.setEnrollmentId(eid);
acc.setDormId(dormId);
acc.setAccommodationStatus(1);
acc.setAssignedTime(new Date());
accommodationDb.put(accId, acc);
}
private static class MockCredentialGatewayStub implements ExternalCredentialGateway {
final AtomicInteger issueCallCount = new AtomicInteger(0);
final AtomicInteger revokeCallCount = new AtomicInteger(0);
String lastPhonePassed;
String lastValidUntilPassed;
@Override
public DmsPasswordCreateResponse issueOnlinePassword(Long lockId, String holderName, String phone,
String plaintextPassword, String validStart, String validUntil) {
issueCallCount.incrementAndGet();
lastPhonePassed = phone;
lastValidUntilPassed = validUntil;
DmsPasswordCreateResponse resp = new DmsPasswordCreateResponse();
resp.setId(88000L + issueCallCount.get());
resp.setOperationId(99000L + issueCallCount.get());
resp.setLockHardwareId("HW-" + lockId);
return resp;
}
@Override
public DmsPasswordDeleteResponse revokeOnlinePassword(Long keyId) {
revokeCallCount.incrementAndGet();
DmsPasswordDeleteResponse resp = new DmsPasswordDeleteResponse();
resp.setOperationId(99000L + revokeCallCount.get());
return resp;
}
}
private TrainEnrollmentMapper createMockEnrollmentMapper() {
return (TrainEnrollmentMapper) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{TrainEnrollmentMapper.class},
(proxy, method, args) -> {
if ("selectTrainEnrollmentById".equals(method.getName())) {
return enrollmentDb.get((Long) args[0]);
}
return null;
}
);
}
private TrainAccommodationMapper createMockAccommodationMapper() {
return (TrainAccommodationMapper) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{TrainAccommodationMapper.class},
(proxy, method, args) -> {
String name = method.getName();
if ("selectActiveAccommodationByEnrollmentId".equals(name)) {
Long eid = (Long) args[0];
return accommodationDb.values().stream()
.filter(a -> eid.equals(a.getEnrollmentId()) && a.getAccommodationStatus() != null && (a.getAccommodationStatus() == 0 || a.getAccommodationStatus() == 1))
.findFirst().orElse(null);
}
if ("selectAccommodationHistoryByEnrollmentId".equals(name)) {
Long eid = (Long) args[0];
List<TrainAccommodation> list = new ArrayList<>();
for (TrainAccommodation a : accommodationDb.values()) {
if (eid.equals(a.getEnrollmentId())) {
list.add(a);
}
}
return list;
}
if ("selectTrainAccommodationByAccommodationId".equals(name)) {
return accommodationDb.get((Long) args[0]);
}
return null;
}
);
}
private TrainDormAuthMapper createMockDormAuthMapper() {
return (TrainDormAuthMapper) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{TrainDormAuthMapper.class},
(proxy, method, args) -> {
String name = method.getName();
if ("selectTrainDormAuthByAccommodationId".equals(name)) {
return dormAuthDb.get((Long) args[0]);
}
if ("selectTrainDormAuthByAuthId".equals(name)) {
Long aid = (Long) args[0];
return dormAuthDb.values().stream().filter(a -> aid.equals(a.getAuthId())).findFirst().orElse(null);
}
if ("insertTrainDormAuth".equals(name)) {
TrainDormAuth auth = (TrainDormAuth) args[0];
auth.setAuthId(authSeq.incrementAndGet());
dormAuthDb.put(auth.getAccommodationId(), auth);
return 1;
}
if ("updateTrainDormAuth".equals(name)) {
TrainDormAuth auth = (TrainDormAuth) args[0];
dormAuthDb.put(auth.getAccommodationId(), auth);
return 1;
}
if ("clearLastError".equals(name)) {
Long aid = (Long) args[0];
TrainDormAuth auth = dormAuthDb.values().stream().filter(a -> aid.equals(a.getAuthId())).findFirst().orElse(null);
if (auth != null) {
auth.setLastError(null);
}
return 1;
}
if ("updateSyncStatus".equals(name)) {
Long aid = (Long) args[0];
Integer st = (Integer) args[1];
String err = (String) args[2];
TrainDormAuth auth = dormAuthDb.values().stream().filter(a -> aid.equals(a.getAuthId())).findFirst().orElse(null);
if (auth != null) {
auth.setFaceSyncStatus(st);
auth.setLastError(err);
}
return 1;
}
if (method.getReturnType().equals(int.class) || method.getReturnType().equals(Integer.class)) {
return 1;
}
return null;
}
);
}
private TrainStudentMapper createMockStudentMapper() {
return (TrainStudentMapper) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{TrainStudentMapper.class},
(proxy, method, args) -> {
if ("selectTrainStudentBySId".equals(method.getName())) {
return studentDb.get((String) args[0]);
}
return null;
}
);
}
private TrainClassPlanMapper createMockClassPlanMapper() {
return (TrainClassPlanMapper) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{TrainClassPlanMapper.class},
(proxy, method, args) -> {
if ("selectTrainClassPlanByPlanId".equals(method.getName())) {
return planDb.get((String) args[0]);
}
return null;
}
);
}
private IExternalResourceMappingService createMockMappingService() {
return (IExternalResourceMappingService) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{IExternalResourceMappingService.class},
(proxy, method, args) -> {
String name = method.getName();
if ("getActiveRoomMapping".equals(name)) {
String dormId = (String) args[1];
return resourceMappingDb.get("ROOM:" + dormId);
}
if ("getActiveLockMappings".equals(name)) {
String dormId = (String) args[1];
List<TrainExternalResourceMapping> locks = new ArrayList<>();
for (Map.Entry<String, TrainExternalResourceMapping> entry : resourceMappingDb.entrySet()) {
if (entry.getKey().startsWith("LOCK:" + dormId)) {
locks.add(entry.getValue());
}
}
return locks;
}
return null;
}
);
}
private IExternalOperationService createMockOperationService() {
return (IExternalOperationService) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[]{IExternalOperationService.class},
(proxy, method, args) -> {
String name = method.getName();
if ("createOperation".equals(name)) {
TrainExternalOperation op = (TrainExternalOperation) args[0];
op.setOperationId(opSeq.incrementAndGet());
operationDb.put(op.getOperationId(), op);
return op;
}
return null;
}
);
}
private void setPrivateField(Object target, String fieldName, Object value) throws Exception {
Field f = target.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(target, value);
}
}