feat: 新增用户端配置获取和批量更新接口
主要修改: 1. 在SystemConfigController中新增获取用户端配置的接口。 2. 实现批量更新系统配置的接口,支持根据配置键进行更新。 3. 增强SystemConfigService,添加用户端相关配置的获取方法及配置值验证逻辑。 技术细节: - 新增的接口提升了系统配置管理的灵活性,支持批量操作和用户端配置的动态获取。
This commit is contained in:
237
docs/系统配置接口使用示例.md
Normal file
237
docs/系统配置接口使用示例.md
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# 系统配置接口使用示例
|
||||||
|
|
||||||
|
## 接口概览
|
||||||
|
|
||||||
|
系统配置接口提供了完整的CRUD操作,支持系统参数的管理和配置。基础路径:`/api/admin/config`
|
||||||
|
|
||||||
|
## 1. 基础配置管理
|
||||||
|
|
||||||
|
### 获取配置列表
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/list?page=1&size=20
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"configKey": "link.default_quantity",
|
||||||
|
"configValue": "50",
|
||||||
|
"configType": "INTEGER",
|
||||||
|
"description": "链接生成默认奖励点数",
|
||||||
|
"isSystem": true,
|
||||||
|
"createdAt": "2025-08-25T14:19:33.974",
|
||||||
|
"updatedAt": "2025-08-25T14:19:33.974"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 12,
|
||||||
|
"page": 1,
|
||||||
|
"size": 20
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根据键获取配置
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/key/link.default_quantity
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根据类型获取配置
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/type/INTEGER
|
||||||
|
```
|
||||||
|
|
||||||
|
### 创建配置
|
||||||
|
```http
|
||||||
|
POST /api/admin/config
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"configKey": "new.config.key",
|
||||||
|
"configValue": "new value",
|
||||||
|
"configType": "STRING",
|
||||||
|
"description": "新的配置项",
|
||||||
|
"isSystem": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更新配置
|
||||||
|
```http
|
||||||
|
PUT /api/admin/config/1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"configKey": "link.default_quantity",
|
||||||
|
"configValue": "100",
|
||||||
|
"configType": "INTEGER",
|
||||||
|
"description": "链接生成默认奖励点数",
|
||||||
|
"isSystem": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 删除配置
|
||||||
|
```http
|
||||||
|
DELETE /api/admin/config/1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根据键删除配置
|
||||||
|
```http
|
||||||
|
DELETE /api/admin/config/key/test.config
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 专门配置获取接口
|
||||||
|
|
||||||
|
### 获取链接默认配置
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/link/defaults
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"defaultQuantity": 50,
|
||||||
|
"refreshInterval": 300,
|
||||||
|
"qrExpireTime": 14400,
|
||||||
|
"maxTimesPerBatch": 100,
|
||||||
|
"minQuantity": 10,
|
||||||
|
"maxQuantity": 1000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取脚本配置
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/script/config
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serverUrl": "http://36.138.184.60:12345",
|
||||||
|
"qrPathTemplate": "/{machineId}/二维码.png"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取用户端配置
|
||||||
|
```http
|
||||||
|
GET /api/admin/config/user/config
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"qrExpireSeconds": 60,
|
||||||
|
"refreshWaitSeconds": 10,
|
||||||
|
"linkExpireHours": 24,
|
||||||
|
"assetsBaseUrl": "http://36.138.184.60:12345"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 批量操作接口
|
||||||
|
|
||||||
|
### 批量更新配置
|
||||||
|
```http
|
||||||
|
POST /api/admin/config/batch
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"configs": [
|
||||||
|
{
|
||||||
|
"configKey": "link.default_quantity",
|
||||||
|
"configValue": "80",
|
||||||
|
"configType": "INTEGER",
|
||||||
|
"description": "链接生成默认奖励点数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"configKey": "link.refresh_interval",
|
||||||
|
"configValue": "600",
|
||||||
|
"configType": "INTEGER",
|
||||||
|
"description": "链接刷新间隔(秒)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "批量更新成功",
|
||||||
|
"updatedCount": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根据键快速更新配置值
|
||||||
|
```http
|
||||||
|
PUT /api/admin/config/key/link.default_quantity
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
"100"
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "更新成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 配置类型说明
|
||||||
|
|
||||||
|
支持的配置类型:
|
||||||
|
- `STRING`: 字符串类型
|
||||||
|
- `INTEGER`: 整数类型
|
||||||
|
- `BOOLEAN`: 布尔类型(true/false)
|
||||||
|
- `JSON`: JSON格式数据
|
||||||
|
|
||||||
|
## 5. 错误处理
|
||||||
|
|
||||||
|
### 配置不存在
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": 404,
|
||||||
|
"message": "配置不存在"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置验证失败
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "配置验证失败",
|
||||||
|
"errors": [
|
||||||
|
"配置键 link.default_quantity 的值格式不正确"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置值格式错误
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "配置值格式不正确,期望类型: INTEGER"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 当前系统配置项
|
||||||
|
|
||||||
|
根据数据库数据,系统现有以下配置项:
|
||||||
|
|
||||||
|
### 链接相关配置
|
||||||
|
- `link.default_quantity`: 链接生成默认奖励点数(默认:50)
|
||||||
|
- `link.refresh_interval`: 链接刷新间隔秒数(默认:300)
|
||||||
|
- `link.qr_expire_time`: 二维码过期时间秒数(默认:14400)
|
||||||
|
- `link.max_times_per_batch`: 每批次最大刷奖励次数(默认:100)
|
||||||
|
- `link.min_quantity`: 最小奖励点数(默认:10)
|
||||||
|
- `link.max_quantity`: 最大奖励点数(默认:1000)
|
||||||
|
|
||||||
|
### 脚本相关配置
|
||||||
|
- `script.server_url`: 脚本服务器地址
|
||||||
|
- `script.qr_path_template`: 二维码图片路径模板
|
||||||
|
|
||||||
|
### 用户端相关配置
|
||||||
|
- `user.qr_expire_seconds`: 用户端二维码有效期秒数(默认:60)
|
||||||
|
- `user.refresh_wait_seconds`: 用户端刷新等待时间秒数(默认:10)
|
||||||
|
- `user.link_expire_hours`: 用户端链接有效期小时数(默认:24)
|
||||||
|
- `user.assets_base_url`: 用户端静态资源基础URL
|
||||||
@@ -5,6 +5,7 @@ import com.gameplatform.server.model.entity.admin.SystemConfig;
|
|||||||
import com.gameplatform.server.model.dto.admin.SystemConfigRequest;
|
import com.gameplatform.server.model.dto.admin.SystemConfigRequest;
|
||||||
import com.gameplatform.server.model.dto.admin.SystemConfigResponse;
|
import com.gameplatform.server.model.dto.admin.SystemConfigResponse;
|
||||||
import com.gameplatform.server.model.dto.admin.SystemConfigConverter;
|
import com.gameplatform.server.model.dto.admin.SystemConfigConverter;
|
||||||
|
import com.gameplatform.server.model.dto.admin.BatchSystemConfigRequest;
|
||||||
import com.gameplatform.server.service.admin.SystemConfigService;
|
import com.gameplatform.server.service.admin.SystemConfigService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -13,6 +14,7 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/admin/config")
|
@RequestMapping("/api/admin/config")
|
||||||
@@ -117,4 +119,93 @@ public class SystemConfigController {
|
|||||||
public final String qrPathTemplate = systemConfigService.getQrPathTemplate();
|
public final String qrPathTemplate = systemConfigService.getQrPathTemplate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/user/config")
|
||||||
|
@Operation(summary = "获取用户端配置", description = "获取用户端相关配置")
|
||||||
|
public ResponseEntity<Object> getUserConfig() {
|
||||||
|
return ResponseEntity.ok(new Object() {
|
||||||
|
public final Integer qrExpireSeconds = systemConfigService.getUserQrExpireSeconds();
|
||||||
|
public final Integer refreshWaitSeconds = systemConfigService.getUserRefreshWaitSeconds();
|
||||||
|
public final Integer linkExpireHours = systemConfigService.getUserLinkExpireHours();
|
||||||
|
public final String assetsBaseUrl = systemConfigService.getUserAssetsBaseUrl();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batch")
|
||||||
|
@Operation(summary = "批量更新配置", description = "批量更新系统配置,支持根据配置键更新")
|
||||||
|
public ResponseEntity<Object> batchUpdateConfigs(@RequestBody BatchSystemConfigRequest request) {
|
||||||
|
if (request.getConfigs() == null || request.getConfigs().isEmpty()) {
|
||||||
|
return ResponseEntity.badRequest().body(new Object() {
|
||||||
|
public final boolean success = false;
|
||||||
|
public final String message = "配置列表不能为空";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SystemConfig> configs = new ArrayList<>();
|
||||||
|
List<String> validationErrors = new ArrayList<>();
|
||||||
|
|
||||||
|
for (BatchSystemConfigRequest.SystemConfigUpdateItem item : request.getConfigs()) {
|
||||||
|
// 验证配置值
|
||||||
|
if (item.getConfigType() != null && item.getConfigValue() != null) {
|
||||||
|
if (!systemConfigService.validateConfigValue(item.getConfigType(), item.getConfigValue())) {
|
||||||
|
validationErrors.add("配置键 " + item.getConfigKey() + " 的值格式不正确");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemConfig config = new SystemConfig();
|
||||||
|
config.setConfigKey(item.getConfigKey());
|
||||||
|
config.setConfigValue(item.getConfigValue());
|
||||||
|
config.setConfigType(item.getConfigType());
|
||||||
|
config.setDescription(item.getDescription());
|
||||||
|
configs.add(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return ResponseEntity.badRequest().body(new Object() {
|
||||||
|
public final boolean success = false;
|
||||||
|
public final String message = "配置验证失败";
|
||||||
|
public final List<String> errors = validationErrors;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = systemConfigService.updateConfigs(configs);
|
||||||
|
final boolean finalSuccess = success;
|
||||||
|
final int finalUpdatedCount = configs.size();
|
||||||
|
|
||||||
|
return ResponseEntity.ok(new Object() {
|
||||||
|
public final boolean success = finalSuccess;
|
||||||
|
public final String message = finalSuccess ? "批量更新成功" : "批量更新失败";
|
||||||
|
public final int updatedCount = finalUpdatedCount;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/key/{configKey}")
|
||||||
|
@Operation(summary = "根据键更新配置值", description = "根据配置键快速更新配置值")
|
||||||
|
public ResponseEntity<Object> updateConfigByKey(
|
||||||
|
@PathVariable String configKey,
|
||||||
|
@RequestBody String configValue) {
|
||||||
|
|
||||||
|
// 先获取现有配置以验证类型
|
||||||
|
SystemConfig existingConfig = systemConfigService.getConfigByKey(configKey);
|
||||||
|
if (existingConfig == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证配置值格式
|
||||||
|
if (!systemConfigService.validateConfigValue(existingConfig.getConfigType(), configValue)) {
|
||||||
|
return ResponseEntity.badRequest().body(new Object() {
|
||||||
|
public final boolean success = false;
|
||||||
|
public final String message = "配置值格式不正确,期望类型: " + existingConfig.getConfigType();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = systemConfigService.updateConfigValueByKey(configKey, configValue);
|
||||||
|
final boolean finalSuccess = success;
|
||||||
|
|
||||||
|
return ResponseEntity.ok(new Object() {
|
||||||
|
public final boolean success = finalSuccess;
|
||||||
|
public final String message = finalSuccess ? "更新成功" : "更新失败";
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.gameplatform.server.model.dto.admin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BatchSystemConfigRequest {
|
||||||
|
private List<SystemConfigUpdateItem> configs;
|
||||||
|
|
||||||
|
public List<SystemConfigUpdateItem> getConfigs() { return configs; }
|
||||||
|
public void setConfigs(List<SystemConfigUpdateItem> configs) { this.configs = configs; }
|
||||||
|
|
||||||
|
public static class SystemConfigUpdateItem {
|
||||||
|
private String configKey;
|
||||||
|
private String configValue;
|
||||||
|
private String configType;
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public String getConfigKey() { return configKey; }
|
||||||
|
public void setConfigKey(String configKey) { this.configKey = configKey; }
|
||||||
|
|
||||||
|
public String getConfigValue() { return configValue; }
|
||||||
|
public void setConfigValue(String configValue) { this.configValue = configValue; }
|
||||||
|
|
||||||
|
public String getConfigType() { return configType; }
|
||||||
|
public void setConfigType(String configType) { this.configType = configType; }
|
||||||
|
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,4 +98,86 @@ public class SystemConfigService {
|
|||||||
public String getQrPathTemplate() {
|
public String getQrPathTemplate() {
|
||||||
return getConfigValue("script.qr_path_template", "/{machineId}/二维码.png");
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
@@ -38,8 +37,6 @@ public class ImageSaveService {
|
|||||||
// 使用信号量限制并发下载数量,防止服务器资源不足
|
// 使用信号量限制并发下载数量,防止服务器资源不足
|
||||||
private final Semaphore downloadSemaphore = new Semaphore(3);
|
private final Semaphore downloadSemaphore = new Semaphore(3);
|
||||||
|
|
||||||
// 时间格式化器
|
|
||||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
|
||||||
|
|
||||||
public ImageSaveService(
|
public ImageSaveService(
|
||||||
ScriptClient scriptClient,
|
ScriptClient scriptClient,
|
||||||
@@ -61,14 +58,11 @@ public class ImageSaveService {
|
|||||||
/**
|
/**
|
||||||
* 下载并保存完成时的所有图片
|
* 下载并保存完成时的所有图片
|
||||||
* @param deviceId 设备ID
|
* @param deviceId 设备ID
|
||||||
* @param taskId 任务ID
|
* @param codeNo 任务编号
|
||||||
* @return 保存后的本地图片URL映射
|
* @return 保存后的本地图片URL映射
|
||||||
*/
|
*/
|
||||||
public Mono<Map<String, String>> downloadAndSaveCompletionImages(String deviceId, Long taskId) {
|
public Mono<Map<String, String>> downloadAndSaveCompletionImages(String deviceId, String codeNo) {
|
||||||
log.info("开始为任务 {} 设备 {} 下载完成图片", taskId, deviceId);
|
log.info("开始为任务 {} 设备 {} 下载完成图片", codeNo, deviceId);
|
||||||
|
|
||||||
String timestamp = LocalDateTime.now().format(TIME_FORMATTER);
|
|
||||||
String taskDir = String.format("task_%d_device_%s_completed_%s", taskId, deviceId, timestamp);
|
|
||||||
|
|
||||||
Map<String, String> imageTypes = Map.of(
|
Map<String, String> imageTypes = Map.of(
|
||||||
"homepage", "首次主页.png",
|
"homepage", "首次主页.png",
|
||||||
@@ -77,23 +71,20 @@ public class ImageSaveService {
|
|||||||
"endReward", "结束赏金.png"
|
"endReward", "结束赏金.png"
|
||||||
);
|
);
|
||||||
|
|
||||||
return downloadAndSaveImages(deviceId, taskDir, imageTypes)
|
return downloadAndSaveImages(deviceId, codeNo, imageTypes)
|
||||||
.doOnSuccess(result -> log.info("任务 {} 设备 {} 完成图片下载保存完成,共保存{}张图片",
|
.doOnSuccess(result -> log.info("任务 {} 设备 {} 完成图片下载保存完成,共保存{}张图片",
|
||||||
taskId, deviceId, result.size()))
|
codeNo, deviceId, result.size()))
|
||||||
.doOnError(error -> log.error("任务 {} 设备 {} 完成图片下载保存失败", taskId, deviceId, error));
|
.doOnError(error -> log.error("任务 {} 设备 {} 完成图片下载保存失败", codeNo, deviceId, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载并保存进行中的任务快照图片
|
* 下载并保存进行中的任务快照图片
|
||||||
* @param deviceId 设备ID
|
* @param deviceId 设备ID
|
||||||
* @param taskId 任务ID
|
* @param codeNo 任务编号
|
||||||
* @return 保存后的本地图片URL映射
|
* @return 保存后的本地图片URL映射
|
||||||
*/
|
*/
|
||||||
public Mono<Map<String, String>> downloadAndSaveProgressImages(String deviceId, Long taskId) {
|
public Mono<Map<String, String>> downloadAndSaveProgressImages(String deviceId, String codeNo) {
|
||||||
log.debug("开始为任务 {} 设备 {} 下载进度图片", taskId, deviceId);
|
log.debug("开始为任务 {} 设备 {} 下载进度图片", codeNo, deviceId);
|
||||||
|
|
||||||
String timestamp = LocalDateTime.now().format(TIME_FORMATTER);
|
|
||||||
String taskDir = String.format("task_%d_device_%s_progress_%s", taskId, deviceId, timestamp);
|
|
||||||
|
|
||||||
// 进行中只保存可能有的图片
|
// 进行中只保存可能有的图片
|
||||||
Map<String, String> imageTypes = Map.of(
|
Map<String, String> imageTypes = Map.of(
|
||||||
@@ -102,21 +93,21 @@ public class ImageSaveService {
|
|||||||
"midReward", "中途赏金.png"
|
"midReward", "中途赏金.png"
|
||||||
);
|
);
|
||||||
|
|
||||||
return downloadAndSaveImages(deviceId, taskDir, imageTypes)
|
return downloadAndSaveImages(deviceId, codeNo, imageTypes)
|
||||||
.doOnSuccess(result -> log.debug("任务 {} 设备 {} 进度图片下载保存完成,共保存{}张图片",
|
.doOnSuccess(result -> log.debug("任务 {} 设备 {} 进度图片下载保存完成,共保存{}张图片",
|
||||||
taskId, deviceId, result.size()))
|
codeNo, deviceId, result.size()))
|
||||||
.doOnError(error -> log.warn("任务 {} 设备 {} 进度图片下载保存失败", taskId, deviceId, error));
|
.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<>();
|
Map<String, String> savedImageUrls = new HashMap<>();
|
||||||
|
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
// 创建任务专用目录
|
// 使用codeNo作为目录名
|
||||||
Path taskDirPath = Paths.get(imageSavePath, taskDir);
|
Path taskDirPath = Paths.get(imageSavePath, codeNo);
|
||||||
Files.createDirectories(taskDirPath);
|
Files.createDirectories(taskDirPath);
|
||||||
return taskDirPath;
|
return taskDirPath;
|
||||||
})
|
})
|
||||||
@@ -181,9 +172,9 @@ public class ImageSaveService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成本地文件名
|
// 直接使用图片类型作为文件名,覆盖同名文件
|
||||||
String extension = getFileExtension(originalName);
|
String extension = getFileExtension(originalName);
|
||||||
String localFileName = imageType + "_" + System.currentTimeMillis() + extension;
|
String localFileName = imageType + extension;
|
||||||
Path localFilePath = taskDirPath.resolve(localFileName);
|
Path localFilePath = taskDirPath.resolve(localFileName);
|
||||||
|
|
||||||
// 保存文件
|
// 保存文件
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class DeviceTaskUpdateService {
|
|||||||
for (LinkTask task : tasks) {
|
for (LinkTask task : tasks) {
|
||||||
try {
|
try {
|
||||||
// 异步下载并保存完成图片
|
// 异步下载并保存完成图片
|
||||||
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getId())
|
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getCodeNo())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
savedImages -> {
|
savedImages -> {
|
||||||
// 保存成功后更新任务
|
// 保存成功后更新任务
|
||||||
@@ -170,7 +170,7 @@ public class DeviceTaskUpdateService {
|
|||||||
for (LinkTask task : tasks) {
|
for (LinkTask task : tasks) {
|
||||||
try {
|
try {
|
||||||
// 异步下载并保存完成图片
|
// 异步下载并保存完成图片
|
||||||
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getId())
|
imageSaveService.downloadAndSaveCompletionImages(deviceId, task.getCodeNo())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
savedImages -> {
|
savedImages -> {
|
||||||
// 保存成功后更新任务
|
// 保存成功后更新任务
|
||||||
@@ -218,36 +218,36 @@ public class DeviceTaskUpdateService {
|
|||||||
/**
|
/**
|
||||||
* 定期保存进行中任务的图片快照
|
* 定期保存进行中任务的图片快照
|
||||||
* @param deviceId 设备ID
|
* @param deviceId 设备ID
|
||||||
* @param taskId 任务ID
|
* @param codeNo 任务编号
|
||||||
*/
|
*/
|
||||||
public void saveProgressImagesForTask(String deviceId, Long taskId) {
|
public void saveProgressImagesForTask(String deviceId, String codeNo) {
|
||||||
log.debug("开始为任务 {} 设备 {} 保存进度图片", taskId, deviceId);
|
log.debug("开始为任务 {} 设备 {} 保存进度图片", codeNo, deviceId);
|
||||||
|
|
||||||
imageSaveService.downloadAndSaveProgressImages(deviceId, taskId)
|
imageSaveService.downloadAndSaveProgressImages(deviceId, codeNo)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
savedImages -> {
|
savedImages -> {
|
||||||
if (!savedImages.isEmpty()) {
|
if (!savedImages.isEmpty()) {
|
||||||
log.info("任务 {} 设备 {} 进度图片保存成功,共保存{}张图片",
|
log.info("任务 {} 设备 {} 进度图片保存成功,共保存{}张图片",
|
||||||
taskId, deviceId, savedImages.size());
|
codeNo, deviceId, savedImages.size());
|
||||||
// 可选:更新任务记录中的进度图片信息
|
// 可选:更新任务记录中的进度图片信息
|
||||||
updateTaskProgressImages(taskId, savedImages);
|
updateTaskProgressImagesByCodeNo(codeNo, savedImages);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error -> log.warn("任务 {} 设备 {} 进度图片保存失败: {}",
|
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 {
|
try {
|
||||||
// 这里可以选择是否要将进度图片也保存到数据库中
|
// 这里可以选择是否要将进度图片也保存到数据库中
|
||||||
// 目前只记录日志,不修改数据库记录
|
// 目前只记录日志,不修改数据库记录
|
||||||
log.debug("任务 {} 进度图片已保存到本地: {}", taskId, savedImages.keySet());
|
log.debug("任务 {} 进度图片已保存到本地: {}", codeNo, savedImages.keySet());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("更新任务 {} 进度图片记录失败", taskId, e);
|
log.warn("更新任务 {} 进度图片记录失败", codeNo, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -445,7 +445,7 @@ public class LinkStatusService {
|
|||||||
log.info("空闲设备检查完成: 总设备数={}, 空闲设备数={}, 空闲设备列表={}",
|
log.info("空闲设备检查完成: 总设备数={}, 空闲设备数={}, 空闲设备列表={}",
|
||||||
deviceStatus.getTotalDevices(), deviceStatus.getAvailableCount(), deviceStatus.getAvailableDevices());
|
deviceStatus.getTotalDevices(), deviceStatus.getAvailableCount(), deviceStatus.getAvailableDevices());
|
||||||
|
|
||||||
// 7. 选择一个空闲设备
|
// 7. 选择一个空闲设备 TODO
|
||||||
// String selectedDevice = deviceStatus.getAvailableDevices().get(0); // 选择第一个空闲设备
|
// String selectedDevice = deviceStatus.getAvailableDevices().get(0); // 选择第一个空闲设备
|
||||||
String selectedDevice = "cc2";
|
String selectedDevice = "cc2";
|
||||||
log.info("从空闲设备列表中选择设备: {}", selectedDevice);
|
log.info("从空闲设备列表中选择设备: {}", selectedDevice);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class ImageSaveScheduleTask {
|
|||||||
Thread.sleep(delaySeconds * 1000L);
|
Thread.sleep(delaySeconds * 1000L);
|
||||||
|
|
||||||
// 保存任务的进度图片
|
// 保存任务的进度图片
|
||||||
deviceTaskUpdateService.saveProgressImagesForTask(task.getMachineId(), task.getId());
|
deviceTaskUpdateService.saveProgressImagesForTask(task.getMachineId(), task.getCodeNo());
|
||||||
|
|
||||||
log.debug("任务 {} (设备: {}) 进度图片保存请求已提交", task.getId(), task.getMachineId());
|
log.debug("任务 {} (设备: {}) 进度图片保存请求已提交", task.getId(), task.getMachineId());
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ public class ImageSaveScheduleTask {
|
|||||||
imageTaskExecutor.execute(() -> {
|
imageTaskExecutor.execute(() -> {
|
||||||
for (LinkTask task : deviceTasks) {
|
for (LinkTask task : deviceTasks) {
|
||||||
try {
|
try {
|
||||||
deviceTaskUpdateService.saveProgressImagesForTask(deviceId, task.getId());
|
deviceTaskUpdateService.saveProgressImagesForTask(deviceId, task.getCodeNo());
|
||||||
Thread.sleep(2000); // 每个任务间延迟2秒
|
Thread.sleep(2000); // 每个任务间延迟2秒
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
|||||||
Reference in New Issue
Block a user