设备时间配置
This commit is contained in:
parent
bf8ec7054c
commit
ee27bba820
@ -0,0 +1,33 @@
|
|||||||
|
package com.cisd.tms.modules.device.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.cisd.tms.common.api.ApiResponse;
|
||||||
|
import com.cisd.tms.common.enums.ErrorCode;
|
||||||
|
import com.cisd.tms.modules.device.dto.TimeConfigRequest;
|
||||||
|
import com.cisd.tms.modules.device.service.TimeConfigService;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/device")
|
||||||
|
@Tag(name = "device-time-config-controller")
|
||||||
|
public class TimeConfigController {
|
||||||
|
|
||||||
|
private final TimeConfigService timeConfigService;
|
||||||
|
|
||||||
|
public TimeConfigController(TimeConfigService timeConfigService) {
|
||||||
|
this.timeConfigService = timeConfigService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/time-config")
|
||||||
|
public ApiResponse<Void> configureTime(@RequestBody TimeConfigRequest request) {
|
||||||
|
//todo 鉴权
|
||||||
|
timeConfigService.processTimeConfig(request);
|
||||||
|
|
||||||
|
return ApiResponse.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.cisd.tms.modules.device.dto;
|
||||||
|
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "设备时间配置请求参数")
|
||||||
|
public class TimeConfigRequest {
|
||||||
|
|
||||||
|
@Schema(description = "配置模式",
|
||||||
|
allowableValues = {"MANUAL", "NTP"},
|
||||||
|
example = "MANUAL",
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String mode;
|
||||||
|
|
||||||
|
@Schema(description = "手工调整输入时间 (仅 MANUAL 模式下必填,格式: yyyy-MM-dd HH:mm)",
|
||||||
|
example = "2026-01-30 00:00")
|
||||||
|
private String datetime;
|
||||||
|
|
||||||
|
@Schema(description = "系统时区标识",
|
||||||
|
example = "Asia/Shanghai")
|
||||||
|
private String timezone;
|
||||||
|
|
||||||
|
@Schema(description = "NTP服务器地址列表 (仅 NTP 模式下必填)",
|
||||||
|
example = "[\"ntp.aliyun.com\", \"ntp.tencent.com\"]")
|
||||||
|
private List<String> ntpServers;
|
||||||
|
|
||||||
|
@Schema(description = "NTP同步间隔时间 (单位: 秒。默认 600)",
|
||||||
|
example = "600")
|
||||||
|
private Integer syncInterval;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
package com.cisd.tms.modules.device.service;
|
||||||
|
|
||||||
|
import com.cisd.tms.modules.device.dto.TimeConfigRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TimeConfigService {
|
||||||
|
|
||||||
|
|
||||||
|
public void processTimeConfig(TimeConfigRequest request) {
|
||||||
|
String mode = request.getMode();
|
||||||
|
|
||||||
|
if ("MANUAL".equalsIgnoreCase(mode)) {
|
||||||
|
|
||||||
|
String timezone = request.getTimezone();
|
||||||
|
if (timezone == null || !timezone.matches("^[A-Za-z0-9/_-]+$")) {
|
||||||
|
throw new IllegalArgumentException("Invalid or empty timezone format");
|
||||||
|
}
|
||||||
|
|
||||||
|
String datetime = request.getDatetime();
|
||||||
|
// 假设格式必须为 YYYY-MM-DD HH:MM
|
||||||
|
if (datetime == null || !datetime.matches("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$")) {
|
||||||
|
throw new IllegalArgumentException("Invalid or empty datetime format. Expected: YYYY-MM-DD HH:MM");
|
||||||
|
}
|
||||||
|
|
||||||
|
executeCommand("timedatectl set-timezone " + request.getTimezone().trim());
|
||||||
|
executeCommand("systemctl stop chronyd");
|
||||||
|
executeCommand("date -s '" + request.getDatetime() + "'");
|
||||||
|
|
||||||
|
} else if ("NTP".equalsIgnoreCase(mode)) {
|
||||||
|
|
||||||
|
List<String> servers = request.getNtpServers();
|
||||||
|
if (servers == null || servers.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("At least one NTP server IP must be provided in NTP mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int interval = request.getSyncInterval() != null ? request.getSyncInterval() : 600;
|
||||||
|
int pollExp = (int) Math.round(Math.log(interval) / Math.log(2));
|
||||||
|
StringBuilder serverConfLines = new StringBuilder();
|
||||||
|
for (String server : servers) {
|
||||||
|
if (!server.matches("^[A-Za-z0-9.-]+$")) {
|
||||||
|
throw new IllegalArgumentException("Invalid NTP server address format: " + server);
|
||||||
|
}
|
||||||
|
|
||||||
|
String pingResult = executeCommand("ping -c 1 -W 1 " + server);
|
||||||
|
if (!pingResult.contains("1 received")) {
|
||||||
|
throw new RuntimeException("NTP server is unreachable via ping: " + server);
|
||||||
|
}
|
||||||
|
serverConfLines.append("server ").append(server)
|
||||||
|
.append(" iburst minpoll ").append(pollExp)
|
||||||
|
.append(" maxpoll ").append(pollExp).append("\\n");
|
||||||
|
}
|
||||||
|
String confContent = serverConfLines +
|
||||||
|
"driftfile /var/lib/chrony/drift\\n" +
|
||||||
|
"makestep 1.0 3\\n" +
|
||||||
|
"rtcsync\\n";
|
||||||
|
executeCommand("echo -e '" + confContent + "' > /etc/chrony.conf");
|
||||||
|
executeCommand("systemctl restart chronyd");
|
||||||
|
executeCommand("chronyc -a makestep");
|
||||||
|
String status = executeCommand("chronyc tracking");
|
||||||
|
if (!status.contains("Reference ID")) {
|
||||||
|
throw new RuntimeException("NTP sync status check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String executeCommand(String command){
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", command);
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
Process process = null;
|
||||||
|
try {
|
||||||
|
process = processBuilder.start();
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new RuntimeException("command execution failed: [" + command + "] ");
|
||||||
|
}
|
||||||
|
return output.toString();
|
||||||
|
|
||||||
|
} catch (IOException e){
|
||||||
|
throw new RuntimeException("I/O error executing command: [" + command + "]", e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (process != null) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new RuntimeException("process was interrupted while executing command: [" + command + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user