feat: 新增用户端配置获取和批量更新接口

主要修改:
1. 在SystemConfigController中新增获取用户端配置的接口。
2. 实现批量更新系统配置的接口,支持根据配置键进行更新。
3. 增强SystemConfigService,添加用户端相关配置的获取方法及配置值验证逻辑。

技术细节:
- 新增的接口提升了系统配置管理的灵活性,支持批量操作和用户端配置的动态获取。
This commit is contained in:
zyh
2025-08-27 17:20:35 +08:00
parent 01bc703ea2
commit 429e12cf50
8 changed files with 472 additions and 42 deletions

View File

@@ -98,4 +98,86 @@ public class SystemConfigService {
public String getQrPathTemplate() {
return getConfigValue("script.qr_path_template", "/{machineId}/二维码.png");
}
// 获取用户端相关配置
public Integer getUserQrExpireSeconds() {
return getConfigValueAsInt("user.qr_expire_seconds", 60);
}
public Integer getUserRefreshWaitSeconds() {
return getConfigValueAsInt("user.refresh_wait_seconds", 10);
}
public Integer getUserLinkExpireHours() {
return getConfigValueAsInt("user.link_expire_hours", 24);
}
public String getUserAssetsBaseUrl() {
return getConfigValue("user.assets_base_url", "http://36.138.184.60:12345");
}
// 批量更新配置
public boolean updateConfigs(List<SystemConfig> configs) {
if (configs == null || configs.isEmpty()) {
return false;
}
int successCount = 0;
for (SystemConfig config : configs) {
if (config.getId() != null) {
if (systemConfigMapper.update(config) > 0) {
successCount++;
}
} else if (config.getConfigKey() != null) {
// 根据key更新
SystemConfig existing = systemConfigMapper.findByKey(config.getConfigKey());
if (existing != null) {
config.setId(existing.getId());
if (systemConfigMapper.update(config) > 0) {
successCount++;
}
}
}
}
return successCount > 0;
}
// 根据key批量更新配置值
public boolean updateConfigValueByKey(String configKey, String configValue) {
SystemConfig config = systemConfigMapper.findByKey(configKey);
if (config == null) {
return false;
}
config.setConfigValue(configValue);
return systemConfigMapper.update(config) > 0;
}
// 验证配置值的格式是否正确
public boolean validateConfigValue(String configType, String configValue) {
if (configValue == null || configValue.trim().isEmpty()) {
return false;
}
try {
switch (configType.toUpperCase()) {
case "INTEGER":
Integer.parseInt(configValue);
return true;
case "BOOLEAN":
String lowerValue = configValue.toLowerCase();
return "true".equals(lowerValue) || "false".equals(lowerValue);
case "JSON":
// 简单的JSON格式验证
String trimmed = configValue.trim();
return (trimmed.startsWith("{") && trimmed.endsWith("}")) ||
(trimmed.startsWith("[") && trimmed.endsWith("]"));
case "STRING":
default:
return true;
}
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@@ -15,8 +15,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
@@ -38,8 +37,6 @@ public class ImageSaveService {
// 使用信号量限制并发下载数量,防止服务器资源不足
private final Semaphore downloadSemaphore = new Semaphore(3);
// 时间格式化器
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
public ImageSaveService(
ScriptClient scriptClient,
@@ -61,14 +58,11 @@ public class ImageSaveService {
/**
* 下载并保存完成时的所有图片
* @param deviceId 设备ID
* @param taskId 任务ID
* @param codeNo 任务编号
* @return 保存后的本地图片URL映射
*/
public Mono<Map<String, String>> downloadAndSaveCompletionImages(String deviceId, Long taskId) {
log.info("开始为任务 {} 设备 {} 下载完成图片", taskId, deviceId);
String timestamp = LocalDateTime.now().format(TIME_FORMATTER);
String taskDir = String.format("task_%d_device_%s_completed_%s", taskId, deviceId, timestamp);
public Mono<Map<String, String>> downloadAndSaveCompletionImages(String deviceId, String codeNo) {
log.info("开始为任务 {} 设备 {} 下载完成图片", codeNo, deviceId);
Map<String, String> imageTypes = Map.of(
"homepage", "首次主页.png",
@@ -77,23 +71,20 @@ public class ImageSaveService {
"endReward", "结束赏金.png"
);
return downloadAndSaveImages(deviceId, taskDir, imageTypes)
return downloadAndSaveImages(deviceId, codeNo, imageTypes)
.doOnSuccess(result -> log.info("任务 {} 设备 {} 完成图片下载保存完成,共保存{}张图片",
taskId, deviceId, result.size()))
.doOnError(error -> log.error("任务 {} 设备 {} 完成图片下载保存失败", taskId, deviceId, error));
codeNo, deviceId, result.size()))
.doOnError(error -> log.error("任务 {} 设备 {} 完成图片下载保存失败", codeNo, deviceId, error));
}
/**
* 下载并保存进行中的任务快照图片
* @param deviceId 设备ID
* @param taskId 任务ID
* @param codeNo 任务编号
* @return 保存后的本地图片URL映射
*/
public Mono<Map<String, String>> downloadAndSaveProgressImages(String deviceId, Long taskId) {
log.debug("开始为任务 {} 设备 {} 下载进度图片", taskId, deviceId);
String timestamp = LocalDateTime.now().format(TIME_FORMATTER);
String taskDir = String.format("task_%d_device_%s_progress_%s", taskId, deviceId, timestamp);
public Mono<Map<String, String>> downloadAndSaveProgressImages(String deviceId, String codeNo) {
log.debug("开始为任务 {} 设备 {} 下载进度图片", codeNo, deviceId);
// 进行中只保存可能有的图片
Map<String, String> imageTypes = Map.of(
@@ -102,21 +93,21 @@ public class ImageSaveService {
"midReward", "中途赏金.png"
);
return downloadAndSaveImages(deviceId, taskDir, imageTypes)
return downloadAndSaveImages(deviceId, codeNo, imageTypes)
.doOnSuccess(result -> log.debug("任务 {} 设备 {} 进度图片下载保存完成,共保存{}张图片",
taskId, deviceId, result.size()))
.doOnError(error -> log.warn("任务 {} 设备 {} 进度图片下载保存失败", taskId, deviceId, error));
codeNo, deviceId, result.size()))
.doOnError(error -> log.warn("任务 {} 设备 {} 进度图片下载保存失败", codeNo, deviceId, error));
}
/**
* 通用的图片下载保存方法
*/
private Mono<Map<String, String>> downloadAndSaveImages(String deviceId, String taskDir, Map<String, String> imageTypes) {
private Mono<Map<String, String>> downloadAndSaveImages(String deviceId, String codeNo, Map<String, String> imageTypes) {
Map<String, String> savedImageUrls = new HashMap<>();
return Mono.fromCallable(() -> {
// 创建任务专用目录
Path taskDirPath = Paths.get(imageSavePath, taskDir);
// 使用codeNo作为目录
Path taskDirPath = Paths.get(imageSavePath, codeNo);
Files.createDirectories(taskDirPath);
return taskDirPath;
})
@@ -181,9 +172,9 @@ public class ImageSaveService {
return null;
}
// 生成本地文件名
// 直接使用图片类型作为文件名,覆盖同名文件
String extension = getFileExtension(originalName);
String localFileName = imageType + "_" + System.currentTimeMillis() + extension;
String localFileName = imageType + extension;
Path localFilePath = taskDirPath.resolve(localFileName);
// 保存文件

View File

@@ -85,7 +85,7 @@ public class DeviceTaskUpdateService {
for (LinkTask task : tasks) {
try {
// 异步下载并保存完成图片
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getId())
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getCodeNo())
.subscribe(
savedImages -> {
// 保存成功后更新任务
@@ -170,7 +170,7 @@ public class DeviceTaskUpdateService {
for (LinkTask task : tasks) {
try {
// 异步下载并保存完成图片
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getId())
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getCodeNo())
.subscribe(
savedImages -> {
// 保存成功后更新任务
@@ -218,36 +218,36 @@ public class DeviceTaskUpdateService {
/**
* 定期保存进行中任务的图片快照
* @param deviceId 设备ID
* @param taskId 任务ID
* @param codeNo 任务编号
*/
public void saveProgressImagesForTask(String deviceId, Long taskId) {
log.debug("开始为任务 {} 设备 {} 保存进度图片", taskId, deviceId);
public void saveProgressImagesForTask(String deviceId, String codeNo) {
log.debug("开始为任务 {} 设备 {} 保存进度图片", codeNo, deviceId);
imageSaveService.downloadAndSaveProgressImages(deviceId, taskId)
imageSaveService.downloadAndSaveProgressImages(deviceId, codeNo)
.subscribe(
savedImages -> {
if (!savedImages.isEmpty()) {
log.info("任务 {} 设备 {} 进度图片保存成功,共保存{}张图片",
taskId, deviceId, savedImages.size());
codeNo, deviceId, savedImages.size());
// 可选:更新任务记录中的进度图片信息
updateTaskProgressImages(taskId, savedImages);
updateTaskProgressImagesByCodeNo(codeNo, savedImages);
}
},
error -> log.warn("任务 {} 设备 {} 进度图片保存失败: {}",
taskId, deviceId, error.getMessage())
codeNo, deviceId, error.getMessage())
);
}
/**
* 更新任务的进度图片信息(可选)
*/
private void updateTaskProgressImages(Long taskId, Map<String, String> savedImages) {
private void updateTaskProgressImagesByCodeNo(String codeNo, Map<String, String> savedImages) {
try {
// 这里可以选择是否要将进度图片也保存到数据库中
// 目前只记录日志,不修改数据库记录
log.debug("任务 {} 进度图片已保存到本地: {}", taskId, savedImages.keySet());
log.debug("任务 {} 进度图片已保存到本地: {}", codeNo, savedImages.keySet());
} catch (Exception e) {
log.warn("更新任务 {} 进度图片记录失败", taskId, e);
log.warn("更新任务 {} 进度图片记录失败", codeNo, e);
}
}

View File

@@ -445,7 +445,7 @@ public class LinkStatusService {
log.info("空闲设备检查完成: 总设备数={}, 空闲设备数={}, 空闲设备列表={}",
deviceStatus.getTotalDevices(), deviceStatus.getAvailableCount(), deviceStatus.getAvailableDevices());
// 7. 选择一个空闲设备
// 7. 选择一个空闲设备 TODO
// String selectedDevice = deviceStatus.getAvailableDevices().get(0); // 选择第一个空闲设备
String selectedDevice = "cc2";
log.info("从空闲设备列表中选择设备: {}", selectedDevice);