fix:设备状态

This commit is contained in:
waner 2026-03-27 13:42:22 +08:00
parent 11ed0b5edf
commit 584af49b25
10 changed files with 173 additions and 266 deletions

View File

@ -6,32 +6,6 @@ server:
port: 8080 port: 8080
tms: tms:
web:
cors:
enabled: true
allowed-origin-patterns:
- http://localhost:*
- http://127.0.0.1:*
allowed-methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowed-headers:
- Authorization
- Content-Type
- X-Requested-With
- X-Internal-Token
- X-App-Id
- X-Timestamp
- X-Nonce
- X-Signature
- X-Trace-Id
exposed-headers:
- X-Trace-Id
allow-credentials: true
max-age-seconds: 3600
upgrade: upgrade:
staging-root-dir: /home/tmp/tms-upgrade-staging staging-root-dir: /home/tmp/tms-upgrade-staging
log-dir: /home/tms/tmp/tms-upgrade-logs log-dir: /home/tms/tmp/tms-upgrade-logs

View File

@ -1,6 +1,5 @@
package com.cisd.tms.common.config; package com.cisd.tms.common.config;
import com.cisd.tms.common.config.properties.TmsCorsProperties;
import com.cisd.tms.modules.auth.security.InternalAuthorizationInterceptor; import com.cisd.tms.modules.auth.security.InternalAuthorizationInterceptor;
import com.cisd.tms.security.internal.InternalApiAuthInterceptor; import com.cisd.tms.security.internal.InternalApiAuthInterceptor;
import com.cisd.tms.security.openapi.OpenApiSignAuthInterceptor; import com.cisd.tms.security.openapi.OpenApiSignAuthInterceptor;
@ -24,18 +23,15 @@ public class WebMvcConfig implements WebMvcConfigurer {
private final InternalApiAuthInterceptor internalApiAuthInterceptor; private final InternalApiAuthInterceptor internalApiAuthInterceptor;
private final InternalAuthorizationInterceptor internalAuthorizationInterceptor; private final InternalAuthorizationInterceptor internalAuthorizationInterceptor;
private final OpenApiSignAuthInterceptor openApiSignAuthInterceptor; private final OpenApiSignAuthInterceptor openApiSignAuthInterceptor;
private final TmsCorsProperties corsProperties;
public WebMvcConfig( public WebMvcConfig(
InternalApiAuthInterceptor internalApiAuthInterceptor, InternalApiAuthInterceptor internalApiAuthInterceptor,
InternalAuthorizationInterceptor internalAuthorizationInterceptor, InternalAuthorizationInterceptor internalAuthorizationInterceptor,
OpenApiSignAuthInterceptor openApiSignAuthInterceptor, OpenApiSignAuthInterceptor openApiSignAuthInterceptor
TmsCorsProperties corsProperties
) { ) {
this.internalApiAuthInterceptor = internalApiAuthInterceptor; this.internalApiAuthInterceptor = internalApiAuthInterceptor;
this.internalAuthorizationInterceptor = internalAuthorizationInterceptor; this.internalAuthorizationInterceptor = internalAuthorizationInterceptor;
this.openApiSignAuthInterceptor = openApiSignAuthInterceptor; this.openApiSignAuthInterceptor = openApiSignAuthInterceptor;
this.corsProperties = corsProperties;
} }
@Bean @Bean
@ -80,16 +76,12 @@ public class WebMvcConfig implements WebMvcConfigurer {
@Override @Override
public void addCorsMappings(CorsRegistry registry) { public void addCorsMappings(CorsRegistry registry) {
if (!corsProperties.isEnabled() || corsProperties.getAllowedOriginPatterns().isEmpty()) {
return;
}
registry.addMapping("/**") registry.addMapping("/**")
.allowedOriginPatterns(corsProperties.getAllowedOriginPatterns().toArray(String[]::new)) .allowedOriginPatterns("*")
.allowedMethods(corsProperties.getAllowedMethods().toArray(String[]::new)) .allowedMethods("*")
.allowedHeaders(corsProperties.getAllowedHeaders().toArray(String[]::new)) .allowedHeaders("*")
.exposedHeaders(corsProperties.getExposedHeaders().toArray(String[]::new)) .exposedHeaders("X-Trace-Id")
.allowCredentials(corsProperties.isAllowCredentials()) .allowCredentials(true)
.maxAge(corsProperties.getMaxAgeSeconds()); .maxAge(3600);
} }
} }

View File

@ -1,94 +0,0 @@
package com.cisd.tms.common.config.properties;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "tms.web.cors")
public class TmsCorsProperties {
private boolean enabled = true;
private List<String> allowedOriginPatterns = new ArrayList<>(List.of(
"http://localhost:*",
"http://127.0.0.1:*"
));
private List<String> allowedMethods = new ArrayList<>(List.of(
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS"
));
private List<String> allowedHeaders = new ArrayList<>(List.of(
"Authorization",
"Content-Type",
"X-Requested-With",
"X-Internal-Token",
"X-App-Id",
"X-Timestamp",
"X-Nonce",
"X-Signature",
"X-Trace-Id"
));
private List<String> exposedHeaders = new ArrayList<>(List.of(
"X-Trace-Id"
));
private boolean allowCredentials = true;
private long maxAgeSeconds = 3600;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getAllowedOriginPatterns() {
return allowedOriginPatterns;
}
public void setAllowedOriginPatterns(List<String> allowedOriginPatterns) {
this.allowedOriginPatterns = allowedOriginPatterns;
}
public List<String> getAllowedMethods() {
return allowedMethods;
}
public void setAllowedMethods(List<String> allowedMethods) {
this.allowedMethods = allowedMethods;
}
public List<String> getAllowedHeaders() {
return allowedHeaders;
}
public void setAllowedHeaders(List<String> allowedHeaders) {
this.allowedHeaders = allowedHeaders;
}
public List<String> getExposedHeaders() {
return exposedHeaders;
}
public void setExposedHeaders(List<String> exposedHeaders) {
this.exposedHeaders = exposedHeaders;
}
public boolean isAllowCredentials() {
return allowCredentials;
}
public void setAllowCredentials(boolean allowCredentials) {
this.allowCredentials = allowCredentials;
}
public long getMaxAgeSeconds() {
return maxAgeSeconds;
}
public void setMaxAgeSeconds(long maxAgeSeconds) {
this.maxAgeSeconds = maxAgeSeconds;
}
}

View File

@ -1628,14 +1628,15 @@ public class JnaPcieCryptoService implements PcieCryptoService {
public boolean checkLmk() { public boolean checkLmk() {
return sessionTemplate.withSession("SDFE_CheckLMK", (lib, deviceHandle, sessionHandle) -> { return sessionTemplate.withSession("SDFE_CheckLMK", (lib, deviceHandle, sessionHandle) -> {
int retCode = lib.SDFE_CheckLMK(sessionHandle); int retCode = lib.SDFE_CheckLMK(sessionHandle);
if (retCode == 0) { return retCode == 0;
return true; // if (retCode == 0) {
} // return true;
if ((retCode & 0xFF) == SDFE_INIT_STATUS_LOW_BYTE) { // }
return false; // if ((retCode & 0xFF) == SDFE_INIT_STATUS_LOW_BYTE) {
} // return false;
sessionTemplate.ensureSuccess("SDFE_CheckLMK", retCode); // }
return false; // sessionTemplate.ensureSuccess("SDFE_CheckLMK", retCode);
// return false;
}); });
} }

View File

@ -38,16 +38,16 @@ public class DeviceController {
} }
@GetMapping("/info") @GetMapping("/info")
@Operation(summary = "查询设备信息", description = "返回设备标识、版本和预置产品类型等基础信息") @Operation(summary = "查询设备信息", description = "返回设备型号、设备名称、TMS版本、设备序列号、管理密钥状态和当前设备初始化状态")
public ApiResponse<DeviceInfoResponse> info() { public ApiResponse<DeviceInfoResponse> info() {
return ApiResponse.success(deviceService.info()); return ApiResponse.success(deviceService.info());
} }
@GetMapping("/status") // @GetMapping("/status")
@Operation(summary = "查询设备状态", description = "返回当前设备运行状态、初始化状态和版本快照。") // @Operation(summary = "查询设备状态", description = "返回当前设备运行状态、初始化状态和版本快照。")
public ApiResponse<DeviceInfoResponse> status() { // public ApiResponse<DeviceInfoResponse> status() {
return ApiResponse.success(deviceService.info()); // return ApiResponse.success(deviceService.info());
} // }
@GetMapping("/profile") @GetMapping("/profile")
@Operation(summary = "查询设备画像", description = "返回设备静态信息,包括设备信息、硬件信息和按初始化方案裁剪后的软件版本信息。") @Operation(summary = "查询设备画像", description = "返回设备静态信息,包括设备信息、硬件信息和按初始化方案裁剪后的软件版本信息。")

View File

@ -5,35 +5,33 @@ import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "设备信息响应") @Schema(description = "设备信息响应")
public class DeviceInfoResponse { public class DeviceInfoResponse {
@Schema(description = "设备唯一标识", example = "TMS-DEVICE-01") @Schema(description = "设备型号", example = "SYD7108")
private String deviceId; private String deviceModel;
@Schema(description = "设备状态", example = "UP") @Schema(description = "设备名称", example = "跨境支付终端一体机")
private String status; private String deviceName;
@Schema(description = "设备版本", example = "1.0.0") @Schema(description = "设备版本", example = "1.0.0")
private String version; private String version;
@Schema(description = "预置产品类型", example = "STANDARD") @Schema(description = "设备序列号", example = "1234567890ABCDEF")
private String presetProductType; private String serialNumber;
@Schema(description = "预置版本", example = "6.6.4") @Schema(description = "管理密钥状态", example = "true")
private String presetVersion; private boolean masterKeyStatus;
@Schema(description = "预置来源", example = "application.yml") @Schema(description = "设备状态", example = "INITIALIZED")
private String presetSource; private String deviceStatus;
@Schema(description = "设备初始化状态", example = "INITIALIZED")
private String initState;
public String getDeviceId() { public String getDeviceModel() {
return deviceId; return deviceModel;
} }
public void setDeviceId(String deviceId) { public void setDeviceModel(String deviceModel) {
this.deviceId = deviceId; this.deviceModel = deviceModel;
} }
public String getStatus() { public String getDeviceName() {
return status; return deviceName;
} }
public void setStatus(String status) { public void setDeviceName(String deviceName) {
this.status = status; this.deviceName = deviceName;
} }
public String getVersion() { public String getVersion() {
@ -44,35 +42,27 @@ public class DeviceInfoResponse {
this.version = version; this.version = version;
} }
public String getPresetProductType() { public String getSerialNumber() {
return presetProductType; return serialNumber;
} }
public void setPresetProductType(String presetProductType) { public void setSerialNumber(String serialNumber) {
this.presetProductType = presetProductType; this.serialNumber = serialNumber;
} }
public String getPresetVersion() { public boolean getMasterKeyStatus() {
return presetVersion; return masterKeyStatus;
} }
public void setPresetVersion(String presetVersion) { public void setMasterKeyStatus(boolean masterKeyStatus) {
this.presetVersion = presetVersion; this.masterKeyStatus = masterKeyStatus;
} }
public String getPresetSource() { public String getDeviceStatus() {
return presetSource; return deviceStatus;
} }
public void setPresetSource(String presetSource) { public void setDeviceStatus(String deviceStatus) {
this.presetSource = presetSource; this.deviceStatus = deviceStatus;
}
public String getInitState() {
return initState;
}
public void setInitState(String initState) {
this.initState = initState;
} }
} }

View File

@ -1,50 +1,47 @@
package com.cisd.tms.modules.device.service; package com.cisd.tms.modules.device.service;
import com.cisd.tms.common.config.properties.CisdPresetProperties; import com.cisd.tms.integration.crypto.pcie.model.DeviceInfoResult;
import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.device.config.DeviceProfileProperties;
import com.cisd.tms.modules.device.dto.DeviceActionResponse; import com.cisd.tms.modules.device.dto.DeviceActionResponse;
import com.cisd.tms.modules.device.dto.DeviceInfoResponse; import com.cisd.tms.modules.device.dto.DeviceInfoResponse;
import com.cisd.tms.modules.device.entity.DeviceNodeEntity; import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
import com.cisd.tms.modules.device.repository.DeviceNodeRepository; import com.cisd.tms.modules.device.repository.DeviceSoftwareVersionRepository;
import com.cisd.tms.modules.init.service.InitService; import com.cisd.tms.modules.init.service.InitService;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Optional;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class DeviceService { public class DeviceService {
private final DeviceNodeRepository deviceNodeRepository; private final DeviceProfileProperties deviceProfileProperties;
private final CisdPresetProperties cisdPresetProperties; private final DeviceSoftwareVersionRepository deviceSoftwareVersionRepository;
private final PcieCryptoService pcieCryptoService;
private final InitService initService; private final InitService initService;
public DeviceService( public DeviceService(
DeviceNodeRepository deviceNodeRepository, DeviceProfileProperties deviceProfileProperties,
CisdPresetProperties cisdPresetProperties, DeviceSoftwareVersionRepository deviceSoftwareVersionRepository,
PcieCryptoService pcieCryptoService,
InitService initService InitService initService
) { ) {
this.deviceNodeRepository = deviceNodeRepository; this.deviceProfileProperties = deviceProfileProperties;
this.cisdPresetProperties = cisdPresetProperties; this.deviceSoftwareVersionRepository = deviceSoftwareVersionRepository;
this.pcieCryptoService = pcieCryptoService;
this.initService = initService; this.initService = initService;
} }
public DeviceInfoResponse info() { public DeviceInfoResponse info() {
DeviceNodeEntity entity = deviceNodeRepository.findByNodeId("node-01").orElse(null);
DeviceInfoResponse response = new DeviceInfoResponse(); DeviceInfoResponse response = new DeviceInfoResponse();
if (entity == null) { response.setDeviceModel(trim(deviceProfileProperties.getModel()));
response.setDeviceId("cisd-all-in-one-001"); response.setDeviceName(trim(deviceProfileProperties.getName()));
response.setStatus("RUNNING"); response.setVersion(loadTmsVersion());
response.setVersion("0.0.1-SNAPSHOT"); response.setSerialNumber(loadDeviceSerial());
} else { response.setMasterKeyStatus(loadManagementKeyReady());
response.setDeviceId(entity.getNodeId());
response.setStatus(entity.getStatus());
response.setVersion("db-loaded");
}
response.setPresetProductType(cisdPresetProperties.getProductType());
response.setPresetVersion(cisdPresetProperties.getVersion());
response.setPresetSource(cisdPresetProperties.getSource());
InitService.DeviceInitStateSnapshot snapshot = initService.getDeviceInitStateSnapshot(); InitService.DeviceInitStateSnapshot snapshot = initService.getDeviceInitStateSnapshot();
response.setInitState(snapshot.initState()); response.setDeviceStatus(snapshot.initState());
return response; return response;
} }
@ -56,4 +53,31 @@ public class DeviceService {
response.setAcceptedAt(OffsetDateTime.now().toString()); response.setAcceptedAt(OffsetDateTime.now().toString());
return response; return response;
} }
private String loadTmsVersion() {
Optional<DeviceSoftwareVersionEntity> version = deviceSoftwareVersionRepository.findByComponentCode("TMS");
return version.map(DeviceSoftwareVersionEntity::getCurrentVersion).map(DeviceService::trim).orElse("");
}
private String loadDeviceSerial() {
try {
DeviceInfoResult result = pcieCryptoService.getDeviceInfo();
return result == null ? "" : trim(result.getDeviceSerial());
} catch (RuntimeException ex) {
return "";
}
}
private boolean loadManagementKeyReady() {
try {
DeviceStatusResult result = pcieCryptoService.getDeviceStatus();
return result != null && result.getFsmState() == 0;
} catch (RuntimeException ex) {
return false;
}
}
private static String trim(String value) {
return value == null ? "" : value.trim();
}
} }

View File

@ -752,9 +752,9 @@ public class InitService {
response.setChannelUsername(firstNonBlank(text(mq, "channelUsername"), text(request, "channelUsername"))); response.setChannelUsername(firstNonBlank(text(mq, "channelUsername"), text(request, "channelUsername")));
response.setNode01Ip(firstNonBlank(text(nodes, "node01Ip"), text(request, "node01Ip"))); response.setNode01Ip(firstNonBlank(text(nodes, "node01Ip"), text(request, "node01Ip")));
response.setNode02Ip(firstNonBlank(text(nodes, "node02Ip"), text(request, "node02Ip"))); response.setNode02Ip(firstNonBlank(text(nodes, "node02Ip"), text(request, "node02Ip")));
response.setSignHost(firstNonBlank(text(signServer, "signHost"), text(request, "signHost"))); // response.setSignHost(firstNonBlank(text(signServer, "signHost"), text(request, "signHost")));
response.setSignPort(integerValue(firstNonBlank(text(signServer, "signPort"), text(request, "signPort")))); // response.setSignPort(integerValue(firstNonBlank(text(signServer, "signPort"), text(request, "signPort"))));
response.setSignType(firstNonBlank(text(signServer, "signType"), text(request, "signType"))); // response.setSignType(firstNonBlank(text(signServer, "signType"), text(request, "signType")));
return response; return response;
} catch (JsonProcessingException ex) { } catch (JsonProcessingException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to parse successful init task snapshot"); throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to parse successful init task snapshot");

View File

@ -167,39 +167,6 @@ tms:
memory-total-default: ${TMS_DEVICE_PROFILE_MEMORY_TOTAL_DEFAULT:16GB} memory-total-default: ${TMS_DEVICE_PROFILE_MEMORY_TOTAL_DEFAULT:16GB}
# 磁盘总量默认展示值;命令读取失败或为空时回退。 # 磁盘总量默认展示值;命令读取失败或为空时回退。
disk-total-default: ${TMS_DEVICE_PROFILE_DISK_TOTAL_DEFAULT:256G} disk-total-default: ${TMS_DEVICE_PROFILE_DISK_TOTAL_DEFAULT:256G}
web:
cors:
# 是否启用浏览器跨域访问控制。
enabled: ${TMS_WEB_CORS_ENABLED:true}
# 允许的前端来源;默认放开本机联调端口,部署时请改成明确前端域名/IP。
allowed-origin-patterns:
- ${TMS_WEB_CORS_ALLOWED_ORIGIN_PATTERN_1:http://localhost:*}
- ${TMS_WEB_CORS_ALLOWED_ORIGIN_PATTERN_2:http://127.0.0.1:*}
# 允许浏览器预检和业务请求使用的方法。
allowed-methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
# 允许前端发送的请求头;覆盖内部 token、OpenAPI 签名头和 traceId。
allowed-headers:
- Authorization
- Content-Type
- X-Requested-With
- X-Internal-Token
- X-App-Id
- X-Timestamp
- X-Nonce
- X-Signature
- X-Trace-Id
# 允许前端读取的响应头。
exposed-headers:
- X-Trace-Id
# 是否允许跨域携带 cookie / 凭据。
allow-credentials: ${TMS_WEB_CORS_ALLOW_CREDENTIALS:true}
# 浏览器预检缓存时间(秒)。
max-age-seconds: ${TMS_WEB_CORS_MAX_AGE_SECONDS:3600}
upgrade: upgrade:
# 升级包解压和脚本执行暂存目录。 # 升级包解压和脚本执行暂存目录。
staging-root-dir: ${TMS_UPGRADE_STAGING_ROOT_DIR:/home/tmp/tms-upgrade-staging} staging-root-dir: ${TMS_UPGRADE_STAGING_ROOT_DIR:/home/tmp/tms-upgrade-staging}

View File

@ -1,9 +1,13 @@
package com.cisd.tms.modules.device.service; package com.cisd.tms.modules.device.service;
import com.cisd.tms.common.config.properties.CisdPresetProperties; import com.cisd.tms.common.config.properties.CisdPresetProperties;
import com.cisd.tms.integration.crypto.pcie.model.DeviceInfoResult;
import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.device.config.DeviceProfileProperties;
import com.cisd.tms.modules.device.dto.DeviceInfoResponse; import com.cisd.tms.modules.device.dto.DeviceInfoResponse;
import com.cisd.tms.modules.device.entity.DeviceNodeEntity; import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
import com.cisd.tms.modules.device.repository.DeviceNodeRepository; import com.cisd.tms.modules.device.repository.DeviceSoftwareVersionRepository;
import com.cisd.tms.modules.init.config.InitCommandProfileService; import com.cisd.tms.modules.init.config.InitCommandProfileService;
import com.cisd.tms.modules.init.config.InitExecutorProperties; import com.cisd.tms.modules.init.config.InitExecutorProperties;
import com.cisd.tms.modules.init.dto.InitCreateTaskResponse; import com.cisd.tms.modules.init.dto.InitCreateTaskResponse;
@ -25,16 +29,22 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
class DeviceServiceTest { class DeviceServiceTest {
@Test @Test
void shouldReportUninitializedWhenNoSuccessfulInitTask() { void shouldReportUninitializedWhenNoSuccessfulInitTask() {
DeviceService service = newDeviceService("ENTERPRISE"); DeviceService service = newDeviceService("ENTERPRISE", "TMS-V2.0.0", "SERIAL-001", 1);
DeviceInfoResponse response = service.info(); DeviceInfoResponse response = service.info();
Assertions.assertEquals("UNINITIALIZED", response.getInitState()); Assertions.assertEquals("SYD7108", response.getDeviceModel());
Assertions.assertEquals("跨境支付终端一体机", response.getDeviceName());
Assertions.assertEquals("TMS-V2.0.0", response.getVersion());
Assertions.assertEquals("SERIAL-001", response.getSerialNumber());
Assertions.assertFalse(response.getMasterKeyStatus());
Assertions.assertEquals("UNINITIALIZED", response.getDeviceStatus());
} }
@Test @Test
@ -42,7 +52,12 @@ class DeviceServiceTest {
InMemoryInitTaskRepository taskRepository = new InMemoryInitTaskRepository(); InMemoryInitTaskRepository taskRepository = new InMemoryInitTaskRepository();
InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository(); InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository();
InitService initService = newInitService(taskRepository, stepRepository, "ENTERPRISE"); InitService initService = newInitService(taskRepository, stepRepository, "ENTERPRISE");
DeviceService deviceService = new DeviceService(new FixedDeviceNodeRepository(), preset("ENTERPRISE"), initService); DeviceService deviceService = new DeviceService(
deviceProfile("MODEL-X", "设备A"),
fixedVersionRepository("TMS-V3.1.4"),
cryptoService("SERIAL-ABC", 0),
initService
);
InitCreateTaskResponse created = initService.createTask(validEnterpriseRequest()); InitCreateTaskResponse created = initService.createTask(validEnterpriseRequest());
initService.executeTask(created.getTaskId()); initService.executeTask(created.getTaskId());
@ -50,14 +65,24 @@ class DeviceServiceTest {
DeviceInfoResponse response = deviceService.info(); DeviceInfoResponse response = deviceService.info();
Assertions.assertEquals("INITIALIZED", response.getInitState()); Assertions.assertEquals("MODEL-X", response.getDeviceModel());
Assertions.assertEquals("设备A", response.getDeviceName());
Assertions.assertEquals("TMS-V3.1.4", response.getVersion());
Assertions.assertEquals("SERIAL-ABC", response.getSerialNumber());
Assertions.assertTrue(response.getMasterKeyStatus());
Assertions.assertEquals("INITIALIZED", response.getDeviceStatus());
} }
private static DeviceService newDeviceService(String productType) { private static DeviceService newDeviceService(String productType, String tmsVersion, String serialNumber, int fsmState) {
InMemoryInitTaskRepository taskRepository = new InMemoryInitTaskRepository(); InMemoryInitTaskRepository taskRepository = new InMemoryInitTaskRepository();
InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository(); InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository();
InitService initService = newInitService(taskRepository, stepRepository, productType); InitService initService = newInitService(taskRepository, stepRepository, productType);
return new DeviceService(new FixedDeviceNodeRepository(), preset(productType), initService); return new DeviceService(
deviceProfile("SYD7108", "跨境支付终端一体机"),
fixedVersionRepository(tmsVersion),
cryptoService(serialNumber, fsmState),
initService
);
} }
private static InitService newInitService( private static InitService newInitService(
@ -132,14 +157,42 @@ class DeviceServiceTest {
Assertions.fail("expected task status " + expectedStatus + " within timeout"); Assertions.fail("expected task status " + expectedStatus + " within timeout");
} }
private static class FixedDeviceNodeRepository implements DeviceNodeRepository { private static DeviceProfileProperties deviceProfile(String model, String name) {
@Override DeviceProfileProperties properties = new DeviceProfileProperties();
public Optional<DeviceNodeEntity> findByNodeId(String nodeId) { properties.setModel(model);
DeviceNodeEntity entity = new DeviceNodeEntity(); properties.setName(name);
entity.setNodeId(nodeId); return properties;
entity.setStatus("RUNNING"); }
return Optional.of(entity);
} private static DeviceSoftwareVersionRepository fixedVersionRepository(String version) {
return new DeviceSoftwareVersionRepository() {
@Override
public Optional<DeviceSoftwareVersionEntity> findByComponentCode(String componentCode) {
if (!"TMS".equals(componentCode)) {
return Optional.empty();
}
DeviceSoftwareVersionEntity entity = new DeviceSoftwareVersionEntity();
entity.setComponentCode("TMS");
entity.setCurrentVersion(version);
return Optional.of(entity);
}
@Override
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
}
};
}
private static PcieCryptoService cryptoService(String serialNumber, int fsmState) {
PcieCryptoService service = Mockito.mock(PcieCryptoService.class);
DeviceInfoResult infoResult = new DeviceInfoResult();
infoResult.setDeviceSerial(serialNumber);
Mockito.when(service.getDeviceInfo()).thenReturn(infoResult);
DeviceStatusResult statusResult = new DeviceStatusResult();
statusResult.setFsmState(fsmState);
Mockito.when(service.getDeviceStatus()).thenReturn(statusResult);
return service;
} }
private static class InMemoryInitTaskRepository implements InitTaskRepository { private static class InMemoryInitTaskRepository implements InitTaskRepository {