fix:设备信息展示
This commit is contained in:
parent
16578208bc
commit
312391052f
@ -13,6 +13,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ -53,6 +54,12 @@ public class GlobalExceptionHandler {
|
||||
.withPath(request.getRequestURI());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public ApiResponse<Void> handleMultipartException(MultipartException ex, HttpServletRequest request) {
|
||||
return ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "failed to parse multipart request")
|
||||
.withPath(request.getRequestURI());
|
||||
}
|
||||
|
||||
@ExceptionHandler(PcieCryptoException.class)
|
||||
public ApiResponse<Void> handleCryptoCardException(PcieCryptoException ex, HttpServletRequest request) {
|
||||
return ApiResponse.fail(ErrorCode.CRYPTO_CARD_ERROR.getCode(), ex.getMessage())
|
||||
|
||||
@ -61,7 +61,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/device/crypto")
|
||||
@RequiredArgsConstructor
|
||||
@ReplayProtected
|
||||
//@ReplayProtected
|
||||
@Tag(name = "密码卡调试", description = "密码卡设备调试、密钥操作和文件管理接口")
|
||||
public class CryptoCardController {
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package com.cisd.tms.modules.device.mapper;
|
||||
|
||||
import com.cisd.tms.infrastructure.persistence.mapper.BaseMapperX;
|
||||
import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -10,5 +11,7 @@ public interface DeviceSoftwareVersionMapper extends BaseMapperX<DeviceSoftwareV
|
||||
|
||||
DeviceSoftwareVersionEntity selectByComponentCode(@Param("componentCode") String componentCode);
|
||||
|
||||
List<DeviceSoftwareVersionEntity> selectAll();
|
||||
|
||||
int updateByComponentCode(DeviceSoftwareVersionEntity entity);
|
||||
}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package com.cisd.tms.modules.device.repository;
|
||||
|
||||
import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface DeviceSoftwareVersionRepository {
|
||||
|
||||
Optional<DeviceSoftwareVersionEntity> findByComponentCode(String componentCode);
|
||||
|
||||
List<DeviceSoftwareVersionEntity> findAll();
|
||||
|
||||
void saveOrUpdate(DeviceSoftwareVersionEntity entity);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.cisd.tms.modules.device.repository.impl;
|
||||
import com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity;
|
||||
import com.cisd.tms.modules.device.mapper.DeviceSoftwareVersionMapper;
|
||||
import com.cisd.tms.modules.device.repository.DeviceSoftwareVersionRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@ -20,6 +21,11 @@ public class DeviceSoftwareVersionRepositoryImpl implements DeviceSoftwareVersio
|
||||
return Optional.ofNullable(deviceSoftwareVersionMapper.selectByComponentCode(componentCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return deviceSoftwareVersionMapper.selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
if (findByComponentCode(entity.getComponentCode()).isPresent()) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.cisd.tms.modules.device.service.impl;
|
||||
|
||||
import com.cisd.tms.common.config.properties.CisdPresetProperties;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||
import com.cisd.tms.modules.device.dto.CryptoCardRuntimeStatus;
|
||||
import com.cisd.tms.modules.device.dto.DeviceRuntimeStatusResponse;
|
||||
@ -15,12 +14,10 @@ import com.cisd.tms.modules.device.support.HardwareStatusProbe;
|
||||
import com.cisd.tms.modules.device.support.SoftwareRuntimeProbe;
|
||||
import com.cisd.tms.modules.device.support.SystemSoftwareVersionProbe;
|
||||
import com.cisd.tms.modules.device.support.UsageSnapshot;
|
||||
import com.cisd.tms.modules.init.dto.CurrentInitConfigResponse;
|
||||
import com.cisd.tms.modules.init.service.InitService;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -31,7 +28,6 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
|
||||
private static final String STATUS_NORMAL = "NORMAL";
|
||||
private static final String STATUS_ABNORMAL = "ABNORMAL";
|
||||
private static final String STATUS_NOT_INSTALLED = "NOT_INSTALLED";
|
||||
private static final String STATUS_UNKNOWN = "UNKNOWN";
|
||||
|
||||
private final InitService initService;
|
||||
@ -104,52 +100,35 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
}
|
||||
|
||||
private List<SoftwareRuntimeStatusItem> buildSoftwareStatuses() {
|
||||
CurrentInitConfigResponse config = loadCurrentInitConfigIfPresent();
|
||||
boolean initialized = config != null;
|
||||
String productType = config == null ? normalize(cisdPresetProperties.getProductType()) : normalize(config.getProductType());
|
||||
String mqType = config == null ? "" : normalize(config.getMqType());
|
||||
|
||||
List<DeviceSoftwareVersionEntity> versions = deviceSoftwareVersionRepository.findAll();
|
||||
List<SoftwareRuntimeStatusItem> items = new ArrayList<>();
|
||||
items.add(buildSoftwareItem("RABBITMQ", "消息队列", cached("RABBITMQ"), initialized && isRabbitmqEnabled(mqType), softwareRuntimeProbe::isRabbitmqRunning));
|
||||
items.add(buildSoftwareItem("TLQ", "消息队列", cached("TLQ"), initialized && isTlqEnabled(mqType), softwareRuntimeProbe::isTlqRunning));
|
||||
items.add(buildSoftwareItem("TIDB", "数据库", cached("TIDB"), initialized && isTidbEnabled(productType), softwareRuntimeProbe::isTidbRunning));
|
||||
items.add(buildSoftwareItem("NGINX", "Nginx", cached("NGINX"), initialized && isNginxEnabled(productType), softwareRuntimeProbe::isNginxRunning));
|
||||
items.add(buildReceiverItem(productType, initialized));
|
||||
items.add(buildSoftwareItem("TMS", "设备管理软件", firstNonBlank(cached("TMS"), systemSoftwareVersionProbe.tmsVersion(), cisdPresetProperties.getVersion()), true, softwareRuntimeProbe::isTmsRunning));
|
||||
for (DeviceSoftwareVersionEntity version : versions) {
|
||||
items.add(buildSoftwareItem(version));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private SoftwareRuntimeStatusItem buildReceiverItem(String productType, boolean initialized) {
|
||||
boolean standard = "ENTERPRISE".equals(productType) || "INDIRECT".equals(productType);
|
||||
BooleanSupplier supplier = standard ? softwareRuntimeProbe::isStandardAppRunning : softwareRuntimeProbe::isDirectAppRunning;
|
||||
String name = "标准收发器";
|
||||
String displayName = firstNonBlank(cached("RECEIVER"), appName(productType));
|
||||
return buildSoftwareItem("RECEIVER", name, displayName, initialized, supplier);
|
||||
}
|
||||
|
||||
private SoftwareRuntimeStatusItem buildSoftwareItem(
|
||||
String code,
|
||||
String name,
|
||||
String displayName,
|
||||
boolean enabled,
|
||||
BooleanSupplier probe
|
||||
) {
|
||||
private SoftwareRuntimeStatusItem buildSoftwareItem(DeviceSoftwareVersionEntity version) {
|
||||
String code = normalize(version.getComponentCode());
|
||||
String name = firstNonBlank(version.getComponentName(), code);
|
||||
String displayName = displayName(version, code, name);
|
||||
SoftwareRuntimeStatusItem item = new SoftwareRuntimeStatusItem();
|
||||
item.setComponentCode(code);
|
||||
item.setComponentName(name);
|
||||
item.setDisplayName(firstNonBlank(displayName, name));
|
||||
if (!enabled) {
|
||||
item.setStatus(STATUS_NOT_INSTALLED);
|
||||
item.setMessage("未安装");
|
||||
BooleanSupplier probe = probeFor(code);
|
||||
if (probe == null) {
|
||||
item.setStatus(STATUS_UNKNOWN);
|
||||
item.setMessage("未知");
|
||||
return item;
|
||||
}
|
||||
try {
|
||||
if (probe.getAsBoolean()) {
|
||||
item.setStatus(STATUS_NORMAL);
|
||||
item.setMessage("正常");
|
||||
item.setMessage("运行中");
|
||||
} else {
|
||||
item.setStatus(STATUS_ABNORMAL);
|
||||
item.setMessage("异常");
|
||||
item.setMessage("未运行");
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
item.setStatus(STATUS_UNKNOWN);
|
||||
@ -158,47 +137,6 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
return item;
|
||||
}
|
||||
|
||||
private CurrentInitConfigResponse loadCurrentInitConfigIfPresent() {
|
||||
try {
|
||||
if (!"INITIALIZED".equals(initService.getDeviceInitStateSnapshot().initState())) {
|
||||
return null;
|
||||
}
|
||||
return initService.loadCurrentInitConfig();
|
||||
} catch (BizException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRabbitmqEnabled(String mqType) {
|
||||
return "RABBITMQ".equals(mqType) || "RABBITMQ_TLQ".equals(mqType) || "RABBITMQ_CFMQ".equals(mqType);
|
||||
}
|
||||
|
||||
private boolean isTlqEnabled(String mqType) {
|
||||
return "TLQ".equals(mqType) || "RABBITMQ_TLQ".equals(mqType);
|
||||
}
|
||||
|
||||
private boolean isTidbEnabled(String productType) {
|
||||
return "ENTERPRISE".equals(productType) || "INDIRECT".equals(productType);
|
||||
}
|
||||
|
||||
private boolean isNginxEnabled(String productType) {
|
||||
return "ENTERPRISE".equals(productType) || "INDIRECT".equals(productType);
|
||||
}
|
||||
|
||||
private String cached(String componentCode) {
|
||||
Optional<DeviceSoftwareVersionEntity> entity = deviceSoftwareVersionRepository.findByComponentCode(componentCode);
|
||||
return entity.map(DeviceSoftwareVersionEntity::getCurrentVersion).orElse("");
|
||||
}
|
||||
|
||||
private String appName(String productType) {
|
||||
return switch (productType) {
|
||||
case "ENTERPRISE" -> "标准收发器企业版";
|
||||
case "INDIRECT" -> "标准收发器间参版";
|
||||
case "DIRECT" -> "直参轻量化终端版";
|
||||
default -> "应用软件";
|
||||
};
|
||||
}
|
||||
|
||||
private String normalize(String value) {
|
||||
return value == null ? "" : value.trim().toUpperCase();
|
||||
}
|
||||
@ -219,6 +157,30 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
return "";
|
||||
}
|
||||
|
||||
private String displayName(DeviceSoftwareVersionEntity version, String code, String name) {
|
||||
if ("TMS".equals(code)) {
|
||||
return firstNonBlank(version.getCurrentVersion(), systemSoftwareVersionProbe.tmsVersion(), cisdPresetProperties.getVersion(), name);
|
||||
}
|
||||
return firstNonBlank(version.getCurrentVersion(), name);
|
||||
}
|
||||
|
||||
private BooleanSupplier probeFor(String componentCode) {
|
||||
return switch (componentCode) {
|
||||
case "RABBITMQ" -> softwareRuntimeProbe::isRabbitmqRunning;
|
||||
case "TLQ" -> softwareRuntimeProbe::isTlqRunning;
|
||||
case "TIDB" -> softwareRuntimeProbe::isTidbRunning;
|
||||
case "NGINX" -> softwareRuntimeProbe::isNginxRunning;
|
||||
case "RECEIVER" -> receiverProbe();
|
||||
case "TMS" -> softwareRuntimeProbe::isTmsRunning;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private BooleanSupplier receiverProbe() {
|
||||
String productType = normalize(cisdPresetProperties.getProductType());
|
||||
return "DIRECT".equals(productType) ? softwareRuntimeProbe::isDirectAppRunning : softwareRuntimeProbe::isStandardAppRunning;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface UsageSupplier {
|
||||
UsageSnapshot get();
|
||||
|
||||
@ -13,10 +13,13 @@ import com.cisd.tms.modules.init.service.InitService;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DeviceServiceImpl implements DeviceService {
|
||||
|
||||
private final DeviceProfileProperties deviceProfileProperties;
|
||||
@ -26,14 +29,27 @@ public class DeviceServiceImpl implements DeviceService {
|
||||
|
||||
@Override
|
||||
public DeviceInfoResponse info() {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start("1");
|
||||
DeviceInfoResponse response = new DeviceInfoResponse();
|
||||
response.setDeviceModel(trim(deviceProfileProperties.getModel()));
|
||||
response.setDeviceName(trim(deviceProfileProperties.getName()));
|
||||
response.setVersion(loadTmsVersion());
|
||||
stopWatch.stop();
|
||||
|
||||
stopWatch.start("2");
|
||||
response.setSerialNumber(loadDeviceSerial());
|
||||
stopWatch.stop();
|
||||
|
||||
stopWatch.start("3");
|
||||
response.setMasterKeyStatus(loadManagementKeyReady());
|
||||
stopWatch.stop();
|
||||
|
||||
stopWatch.start("4");
|
||||
InitService.DeviceInitStateSnapshot snapshot = initService.getDeviceInitStateSnapshot();
|
||||
response.setDeviceStatus(snapshot.initState());
|
||||
stopWatch.stop();
|
||||
log.info(stopWatch.prettyPrint());
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -63,8 +79,9 @@ public class DeviceServiceImpl implements DeviceService {
|
||||
|
||||
private boolean loadManagementKeyReady() {
|
||||
try {
|
||||
DeviceStatusResult result = pcieCryptoService.getDeviceStatus();
|
||||
return result != null && result.getFsmState() == 0;
|
||||
// DeviceStatusResult result = pcieCryptoService.getDeviceStatus();
|
||||
// return result != null && result.getFsmState() == 0;
|
||||
return pcieCryptoService.checkLmk();
|
||||
} catch (RuntimeException ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,10 +1,41 @@
|
||||
package com.cisd.tms.modules.device.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DefaultSoftwareRuntimeProbe implements SoftwareRuntimeProbe {
|
||||
|
||||
private static final Duration DEFAULT_CACHE_TTL = Duration.ofSeconds(1);
|
||||
|
||||
private final Supplier<List<String>> processSnapshotLoader;
|
||||
private final Duration cacheTtl;
|
||||
private final LongSupplier nanoTimeSupplier;
|
||||
|
||||
private final Object lock = new Object();
|
||||
private volatile CachedProcessSnapshot cachedSnapshot;
|
||||
|
||||
public DefaultSoftwareRuntimeProbe() {
|
||||
this(DefaultSoftwareRuntimeProbe::loadProcessSnapshot, DEFAULT_CACHE_TTL, System::nanoTime);
|
||||
}
|
||||
|
||||
DefaultSoftwareRuntimeProbe(
|
||||
Supplier<List<String>> processSnapshotLoader,
|
||||
Duration cacheTtl,
|
||||
LongSupplier nanoTimeSupplier
|
||||
) {
|
||||
this.processSnapshotLoader = Objects.requireNonNull(processSnapshotLoader, "processSnapshotLoader");
|
||||
this.cacheTtl = Objects.requireNonNull(cacheTtl, "cacheTtl");
|
||||
this.nanoTimeSupplier = Objects.requireNonNull(nanoTimeSupplier, "nanoTimeSupplier");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRabbitmqRunning() {
|
||||
return isRunning("rabbitmq-server");
|
||||
@ -41,22 +72,64 @@ public class DefaultSoftwareRuntimeProbe implements SoftwareRuntimeProbe {
|
||||
}
|
||||
|
||||
private boolean isRunning(String pattern) {
|
||||
Process process;
|
||||
try {
|
||||
process = new ProcessBuilder("bash", "-lc", "pgrep -f '" + pattern + "' >/dev/null").start();
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode == 0) {
|
||||
return true;
|
||||
}
|
||||
if (exitCode == 1) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalStateException("pgrep failed for pattern: " + pattern);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("process probe interrupted: " + pattern, ex);
|
||||
} catch (Exception ex) {
|
||||
Pattern compiled = Pattern.compile(pattern);
|
||||
return processSnapshot().stream().anyMatch(commandLine -> compiled.matcher(commandLine).find());
|
||||
} catch (PatternSyntaxException ex) {
|
||||
throw new IllegalStateException("invalid process probe pattern: " + pattern, ex);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new IllegalStateException("process probe failed: " + pattern, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> processSnapshot() {
|
||||
long now = nanoTimeSupplier.getAsLong();
|
||||
CachedProcessSnapshot cached = cachedSnapshot;
|
||||
if (isUsable(cached, now)) {
|
||||
return cached.commandLines();
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
now = nanoTimeSupplier.getAsLong();
|
||||
cached = cachedSnapshot;
|
||||
if (isUsable(cached, now)) {
|
||||
return cached.commandLines();
|
||||
}
|
||||
List<String> commandLines = List.copyOf(processSnapshotLoader.get());
|
||||
cachedSnapshot = new CachedProcessSnapshot(commandLines, now);
|
||||
return commandLines;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUsable(CachedProcessSnapshot cached, long now) {
|
||||
if (cached == null) {
|
||||
return false;
|
||||
}
|
||||
long ttlNanos = cacheTtl.toNanos();
|
||||
return ttlNanos <= 0 || now - cached.loadedAtNanos() < ttlNanos;
|
||||
}
|
||||
|
||||
private static List<String> loadProcessSnapshot() {
|
||||
Process process;
|
||||
try {
|
||||
process = new ProcessBuilder("bash", "-lc", "ps -eo args=").start();
|
||||
int exitCode = process.waitFor();
|
||||
List<String> commandLines = new String(process.getInputStream().readAllBytes()).lines()
|
||||
.map(String::trim)
|
||||
.filter(line -> !line.isBlank())
|
||||
.toList();
|
||||
if (exitCode != 0) {
|
||||
throw new IllegalStateException("ps command failed");
|
||||
}
|
||||
return commandLines;
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("process snapshot interrupted", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException("process snapshot failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private record CachedProcessSnapshot(List<String> commandLines, long loadedAtNanos) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
package com.cisd.tms.modules.sign.controller.openapi;
|
||||
|
||||
import com.cisd.tms.modules.sign.dto.openapi.OpenSignRequest;
|
||||
import com.cisd.tms.modules.sign.dto.openapi.OpenSignResponse;
|
||||
import com.cisd.tms.modules.sign.service.SignService;
|
||||
import com.cisd.tms.modules.sign.support.SignCommand;
|
||||
import com.cisd.tms.modules.sign.support.SignResult;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/openapi/v1/sign")
|
||||
public class OpenSignController {
|
||||
|
||||
private final SignService signService;
|
||||
|
||||
public OpenSignController(SignService signService) {
|
||||
this.signService = signService;
|
||||
}
|
||||
|
||||
@PostMapping("/signature")
|
||||
public OpenSignResponse signature(@Valid @RequestBody OpenSignRequest request) {
|
||||
SignCommand command = new SignCommand();
|
||||
command.setRequestId(request.getRequestId());
|
||||
command.setAlgorithm(request.getAlgorithm());
|
||||
command.setPlainText(request.getPayload());
|
||||
|
||||
SignResult result = signService.sign(command);
|
||||
|
||||
OpenSignResponse response = new OpenSignResponse();
|
||||
response.setRequestId(result.getRequestId());
|
||||
response.setAlgorithm(result.getAlgorithm());
|
||||
response.setDigestHex(result.getDigestHex());
|
||||
response.setSignature(result.getSignature());
|
||||
response.setSignedAt(result.getSignedAt().toString());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,12 @@
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectAll" resultMap="DeviceSoftwareVersionResultMap">
|
||||
SELECT id, component_code, component_name, current_version, source_type, detected_at, remarks, create_time, update_time
|
||||
FROM tms_device_software_version
|
||||
ORDER BY id ASC
|
||||
</select>
|
||||
|
||||
<update id="updateByComponentCode" parameterType="com.cisd.tms.modules.device.entity.DeviceSoftwareVersionEntity">
|
||||
UPDATE tms_device_software_version
|
||||
SET component_name = #{componentName},
|
||||
|
||||
@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
class GlobalExceptionHandlerTest {
|
||||
|
||||
@ -24,4 +25,20 @@ class GlobalExceptionHandlerTest {
|
||||
Assertions.assertEquals("upload file size exceeds limit", response.getMsg());
|
||||
Assertions.assertEquals("/api/v1/upgrade-packages", response.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMultipartParseFailureAsValidationFailure() {
|
||||
GlobalExceptionHandler handler = new GlobalExceptionHandler();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/api/v1/files/upload");
|
||||
|
||||
ApiResponse<Void> response = handler.handleMultipartException(
|
||||
new MultipartException("Failed to parse multipart servlet request"),
|
||||
request
|
||||
);
|
||||
|
||||
Assertions.assertFalse(response.isSuccess());
|
||||
Assertions.assertEquals(ErrorCode.VALIDATE_FAILED.getCode(), response.getCode());
|
||||
Assertions.assertEquals("failed to parse multipart request", response.getMsg());
|
||||
Assertions.assertEquals("/api/v1/files/upload", response.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,6 +391,11 @@ class DeviceProfileServiceTest {
|
||||
return Optional.ofNullable(data.get(componentCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return new ArrayList<>(data.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
data.put(entity.getComponentCode(), entity);
|
||||
|
||||
@ -29,6 +29,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -45,10 +46,10 @@ class DeviceRuntimeStatusServiceTest {
|
||||
|
||||
InMemoryDeviceSoftwareVersionRepository versions = new InMemoryDeviceSoftwareVersionRepository();
|
||||
versions.save(component("RABBITMQ", "消息队列", "RabbitMQ V3.8.21"));
|
||||
versions.save(component("TLQ", "消息队列", "tonglink/Q V8.1.11"));
|
||||
versions.save(component("TIDB", "数据库", "TiDB V8.1.0"));
|
||||
versions.save(component("NGINX", "Nginx", "Nginx V1.20"));
|
||||
versions.save(component("RECEIVER", "标准收发器企业版", "标准收发器企业版 V1.3.2"));
|
||||
versions.save(component("TMS", "设备管理软件", "SYD TMS V1.00"));
|
||||
|
||||
DeviceRuntimeStatusService service = new DeviceRuntimeStatusServiceImpl(
|
||||
initService,
|
||||
@ -75,39 +76,50 @@ class DeviceRuntimeStatusServiceTest {
|
||||
Assertions.assertEquals("NORMAL", response.getHardware().getCryptoCard().getStatus());
|
||||
|
||||
assertSoftwareStatus(response.getSoftware(), "RABBITMQ", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "ABNORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "NORMAL");
|
||||
assertSoftwareAbsent(response.getSoftware(), "TLQ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkNonInitializedSoftwareAsNotInstalled() {
|
||||
void shouldReturnOnlyComponentsThatExistInVersionTable() {
|
||||
InMemoryDeviceSoftwareVersionRepository versions = new InMemoryDeviceSoftwareVersionRepository();
|
||||
versions.save(component("RABBITMQ", "消息队列", "RabbitMQ V3.8.21"));
|
||||
versions.save(component("TMS", "设备管理软件", "SYD TMS V1.00"));
|
||||
|
||||
DeviceRuntimeStatusService service = new DeviceRuntimeStatusServiceImpl(
|
||||
newInitService("ENTERPRISE"),
|
||||
cryptoService(),
|
||||
hardwareProbe(10.0, 8.0, 16.0, 100.0, 256.0),
|
||||
softwareVersionProbe("SYD TMS V1.00"),
|
||||
softwareRuntimeProbe(Map.of("TMS", true)),
|
||||
new InMemoryDeviceSoftwareVersionRepository(),
|
||||
softwareRuntimeProbe(Map.of("RABBITMQ", false, "TMS", true)),
|
||||
versions,
|
||||
preset("ENTERPRISE")
|
||||
);
|
||||
|
||||
DeviceRuntimeStatusResponse response = service.loadRuntimeStatus();
|
||||
|
||||
assertSoftwareStatus(response.getSoftware(), "RABBITMQ", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "NOT_INSTALLED");
|
||||
Assertions.assertEquals(2, response.getSoftware().size());
|
||||
assertSoftwareStatus(response.getSoftware(), "RABBITMQ", "ABNORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "NORMAL");
|
||||
assertSoftwareAbsent(response.getSoftware(), "TLQ");
|
||||
assertSoftwareAbsent(response.getSoftware(), "TIDB");
|
||||
assertSoftwareAbsent(response.getSoftware(), "NGINX");
|
||||
assertSoftwareAbsent(response.getSoftware(), "RECEIVER");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUnknownWhenProbeFails() {
|
||||
InitService initService = newInitService("INDIRECT");
|
||||
createSuccessfulInitTask(initService, validInitRequest("RABBITMQ_TLQ"));
|
||||
InMemoryDeviceSoftwareVersionRepository versions = new InMemoryDeviceSoftwareVersionRepository();
|
||||
versions.save(component("RABBITMQ", "消息队列", "RabbitMQ V3.8.21"));
|
||||
versions.save(component("TIDB", "数据库", "TiDB V8.1.0"));
|
||||
versions.save(component("NGINX", "Nginx", "Nginx V1.20"));
|
||||
versions.save(component("RECEIVER", "标准收发器间参版", "标准收发器间参版 V1.3.2"));
|
||||
versions.save(component("TMS", "设备管理软件", "SYD TMS V1.00"));
|
||||
|
||||
DeviceRuntimeStatusService service = new DeviceRuntimeStatusServiceImpl(
|
||||
initService,
|
||||
@ -115,7 +127,7 @@ class DeviceRuntimeStatusServiceTest {
|
||||
failingHardwareProbe(),
|
||||
softwareVersionProbe("SYD TMS V1.00"),
|
||||
failingSoftwareRuntimeProbe(),
|
||||
new InMemoryDeviceSoftwareVersionRepository(),
|
||||
versions,
|
||||
preset("INDIRECT")
|
||||
);
|
||||
|
||||
@ -127,11 +139,11 @@ class DeviceRuntimeStatusServiceTest {
|
||||
Assertions.assertEquals("ABNORMAL", response.getHardware().getCryptoCard().getStatus());
|
||||
|
||||
assertSoftwareStatus(response.getSoftware(), "RABBITMQ", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "UNKNOWN");
|
||||
assertSoftwareAbsent(response.getSoftware(), "TLQ");
|
||||
}
|
||||
|
||||
private static void assertSoftwareStatus(List<SoftwareRuntimeStatusItem> items, String code, String expectedStatus) {
|
||||
@ -142,6 +154,13 @@ class DeviceRuntimeStatusServiceTest {
|
||||
Assertions.assertEquals(expectedStatus, item.getStatus());
|
||||
}
|
||||
|
||||
private static void assertSoftwareAbsent(List<SoftwareRuntimeStatusItem> items, String code) {
|
||||
Assertions.assertTrue(
|
||||
items.stream().noneMatch(candidate -> code.equals(candidate.getComponentCode())),
|
||||
() -> "expected software item " + code + " to be absent but got " + items.stream().map(SoftwareRuntimeStatusItem::getComponentCode).toList()
|
||||
);
|
||||
}
|
||||
|
||||
private static HardwareStatusProbe hardwareProbe(double cpu, double memoryUsed, double memoryTotal, double diskUsed, double diskTotal) {
|
||||
return new HardwareStatusProbe() {
|
||||
@Override
|
||||
@ -410,13 +429,18 @@ class DeviceRuntimeStatusServiceTest {
|
||||
|
||||
private static class InMemoryDeviceSoftwareVersionRepository implements DeviceSoftwareVersionRepository {
|
||||
|
||||
private final Map<String, DeviceSoftwareVersionEntity> data = new HashMap<>();
|
||||
private final Map<String, DeviceSoftwareVersionEntity> data = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<DeviceSoftwareVersionEntity> findByComponentCode(String componentCode) {
|
||||
return Optional.ofNullable(data.get(componentCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return new ArrayList<>(data.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
data.put(entity.getComponentCode(), entity);
|
||||
|
||||
@ -179,6 +179,11 @@ class DeviceServiceTest {
|
||||
return Optional.of(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
package com.cisd.tms.modules.device.support;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DefaultSoftwareRuntimeProbeTest {
|
||||
|
||||
@Test
|
||||
void shouldReuseSingleProcessSnapshotAcrossMultipleProbeCallsWithinCacheWindow() {
|
||||
AtomicInteger loads = new AtomicInteger();
|
||||
AtomicLong now = new AtomicLong(1_000_000L);
|
||||
DefaultSoftwareRuntimeProbe probe = new DefaultSoftwareRuntimeProbe(
|
||||
() -> {
|
||||
loads.incrementAndGet();
|
||||
return List.of(
|
||||
"/usr/sbin/rabbitmq-server",
|
||||
"/usr/local/bin/tidb-server",
|
||||
"/opt/app/cmsp",
|
||||
"/opt/app/cmtp",
|
||||
"java -jar tms-framework.jar"
|
||||
);
|
||||
},
|
||||
Duration.ofSeconds(1),
|
||||
now::get
|
||||
);
|
||||
|
||||
Assertions.assertTrue(probe.isRabbitmqRunning());
|
||||
Assertions.assertTrue(probe.isTidbRunning());
|
||||
Assertions.assertTrue(probe.isStandardAppRunning());
|
||||
Assertions.assertTrue(probe.isTmsRunning());
|
||||
|
||||
Assertions.assertEquals(1, loads.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRefreshSnapshotAfterCacheWindowExpires() {
|
||||
AtomicInteger loads = new AtomicInteger();
|
||||
AtomicLong now = new AtomicLong(1_000_000L);
|
||||
DefaultSoftwareRuntimeProbe probe = new DefaultSoftwareRuntimeProbe(
|
||||
() -> {
|
||||
loads.incrementAndGet();
|
||||
return List.of("java -jar tms-framework.jar");
|
||||
},
|
||||
Duration.ofMillis(100),
|
||||
now::get
|
||||
);
|
||||
|
||||
Assertions.assertTrue(probe.isTmsRunning());
|
||||
now.addAndGet(Duration.ofMillis(150).toNanos());
|
||||
Assertions.assertTrue(probe.isTmsRunning());
|
||||
|
||||
Assertions.assertEquals(2, loads.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRequireBothStandardReceiverProcesses() {
|
||||
DefaultSoftwareRuntimeProbe probe = new DefaultSoftwareRuntimeProbe(
|
||||
() -> List.of("/opt/app/cmsp"),
|
||||
Duration.ofSeconds(1),
|
||||
System::nanoTime
|
||||
);
|
||||
|
||||
Assertions.assertFalse(probe.isStandardAppRunning());
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,9 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
@ -317,6 +319,11 @@ class UpgradeTaskRunnerTest {
|
||||
return Optional.ofNullable(store.get(componentCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return new ArrayList<>(store.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
store.put(entity.getComponentCode(), entity);
|
||||
|
||||
@ -26,7 +26,9 @@ import java.security.KeyPairGenerator;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
@ -473,6 +475,11 @@ class UpgradePackageServiceTest {
|
||||
return Optional.ofNullable(store.get(componentCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceSoftwareVersionEntity> findAll() {
|
||||
return new ArrayList<>(store.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdate(DeviceSoftwareVersionEntity entity) {
|
||||
store.put(entity.getComponentCode(), entity);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user