fix:升级tms

This commit is contained in:
waner
2026-04-22 15:12:05 +08:00
parent 80e553fb23
commit 1fc32dbcf0
6 changed files with 120 additions and 10 deletions
@@ -28,7 +28,7 @@ public class UpgradeController {
@PostMapping("/upgrade-packages")
@Operation(summary = "上传离线升级包", description = "上传离线升级包,返回 fileId 供升级预检和任务创建使用。")
@ReplayProtected
// @ReplayProtected
public ApiResponse<FileUploadResponse> uploadPackage(
@Parameter(description = "待上传的离线升级包")
@RequestParam("file") MultipartFile file
@@ -44,7 +44,7 @@ public class UpgradeController {
@PostMapping("/upgrades")
@Operation(summary = "创建离线升级任务", description = "根据 fileId 创建离线升级任务,状态初始为 PENDING_CONFIRM。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.CREATE, summary = "创建离线升级任务")
public ApiResponse<UpgradeCreateTaskResponse> create(@Valid @RequestBody UpgradeCreateTaskRequest request) {
return ApiResponse.success(upgradeService.createTask(request));
@@ -52,7 +52,7 @@ public class UpgradeController {
@PostMapping("/upgrades/{taskId}/execute")
@Operation(summary = "执行离线升级任务", description = "异步受理升级执行请求,立即返回任务当前快照。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.EXECUTE, summary = "执行离线升级任务")
public ApiResponse<UpgradeTaskResponse> execute(
@Parameter(description = "升级任务号")
@@ -63,7 +63,7 @@ public class UpgradeController {
@PostMapping("/upgrades/{taskId}/rollback")
@Operation(summary = "回滚离线升级任务", description = "异步受理升级回滚请求,立即返回任务当前快照。")
@ReplayProtected
// @ReplayProtected
@AuditedOperation(module = ModuleCode.UPGRADE, action = ActionType.ROLLBACK, summary = "回滚离线升级任务")
public ApiResponse<UpgradeTaskResponse> rollback(
@Parameter(description = "升级任务号")
@@ -110,6 +110,7 @@ public class UpgradeTaskRunner {
startedAt,
LocalDateTime.now()
);
scheduleDetachedRestartIfRequired(task, logPath, false);
} catch (Exception ex) {
try {
Files.createDirectories(logPath.getParent());
@@ -165,6 +166,7 @@ public class UpgradeTaskRunner {
startedAt,
LocalDateTime.now()
);
scheduleDetachedRestartIfRequired(task, logPath, true);
} catch (Exception ex) {
try {
Files.createDirectories(logPath.getParent());
@@ -207,6 +209,52 @@ public class UpgradeTaskRunner {
return process.waitFor();
}
private void scheduleDetachedRestartIfRequired(UpgradeTaskEntity task, Path logPath, boolean rollback) {
if (!"TMS".equals(componentCode(task.getTaskType()))) {
return;
}
try {
writeLogLine(logPath, rollback ? "rollback success, scheduling detached TMS restart"
: "upgrade success, scheduling detached TMS restart");
triggerDetachedTmsRestart(logPath, rollback);
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to schedule detached tms restart");
}
}
/**
* TMS 自升级不能继续依赖包内脚本 stop/start 当前 Web 进程,
* 否则执行器还未落库终态就会把自己杀掉。这里在状态写回后再异步拉起重启。
*/
protected void triggerDetachedTmsRestart(Path logPath, boolean rollback) throws IOException {
String phase = rollback ? "rollback" : "upgrade";
String command = "nohup /home/tms/scripts/tms.sh restart >> "
+ shellQuote(logPath.toString())
+ " 2>&1 < /dev/null &";
Process shell = new ProcessBuilder("bash", "-lc", command)
.redirectErrorStream(true)
.redirectOutput(Redirect.appendTo(logPath.toFile()))
.start();
try {
int exitCode = shell.waitFor();
if (exitCode != 0) {
throw new IOException("detached TMS " + phase + " restart command exited with " + exitCode);
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IOException("interrupted while scheduling detached TMS restart", ex);
}
}
private void writeLogLine(Path logPath, String message) throws IOException {
Files.writeString(
logPath,
"[" + LocalDateTime.now() + "] " + trim(message) + System.lineSeparator(),
java.nio.file.StandardOpenOption.CREATE,
java.nio.file.StandardOpenOption.APPEND
);
}
private String componentName(String componentCode) {
return switch (trim(componentCode)) {
case "TMS" -> "设备管理软件";
@@ -237,4 +285,8 @@ public class UpgradeTaskRunner {
private String trim(String value) {
return value == null ? "" : value.trim();
}
private String shellQuote(String value) {
return "'" + trim(value).replace("'", "'\"'\"'") + "'";
}
}
@@ -154,7 +154,8 @@ public class UpgradeService {
return toResponse(loadTask(taskId));
}
if ("SUCCESS".equals(task.getStatus()) || "FAILED".equals(task.getStatus()) || "PENDING_CONFIRM".equals(task.getStatus())) {
upgradeTaskRepository.updateStatus(taskId, "RUNNING", "", task.getDetailLogPath(), LocalDateTime.now(), null);
Path logPath = prepareLogFile(taskId);
upgradeTaskRepository.updateStatus(taskId, "RUNNING", "", logPath.toString(), LocalDateTime.now(), null);
}
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
@@ -183,7 +184,8 @@ public class UpgradeService {
if (activeExecutions.containsKey(taskId)) {
return toResponse(loadTask(taskId));
}
upgradeTaskRepository.updateStatus(taskId, "ROLLING_BACK", "", task.getDetailLogPath(), LocalDateTime.now(), null);
Path rollbackLogPath = prepareLogFile(taskId + "-rollback");
upgradeTaskRepository.updateStatus(taskId, "ROLLING_BACK", "", rollbackLogPath.toString(), LocalDateTime.now(), null);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
upgradeTaskRunner.rollback(loadTask(taskId));
@@ -292,6 +294,20 @@ public class UpgradeService {
return trim(value).isEmpty() ? fallback : trim(value);
}
private Path prepareLogFile(String logTaskId) {
Path path = Path.of(trim(upgradeProperties.getLogDir())).normalize().toAbsolutePath()
.resolve(trim(logTaskId) + ".log");
try {
Files.createDirectories(path.getParent());
if (!Files.exists(path)) {
Files.writeString(path, "");
}
return path;
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to prepare upgrade log file");
}
}
private LocalDate parseUpgradeDate(String upgradeDate) {
try {
return LocalDate.parse(upgradeDate, UPGRADE_DATE_FORMAT);
@@ -29,6 +29,8 @@ public class InternalApiAuthInterceptor implements HandlerInterceptor {
public static final String ATTR_SESSION_TOKEN = "CURRENT_SESSION_TOKEN";
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String SESSION_HEADER = "X-Session-Token";
//会话空闲实效时间
private static final int IDLE_TIMEOUT_MINUTES = 10;
private final AuthSessionRepository authSessionRepository;