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
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:
staging-root-dir: /home/tmp/tms-upgrade-staging
log-dir: /home/tms/tmp/tms-upgrade-logs

View File

@ -1,6 +1,5 @@
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.security.internal.InternalApiAuthInterceptor;
import com.cisd.tms.security.openapi.OpenApiSignAuthInterceptor;
@ -24,18 +23,15 @@ public class WebMvcConfig implements WebMvcConfigurer {
private final InternalApiAuthInterceptor internalApiAuthInterceptor;
private final InternalAuthorizationInterceptor internalAuthorizationInterceptor;
private final OpenApiSignAuthInterceptor openApiSignAuthInterceptor;
private final TmsCorsProperties corsProperties;
public WebMvcConfig(
InternalApiAuthInterceptor internalApiAuthInterceptor,
InternalAuthorizationInterceptor internalAuthorizationInterceptor,
OpenApiSignAuthInterceptor openApiSignAuthInterceptor,
TmsCorsProperties corsProperties
OpenApiSignAuthInterceptor openApiSignAuthInterceptor
) {
this.internalApiAuthInterceptor = internalApiAuthInterceptor;
this.internalAuthorizationInterceptor = internalAuthorizationInterceptor;
this.openApiSignAuthInterceptor = openApiSignAuthInterceptor;
this.corsProperties = corsProperties;
}
@Bean
@ -80,16 +76,12 @@ public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
if (!corsProperties.isEnabled() || corsProperties.getAllowedOriginPatterns().isEmpty()) {
return;
}
registry.addMapping("/**")
.allowedOriginPatterns(corsProperties.getAllowedOriginPatterns().toArray(String[]::new))
.allowedMethods(corsProperties.getAllowedMethods().toArray(String[]::new))
.allowedHeaders(corsProperties.getAllowedHeaders().toArray(String[]::new))
.exposedHeaders(corsProperties.getExposedHeaders().toArray(String[]::new))
.allowCredentials(corsProperties.isAllowCredentials())
.maxAge(corsProperties.getMaxAgeSeconds());
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("X-Trace-Id")
.allowCredentials(true)
.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() {
return sessionTemplate.withSession("SDFE_CheckLMK", (lib, deviceHandle, sessionHandle) -> {
int retCode = lib.SDFE_CheckLMK(sessionHandle);
if (retCode == 0) {
return true;
}
if ((retCode & 0xFF) == SDFE_INIT_STATUS_LOW_BYTE) {
return false;
}
sessionTemplate.ensureSuccess("SDFE_CheckLMK", retCode);
return false;
return retCode == 0;
// if (retCode == 0) {
// return true;
// }
// if ((retCode & 0xFF) == SDFE_INIT_STATUS_LOW_BYTE) {
// return false;
// }
// sessionTemplate.ensureSuccess("SDFE_CheckLMK", retCode);
// return false;
});
}

View File

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

View File

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

View File

@ -1,50 +1,47 @@
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.DeviceInfoResponse;
import com.cisd.tms.modules.device.entity.DeviceNodeEntity;
import com.cisd.tms.modules.device.repository.DeviceNodeRepository;
import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
import com.cisd.tms.modules.device.repository.DeviceSoftwareVersionRepository;
import com.cisd.tms.modules.init.service.InitService;
import java.time.OffsetDateTime;
import java.util.Optional;
import org.springframework.stereotype.Service;
@Service
public class DeviceService {
private final DeviceNodeRepository deviceNodeRepository;
private final CisdPresetProperties cisdPresetProperties;
private final DeviceProfileProperties deviceProfileProperties;
private final DeviceSoftwareVersionRepository deviceSoftwareVersionRepository;
private final PcieCryptoService pcieCryptoService;
private final InitService initService;
public DeviceService(
DeviceNodeRepository deviceNodeRepository,
CisdPresetProperties cisdPresetProperties,
DeviceProfileProperties deviceProfileProperties,
DeviceSoftwareVersionRepository deviceSoftwareVersionRepository,
PcieCryptoService pcieCryptoService,
InitService initService
) {
this.deviceNodeRepository = deviceNodeRepository;
this.cisdPresetProperties = cisdPresetProperties;
this.deviceProfileProperties = deviceProfileProperties;
this.deviceSoftwareVersionRepository = deviceSoftwareVersionRepository;
this.pcieCryptoService = pcieCryptoService;
this.initService = initService;
}
public DeviceInfoResponse info() {
DeviceNodeEntity entity = deviceNodeRepository.findByNodeId("node-01").orElse(null);
DeviceInfoResponse response = new DeviceInfoResponse();
if (entity == null) {
response.setDeviceId("cisd-all-in-one-001");
response.setStatus("RUNNING");
response.setVersion("0.0.1-SNAPSHOT");
} else {
response.setDeviceId(entity.getNodeId());
response.setStatus(entity.getStatus());
response.setVersion("db-loaded");
}
response.setPresetProductType(cisdPresetProperties.getProductType());
response.setPresetVersion(cisdPresetProperties.getVersion());
response.setPresetSource(cisdPresetProperties.getSource());
response.setDeviceModel(trim(deviceProfileProperties.getModel()));
response.setDeviceName(trim(deviceProfileProperties.getName()));
response.setVersion(loadTmsVersion());
response.setSerialNumber(loadDeviceSerial());
response.setMasterKeyStatus(loadManagementKeyReady());
InitService.DeviceInitStateSnapshot snapshot = initService.getDeviceInitStateSnapshot();
response.setInitState(snapshot.initState());
response.setDeviceStatus(snapshot.initState());
return response;
}
@ -56,4 +53,31 @@ public class DeviceService {
response.setAcceptedAt(OffsetDateTime.now().toString());
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.setNode01Ip(firstNonBlank(text(nodes, "node01Ip"), text(request, "node01Ip")));
response.setNode02Ip(firstNonBlank(text(nodes, "node02Ip"), text(request, "node02Ip")));
response.setSignHost(firstNonBlank(text(signServer, "signHost"), text(request, "signHost")));
response.setSignPort(integerValue(firstNonBlank(text(signServer, "signPort"), text(request, "signPort"))));
response.setSignType(firstNonBlank(text(signServer, "signType"), text(request, "signType")));
// response.setSignHost(firstNonBlank(text(signServer, "signHost"), text(request, "signHost")));
// response.setSignPort(integerValue(firstNonBlank(text(signServer, "signPort"), text(request, "signPort"))));
// response.setSignType(firstNonBlank(text(signServer, "signType"), text(request, "signType")));
return response;
} catch (JsonProcessingException ex) {
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}
# 磁盘总量默认展示值;命令读取失败或为空时回退。
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:
# 升级包解压和脚本执行暂存目录。
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;
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.entity.DeviceNodeEntity;
import com.cisd.tms.modules.device.repository.DeviceNodeRepository;
import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
import com.cisd.tms.modules.device.repository.DeviceSoftwareVersionRepository;
import com.cisd.tms.modules.init.config.InitCommandProfileService;
import com.cisd.tms.modules.init.config.InitExecutorProperties;
import com.cisd.tms.modules.init.dto.InitCreateTaskResponse;
@ -25,16 +29,22 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
class DeviceServiceTest {
@Test
void shouldReportUninitializedWhenNoSuccessfulInitTask() {
DeviceService service = newDeviceService("ENTERPRISE");
DeviceService service = newDeviceService("ENTERPRISE", "TMS-V2.0.0", "SERIAL-001", 1);
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
@ -42,7 +52,12 @@ class DeviceServiceTest {
InMemoryInitTaskRepository taskRepository = new InMemoryInitTaskRepository();
InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository();
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());
initService.executeTask(created.getTaskId());
@ -50,14 +65,24 @@ class DeviceServiceTest {
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();
InMemoryInitTaskStepRepository stepRepository = new InMemoryInitTaskStepRepository();
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(
@ -132,14 +157,42 @@ class DeviceServiceTest {
Assertions.fail("expected task status " + expectedStatus + " within timeout");
}
private static class FixedDeviceNodeRepository implements DeviceNodeRepository {
@Override
public Optional<DeviceNodeEntity> findByNodeId(String nodeId) {
DeviceNodeEntity entity = new DeviceNodeEntity();
entity.setNodeId(nodeId);
entity.setStatus("RUNNING");
return Optional.of(entity);
}
private static DeviceProfileProperties deviceProfile(String model, String name) {
DeviceProfileProperties properties = new DeviceProfileProperties();
properties.setModel(model);
properties.setName(name);
return properties;
}
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 {