fix:app映射
This commit is contained in:
parent
2f81beb0d8
commit
3d28f84115
@ -83,9 +83,9 @@ public class DeviceProfileServiceImpl implements DeviceProfileService {
|
||||
if ("ENTERPRISE".equals(productType) || "INDIRECT".equals(productType)) {
|
||||
addSoftwareItem(items, "TIDB", "数据库", cached("TIDB"));
|
||||
addSoftwareItem(items, "NGINX", "Nginx", cached("NGINX"));
|
||||
addSoftwareItem(items, "APP", appName(productType), firstNonBlank(cached("APP"), cisdPresetProperties.getVersion()));
|
||||
addSoftwareItem(items, "RECEIVER", appName(productType), firstNonBlank(cached("RECEIVER"), cisdPresetProperties.getVersion()));
|
||||
} else if ("DIRECT".equals(productType)) {
|
||||
addSoftwareItem(items, "APP", appName(productType), firstNonBlank(cached("APP"), cisdPresetProperties.getVersion()));
|
||||
addSoftwareItem(items, "RECEIVER", appName(productType), firstNonBlank(cached("RECEIVER"), cisdPresetProperties.getVersion()));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@ -114,17 +114,17 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
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(buildAppItem(productType, initialized));
|
||||
items.add(buildReceiverItem(productType, initialized));
|
||||
items.add(buildSoftwareItem("TMS", "设备管理软件", firstNonBlank(cached("TMS"), systemSoftwareVersionProbe.tmsVersion(), cisdPresetProperties.getVersion()), true, softwareRuntimeProbe::isTmsRunning));
|
||||
return items;
|
||||
}
|
||||
|
||||
private SoftwareRuntimeStatusItem buildAppItem(String productType, boolean initialized) {
|
||||
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("APP"), appName(productType));
|
||||
return buildSoftwareItem("APP", name, displayName, initialized, supplier);
|
||||
String name = "标准收发器";
|
||||
String displayName = firstNonBlank(cached("RECEIVER"), appName(productType));
|
||||
return buildSoftwareItem("RECEIVER", name, displayName, initialized, supplier);
|
||||
}
|
||||
|
||||
private SoftwareRuntimeStatusItem buildSoftwareItem(
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package com.cisd.tms.modules.upgrade.enums;
|
||||
|
||||
/**
|
||||
* 升级任务类型。具体应用名称由升级包脚本处理,后端不按应用名拆分。
|
||||
*/
|
||||
public enum UpgradeTargetComponent {
|
||||
TMS,
|
||||
RECEIVER,
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package com.cisd.tms.modules.upgrade.enums;
|
||||
|
||||
/**
|
||||
* 升级任务主状态。脚本级细分状态当前通过日志体现,尚未独立落表。
|
||||
*/
|
||||
public enum UpgradeTaskStatus {
|
||||
PENDING_CONFIRM,
|
||||
RUNNING,
|
||||
|
||||
@ -58,6 +58,7 @@ public class UpgradeTaskRunner {
|
||||
UpgradeManifest manifest = objectMapper.readValue(Files.readString(stagedDir.resolve("manifest.json")), UpgradeManifest.class);
|
||||
Path root = stagedDir.normalize().toAbsolutePath();
|
||||
|
||||
// 包内脚本按固定阶段执行;可选阶段未声明时跳过,execute 阶段必须存在。
|
||||
int precheckCode = runOptionalScript("precheck", root, manifest.getEntrypoints() == null ? "" : manifest.getEntrypoints().getPrecheck(), task, logPath);
|
||||
if (precheckCode != 0) {
|
||||
upgradeTaskRepository.updateStatus(
|
||||
@ -97,6 +98,7 @@ public class UpgradeTaskRunner {
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有 execute 和 verify 都成功后,才认为升级成功并回写设备软件版本表。
|
||||
DeviceSoftwareVersionEntity versionEntity = new DeviceSoftwareVersionEntity();
|
||||
String componentCode = componentCode(task.getTaskType());
|
||||
versionEntity.setComponentCode(componentCode);
|
||||
@ -147,6 +149,7 @@ public class UpgradeTaskRunner {
|
||||
if (rollbackPath.isEmpty()) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade rollback script not found");
|
||||
}
|
||||
// 回滚不自动触发,只有外部调用 rollback 接口后才执行包内 rollback 脚本。
|
||||
int exitCode = runRequiredScript("rollback", stagedDir.normalize().toAbsolutePath(), rollbackPath, task, logPath);
|
||||
if (exitCode != 0) {
|
||||
upgradeTaskRepository.updateStatus(
|
||||
@ -198,6 +201,7 @@ public class UpgradeTaskRunner {
|
||||
|
||||
private int runRequiredScript(String phase, Path stagedDir, String scriptPath, UpgradeTaskEntity task, Path logPath) throws IOException, InterruptedException {
|
||||
Path resolved = stagedDir.resolve(trim(scriptPath)).normalize().toAbsolutePath();
|
||||
// 防止脚本路径通过 ../ 跳出升级包解压目录。
|
||||
if (!resolved.startsWith(stagedDir) || !Files.exists(resolved)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade " + phase + " script not found");
|
||||
}
|
||||
@ -212,16 +216,13 @@ public class UpgradeTaskRunner {
|
||||
private String componentName(String componentCode) {
|
||||
return switch (trim(componentCode)) {
|
||||
case "TMS" -> "设备管理软件";
|
||||
case "APP" -> "标准收发器";
|
||||
case "RECEIVER" -> "标准收发器";
|
||||
default -> componentCode;
|
||||
};
|
||||
}
|
||||
|
||||
private String componentCode(String taskType) {
|
||||
return switch (trim(taskType)) {
|
||||
case "RECEIVER" -> "APP";
|
||||
default -> trim(taskType);
|
||||
};
|
||||
return trim(taskType);
|
||||
}
|
||||
|
||||
private String trim(String value) {
|
||||
|
||||
@ -51,11 +51,14 @@ public class UpgradePackageService {
|
||||
public UpgradePreviewResponse preview(String fileId) {
|
||||
StagedUpgradePackage stagedPackage = stagePackage(fileId);
|
||||
UpgradeManifest manifest = stagedPackage.manifest();
|
||||
// 预检顺序保持“结构/产品/签名/脚本/版本”,先挡住不属于当前设备或不可信的包。
|
||||
validateManifest(manifest);
|
||||
validateProductType(manifest.getProductType());
|
||||
// 签名对象固定为 manifest.json 原始字节,具体算法由 UpgradePackageSignatureVerifier 实现。
|
||||
signatureVerifier.verify(manifest, stagedPackage.manifestPath(), stagedPackage.signaturePath());
|
||||
validateExecuteScript(stagedPackage.stagedDir(), manifest);
|
||||
|
||||
// 当前版本来自设备软件版本表;空版本允许继续预检,由前端展示“未记录版本”。
|
||||
String currentVersion = currentVersion(manifest.getTaskType());
|
||||
String targetVersion = normalize(manifest.getVersion());
|
||||
|
||||
@ -101,6 +104,7 @@ public class UpgradePackageService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package file not found on disk");
|
||||
}
|
||||
|
||||
// 同一个 fileId 每次预检都会重新解压,确保读取的是当前上传文件内容。
|
||||
Path stagedDir = upgradePackageStagingService.stage(record.getFileId(), packagePath);
|
||||
Path manifestPath = stagedDir.resolve("manifest.json");
|
||||
Path signaturePath = stagedDir.resolve("signature.sig");
|
||||
@ -142,6 +146,7 @@ public class UpgradePackageService {
|
||||
|
||||
private void validateExecuteScript(Path stagedDir, UpgradeManifest manifest) {
|
||||
Path executePath = stagedDir.resolve(normalize(manifest.getEntrypoints().getExecute())).normalize().toAbsolutePath();
|
||||
// execute 路径必须在 staging 目录内,避免 manifest 声明外部系统脚本。
|
||||
if (!executePath.startsWith(stagedDir.normalize().toAbsolutePath()) || !Files.exists(executePath)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade execute script not found");
|
||||
}
|
||||
|
||||
@ -43,6 +43,10 @@ public class UpgradeService {
|
||||
private final UpgradeTaskRunner upgradeTaskRunner;
|
||||
private final UpgradeProperties upgradeProperties;
|
||||
private final Executor backgroundExecutor;
|
||||
/**
|
||||
* 进程内执行互斥表:避免同一任务被前端重复点击后并发启动。
|
||||
* 跨进程/重启后的互斥仍以数据库中的 RUNNING 任务为准。
|
||||
*/
|
||||
private final ConcurrentMap<String, CompletableFuture<Void>> activeExecutions;
|
||||
|
||||
@Autowired
|
||||
@ -93,6 +97,7 @@ public class UpgradeService {
|
||||
if (request == null || request.getFileId() == null || request.getFileId().isBlank()) {
|
||||
throw new IllegalArgumentException("fileId must not be blank");
|
||||
}
|
||||
// 创建任务前重新预检一次,避免用户上传后替换文件或设备版本状态发生变化。
|
||||
UpgradePreviewResponse preview = upgradePackageService.preview(request.getFileId());
|
||||
if (!preview.isAllowed()) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), blankAs(preview.getBlockReason(), "upgrade preview not allowed"));
|
||||
@ -131,6 +136,7 @@ public class UpgradeService {
|
||||
|
||||
public UpgradeTaskResponse executeTask(String taskId) {
|
||||
UpgradeTaskEntity task = loadTask(taskId);
|
||||
// 第一版按一体机单机串行升级处理,避免同时升级 TMS、收发器或固件造成状态不可控。
|
||||
UpgradeTaskEntity runningTask = upgradeTaskRepository.findRunningTask().orElse(null);
|
||||
if (runningTask != null && !runningTask.getTaskId().equals(taskId)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "another upgrade task is running");
|
||||
@ -144,6 +150,7 @@ public class UpgradeService {
|
||||
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// 后台执行重新加载任务,确保使用已落库的最新状态和日志路径。
|
||||
upgradeTaskRunner.run(loadTask(taskId));
|
||||
} finally {
|
||||
activeExecutions.remove(taskId);
|
||||
@ -155,6 +162,7 @@ public class UpgradeService {
|
||||
|
||||
public UpgradeTaskResponse rollbackTask(String taskId) {
|
||||
UpgradeTaskEntity task = loadTask(taskId);
|
||||
// 回滚能力由升级包声明;TMS 不假定所有包都能安全回滚。
|
||||
UpgradeManifest manifest = upgradePackageService.loadManifest(task.getPackageFileId());
|
||||
if (manifest.getEntrypoints() == null || trim(manifest.getEntrypoints().getRollback()).isEmpty()) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade rollback script not found");
|
||||
@ -205,6 +213,7 @@ public class UpgradeService {
|
||||
}
|
||||
Path path = Path.of(logPath).normalize().toAbsolutePath();
|
||||
Path allowedRoot = Path.of(trim(upgradeProperties.getLogDir())).normalize().toAbsolutePath();
|
||||
// 日志路径必须落在升级日志根目录下,防止通过篡改任务记录读取任意文件。
|
||||
if (!path.startsWith(allowedRoot)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade log path is out of allowed directory");
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package signature file not found");
|
||||
}
|
||||
|
||||
// 第一版升级包仅对 manifest.json 做离线软验签,避免在包内脚本执行前信任未授权包。
|
||||
PublicKey publicKey = loadPublicKey();
|
||||
byte[] manifestBytes = readBytes(manifestPath, "failed to read manifest for signature verification");
|
||||
byte[] signatureBytes = loadSignature(signaturePath);
|
||||
@ -74,6 +75,7 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
|
||||
String pemContent = Files.readString(pemPath, StandardCharsets.UTF_8);
|
||||
try (Reader reader = new StringReader(pemContent); PEMParser parser = new PEMParser(reader)) {
|
||||
Object parsed = parser.readObject();
|
||||
// 约定使用 X.509 SubjectPublicKeyInfo 格式的 PUBLIC KEY PEM。
|
||||
if (!(parsed instanceof SubjectPublicKeyInfo publicKeyInfo)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade signature public key pem is invalid");
|
||||
}
|
||||
@ -97,12 +99,13 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
|
||||
String text = new String(raw, StandardCharsets.UTF_8).trim();
|
||||
if (!text.isEmpty()) {
|
||||
try {
|
||||
// 兼容联调阶段生成的 Base64 文本签名;生产包可直接使用二进制签名。
|
||||
byte[] decoded = Base64.getDecoder().decode(text);
|
||||
if (decoded.length > 0) {
|
||||
return decoded;
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// Keep compatibility with raw binary signature files.
|
||||
// 非 Base64 文本时按原始二进制签名处理。
|
||||
}
|
||||
}
|
||||
return raw;
|
||||
|
||||
@ -39,6 +39,7 @@ public class UpgradePackageStagingService {
|
||||
ZipEntry entry;
|
||||
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||
Path target = targetDir.resolve(entry.getName()).normalize().toAbsolutePath();
|
||||
// 防止恶意 zip 条目通过 ../ 写出 staging 目录。
|
||||
if (!target.startsWith(targetDir)) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "invalid upgrade package entry");
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ public class UpgradeVersionComparator {
|
||||
String value = version == null ? "" : version.trim();
|
||||
List<Integer> parts = new ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
// 只提取版本号中的数字段,兼容 V1.2.3、1.2.3-build4 等简单格式。
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char ch = value.charAt(i);
|
||||
if (Character.isDigit(ch)) {
|
||||
|
||||
@ -110,7 +110,7 @@ VALUES
|
||||
(1002, 'TLQ', '消息队列', 'V8.1.11', 'MANUAL', NOW(), '出厂预置'),
|
||||
(1003, 'TIDB', '数据库', 'V8.1.0', 'MANUAL', NOW(), '出厂预置'),
|
||||
(1004, 'NGINX', 'Nginx', 'V1.20', 'MANUAL', NOW(), '出厂预置'),
|
||||
(1005, 'APP', '标准收发器企业版', 'V1.3.2', 'MANUAL', NOW(), '出厂预置'),
|
||||
(1005, 'RECEIVER', '标准收发器', 'V1.3.2', 'MANUAL', NOW(), '出厂预置'),
|
||||
(1006, 'TMS', 'CISD终端管理服务', 'V1.0.0', 'MANUAL', NOW(), '出厂预置')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
component_name = VALUES(component_name),
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
-- 组件版本表统一使用 RECEIVER 表示标准收发器,废弃旧编码 APP。
|
||||
-- 若环境中已存在 RECEIVER 行,则保留 RECEIVER 并删除旧 APP,避免唯一键冲突。
|
||||
DELETE FROM tms_device_software_version
|
||||
WHERE component_code = 'APP'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM (SELECT id FROM tms_device_software_version WHERE component_code = 'RECEIVER') receiver_row
|
||||
);
|
||||
|
||||
UPDATE tms_device_software_version
|
||||
SET component_code = 'RECEIVER',
|
||||
component_name = '标准收发器',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE component_code = 'APP';
|
||||
|
||||
UPDATE tms_device_software_version
|
||||
SET component_name = '标准收发器',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE component_code = 'RECEIVER';
|
||||
@ -47,7 +47,7 @@ class DeviceProfileServiceTest {
|
||||
versionRepository.save(component("TLQ", "消息队列", "tonglink/Q V8.1.11"));
|
||||
versionRepository.save(component("TIDB", "数据库", "TiDB V8.1.0"));
|
||||
versionRepository.save(component("NGINX", "Nginx", "Nginx V1.20"));
|
||||
versionRepository.save(component("APP", "标准收发器间参版", "标准收发器间参版 V1.3.2"));
|
||||
versionRepository.save(component("RECEIVER", "标准收发器间参版", "标准收发器间参版 V1.3.2"));
|
||||
|
||||
DeviceProfileService service = new DeviceProfileServiceImpl(
|
||||
initService,
|
||||
@ -73,7 +73,7 @@ class DeviceProfileServiceTest {
|
||||
Assertions.assertEquals("32GB", response.getHardware().getMemoryTotal());
|
||||
Assertions.assertEquals("953.87GB", response.getHardware().getDiskTotal());
|
||||
Assertions.assertEquals(
|
||||
List.of("OS", "TMS", "RABBITMQ", "TLQ", "TIDB", "NGINX", "APP"),
|
||||
List.of("OS", "TMS", "RABBITMQ", "TLQ", "TIDB", "NGINX", "RECEIVER"),
|
||||
response.getSoftware().stream().map(DeviceProfileSoftwareItem::getComponentCode).toList()
|
||||
);
|
||||
}
|
||||
@ -156,7 +156,7 @@ class DeviceProfileServiceTest {
|
||||
DeviceProfileResponse response = service.profile();
|
||||
|
||||
Assertions.assertEquals(
|
||||
List.of("OS", "TMS", "RABBITMQ", "TIDB", "NGINX", "APP"),
|
||||
List.of("OS", "TMS", "RABBITMQ", "TIDB", "NGINX", "RECEIVER"),
|
||||
response.getSoftware().stream().map(DeviceProfileSoftwareItem::getComponentCode).toList()
|
||||
);
|
||||
DeviceProfileSoftwareItem rabbitmq = response.getSoftware().stream()
|
||||
|
||||
@ -48,7 +48,7 @@ class DeviceRuntimeStatusServiceTest {
|
||||
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("APP", "标准收发器企业版", "标准收发器企业版 V1.3.2"));
|
||||
versions.save(component("RECEIVER", "标准收发器企业版", "标准收发器企业版 V1.3.2"));
|
||||
|
||||
DeviceRuntimeStatusService service = new DeviceRuntimeStatusServiceImpl(
|
||||
initService,
|
||||
@ -78,7 +78,7 @@ class DeviceRuntimeStatusServiceTest {
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "ABNORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "APP", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "NORMAL");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "NORMAL");
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ class DeviceRuntimeStatusServiceTest {
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "APP", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "NOT_INSTALLED");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "NORMAL");
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ class DeviceRuntimeStatusServiceTest {
|
||||
assertSoftwareStatus(response.getSoftware(), "TLQ", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "TIDB", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "NGINX", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "APP", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "RECEIVER", "UNKNOWN");
|
||||
assertSoftwareStatus(response.getSoftware(), "TMS", "UNKNOWN");
|
||||
}
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ class UpgradeTaskRunnerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateAppSoftwareVersionWhenReceiverUpgradeSucceeds() throws Exception {
|
||||
void shouldUpdateReceiverSoftwareVersionWhenReceiverUpgradeSucceeds() throws Exception {
|
||||
Path tempDir = Files.createTempDirectory("upgrade-runner-test");
|
||||
FileRecordEntity fileRecord = buildPackage(tempDir, false, "#!/bin/sh\necho execute\nexit 0\n", false);
|
||||
InMemoryFileRecordRepository fileRepository = new InMemoryFileRecordRepository();
|
||||
@ -77,9 +77,9 @@ class UpgradeTaskRunnerTest {
|
||||
runner.run(task("UPG-001-R", fileRecord.getFileId(), "RECEIVER", "V1.4.0"));
|
||||
|
||||
Assertions.assertEquals("SUCCESS", taskRepository.status);
|
||||
Assertions.assertNull(versionRepository.store.get("RECEIVER"));
|
||||
Assertions.assertEquals("V1.4.0", versionRepository.store.get("APP").getCurrentVersion());
|
||||
Assertions.assertEquals("标准收发器", versionRepository.store.get("APP").getComponentName());
|
||||
Assertions.assertNull(versionRepository.store.get("APP"));
|
||||
Assertions.assertEquals("V1.4.0", versionRepository.store.get("RECEIVER").getCurrentVersion());
|
||||
Assertions.assertEquals("标准收发器", versionRepository.store.get("RECEIVER").getComponentName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user