设备时间配置
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