初始化配置完善
This commit is contained in:
+18
-1
@@ -14,6 +14,7 @@ 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;
|
||||
@@ -177,10 +178,26 @@ public class DeviceRuntimeStatusServiceImpl implements DeviceRuntimeStatusServic
|
||||
}
|
||||
|
||||
private BooleanSupplier receiverProbe() {
|
||||
String productType = normalize(cisdPresetProperties.getProductType());
|
||||
String productType = resolveReceiverProductType();
|
||||
return "DIRECT".equals(productType) ? softwareRuntimeProbe::isDirectAppRunning : softwareRuntimeProbe::isStandardAppRunning;
|
||||
}
|
||||
|
||||
private String resolveReceiverProductType() {
|
||||
try {
|
||||
String initState = normalize(initService.getDeviceInitStateSnapshot().initState());
|
||||
if ("INITIALIZED".equals(initState)) {
|
||||
CurrentInitConfigResponse current = initService.loadCurrentInitConfig();
|
||||
String currentProductType = normalize(current == null ? null : current.getProductType());
|
||||
if (!currentProductType.isBlank()) {
|
||||
return currentProductType;
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ignored) {
|
||||
// Fallback to the preset product type when runtime init context is unavailable.
|
||||
}
|
||||
return normalize(cisdPresetProperties.getProductType());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface UsageSupplier {
|
||||
UsageSnapshot get();
|
||||
|
||||
+91
-13
@@ -18,67 +18,128 @@ public class DefaultSoftwareRuntimeProbe implements SoftwareRuntimeProbe {
|
||||
private final Supplier<List<String>> processSnapshotLoader;
|
||||
private final Duration cacheTtl;
|
||||
private final LongSupplier nanoTimeSupplier;
|
||||
private final CommandExitCodeSupplier commandExitCodeSupplier;
|
||||
|
||||
private final Object lock = new Object();
|
||||
private volatile CachedProcessSnapshot cachedSnapshot;
|
||||
|
||||
public DefaultSoftwareRuntimeProbe() {
|
||||
this(DefaultSoftwareRuntimeProbe::loadProcessSnapshot, DEFAULT_CACHE_TTL, System::nanoTime);
|
||||
this(DefaultSoftwareRuntimeProbe::loadProcessSnapshot, DEFAULT_CACHE_TTL, System::nanoTime, DefaultSoftwareRuntimeProbe::runCommand);
|
||||
}
|
||||
|
||||
DefaultSoftwareRuntimeProbe(
|
||||
Supplier<List<String>> processSnapshotLoader,
|
||||
Duration cacheTtl,
|
||||
LongSupplier nanoTimeSupplier
|
||||
) {
|
||||
this(processSnapshotLoader, cacheTtl, nanoTimeSupplier, DefaultSoftwareRuntimeProbe::runCommand);
|
||||
}
|
||||
|
||||
DefaultSoftwareRuntimeProbe(
|
||||
Supplier<List<String>> processSnapshotLoader,
|
||||
Duration cacheTtl,
|
||||
LongSupplier nanoTimeSupplier,
|
||||
CommandExitCodeSupplier commandExitCodeSupplier
|
||||
) {
|
||||
this.processSnapshotLoader = Objects.requireNonNull(processSnapshotLoader, "processSnapshotLoader");
|
||||
this.cacheTtl = Objects.requireNonNull(cacheTtl, "cacheTtl");
|
||||
this.nanoTimeSupplier = Objects.requireNonNull(nanoTimeSupplier, "nanoTimeSupplier");
|
||||
this.commandExitCodeSupplier = Objects.requireNonNull(commandExitCodeSupplier, "commandExitCodeSupplier");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRabbitmqRunning() {
|
||||
return isRunning("rabbitmq-server");
|
||||
return commandSucceeds("bash", "-lc", "rabbitmqctl status >/dev/null 2>&1")
|
||||
|| commandSucceeds("bash", "-lc", "systemctl is-active --quiet rabbitmq.service")
|
||||
|| isRunningAny("beam.smp", "rabbitmq-server", "epmd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTlqRunning() {
|
||||
return isRunning("tonglink|TLQ");
|
||||
return isRunningAny("tonglink", "tlq", "tlqremote");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTidbRunning() {
|
||||
return isRunning("tidb-server");
|
||||
return isRunningAny("tidb-server");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNginxRunning() {
|
||||
return isRunning("nginx");
|
||||
return isRunningAny("nginx");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStandardAppRunning() {
|
||||
return isRunning("cmsp") && isRunning("cmtp");
|
||||
return (commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmsp.service") || isRunningAny("cmsp"))
|
||||
&& (commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmtp.service") || isRunningAny("cmtp"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectAppRunning() {
|
||||
return isRunning("cmep4d|CAE");
|
||||
boolean pmts = commandSucceeds("bash", "-lc", "systemctl is-active --quiet pmts.service")
|
||||
|| isRunningAny("pmts", "pmtsstat");
|
||||
boolean cmcp = commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmcp.service")
|
||||
|| isRunningAny("cmcp");
|
||||
boolean cmsp = commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmsp.service")
|
||||
|| isRunningAny("cmsp");
|
||||
boolean cmtp = commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmtp.service")
|
||||
|| isRunningAny("cmtp");
|
||||
boolean cmtp4cae = commandSucceeds("bash", "-lc", "systemctl is-active --quiet cmtp4cae.service")
|
||||
|| isRunningAny("cmtp4cae");
|
||||
return pmts && cmcp && cmsp && cmtp && cmtp4cae;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTmsRunning() {
|
||||
return isRunning("tms-framework.jar");
|
||||
return isRunningAny("tms-framework.jar");
|
||||
}
|
||||
|
||||
private boolean isRunning(String pattern) {
|
||||
private boolean isRunningAll(String... tokens) {
|
||||
for (String token : tokens) {
|
||||
if (!isRunningAny(token)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isRunningAny(String... tokens) {
|
||||
try {
|
||||
Pattern compiled = Pattern.compile(pattern);
|
||||
return processSnapshot().stream().anyMatch(commandLine -> compiled.matcher(commandLine).find());
|
||||
List<Pattern> patterns = compilePatterns(tokens);
|
||||
return processSnapshot().stream().anyMatch(commandLine -> matchesAny(commandLine, patterns));
|
||||
} catch (PatternSyntaxException ex) {
|
||||
throw new IllegalStateException("invalid process probe pattern: " + pattern, ex);
|
||||
throw new IllegalStateException("invalid process probe tokens: " + String.join(",", tokens), ex);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new IllegalStateException("process probe failed: " + pattern, ex);
|
||||
throw new IllegalStateException("process probe failed: " + String.join(",", tokens), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Pattern> compilePatterns(String... tokens) {
|
||||
return List.of(tokens).stream()
|
||||
.filter(token -> token != null && !token.isBlank())
|
||||
.map(this::compileTokenPattern)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Pattern compileTokenPattern(String token) {
|
||||
return Pattern.compile("(?i)(?<![a-z0-9])" + Pattern.quote(token.trim()) + "(?![a-z0-9])");
|
||||
}
|
||||
|
||||
private boolean matchesAny(String commandLine, List<Pattern> patterns) {
|
||||
for (Pattern pattern : patterns) {
|
||||
if (pattern.matcher(commandLine).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean commandSucceeds(String... command) {
|
||||
try {
|
||||
return commandExitCodeSupplier.get(command) == 0;
|
||||
} catch (RuntimeException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +191,23 @@ public class DefaultSoftwareRuntimeProbe implements SoftwareRuntimeProbe {
|
||||
}
|
||||
}
|
||||
|
||||
private static int runCommand(String... command) {
|
||||
try {
|
||||
Process process = new ProcessBuilder(command).start();
|
||||
return process.waitFor();
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("command interrupted", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException("command failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface CommandExitCodeSupplier {
|
||||
int get(String... command);
|
||||
}
|
||||
|
||||
private record CachedProcessSnapshot(List<String> commandLines, long loadedAtNanos) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,19 @@ public class InitExecutorProperties {
|
||||
private String directDbScriptDir = "/opt/CAE_Install_Base_Resource/DB";
|
||||
// 直参版 TLQ init_cfg.sh 脚本目录
|
||||
private String directTlqInitCfgDir = "/opt/CAE_Install_App_Resource/CAE";
|
||||
// CFMQ 配置落地目录(为空表示只做文件存在校验,不做拷贝)
|
||||
// 直参版 CAE 客户化配置目标目录
|
||||
private String directCaeTargetDir = "/pmts/cfg";
|
||||
// 直参版 CAE 更新脚本目录
|
||||
private String directCaeUpdateScriptDir = "/opt/CAE_Install_App_Resource/CAE";
|
||||
// 直参版 CAE 更新命令
|
||||
private String directCaeUpdateCommand = "bash ./cae_update.sh";
|
||||
// TLQ 网关脚本目标目录
|
||||
private String directTlqGatewayScriptTargetDir = "/home/tlq/bin";
|
||||
// TLQ 重启命令
|
||||
private String directTlqRestartCommand = "systemctl restart tonglinkmq.service";
|
||||
// CFMQ 运行目录
|
||||
private String directCfmqInstallPath = "/home/cfmq/dev/cfmq";
|
||||
// CFMQ 配置落地目录(兼容旧实现)
|
||||
private String cfmqTargetDir = "/pmts/cfg";
|
||||
// RabbitMQ 管理员账号(预制)
|
||||
private String rabbitAdminUsername = "admin";
|
||||
@@ -392,6 +404,54 @@ public class InitExecutorProperties {
|
||||
this.directTlqLicenseTargetPath = directTlqLicenseTargetPath;
|
||||
}
|
||||
|
||||
public String getDirectCaeTargetDir() {
|
||||
return directCaeTargetDir;
|
||||
}
|
||||
|
||||
public void setDirectCaeTargetDir(String directCaeTargetDir) {
|
||||
this.directCaeTargetDir = directCaeTargetDir;
|
||||
}
|
||||
|
||||
public String getDirectCaeUpdateScriptDir() {
|
||||
return directCaeUpdateScriptDir;
|
||||
}
|
||||
|
||||
public void setDirectCaeUpdateScriptDir(String directCaeUpdateScriptDir) {
|
||||
this.directCaeUpdateScriptDir = directCaeUpdateScriptDir;
|
||||
}
|
||||
|
||||
public String getDirectCaeUpdateCommand() {
|
||||
return directCaeUpdateCommand;
|
||||
}
|
||||
|
||||
public void setDirectCaeUpdateCommand(String directCaeUpdateCommand) {
|
||||
this.directCaeUpdateCommand = directCaeUpdateCommand;
|
||||
}
|
||||
|
||||
public String getDirectTlqGatewayScriptTargetDir() {
|
||||
return directTlqGatewayScriptTargetDir;
|
||||
}
|
||||
|
||||
public void setDirectTlqGatewayScriptTargetDir(String directTlqGatewayScriptTargetDir) {
|
||||
this.directTlqGatewayScriptTargetDir = directTlqGatewayScriptTargetDir;
|
||||
}
|
||||
|
||||
public String getDirectTlqRestartCommand() {
|
||||
return directTlqRestartCommand;
|
||||
}
|
||||
|
||||
public void setDirectTlqRestartCommand(String directTlqRestartCommand) {
|
||||
this.directTlqRestartCommand = directTlqRestartCommand;
|
||||
}
|
||||
|
||||
public String getDirectCfmqInstallPath() {
|
||||
return directCfmqInstallPath;
|
||||
}
|
||||
|
||||
public void setDirectCfmqInstallPath(String directCfmqInstallPath) {
|
||||
this.directCfmqInstallPath = directCfmqInstallPath;
|
||||
}
|
||||
|
||||
public String getCfmqTargetDir() {
|
||||
return cfmqTargetDir;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "当前已初始化的 CISD 核心配置响应")
|
||||
public class CurrentInitConfigResponse {
|
||||
@@ -27,6 +29,12 @@ public class CurrentInitConfigResponse {
|
||||
private String node02Ip;
|
||||
@Schema(description = "CIPS 中心网关地址", example = "192.168.10.20")
|
||||
private String cipsGatewayHost;
|
||||
@Schema(description = "TLQ 相关文件名列表,包含 TLQ License、TLQ 配置包等")
|
||||
private List<String> tlqFileNames = new ArrayList<>();
|
||||
@Schema(description = "CFMQ 相关文件名列表,包含 CFMQ 配置包、CFMQ 配置脚本等")
|
||||
private List<String> cfmqFileNames = new ArrayList<>();
|
||||
@Schema(description = "收发器 LICENSE 文件名", example = "receiver-license.dat")
|
||||
private String receiverLicenseFileName;
|
||||
|
||||
public String getOrgCodeType() {
|
||||
return orgCodeType;
|
||||
@@ -115,4 +123,28 @@ public class CurrentInitConfigResponse {
|
||||
public void setCipsGatewayHost(String cipsGatewayHost) {
|
||||
this.cipsGatewayHost = cipsGatewayHost;
|
||||
}
|
||||
|
||||
public List<String> getTlqFileNames() {
|
||||
return tlqFileNames;
|
||||
}
|
||||
|
||||
public void setTlqFileNames(List<String> tlqFileNames) {
|
||||
this.tlqFileNames = tlqFileNames;
|
||||
}
|
||||
|
||||
public List<String> getCfmqFileNames() {
|
||||
return cfmqFileNames;
|
||||
}
|
||||
|
||||
public void setCfmqFileNames(List<String> cfmqFileNames) {
|
||||
this.cfmqFileNames = cfmqFileNames;
|
||||
}
|
||||
|
||||
public String getReceiverLicenseFileName() {
|
||||
return receiverLicenseFileName;
|
||||
}
|
||||
|
||||
public void setReceiverLicenseFileName(String receiverLicenseFileName) {
|
||||
this.receiverLicenseFileName = receiverLicenseFileName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
@@ -51,14 +49,9 @@ public class InitPreviewRequest {
|
||||
private Mq mq;
|
||||
|
||||
@Valid
|
||||
@NotNull(message = "licenses is required")
|
||||
@Schema(description = "初始化许可证与配置包引用")
|
||||
private Licenses licenses;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "直连 TLQ 配置,直参版按需填写")
|
||||
private DirectTlqConfig directTlq;
|
||||
|
||||
public String getOrgCodeType() {
|
||||
return orgCodeType;
|
||||
}
|
||||
@@ -123,14 +116,6 @@ public class InitPreviewRequest {
|
||||
this.licenses = licenses;
|
||||
}
|
||||
|
||||
public DirectTlqConfig getDirectTlq() {
|
||||
return directTlq;
|
||||
}
|
||||
|
||||
public void setDirectTlq(DirectTlqConfig directTlq) {
|
||||
this.directTlq = directTlq;
|
||||
}
|
||||
|
||||
@Schema(description = "节点 IP 配置")
|
||||
public static class Nodes {
|
||||
|
||||
@@ -193,28 +178,34 @@ public class InitPreviewRequest {
|
||||
|
||||
@NotBlank(message = "mq.channelUsername is required")
|
||||
@Size(max = 32, message = "mq.channelUsername length must be <= 32")
|
||||
@Schema(description = "通道用户名", example = "tms")
|
||||
@Schema(description = "RABBITMQ通道用户名", example = "tms")
|
||||
private String channelUsername;
|
||||
|
||||
@NotBlank(message = "mq.channelPassword is required")
|
||||
@Size(max = 128, message = "mq.channelPassword length must be <= 128")
|
||||
@Schema(description = "通道密码")
|
||||
@Schema(description = "RABBITMQ通道密码")
|
||||
private String channelPassword;
|
||||
|
||||
@Size(max = 128, message = "mq.tlqLicenseFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "mq.tlqLicenseFileId must be a fileId")
|
||||
@Schema(description = "TLQ 许可证文件 fileId", example = "FILE-20260310-000001")
|
||||
@Schema(description = "TLQ 许可证文件 fileId。作用:用于 TLQ 相关模式的许可证下发。使用条件:`TLQ` 或 `RABBITMQ_TLQ` 场景必填", example = "FILE-20260310-000001")
|
||||
private String tlqLicenseFileId;
|
||||
|
||||
@Size(max = 64, message = "mq.cipsGatewayHost length must be <= 64")
|
||||
@Schema(description = "CIPS 中心网关地址。作用:回写到 TLQ 网关脚本中的中心网关主机地址。使用条件:仅 `DIRECT + RABBITMQ_TLQ` 必填", example = "192.168.10.20")
|
||||
private String cipsGatewayHost;
|
||||
|
||||
@Size(max = 128, message = "mq.cfgZipFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "mq.cfgZipFileId must be a fileId")
|
||||
@Schema(description = "直参 PMTS-CAE 客户化配置包 fileId。使用条件:仅 `DIRECT` 使用;`RABBITMQ_TLQ`必填", example = "FILE-20260310-000201")
|
||||
private String cfgZipFileId;
|
||||
|
||||
|
||||
@Size(max = 128, message = "mq.cfmqConfigFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "mq.cfmqConfigFileId must be a fileId")
|
||||
@Schema(description = "CFMQ 配置文件 fileId", example = "FILE-20260310-000002")
|
||||
@Schema(description = "直参 CFMQ 客户化脚本/配置 fileId。使用条件:仅 `DIRECT + RABBITMQ_CFMQ` 必填", example = "FILE-20260310-000002")
|
||||
private String cfmqConfigFileId;
|
||||
|
||||
@Size(max = 64, message = "mq.cipsGatewayHost length must be <= 64")
|
||||
@Schema(description = "CIPS 中心网关地址,仅 DIRECT + RABBITMQ_TLQ 使用", example = "192.168.10.20")
|
||||
private String cipsGatewayHost;
|
||||
|
||||
public String getMqType() {
|
||||
return mqType;
|
||||
}
|
||||
@@ -262,6 +253,15 @@ public class InitPreviewRequest {
|
||||
public void setCipsGatewayHost(String cipsGatewayHost) {
|
||||
this.cipsGatewayHost = cipsGatewayHost;
|
||||
}
|
||||
|
||||
public String getCfgZipFileId() {
|
||||
return cfgZipFileId;
|
||||
}
|
||||
|
||||
public void setCfgZipFileId(String cfgZipFileId) {
|
||||
this.cfgZipFileId = cfgZipFileId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "许可证和配置包引用")
|
||||
@@ -275,7 +275,7 @@ public class InitPreviewRequest {
|
||||
|
||||
@Size(max = 128, message = "licenses.cfgZipFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "licenses.cfgZipFileId must be a fileId")
|
||||
@Schema(description = "标准版配置包 zip 文件 fileId", example = "FILE-20260310-000004")
|
||||
@Schema(description = "标准版配置包 zip 文件 fileId。作用:用于标准版/间参版初始化时引用配置包。使用条件:`ENTERPRISE`、`INDIRECT` 场景按对应流程使用,`DIRECT` 不使用此字段", example = "FILE-20260310-000004")
|
||||
private String cfgZipFileId;
|
||||
|
||||
public String getReceiverLicenseFileId() {
|
||||
@@ -294,45 +294,4 @@ public class InitPreviewRequest {
|
||||
this.cfgZipFileId = cfgZipFileId;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "直连 TLQ 配置")
|
||||
public static class DirectTlqConfig {
|
||||
|
||||
@Pattern(regexp = "A|B", message = "directTlq.localNodeSeq must be A/B")
|
||||
@Schema(description = "本节点序号", example = "A")
|
||||
private String localNodeSeq;
|
||||
|
||||
@Size(max = 256, message = "directTlq.upstreamInstitutions length must be <= 256")
|
||||
@Schema(description = "上游机构列表", example = "BANKA,BANKB")
|
||||
private String upstreamInstitutions;
|
||||
|
||||
@Min(value = 1, message = "directTlq.upstreamCocCount must be >= 1")
|
||||
@Max(value = 2, message = "directTlq.upstreamCocCount must be <= 2")
|
||||
@Schema(description = "上游 COC 数量", example = "2")
|
||||
private Integer upstreamCocCount;
|
||||
|
||||
public String getLocalNodeSeq() {
|
||||
return localNodeSeq;
|
||||
}
|
||||
|
||||
public void setLocalNodeSeq(String localNodeSeq) {
|
||||
this.localNodeSeq = localNodeSeq;
|
||||
}
|
||||
|
||||
public String getUpstreamInstitutions() {
|
||||
return upstreamInstitutions;
|
||||
}
|
||||
|
||||
public void setUpstreamInstitutions(String upstreamInstitutions) {
|
||||
this.upstreamInstitutions = upstreamInstitutions;
|
||||
}
|
||||
|
||||
public Integer getUpstreamCocCount() {
|
||||
return upstreamCocCount;
|
||||
}
|
||||
|
||||
public void setUpstreamCocCount(Integer upstreamCocCount) {
|
||||
this.upstreamCocCount = upstreamCocCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+260
-156
@@ -149,7 +149,12 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
case "verify_reset" -> executeVerifyReset(task, step);
|
||||
case "direct_db_init" -> executeDirectDbInit(task, step);
|
||||
case "tlq_init_cfg" -> executeTlqInitCfg(task, step);
|
||||
case "cfmq_apply" -> executeCfmqApply(task, step);
|
||||
case "cae_package_apply" -> executeCaePackageApply(task, step);
|
||||
case "cae_update" -> executeCaeUpdate(task, step);
|
||||
case "tlq_gateway_apply" -> executeTlqGatewayApply(task, step);
|
||||
case "restart_tlq" -> executeRestartTlq(task, step);
|
||||
case "cfmq_link_apply", "cfmq_apply" -> executeCfmqLinkApply(task, step);
|
||||
case "cfmq_route_apply" -> executeCfmqRouteApply(task, step);
|
||||
case "verify" -> executeVerify(task, step);
|
||||
default -> {
|
||||
String logPath = writeInternalLog(task, step, "skip unsupported internal step: " + internalCode);
|
||||
@@ -177,6 +182,18 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
: buildStandardReplacements(context);
|
||||
int changed = replaceConfigValues(cpconfigPath, replacements);
|
||||
String message = "cpconfig rendered, path=" + cpconfigPath + ", changedKeys=" + changed;
|
||||
if ("DIRECT".equals(context.productType())) {
|
||||
Path targetCpconfigPath = Paths.get(firstNonBlank(properties.getDirectCpconfigTargetPath(), "/opt/cpconfig.cfg"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
if (targetCpconfigPath.getParent() != null) {
|
||||
Files.createDirectories(targetCpconfigPath.getParent());
|
||||
}
|
||||
if (!cpconfigPath.equals(targetCpconfigPath)) {
|
||||
Files.copy(cpconfigPath, targetCpconfigPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
message = message + ", syncedTarget=" + targetCpconfigPath;
|
||||
}
|
||||
}
|
||||
String logPath = writeInternalLog(task, step, message);
|
||||
return InitStepExecutionResult.success(message, 0, logPath);
|
||||
}
|
||||
@@ -794,57 +811,8 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
String logPath = writeInternalLog(task, step, "direct_db_init skipped for non-direct product type");
|
||||
return InitStepExecutionResult.success("direct_db_init skipped", 0, logPath);
|
||||
}
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "direct_db_init simulated");
|
||||
return InitStepExecutionResult.success("direct_db_init simulated", 0, logPath);
|
||||
}
|
||||
|
||||
Path scriptDir = Paths.get(firstNonBlank(properties.getDirectDbScriptDir(), "/opt/CAE_Install_Base_Resource/DB"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
if (!Files.isDirectory(scriptDir)) {
|
||||
String logPath = writeInternalLog(task, step, "direct_db_init failed: script dir not found: " + scriptDir);
|
||||
return InitStepExecutionResult.failure("direct_db_init failed: script dir not found", -1, logPath);
|
||||
}
|
||||
|
||||
ResolvedCpconfig resolvedCpconfig = resolveCpconfigPath(task, context);
|
||||
Path cpconfigPath = resolvedCpconfig.path();
|
||||
if (!Files.exists(cpconfigPath) || !Files.isRegularFile(cpconfigPath)) {
|
||||
String logPath = writeInternalLog(
|
||||
task,
|
||||
step,
|
||||
"direct_db_init failed: cpconfig not found: " + cpconfigPath
|
||||
+ ", candidates=" + formatPathList(resolvedCpconfig.candidates())
|
||||
);
|
||||
return InitStepExecutionResult.failure("direct_db_init failed: cpconfig not found", -1, logPath);
|
||||
}
|
||||
Path targetCpconfigPath = Paths.get(firstNonBlank(properties.getDirectCpconfigTargetPath(), "/opt/cpconfig.cfg"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
|
||||
try {
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
if (targetCpconfigPath.getParent() != null) {
|
||||
Files.createDirectories(targetCpconfigPath.getParent());
|
||||
}
|
||||
Files.copy(cpconfigPath, targetCpconfigPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
appendLog(logFile, "cpconfig prepared: " + cpconfigPath + " -> " + targetCpconfigPath);
|
||||
|
||||
int code = runCommand(
|
||||
logFile,
|
||||
Arrays.asList("bash", "./setsql.sh"),
|
||||
false,
|
||||
scriptDir
|
||||
);
|
||||
if (code != 0) {
|
||||
return InitStepExecutionResult.failure("direct_db_init failed", code, logFile.toString());
|
||||
}
|
||||
return InitStepExecutionResult.success("direct db init applied", 0, logFile.toString());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
String logPath = writeInternalLog(task, step, "direct_db_init interrupted");
|
||||
return InitStepExecutionResult.failure("direct_db_init interrupted", -1, logPath);
|
||||
}
|
||||
String logPath = writeInternalLog(task, step, "direct_db_init skipped: out of direct customer-config scope");
|
||||
return InitStepExecutionResult.success("direct_db_init skipped", 0, logPath);
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeStandardDbRender(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
@@ -1229,83 +1197,197 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg skipped for non-direct TLQ mode");
|
||||
return InitStepExecutionResult.success("tlq_init_cfg skipped", 0, logPath);
|
||||
}
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg simulated");
|
||||
return InitStepExecutionResult.success("tlq_init_cfg simulated", 0, logPath);
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg skipped: no longer part of direct customer-config scope");
|
||||
return InitStepExecutionResult.success("tlq_init_cfg skipped", 0, logPath);
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeCaePackageApply(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType())) {
|
||||
String logPath = writeInternalLog(task, step, "cae_package_apply skipped for non-direct product type");
|
||||
return InitStepExecutionResult.success("cae_package_apply skipped", 0, logPath);
|
||||
}
|
||||
|
||||
String localNodeSeq = normalizeUpper(context.directTlqLocalNodeSeq());
|
||||
String upstreamInstitutions = normalize(context.directTlqUpstreamInstitutions());
|
||||
Integer upstreamCocCount = context.directTlqUpstreamCocCount();
|
||||
if (!"A".equals(localNodeSeq) && !"B".equals(localNodeSeq)) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg failed: localNodeSeq must be A/B");
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg failed: invalid localNodeSeq", -1, logPath);
|
||||
}
|
||||
if (isBlank(upstreamInstitutions)) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg failed: upstreamInstitutions is blank");
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg failed: upstreamInstitutions is blank", -1, logPath);
|
||||
}
|
||||
if (upstreamCocCount == null || upstreamCocCount < 1 || upstreamCocCount > 2) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg failed: upstreamCocCount must be in [1,2]");
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg failed: invalid upstreamCocCount", -1, logPath);
|
||||
}
|
||||
Path zipFile = resolveAndStageInputFile(task, context.cfgZipFileId(), "cae_package", null);
|
||||
Path unpackDir = resolveDirectStageDir(task, "cae/unpacked");
|
||||
recreateDirectory(unpackDir);
|
||||
unzipToDirectory(zipFile, unpackDir);
|
||||
|
||||
Path scriptDir = Paths.get(firstNonBlank(properties.getDirectTlqInitCfgDir(), "/opt/CAE_Install_App_Resource/CAE"))
|
||||
Path targetDir = Paths.get(firstNonBlank(properties.getDirectCaeTargetDir(), properties.getCfmqTargetDir()))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
if (!Files.isDirectory(scriptDir)) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg failed: script dir not found: " + scriptDir);
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg failed: script dir not found", -1, logPath);
|
||||
Files.createDirectories(targetDir);
|
||||
copyDirectoryContents(unpackDir, targetDir);
|
||||
|
||||
String message = "cae package applied: " + zipFile + " -> " + targetDir;
|
||||
String logPath = writeInternalLog(task, step, message);
|
||||
return InitStepExecutionResult.success(message, 0, logPath);
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeCaeUpdate(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType())) {
|
||||
String logPath = writeInternalLog(task, step, "cae_update skipped for non-direct product type");
|
||||
return InitStepExecutionResult.success("cae_update skipped", 0, logPath);
|
||||
}
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "cae_update simulated");
|
||||
return InitStepExecutionResult.success("cae_update simulated", 0, logPath);
|
||||
}
|
||||
|
||||
String command = normalize(properties.getDirectCaeUpdateCommand());
|
||||
if (isBlank(command)) {
|
||||
String logPath = writeInternalLog(task, step, "cae_update validated only: command is blank");
|
||||
return InitStepExecutionResult.success("cae_update validated", 0, logPath);
|
||||
}
|
||||
|
||||
Path scriptDir = Paths.get(firstNonBlank(properties.getDirectCaeUpdateScriptDir(), "/opt/CAE_Install_App_Resource/CAE"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
try {
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
int code = runCommand(
|
||||
logFile,
|
||||
Arrays.asList(
|
||||
"bash",
|
||||
"./init_cfg.sh",
|
||||
normalize(context.orgCode()),
|
||||
localNodeSeq,
|
||||
"TLQ",
|
||||
upstreamInstitutions,
|
||||
String.valueOf(upstreamCocCount)
|
||||
),
|
||||
false,
|
||||
scriptDir
|
||||
);
|
||||
int code = runCommand(logFile, Arrays.asList("/bin/zsh", "-lc", command), false, scriptDir);
|
||||
if (code != 0) {
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg failed", code, logFile.toString());
|
||||
return InitStepExecutionResult.failure("cae_update failed", code, logFile.toString());
|
||||
}
|
||||
return InitStepExecutionResult.success("tlq_init_cfg applied", 0, logFile.toString());
|
||||
return InitStepExecutionResult.success("cae_update applied", 0, logFile.toString());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
String logPath = writeInternalLog(task, step, "tlq_init_cfg interrupted");
|
||||
return InitStepExecutionResult.failure("tlq_init_cfg interrupted", -1, logPath);
|
||||
return InitStepExecutionResult.failure("cae_update interrupted", -1, logFile.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeCfmqApply(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
private InitStepExecutionResult executeTlqGatewayApply(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType()) || !"RABBITMQ_TLQ".equals(context.mqType())) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_gateway_apply skipped for non-direct TLQ mode");
|
||||
return InitStepExecutionResult.success("tlq_gateway_apply skipped", 0, logPath);
|
||||
}
|
||||
|
||||
Path sourceScript = resolveTlqGatewaySourceScript(task);
|
||||
if (!Files.exists(sourceScript) || !Files.isRegularFile(sourceScript)) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_gateway_apply failed: source script not found: " + sourceScript);
|
||||
return InitStepExecutionResult.failure("tlq_gateway_apply failed: source script not found", -1, logPath);
|
||||
}
|
||||
|
||||
String rewritten = rewriteGatewayHost(Files.readString(sourceScript), context.cipsGatewayHost());
|
||||
Path targetDir = Paths.get(firstNonBlank(properties.getDirectTlqGatewayScriptTargetDir(), "/home/tlq/bin"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
Files.createDirectories(targetDir);
|
||||
Path targetScript = targetDir.resolve(sourceScript.getFileName());
|
||||
Files.writeString(targetScript, rewritten, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "tlq_gateway_apply simulated: " + targetScript);
|
||||
return InitStepExecutionResult.success("tlq_gateway_apply simulated", 0, logPath);
|
||||
}
|
||||
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
try {
|
||||
int code = runCommand(logFile, Arrays.asList("bash", targetScript.toString()), false);
|
||||
if (code != 0) {
|
||||
return InitStepExecutionResult.failure("tlq_gateway_apply failed", code, logFile.toString());
|
||||
}
|
||||
return InitStepExecutionResult.success("tlq gateway applied: " + targetScript, 0, logFile.toString());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return InitStepExecutionResult.failure("tlq_gateway_apply interrupted", -1, logFile.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeRestartTlq(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType()) || !"RABBITMQ_TLQ".equals(context.mqType())) {
|
||||
String logPath = writeInternalLog(task, step, "restart_tlq skipped for non-direct TLQ mode");
|
||||
return InitStepExecutionResult.success("restart_tlq skipped", 0, logPath);
|
||||
}
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "restart_tlq simulated");
|
||||
return InitStepExecutionResult.success("restart_tlq simulated", 0, logPath);
|
||||
}
|
||||
|
||||
String command = normalize(properties.getDirectTlqRestartCommand());
|
||||
if (isBlank(command)) {
|
||||
String logPath = writeInternalLog(task, step, "restart_tlq validated only: command is blank");
|
||||
return InitStepExecutionResult.success("restart_tlq validated", 0, logPath);
|
||||
}
|
||||
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
try {
|
||||
int code = runCommand(logFile, Arrays.asList("/bin/zsh", "-lc", command), false);
|
||||
if (code != 0) {
|
||||
return InitStepExecutionResult.failure("restart_tlq failed", code, logFile.toString());
|
||||
}
|
||||
return InitStepExecutionResult.success("restart_tlq applied", 0, logFile.toString());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return InitStepExecutionResult.failure("restart_tlq interrupted", -1, logFile.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeCfmqLinkApply(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType()) || !"RABBITMQ_CFMQ".equals(context.mqType())) {
|
||||
String logPath = writeInternalLog(task, step, "mq is not RABBITMQ_CFMQ, cfmq_apply skipped");
|
||||
return InitStepExecutionResult.success("cfmq_apply skipped", 0, logPath);
|
||||
String logPath = writeInternalLog(task, step, "cfmq_link_apply skipped for non-direct CFMQ mode");
|
||||
return InitStepExecutionResult.success("cfmq_link_apply skipped", 0, logPath);
|
||||
}
|
||||
|
||||
Path source = resolveAndStageInputFile(task, context.cfmqConfigFileId(), "cfmq_config", null);
|
||||
String targetDirValue = normalize(properties.getCfmqTargetDir());
|
||||
if (isBlank(targetDirValue)) {
|
||||
String logPath = writeInternalLog(task, step, "cfmq config validated only: " + source);
|
||||
return InitStepExecutionResult.success("cfmq config validated", 0, logPath);
|
||||
Path sourceScript = resolveAndStageInputFile(task, context.cfmqConfigFileId(), "cfmq_link_script", null);
|
||||
String rewritten = Files.readString(sourceScript)
|
||||
.replace("/cfmq/cfmq-server", normalize(properties.getDirectCfmqInstallPath()));
|
||||
Path stagedScript = resolveDirectStageDir(task, "cfmq/link").resolve(sourceScript.getFileName());
|
||||
Files.createDirectories(stagedScript.getParent());
|
||||
Files.writeString(stagedScript, rewritten, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
if (properties.getMode() == InitExecutorProperties.Mode.SIMULATE) {
|
||||
String logPath = writeInternalLog(task, step, "cfmq_link_apply simulated: " + stagedScript);
|
||||
return InitStepExecutionResult.success("cfmq_link_apply simulated", 0, logPath);
|
||||
}
|
||||
|
||||
Path targetDir = Paths.get(targetDirValue).normalize().toAbsolutePath();
|
||||
Files.createDirectories(targetDir);
|
||||
Path target = targetDir.resolve(source.getFileName());
|
||||
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
String message = "cfmq config applied: " + source + " -> " + target;
|
||||
String logPath = writeInternalLog(task, step, message);
|
||||
return InitStepExecutionResult.success(message, 0, logPath);
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
try {
|
||||
int code = runCommand(logFile, Arrays.asList("bash", stagedScript.toString()), false);
|
||||
if (code != 0) {
|
||||
return InitStepExecutionResult.failure("cfmq_link_apply failed", code, logFile.toString());
|
||||
}
|
||||
return InitStepExecutionResult.success("cfmq_link_apply applied", 0, logFile.toString());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return InitStepExecutionResult.failure("cfmq_link_apply interrupted", -1, logFile.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeCfmqRouteApply(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
PlanContext context = loadPlanContext(task);
|
||||
if (!"DIRECT".equals(context.productType()) || !"RABBITMQ_CFMQ".equals(context.mqType())) {
|
||||
String logPath = writeInternalLog(task, step, "cfmq_route_apply skipped for non-direct CFMQ mode");
|
||||
return InitStepExecutionResult.success("cfmq_route_apply skipped", 0, logPath);
|
||||
}
|
||||
|
||||
Path targetDir = Paths.get(firstNonBlank(properties.getDirectCaeTargetDir(), properties.getCfmqTargetDir()))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
Path logFile = prepareLogPath(task, step);
|
||||
int routeChanged = 0;
|
||||
try (Stream<Path> stream = Files.walk(targetDir)) {
|
||||
for (Path item : stream.filter(Files::isRegularFile).toList()) {
|
||||
String fileName = item.getFileName() == null ? "" : item.getFileName().toString();
|
||||
if (fileName.startsWith("route_") && fileName.endsWith(".ini")) {
|
||||
String content = Files.readString(item);
|
||||
String rewritten = content.replace("MSGMB_1", "MSGTOMB_1");
|
||||
if (!rewritten.equals(content)) {
|
||||
Files.writeString(item, rewritten, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
|
||||
routeChanged++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path pmtsMsgHdl = targetDir.resolve("pmtsmsghdl.cfg");
|
||||
Files.deleteIfExists(pmtsMsgHdl);
|
||||
appendLog(logFile, "cfmq route files changed=" + routeChanged + ", pmtsmsghdl deleted if exists=" + pmtsMsgHdl);
|
||||
return InitStepExecutionResult.success("cfmq_route_apply applied", 0, logFile.toString());
|
||||
}
|
||||
|
||||
private InitStepExecutionResult executeVerify(InitTaskEntity task, InitTaskStepEntity step) throws IOException {
|
||||
@@ -1363,12 +1445,6 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
private void verifyDirect(Map<String, String> configMap, PlanContext context, List<String> failures) {
|
||||
requireNotBlank(configMap, "MQ_TYPE", failures);
|
||||
requireNotBlank(configMap, "node_01_ip", failures);
|
||||
requireNotBlank(configMap, "db_ip", failures);
|
||||
requireNotBlank(configMap, "sign_host", failures);
|
||||
requireNotBlank(configMap, "sign_port", failures);
|
||||
requireNotBlank(configMap, "sign_type", failures);
|
||||
requireNotBlank(configMap, "sign_cert_dn", failures);
|
||||
requireNotBlank(configMap, "sign_cert_sn", failures);
|
||||
|
||||
if ("DUAL".equals(context.deployMode()) || "QUAD".equals(context.deployMode())) {
|
||||
requireNotBlank(configMap, "node_02_ip", failures);
|
||||
@@ -1432,13 +1508,6 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
replacements.put("node_02_ip", node02);
|
||||
replacements.put("node_03_ip", node03);
|
||||
replacements.put("node_04_ip", node04);
|
||||
replacements.put("db_ip", node01);
|
||||
replacements.put("sign_host", context.signHost());
|
||||
replacements.put("sign_port", context.signPort() == null ? "" : String.valueOf(context.signPort()));
|
||||
replacements.put("sign_password", quoteShellValue(context.signPassword()));
|
||||
replacements.put("sign_type", context.signType());
|
||||
replacements.put("sign_cert_dn", quoteShellValue(context.signCertDn()));
|
||||
replacements.put("sign_cert_sn", context.signCertSn());
|
||||
fillOrgCodeMappings(context, replacements);
|
||||
return replacements;
|
||||
}
|
||||
@@ -1531,6 +1600,12 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
}
|
||||
|
||||
private ResolvedCpconfig resolveCpconfigPath(InitTaskEntity task, PlanContext context) throws IOException {
|
||||
if ("DIRECT".equals(context.productType())) {
|
||||
Path stagedCpconfig = resolveDirectStageCpconfigPath(task);
|
||||
if (Files.exists(stagedCpconfig) && Files.isRegularFile(stagedCpconfig)) {
|
||||
return new ResolvedCpconfig(stagedCpconfig, List.of(stagedCpconfig));
|
||||
}
|
||||
}
|
||||
List<Path> candidates = buildCpconfigCandidates(context);
|
||||
if (candidates.isEmpty()) {
|
||||
Path uploadBase = Paths.get(firstNonBlank(properties.getUploadBaseDir(), "/home/tms/uploads"))
|
||||
@@ -1541,7 +1616,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
for (Path candidate : candidates) {
|
||||
if (Files.exists(candidate) && Files.isRegularFile(candidate)) {
|
||||
Path cpconfig = candidate;
|
||||
if ("DIRECT".equals(context.productType()) && !isBlank(context.cfgZipFileId())) {
|
||||
if ("DIRECT".equals(context.productType())) {
|
||||
cpconfig = stageFile(task, "cpconfig", firstNonBlank(properties.getDirectCpconfigFileName(), "cpconfig.cfg"), candidate);
|
||||
}
|
||||
return new ResolvedCpconfig(cpconfig, candidates);
|
||||
@@ -1550,10 +1625,27 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
return new ResolvedCpconfig(candidates.get(0), candidates);
|
||||
}
|
||||
|
||||
private Path resolveDirectStageCpconfigPath(InitTaskEntity task) {
|
||||
Path stageRoot = Paths.get(firstNonBlank(properties.getStagingRootDir(), "/tmp/tms-init-staging"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
return stageRoot.resolve(task.getTaskId())
|
||||
.resolve("inputs")
|
||||
.resolve("cpconfig")
|
||||
.resolve(firstNonBlank(properties.getDirectCpconfigFileName(), "cpconfig.cfg"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
}
|
||||
|
||||
private List<Path> buildCpconfigCandidates(PlanContext context) {
|
||||
String cpconfigFileName = firstNonBlank(properties.getDirectCpconfigFileName(), "cpconfig.cfg");
|
||||
if ("DIRECT".equals(context.productType())) {
|
||||
return buildFileIdCandidates(context.cfgZipFileId(), cpconfigFileName);
|
||||
List<Path> candidates = new ArrayList<>();
|
||||
addAbsoluteCandidate(candidates, properties.getDirectCpconfigTargetPath());
|
||||
addAbsoluteCandidate(candidates, Paths.get(firstNonBlank(properties.getDirectCpconfigTargetPath(), "/opt/cpconfig.cfg"))
|
||||
.resolveSibling(cpconfigFileName)
|
||||
.toString());
|
||||
return deduplicatePaths(candidates);
|
||||
}
|
||||
|
||||
List<Path> candidates = new ArrayList<>();
|
||||
@@ -2024,18 +2116,6 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
JsonNode nodes = request.path("nodes");
|
||||
JsonNode mq = request.path("mq");
|
||||
JsonNode licenses = request.path("licenses");
|
||||
JsonNode signServer = request.path("signServer");
|
||||
JsonNode directTlq = request.path("directTlq");
|
||||
|
||||
Integer signPort = null;
|
||||
if (!signServer.path("signPort").isMissingNode() && !signServer.path("signPort").isNull()) {
|
||||
signPort = signServer.path("signPort").asInt();
|
||||
}
|
||||
Integer directTlqUpstreamCocCount = null;
|
||||
if (!directTlq.path("upstreamCocCount").isMissingNode() && !directTlq.path("upstreamCocCount").isNull()) {
|
||||
directTlqUpstreamCocCount = directTlq.path("upstreamCocCount").asInt();
|
||||
}
|
||||
|
||||
return new PlanContext(
|
||||
firstNonBlank(normalizeUpper(text(root, "productType")), normalizeUpper(task.getProductType())),
|
||||
normalizeUpper(text(request, "orgCodeType")),
|
||||
@@ -2051,18 +2131,10 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
text(nodes, "node03Ip"),
|
||||
text(nodes, "node04Ip"),
|
||||
text(mq, "tlqLicenseFileId"),
|
||||
text(mq, "cfmqConfigFileId"),
|
||||
text(licenses, "receiverLicenseFileId"),
|
||||
text(licenses, "cfgZipFileId"),
|
||||
text(signServer, "signHost"),
|
||||
signPort,
|
||||
text(signServer, "signType"),
|
||||
text(signServer, "signCertDn"),
|
||||
text(signServer, "signCertSn"),
|
||||
text(signServer, "signPassword"),
|
||||
text(directTlq, "localNodeSeq"),
|
||||
text(directTlq, "upstreamInstitutions"),
|
||||
directTlqUpstreamCocCount
|
||||
text(mq, "cipsGatewayHost"),
|
||||
text(mq, "cfgZipFileId"),
|
||||
text(mq, "cfmqConfigFileId")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2130,6 +2202,46 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
return "'" + escaped + "'";
|
||||
}
|
||||
|
||||
private Path resolveDirectStageDir(InitTaskEntity task, String relative) {
|
||||
Path stageRoot = Paths.get(firstNonBlank(properties.getStagingRootDir(), "/tmp/tms-init-staging"))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
return stageRoot.resolve(task.getTaskId()).resolve("direct").resolve(relative).normalize().toAbsolutePath();
|
||||
}
|
||||
|
||||
private Path resolveTlqGatewaySourceScript(InitTaskEntity task) throws IOException {
|
||||
Path caeTargetDir = Paths.get(firstNonBlank(properties.getDirectCaeTargetDir(), properties.getCfmqTargetDir()))
|
||||
.normalize()
|
||||
.toAbsolutePath();
|
||||
Path preferred = caeTargetDir.resolve("bin").resolve("tlq_gateway.sh").normalize().toAbsolutePath();
|
||||
if (Files.exists(preferred) && Files.isRegularFile(preferred)) {
|
||||
return preferred;
|
||||
}
|
||||
try (Stream<Path> stream = Files.walk(caeTargetDir)) {
|
||||
for (Path item : stream.filter(Files::isRegularFile).toList()) {
|
||||
String name = item.getFileName() == null ? "" : item.getFileName().toString().toLowerCase(Locale.ROOT);
|
||||
if (name.contains("gateway") && name.endsWith(".sh")) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return preferred;
|
||||
}
|
||||
|
||||
private String rewriteGatewayHost(String content, String gatewayHost) {
|
||||
String normalizedHost = normalize(gatewayHost);
|
||||
String rewritten = normalize(content)
|
||||
.replaceAll("(?m)^(\\s*gateway\\s*=\\s*).*$", "$1" + normalizedHost)
|
||||
.replaceAll("(?m)^(\\s*host\\s*=\\s*).*$", "$1" + normalizedHost);
|
||||
if (rewritten.equals(normalize(content))) {
|
||||
rewritten = normalize(content).replace("127.0.0.1", normalizedHost);
|
||||
}
|
||||
if (!rewritten.endsWith(System.lineSeparator())) {
|
||||
rewritten = rewritten + System.lineSeparator();
|
||||
}
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
private void verifyProcessAbsent(Path logFile, String pattern, String label, List<String> failures) throws IOException {
|
||||
try {
|
||||
int code = runCommand(
|
||||
@@ -2249,18 +2361,10 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
|
||||
String node03Ip,
|
||||
String node04Ip,
|
||||
String tlqLicenseFileId,
|
||||
String cfmqConfigFileId,
|
||||
String receiverLicenseFileId,
|
||||
String cipsGatewayHost,
|
||||
String cfgZipFileId,
|
||||
String signHost,
|
||||
Integer signPort,
|
||||
String signType,
|
||||
String signCertDn,
|
||||
String signCertSn,
|
||||
String signPassword,
|
||||
String directTlqLocalNodeSeq,
|
||||
String directTlqUpstreamInstitutions,
|
||||
Integer directTlqUpstreamCocCount
|
||||
String cfmqConfigFileId
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.cisd.tms.modules.init.service.impl;
|
||||
import com.cisd.tms.common.config.properties.CisdPresetProperties;
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.modules.file.repository.FileRecordRepository;
|
||||
import com.cisd.tms.modules.init.config.InitCommandProfileService;
|
||||
import com.cisd.tms.modules.init.config.InitExecutorProperties;
|
||||
import com.cisd.tms.modules.init.dto.InitCreateTaskResponse;
|
||||
@@ -59,6 +60,7 @@ public class InitServiceImpl implements InitService {
|
||||
|
||||
private final InitTaskRepository initTaskRepository;
|
||||
private final InitTaskStepRepository initTaskStepRepository;
|
||||
private final FileRecordRepository fileRecordRepository;
|
||||
private final CisdPresetProperties cisdPresetProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final InitStepExecutor initStepExecutor;
|
||||
@@ -507,9 +509,25 @@ public class InitServiceImpl implements InitService {
|
||||
// 该步骤序列只描述“逻辑编排”,具体脚本命令在 step-command-profiles.json 中定义
|
||||
if ("DIRECT".equals(productType)) {
|
||||
if ("RABBITMQ_CFMQ".equals(mqType)) {
|
||||
return List.of("RENDER_CONFIG", "FILE_PREPARE", "MQ_USER_APPLY", "DB_INIT", "CFMQ_APPLY", "VERIFY");
|
||||
return List.of(
|
||||
"RENDER_CONFIG",
|
||||
"MQ_USER_APPLY",
|
||||
"CFMQ_LINK_APPLY",
|
||||
"CAE_PACKAGE_APPLY",
|
||||
"CFMQ_ROUTE_APPLY",
|
||||
"CAE_UPDATE",
|
||||
"VERIFY"
|
||||
);
|
||||
}
|
||||
return List.of("RENDER_CONFIG", "FILE_PREPARE", "MQ_USER_APPLY", "DB_INIT", "TLQ_INIT_CFG", "VERIFY");
|
||||
return List.of(
|
||||
"RENDER_CONFIG",
|
||||
"MQ_USER_APPLY",
|
||||
"CAE_PACKAGE_APPLY",
|
||||
"TLQ_GATEWAY_APPLY",
|
||||
"CAE_UPDATE",
|
||||
"RESTART_TLQ",
|
||||
"VERIFY"
|
||||
);
|
||||
}
|
||||
if ("ENTERPRISE".equals(productType) && "TLQ".equals(mqType)) {
|
||||
return List.of(
|
||||
@@ -621,9 +639,6 @@ public class InitServiceImpl implements InitService {
|
||||
if (request.getMq() == null) {
|
||||
throw new IllegalArgumentException("mq is required");
|
||||
}
|
||||
if (request.getLicenses() == null) {
|
||||
throw new IllegalArgumentException("licenses is required");
|
||||
}
|
||||
|
||||
requireNotBlank("orgCode", request.getOrgCode());
|
||||
requireNotBlank("deployMode", request.getDeployMode());
|
||||
@@ -631,12 +646,14 @@ public class InitServiceImpl implements InitService {
|
||||
requireNotBlank("mq.mqType", request.getMq().getMqType());
|
||||
requireNotBlank("mq.channelUsername", request.getMq().getChannelUsername());
|
||||
requireNotBlank("mq.channelPassword", request.getMq().getChannelPassword());
|
||||
requireNotBlank("licenses.receiverLicenseFileId", request.getLicenses().getReceiverLicenseFileId());
|
||||
requireFileId("licenses.receiverLicenseFileId", request.getLicenses().getReceiverLicenseFileId());
|
||||
|
||||
if (request.getLicenses() != null) {
|
||||
requireOptionalFileId("licenses.receiverLicenseFileId", request.getLicenses().getReceiverLicenseFileId());
|
||||
requireOptionalFileId("licenses.cfgZipFileId", request.getLicenses().getCfgZipFileId());
|
||||
}
|
||||
requireOptionalFileId("mq.tlqLicenseFileId", request.getMq().getTlqLicenseFileId());
|
||||
requireOptionalFileId("mq.cfmqConfigFileId", request.getMq().getCfmqConfigFileId());
|
||||
requireOptionalFileId("licenses.cfgZipFileId", request.getLicenses().getCfgZipFileId());
|
||||
requireOptionalFileId("mq.cfgZipFileId", request.getMq().getCfgZipFileId());
|
||||
}
|
||||
|
||||
private void validateByProductType(String productType, InitPreviewRequest request) {
|
||||
@@ -709,8 +726,8 @@ public class InitServiceImpl implements InitService {
|
||||
JsonNode root = objectMapper.readTree(task.getInitPlanJson());
|
||||
JsonNode request = root.path("request");
|
||||
JsonNode mq = request.path("mq");
|
||||
JsonNode licenses = request.path("licenses");
|
||||
JsonNode nodes = request.path("nodes");
|
||||
JsonNode directTlq = request.path("directTlq");
|
||||
|
||||
CurrentInitConfigResponse response = new CurrentInitConfigResponse();
|
||||
response.setOrgCodeType(firstNonBlank(normalizeUpper(text(request, "orgCodeType")), normalizeUpper(text(root, "orgCodeType"))));
|
||||
@@ -724,12 +741,60 @@ public class InitServiceImpl implements InitService {
|
||||
response.setNode01Ip(firstNonBlank(text(nodes, "node01Ip"), text(request, "node01Ip")));
|
||||
response.setNode02Ip(firstNonBlank(text(nodes, "node02Ip"), text(request, "node02Ip")));
|
||||
response.setCipsGatewayHost(firstNonBlank(text(mq, "cipsGatewayHost"), text(request, "cipsGatewayHost")));
|
||||
response.setReceiverLicenseFileName(resolveUploadedFileName(text(licenses, "receiverLicenseFileId")));
|
||||
String mqType = firstNonBlank(normalizeUpper(text(mq, "mqType")), normalizeUpper(text(root, "mqType")), normalizeUpper(task.getMqType()));
|
||||
response.setTlqFileNames(buildTlqFileNames(mq, mqType));
|
||||
response.setCfmqFileNames(buildCfmqFileNames(mq, mqType));
|
||||
return response;
|
||||
} catch (JsonProcessingException ex) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to parse successful init task snapshot");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> buildTlqFileNames(
|
||||
JsonNode mq,
|
||||
String mqType
|
||||
) {
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
if (!Set.of("TLQ", "RABBITMQ_TLQ").contains(normalizeUpper(mqType))) {
|
||||
return fileNames;
|
||||
}
|
||||
addUploadedFileName(fileNames, text(mq, "tlqLicenseFileId"));
|
||||
addUploadedFileName(fileNames, text(mq, "cfgZipFileId"));
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
private List<String> buildCfmqFileNames(
|
||||
JsonNode mq,
|
||||
String mqType
|
||||
) {
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
if (!"RABBITMQ_CFMQ".equals(normalizeUpper(mqType))) {
|
||||
return fileNames;
|
||||
}
|
||||
addUploadedFileName(fileNames, text(mq, "cfgZipFileId"));
|
||||
addUploadedFileName(fileNames, text(mq, "cfmqConfigFileId"));
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
private void addUploadedFileName(List<String> fileNames, String fileId) {
|
||||
String fileName = resolveUploadedFileName(fileId);
|
||||
if (isBlank(fileName)) {
|
||||
return;
|
||||
}
|
||||
fileNames.add(fileName);
|
||||
}
|
||||
|
||||
private String resolveUploadedFileName(String fileId) {
|
||||
if (isBlank(fileId)) {
|
||||
return null;
|
||||
}
|
||||
return fileRecordRepository.findByFileId(normalize(fileId))
|
||||
.map(record -> normalize(record.getOriginalFilename()))
|
||||
.filter(name -> !name.isEmpty())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private int compareTaskRecency(InitTaskEntity left, InitTaskEntity right) {
|
||||
Comparator<LocalDateTime> comparator = Comparator.nullsFirst(LocalDateTime::compareTo);
|
||||
int byUpdate = comparator.compare(left.getUpdateTime(), right.getUpdateTime());
|
||||
@@ -746,6 +811,11 @@ public class InitServiceImpl implements InitService {
|
||||
private void validateStandard(String productType, InitPreviewRequest request, String deployMode, String mqType) {
|
||||
requireAllowed("deployMode", deployMode, DEPLOY_STANDARD);
|
||||
requireAllowed("mq.mqType", mqType, resolveStandardMqAllowed(productType));
|
||||
if (request.getLicenses() == null) {
|
||||
throw new IllegalArgumentException("licenses is required");
|
||||
}
|
||||
requireNotBlank("licenses.receiverLicenseFileId", request.getLicenses().getReceiverLicenseFileId());
|
||||
requireFileId("licenses.receiverLicenseFileId", request.getLicenses().getReceiverLicenseFileId());
|
||||
|
||||
if ("DUAL".equals(deployMode)) {
|
||||
requireNotBlank("nodes.node02Ip", request.getNodes().getNode02Ip());
|
||||
@@ -779,26 +849,14 @@ public class InitServiceImpl implements InitService {
|
||||
requireNotBlank("nodes.node04Ip", request.getNodes().getNode04Ip());
|
||||
}
|
||||
|
||||
requireNotBlank("licenses.cfgZipFileId", request.getLicenses().getCfgZipFileId());
|
||||
requireFileId("licenses.cfgZipFileId", request.getLicenses().getCfgZipFileId());
|
||||
|
||||
if ("RABBITMQ_TLQ".equals(mqType)) {
|
||||
requireNotBlank("mq.tlqLicenseFileId", request.getMq().getTlqLicenseFileId());
|
||||
requireFileId("mq.tlqLicenseFileId", request.getMq().getTlqLicenseFileId());
|
||||
InitPreviewRequest.DirectTlqConfig directTlq = request.getDirectTlq();
|
||||
if (directTlq == null) {
|
||||
throw new IllegalArgumentException("directTlq is required for RABBITMQ_TLQ");
|
||||
}
|
||||
requireNotBlank("directTlq.localNodeSeq", directTlq.getLocalNodeSeq());
|
||||
requireNotBlank("directTlq.upstreamInstitutions", directTlq.getUpstreamInstitutions());
|
||||
requireNotBlank("mq.cfgZipFileId", request.getMq().getCfgZipFileId());
|
||||
requireFileId("mq.cfgZipFileId", request.getMq().getCfgZipFileId());
|
||||
requireNotBlank("mq.cipsGatewayHost", request.getMq().getCipsGatewayHost());
|
||||
if (directTlq.getUpstreamCocCount() == null
|
||||
|| directTlq.getUpstreamCocCount() < 1
|
||||
|| directTlq.getUpstreamCocCount() > 2) {
|
||||
throw new IllegalArgumentException("directTlq.upstreamCocCount must be in [1,2]");
|
||||
}
|
||||
}
|
||||
if ("RABBITMQ_CFMQ".equals(mqType)) {
|
||||
requireNotBlank("mq.cfgZipFileId", request.getMq().getCfgZipFileId());
|
||||
requireFileId("mq.cfgZipFileId", request.getMq().getCfgZipFileId());
|
||||
requireNotBlank("mq.cfmqConfigFileId", request.getMq().getCfmqConfigFileId());
|
||||
requireFileId("mq.cfmqConfigFileId", request.getMq().getCfmqConfigFileId());
|
||||
}
|
||||
@@ -859,6 +917,7 @@ public class InitServiceImpl implements InitService {
|
||||
return normalize(child.toString());
|
||||
}
|
||||
|
||||
|
||||
private static String normalizeUpper(String value) {
|
||||
return normalize(value).toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@@ -73,14 +73,16 @@ public class UpgradeController {
|
||||
}
|
||||
|
||||
@GetMapping("/upgrades")
|
||||
@Operation(summary = "查询升级记录列表", description = "分页查询升级任务列表,支持按升级对象和状态筛选。")
|
||||
@Operation(summary = "查询升级记录列表", description = "分页查询升级任务列表,支持按升级对象、状态和升级日期筛选。")
|
||||
public ApiResponse<UpgradeTaskPageResponse> list(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String taskType,
|
||||
@RequestParam(required = false) String status
|
||||
@RequestParam(required = false) String status,
|
||||
@Parameter(description = "升级日期,格式 yyyy-MM-dd,按 startedAt 所在自然日筛选")
|
||||
@RequestParam(required = false) String upgradeDate
|
||||
) {
|
||||
return ApiResponse.success(upgradeService.listTasks(page, pageSize, taskType, status));
|
||||
return ApiResponse.success(upgradeService.listTasks(page, pageSize, taskType, status, upgradeDate));
|
||||
}
|
||||
|
||||
@GetMapping("/upgrades/{taskId}")
|
||||
|
||||
@@ -7,6 +7,8 @@ public class UpgradeCreateTaskRequest {
|
||||
|
||||
@Schema(description = "升级包文件标识", example = "FILE-20260319-000001", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String fileId;
|
||||
@Schema(description = "用户选择的升级任务类型,必须与升级包解析结果一致", example = "TMS", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String taskType;
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
@@ -15,4 +17,12 @@ public class UpgradeCreateTaskRequest {
|
||||
public void setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
public String getTaskType() {
|
||||
return taskType;
|
||||
}
|
||||
|
||||
public void setTaskType(String taskType) {
|
||||
this.taskType = taskType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,17 @@ public interface UpgradeTaskMapper extends BaseMapperX<UpgradeTaskEntity> {
|
||||
List<UpgradeTaskEntity> selectPage(
|
||||
@Param("taskType") String taskType,
|
||||
@Param("status") String status,
|
||||
@Param("startedAtBegin") LocalDateTime startedAtBegin,
|
||||
@Param("startedAtEndExclusive") LocalDateTime startedAtEndExclusive,
|
||||
@Param("offset") int offset,
|
||||
@Param("limit") int limit
|
||||
);
|
||||
|
||||
long countPage(
|
||||
@Param("taskType") String taskType,
|
||||
@Param("status") String status
|
||||
@Param("status") String status,
|
||||
@Param("startedAtBegin") LocalDateTime startedAtBegin,
|
||||
@Param("startedAtEndExclusive") LocalDateTime startedAtEndExclusive
|
||||
);
|
||||
|
||||
int updateStatusByTaskId(
|
||||
|
||||
@@ -13,9 +13,16 @@ public interface UpgradeTaskRepository {
|
||||
|
||||
Optional<UpgradeTaskEntity> findRunningTask();
|
||||
|
||||
List<UpgradeTaskEntity> findPage(String taskType, String status, int offset, int limit);
|
||||
List<UpgradeTaskEntity> findPage(
|
||||
String taskType,
|
||||
String status,
|
||||
LocalDateTime startedAtBegin,
|
||||
LocalDateTime startedAtEndExclusive,
|
||||
int offset,
|
||||
int limit
|
||||
);
|
||||
|
||||
long countPage(String taskType, String status);
|
||||
long countPage(String taskType, String status, LocalDateTime startedAtBegin, LocalDateTime startedAtEndExclusive);
|
||||
|
||||
void updateStatus(
|
||||
String taskId,
|
||||
|
||||
+11
-4
@@ -33,13 +33,20 @@ public class UpgradeTaskRepositoryImpl implements UpgradeTaskRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UpgradeTaskEntity> findPage(String taskType, String status, int offset, int limit) {
|
||||
return upgradeTaskMapper.selectPage(taskType, status, offset, limit);
|
||||
public List<UpgradeTaskEntity> findPage(
|
||||
String taskType,
|
||||
String status,
|
||||
LocalDateTime startedAtBegin,
|
||||
LocalDateTime startedAtEndExclusive,
|
||||
int offset,
|
||||
int limit
|
||||
) {
|
||||
return upgradeTaskMapper.selectPage(taskType, status, startedAtBegin, startedAtEndExclusive, offset, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countPage(String taskType, String status) {
|
||||
return upgradeTaskMapper.countPage(taskType, status);
|
||||
public long countPage(String taskType, String status, LocalDateTime startedAtBegin, LocalDateTime startedAtEndExclusive) {
|
||||
return upgradeTaskMapper.countPage(taskType, status, startedAtBegin, startedAtEndExclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,8 +19,10 @@ import com.cisd.tms.modules.upgrade.repository.UpgradeTaskRepository;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -36,6 +38,7 @@ import org.springframework.stereotype.Service;
|
||||
public class UpgradeService {
|
||||
|
||||
private static final DateTimeFormatter TASK_ID_TS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
private static final DateTimeFormatter UPGRADE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
private final UpgradeTaskRepository upgradeTaskRepository;
|
||||
private final FileRecordRepository fileRecordRepository;
|
||||
@@ -97,11 +100,17 @@ public class UpgradeService {
|
||||
if (request == null || request.getFileId() == null || request.getFileId().isBlank()) {
|
||||
throw new IllegalArgumentException("fileId must not be blank");
|
||||
}
|
||||
if (request.getTaskType() == null || request.getTaskType().isBlank()) {
|
||||
throw new IllegalArgumentException("taskType 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"));
|
||||
}
|
||||
if (!trim(request.getTaskType()).equals(trim(preview.getTaskType()))) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "selected taskType does not match package taskType");
|
||||
}
|
||||
FileRecordEntity fileRecord = fileRecordRepository.findByFileId(request.getFileId().trim())
|
||||
.orElseThrow(() -> new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package file not found: " + request.getFileId().trim()));
|
||||
|
||||
@@ -186,12 +195,27 @@ public class UpgradeService {
|
||||
return toResponse(loadTask(taskId));
|
||||
}
|
||||
|
||||
public UpgradeTaskPageResponse listTasks(int page, int pageSize, String taskType, String status) {
|
||||
public UpgradeTaskPageResponse listTasks(int page, int pageSize, String taskType, String status, String upgradeDate) {
|
||||
int normalizedPage = Math.max(1, page);
|
||||
int normalizedPageSize = Math.max(1, pageSize);
|
||||
int offset = (normalizedPage - 1) * normalizedPageSize;
|
||||
List<UpgradeTaskEntity> entities = upgradeTaskRepository.findPage(trim(taskType), trim(status), offset, normalizedPageSize);
|
||||
long total = upgradeTaskRepository.countPage(trim(taskType), trim(status));
|
||||
LocalDateTime startedAtBegin = null;
|
||||
LocalDateTime startedAtEndExclusive = null;
|
||||
String normalizedUpgradeDate = trim(upgradeDate);
|
||||
if (!normalizedUpgradeDate.isEmpty()) {
|
||||
LocalDate filterDate = parseUpgradeDate(normalizedUpgradeDate);
|
||||
startedAtBegin = filterDate.atStartOfDay();
|
||||
startedAtEndExclusive = filterDate.plusDays(1).atStartOfDay();
|
||||
}
|
||||
List<UpgradeTaskEntity> entities = upgradeTaskRepository.findPage(
|
||||
trim(taskType),
|
||||
trim(status),
|
||||
startedAtBegin,
|
||||
startedAtEndExclusive,
|
||||
offset,
|
||||
normalizedPageSize
|
||||
);
|
||||
long total = upgradeTaskRepository.countPage(trim(taskType), trim(status), startedAtBegin, startedAtEndExclusive);
|
||||
|
||||
UpgradeTaskPageResponse response = new UpgradeTaskPageResponse();
|
||||
response.setPage(normalizedPage);
|
||||
@@ -267,4 +291,12 @@ public class UpgradeService {
|
||||
private String blankAs(String value, String fallback) {
|
||||
return trim(value).isEmpty() ? fallback : trim(value);
|
||||
}
|
||||
|
||||
private LocalDate parseUpgradeDate(String upgradeDate) {
|
||||
try {
|
||||
return LocalDate.parse(upgradeDate, UPGRADE_DATE_FORMAT);
|
||||
} catch (DateTimeParseException ex) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "upgradeDate must be yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,12 @@
|
||||
<if test="status != null and status != ''">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="startedAtBegin != null">
|
||||
AND started_at <![CDATA[ >= ]]> #{startedAtBegin}
|
||||
</if>
|
||||
<if test="startedAtEndExclusive != null">
|
||||
AND started_at <![CDATA[ < ]]> #{startedAtEndExclusive}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY create_time DESC, id DESC
|
||||
LIMIT #{limit} OFFSET #{offset}
|
||||
@@ -74,6 +80,12 @@
|
||||
<if test="status != null and status != ''">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="startedAtBegin != null">
|
||||
AND started_at <![CDATA[ >= ]]> #{startedAtBegin}
|
||||
</if>
|
||||
<if test="startedAtEndExclusive != null">
|
||||
AND started_at <![CDATA[ < ]]> #{startedAtEndExclusive}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user