From 7882a0c9032bb7cf158f4900cd8d538e19e85ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E5=87=A4=E5=90=89?= Date: Thu, 17 Jul 2025 17:30:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qyft/ms/app/controller/SystemController.java | 30 +++++++++- .../java/com/qyft/ms/app/model/dto/TimeSetDTO.java | 21 +++++++ .../com/qyft/ms/app/model/vo/TimeResponseVO.java | 22 +++++++ .../com/qyft/ms/app/service/SystemService.java | 69 ++++++++++++++++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/qyft/ms/app/model/dto/TimeSetDTO.java create mode 100644 src/main/java/com/qyft/ms/app/model/vo/TimeResponseVO.java create mode 100644 src/main/java/com/qyft/ms/app/service/SystemService.java diff --git a/src/main/java/com/qyft/ms/app/controller/SystemController.java b/src/main/java/com/qyft/ms/app/controller/SystemController.java index 2e52e76..ab7f635 100644 --- a/src/main/java/com/qyft/ms/app/controller/SystemController.java +++ b/src/main/java/com/qyft/ms/app/controller/SystemController.java @@ -1,16 +1,21 @@ package com.qyft.ms.app.controller; import com.qyft.ms.app.device.spray.SprayTaskExecutor; +import com.qyft.ms.app.model.dto.TimeSetDTO; +import com.qyft.ms.app.model.vo.TimeResponseVO; import com.qyft.ms.app.service.PositionService; import com.qyft.ms.app.service.SysSettingsService; +import com.qyft.ms.app.service.SystemService; import com.qyft.ms.system.common.result.Result; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.time.Instant; +import java.time.ZoneId; @Tag(name = "系统接口") @RestController @@ -21,6 +26,7 @@ public class SystemController { private final PositionService positionService; private final SysSettingsService sysSettingsService; private final SprayTaskExecutor sprayTaskExecutor; + private final SystemService systemService; @Operation(summary = "还原出厂配置") @GetMapping("/reset") @@ -35,4 +41,22 @@ public class SystemController { return Result.failed(); } } + + @Operation(summary = "设置系统时间") + @PostMapping("/datetime") + public Result setDatetime(@Valid @RequestBody TimeSetDTO timeSetDTO) { + systemService.setSystemTime(timeSetDTO.getEpochMilli(), timeSetDTO.getZone()); + return Result.success(); + } + + @Operation(summary = "获取系统时间") + @GetMapping("/datetime") + public Result getDatetime() { + long epochMilli = systemService.getCurrentEpochMilli(); + ZoneId systemZone = ZoneId.systemDefault(); + return Result.success(new TimeResponseVO(epochMilli, systemZone)); + } + + + } diff --git a/src/main/java/com/qyft/ms/app/model/dto/TimeSetDTO.java b/src/main/java/com/qyft/ms/app/model/dto/TimeSetDTO.java new file mode 100644 index 0000000..3cb34e9 --- /dev/null +++ b/src/main/java/com/qyft/ms/app/model/dto/TimeSetDTO.java @@ -0,0 +1,21 @@ +package com.qyft.ms.app.model.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class TimeSetDTO { + /** + * 要设置的时间戳(毫秒),UTC 纪元毫秒数 + */ + @NotNull + @Schema(description = "要设置的时间戳(毫秒),UTC 纪元毫秒数") + private Long epochMilli; + + /** + * 可选:要设置的系统时区,如 "Asia/Tokyo"、"Europe/London" + */ + @Schema(description = "可选:要设置的系统时区,如 \"Asia/Tokyo\"、\"Europe/London\"") + private String zone; +} diff --git a/src/main/java/com/qyft/ms/app/model/vo/TimeResponseVO.java b/src/main/java/com/qyft/ms/app/model/vo/TimeResponseVO.java new file mode 100644 index 0000000..f8595cb --- /dev/null +++ b/src/main/java/com/qyft/ms/app/model/vo/TimeResponseVO.java @@ -0,0 +1,22 @@ +package com.qyft.ms.app.model.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.ZoneId; + +@Data +public class TimeResponseVO { + + @Schema(description = "当前系统时间的 UTC 毫秒时间戳") + private long epochMilli; + + @Schema(description = "时区") + private String zone; + + public TimeResponseVO(long epochMilli, ZoneId zoneId) { + this.epochMilli = epochMilli; + this.zone = zoneId.getId(); + } +} diff --git a/src/main/java/com/qyft/ms/app/service/SystemService.java b/src/main/java/com/qyft/ms/app/service/SystemService.java new file mode 100644 index 0000000..3c584e0 --- /dev/null +++ b/src/main/java/com/qyft/ms/app/service/SystemService.java @@ -0,0 +1,69 @@ +package com.qyft.ms.app.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.time.Instant; + +/** + * 系统相关 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SystemService { + + /** + * 获取当前时间戳(毫秒) + */ + public long getCurrentEpochMilli() { + return Instant.now().toEpochMilli(); + } + + + /** + * 将系统时区(可选)和系统时间(必选)一起设定 + * + * @param epochMilli UTC 毫秒时间戳 + * @param zone 可选 IANA 时区 ID;若为 null 或空,则不修改时区 + */ + public void setSystemTime(long epochMilli, String zone) { + // 1) 确定要设置的时区:客户端没传就用“Asia/Shanghai” + String effectiveZone = (zone != null && !zone.isBlank()) + ? zone + : "Asia/Shanghai"; + + // 2) 先修改系统时区(总是执行) + runCommand("timedatectl", "set-timezone", effectiveZone); + + // 3) 再用 date -s @<秒数> 方式设置系统时间 + long epochSecond = epochMilli / 1_000; + runCommand("date", "-s", "@" + epochSecond); + + + // 4) 将系统时钟写入到硬件时钟(RTC) + runCommand("hwclock", "--systohc"); + } + + /** + * 辅助:执行系统命令并检查退出码 + */ + private void runCommand(String... cmd) { + try { + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.inheritIO(); + Process p = pb.start(); + int exit = p.waitFor(); + if (exit != 0) { + throw new IllegalStateException( + String.format("命令 %s 执行失败,退出码=%d", String.join(" ", cmd), exit) + ); + } + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("执行系统命令失败: " + String.join(" ", cmd), e); + } + } +}