feat(device): 添加默认配置管理功能

- 新增 DefaultConfigClient接口,用于与设备微服务进行默认配置相关的操作
- 实现 DefaultConfigIntegrationService 类,提供默认配置管理的高阶服务- 添加批量配置请求构建器 BatchDefaultConfigRequestBuilder,简化批量操作
- 新增 DefaultConfigIntegrationExample 示例类,演示默认配置管理的使用方法
- 更新 CLAUDE.md 文档,增加默认配置管理的详细使用说明和示例代码
This commit is contained in:
2025-09-05 14:49:06 +08:00
parent 933a1209e7
commit 933818d458
9 changed files with 1046 additions and 0 deletions

View File

@@ -227,10 +227,12 @@ fallbackService.clearAllFallbackCache("zt-scenic");
#### Feign Clients
- **DeviceV2Client**: Main device operations (CRUD, filtering, listing)
- **DeviceConfigV2Client**: Device configuration management
- **DefaultConfigClient**: Default configuration management
#### Services
- **DeviceIntegrationService**: High-level device operations (with automatic fallback)
- **DeviceConfigIntegrationService**: Device configuration management (with automatic fallback)
- **DefaultConfigIntegrationService**: Default configuration management (with automatic fallback)
#### Configuration
```yaml
@@ -377,6 +379,355 @@ fallbackService.clearFallbackCache("zt-device", "device:1001");
fallbackService.clearAllFallbackCache("zt-device");
```
## Default Configuration Management (ZT-Device Microservice)
### Key Components
#### Default Configuration API
The zt-device microservice provides a comprehensive default configuration management API at `/api/device/config/v2/defaults` that allows:
- Creating and managing default configuration templates
- Batch operations with conflict detection
- Configuration type validation and enforcement
- Usage tracking and analytics
#### Feign Client
- **DefaultConfigClient**: Default configuration management operations
#### Service
- **DefaultConfigIntegrationService**: High-level default configuration operations (with automatic fallback for queries)
### Usage Examples
#### Basic Default Configuration Operations (with Automatic Fallback)
```java
@Autowired
private DefaultConfigIntegrationService defaultConfigService;
// Get default configuration list (automatically falls back to cache on failure)
PageResponse<DefaultConfigResponse> configList = defaultConfigService.listDefaultConfigs(1, 10);
log.info("Default configs: total={}, current page={}",
configList.getData().getTotal(), configList.getData().getList().size());
// Get specific default configuration (automatically falls back to cache on failure)
DefaultConfigResponse config = defaultConfigService.getDefaultConfig("resolution");
if (config != null) {
log.info("Default resolution: {}", config.getConfigValue());
}
// Create new default configuration (direct operation, fails immediately on error)
DefaultConfigRequest newConfig = new DefaultConfigRequest();
newConfig.setConfigKey("bitrate");
newConfig.setConfigValue("2000000");
newConfig.setConfigType("int");
newConfig.setDescription("Default video bitrate in bps");
boolean created = defaultConfigService.createDefaultConfig(newConfig);
// Update default configuration with conflict detection (direct operation)
Map<String, Object> updates = new HashMap<>();
updates.put("configValue", "4000000");
updates.put("description", "Updated default bitrate - high quality");
DefaultConfigConflict conflict = defaultConfigService.updateDefaultConfig("bitrate", updates);
if (conflict != null) {
log.warn("Configuration update conflict detected:");
log.warn(" Key: {}, Type: {}, Affected devices: {}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
log.warn(" Current type: {}, Proposed type: {}",
conflict.getCurrentType(), conflict.getProposedType());
// Handle conflict - show affected device IDs
if (conflict.getConflictDevices() != null) {
log.warn(" Conflicted device IDs: {}", conflict.getConflictDevices());
}
} else {
log.info("Configuration updated successfully without conflicts");
}
// Delete default configuration (direct operation, fails immediately on error)
boolean deleted = defaultConfigService.deleteDefaultConfig("bitrate");
```
#### Batch Default Configuration Operations
```java
// Using Builder Pattern for batch operations
BatchDefaultConfigRequest batchRequest = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 30, "H265") // Add video configuration group
.addNetworkConfig("192.168.1.100", 554, "RTSP") // Add network configuration group
.addConfig("recording_enabled", "true", "bool", "Enable recording by default")
.addConfig("storage_retention", "30", "int", "Storage retention in days")
.addConfig("quality_profile", "high", "string", "Default video quality profile")
.build();
// Execute batch update (direct operation, no fallback)
BatchDefaultConfigResponse result = defaultConfigService.batchUpdateDefaultConfigs(batchRequest);
// Process batch results
log.info("Batch operation completed: {} success, {} failed", result.getSuccess(), result.getFailed());
// Handle conflicts
if (result.getConflicts() != null && !result.getConflicts().isEmpty()) {
log.warn("Configuration conflicts detected:");
for (DefaultConfigConflict conflict : result.getConflicts()) {
log.warn(" Key: {}, Type: {}, Devices affected: {}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
}
}
// Review processed items
if (result.getProcessedItems() != null) {
result.getProcessedItems().forEach(item -> {
if ("success".equals(item.getStatus())) {
log.info("✅ {} {} successfully (type: {})",
item.getConfigKey(), item.getAction(), item.getFinalType());
} else {
log.warn("❌ {} failed: {}", item.getConfigKey(), item.getMessage());
}
});
}
// Handle errors
if (result.getErrors() != null && !result.getErrors().isEmpty()) {
result.getErrors().forEach(error -> log.error("Batch operation error: {}", error));
}
```
#### Device Type Specific Default Configuration Patterns
```java
// Create IPC camera default configurations
BatchDefaultConfigRequest ipcDefaults = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 25, "H264") // Standard HD resolution
.addConfig("night_vision", "true", "bool", "Enable night vision")
.addConfig("motion_detection", "true", "bool", "Enable motion detection")
.addConfig("stream_profile", "main", "string", "Default stream profile")
.addConfig("ptz_enabled", "false", "bool", "PTZ control enabled")
.addConfig("audio_enabled", "true", "bool", "Audio recording enabled")
.build();
// Create NVR default configurations
BatchDefaultConfigRequest nvrDefaults = defaultConfigService.createBatchConfigBuilder()
.addConfig("max_channels", "16", "int", "Maximum supported channels")
.addConfig("storage_mode", "continuous", "string", "Recording storage mode")
.addConfig("backup_enabled", "true", "bool", "Enable automatic backup")
.addConfig("raid_level", "5", "int", "Default RAID level")
.addConfig("disk_quota", "80", "int", "Disk usage quota percentage")
.build();
// Apply device-type-specific defaults
BatchDefaultConfigResponse ipcResult = defaultConfigService.batchUpdateDefaultConfigs(ipcDefaults);
BatchDefaultConfigResponse nvrResult = defaultConfigService.batchUpdateDefaultConfigs(nvrDefaults);
log.info("IPC defaults: {} success, {} failed", ipcResult.getSuccess(), ipcResult.getFailed());
log.info("NVR defaults: {} success, {} failed", nvrResult.getSuccess(), nvrResult.getFailed());
```
#### Configuration Validation and Management Patterns
```java
// Validate system configuration completeness
PageResponse<DefaultConfigResponse> allDefaults = defaultConfigService.listDefaultConfigs(1, 100);
// Check for required default configurations
String[] requiredDefaults = {
"resolution", "frameRate", "codec", "bitrate",
"protocol", "port", "username", "password"
};
Map<String, Boolean> configStatus = new HashMap<>();
for (String required : requiredDefaults) {
boolean exists = allDefaults.getData().getList().stream()
.anyMatch(config -> required.equals(config.getConfigKey()));
configStatus.put(required, exists);
if (!exists) {
log.warn("Missing required default configuration: {}", required);
}
}
// Generate configuration completeness report
long totalConfigs = allDefaults.getData().getTotal();
long completeConfigs = configStatus.values().stream().mapToLong(exists -> exists ? 1 : 0).sum();
double completeness = (double) completeConfigs / requiredDefaults.length * 100;
log.info("Configuration completeness: {:.1f}% ({}/{} required configs present)",
completeness, completeConfigs, requiredDefaults.length);
log.info("Total default configurations in system: {}", totalConfigs);
// Analyze configuration type distribution
Map<String, Long> typeDistribution = allDefaults.getData().getList().stream()
.collect(Collectors.groupingBy(
DefaultConfigResponse::getConfigType,
Collectors.counting()
));
log.info("Configuration type distribution: {}", typeDistribution);
// Find most and least used configurations
DefaultConfigResponse mostUsed = allDefaults.getData().getList().stream()
.max(Comparator.comparing(DefaultConfigResponse::getUsageCount))
.orElse(null);
DefaultConfigResponse leastUsed = allDefaults.getData().getList().stream()
.min(Comparator.comparing(DefaultConfigResponse::getUsageCount))
.orElse(null);
if (mostUsed != null) {
log.info("Most used default config: {} (used {} times)",
mostUsed.getConfigKey(), mostUsed.getUsageCount());
}
if (leastUsed != null) {
log.info("Least used default config: {} (used {} times)",
leastUsed.getConfigKey(), leastUsed.getUsageCount());
}
```
#### Advanced Batch Configuration with Error Handling
```java
// Complex batch operation with comprehensive error handling
try {
BatchDefaultConfigRequest complexBatch = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("3840x2160", 60, "H265") // 4K configuration
.addConfig("hdr_enabled", "true", "bool", "HDR video support")
.addConfig("ai_analysis", "enabled", "string", "AI analysis features")
.addConfig("edge_processing", "true", "bool", "Edge computing enabled")
.addConfig("cloud_sync", "auto", "string", "Cloud synchronization mode")
.build();
BatchDefaultConfigResponse result = defaultConfigService.batchUpdateDefaultConfigs(complexBatch);
// Detailed result analysis
if (result.getSuccess() == complexBatch.getConfigs().size()) {
log.info("✅ All {} configurations processed successfully", result.getSuccess());
} else if (result.getSuccess() > 0) {
log.warn("⚠️ Partial success: {} succeeded, {} failed", result.getSuccess(), result.getFailed());
// Analyze what succeeded vs failed
if (result.getProcessedItems() != null) {
Map<String, Long> statusCounts = result.getProcessedItems().stream()
.collect(Collectors.groupingBy(
ProcessedConfigItem::getStatus,
Collectors.counting()
));
log.info("Processing breakdown: {}", statusCounts);
}
} else {
log.error("❌ All configurations failed to process");
}
// Handle different types of conflicts
if (result.getConflicts() != null) {
Map<String, List<DefaultConfigConflict>> conflictsByType = result.getConflicts().stream()
.collect(Collectors.groupingBy(DefaultConfigConflict::getConflictType));
conflictsByType.forEach((conflictType, conflicts) -> {
log.warn("Conflict type '{}' affects {} configurations:", conflictType, conflicts.size());
conflicts.forEach(conflict ->
log.warn(" {} affects {} devices", conflict.getConfigKey(), conflict.getDeviceCount())
);
});
}
} catch (Exception e) {
log.error("Batch default configuration operation failed", e);
// Implement retry logic or fallback behavior as needed
}
```
#### Fallback Cache Management for Default Configurations
```java
@Autowired
private IntegrationFallbackService fallbackService;
// Check fallback cache status for default configurations
boolean hasDefaultListCache = fallbackService.hasFallbackCache("zt-device", "defaults:list:1:10");
boolean hasSpecificConfigCache = fallbackService.hasFallbackCache("zt-device", "defaults:config:resolution");
// Get cache statistics
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats("zt-device");
log.info("Default config fallback cache: {} items, TTL: {} days",
stats.getTotalCacheCount(), stats.getFallbackTtlDays());
// Clear specific default configuration cache
fallbackService.clearFallbackCache("zt-device", "defaults:config:resolution");
// Clear all default configuration caches
fallbackService.clearAllFallbackCache("zt-device");
```
### Default Configuration Types and Common Keys
#### Video Configuration Defaults
- `resolution`: Video resolution ("1920x1080", "3840x2160", "1280x720")
- `frameRate`: Video frame rate (integer: 15, 25, 30, 60)
- `codec`: Video codec ("H264", "H265", "MJPEG")
- `bitrate`: Video bitrate in bps (integer)
- `quality`: Video quality level ("low", "medium", "high", "ultra")
#### Network Configuration Defaults
- `protocol`: Network protocol ("RTSP", "HTTP", "ONVIF")
- `port`: Network port (integer: 554, 80, 8080)
- `timeout`: Connection timeout in seconds (integer)
- `retry_count`: Maximum retry attempts (integer)
#### Authentication Defaults
- `username`: Default username (string)
- `password`: Default password (string)
- `auth_method`: Authentication method ("basic", "digest", "none")
#### Storage and Recording Defaults
- `storage_path`: Default storage path (string)
- `recording_enabled`: Enable recording (boolean)
- `retention_days`: Storage retention period (integer)
- `max_file_size`: Maximum file size in MB (integer)
#### Feature Control Defaults
- `motion_detection`: Enable motion detection (boolean)
- `night_vision`: Enable night vision (boolean)
- `audio_enabled`: Enable audio recording (boolean)
- `ptz_enabled`: Enable PTZ control (boolean)
- `ai_analysis`: AI analysis features ("enabled", "disabled", "auto")
### Error Handling and Responses
#### HTTP Status Codes
- **200 OK**: Configuration retrieved/updated successfully
- **201 Created**: New configuration created successfully
- **202 Accepted**: Operation completed with conflicts (check response for details)
- **400 Bad Request**: Invalid request parameters or validation errors
- **404 Not Found**: Configuration key not found
- **409 Conflict**: Configuration conflicts prevent operation (includes conflict details)
#### Conflict Resolution
When updating default configurations that are actively used by devices, the API performs conflict detection:
1. **Type Conflicts**: Changing configuration type when devices use different types
2. **Value Validation**: Ensuring new values are compatible with existing device configurations
3. **Dependency Conflicts**: Checking for configuration dependencies and relationships
The response includes detailed conflict information to help resolve issues:
```java
// Example conflict handling
DefaultConfigConflict conflict = updateResult;
if (conflict != null) {
switch (conflict.getConflictType()) {
case "TYPE_MISMATCH":
log.warn("Type conflict: {} devices using '{}' type, proposed '{}'type",
conflict.getDeviceCount(), conflict.getCurrentType(), conflict.getProposedType());
// Handle type conversion or device updates
break;
case "VALUE_RANGE":
log.warn("Value range conflict for key '{}' affects {} devices",
conflict.getConfigKey(), conflict.getDeviceCount());
// Handle value range adjustments
break;
case "DEPENDENCY":
log.warn("Dependency conflict detected for '{}'", conflict.getConfigKey());
// Handle dependency resolution
break;
}
}
```
### Enhanced Batch Configuration API
Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules.
@@ -432,6 +783,23 @@ Each processed item includes:
- **IPC**: IP Camera devices for video monitoring
- **CUSTOM**: Custom device types for sensors, controllers, etc.
### Testing Default Configuration Integration
```bash
# Run default configuration integration tests
mvn test -Dtest=DefaultConfigIntegrationServiceTest
# Run all device integration tests (including default configs)
mvn test -Dtest="com.ycwl.basic.integration.device.*Test"
# Enable example runner in application-dev.yml
integration:
device:
example:
default-config:
enabled: true
```
### Common Configuration Keys
- `ip_address`: Device IP address
- `resolution`: Video resolution (e.g., "1920x1080", "3840x2160")

View File

@@ -0,0 +1,54 @@
package com.ycwl.basic.integration.device.client;
import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.defaults.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 默认配置管理 Feign 客户端
*/
@FeignClient(name = "zt-device", contextId = "device-default-config", path = "/api/device/config/v2/defaults")
public interface DefaultConfigClient {
/**
* 获取默认配置列表
*/
@GetMapping
PageResponse<DefaultConfigResponse> listDefaultConfigs(@RequestParam(value = "page", defaultValue = "1") int page,
@RequestParam(value = "pageSize", defaultValue = "10") int pageSize);
/**
* 根据配置键获取默认配置
*/
@GetMapping("/{configKey}")
CommonResponse<DefaultConfigResponse> getDefaultConfig(@PathVariable("configKey") String configKey);
/**
* 创建默认配置
*/
@PostMapping
CommonResponse<String> createDefaultConfig(@RequestBody DefaultConfigRequest request);
/**
* 更新默认配置
*/
@PutMapping("/{configKey}")
CommonResponse<Map<String, Object>> updateDefaultConfig(@PathVariable("configKey") String configKey,
@RequestBody Map<String, Object> updates);
/**
* 删除默认配置
*/
@DeleteMapping("/{configKey}")
CommonResponse<String> deleteDefaultConfig(@PathVariable("configKey") String configKey);
/**
* 批量更新默认配置
*/
@PostMapping("/batch")
CommonResponse<BatchDefaultConfigResponse> batchUpdateDefaultConfigs(@RequestBody BatchDefaultConfigRequest request);
}

View File

@@ -0,0 +1,18 @@
package com.ycwl.basic.integration.device.dto.defaults;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* 批量默认配置操作请求模型
*/
@Data
public class BatchDefaultConfigRequest {
@NotEmpty(message = "配置列表不能为空")
@Valid
private List<DefaultConfigRequest> configs;
}

View File

@@ -0,0 +1,23 @@
package com.ycwl.basic.integration.device.dto.defaults;
import com.ycwl.basic.integration.device.dto.config.ProcessedConfigItem;
import lombok.Data;
import java.util.List;
/**
* 批量默认配置操作响应模型
*/
@Data
public class BatchDefaultConfigResponse {
private Integer success;
private Integer failed;
private List<DefaultConfigConflict> conflicts;
private List<ProcessedConfigItem> processedItems;
private List<String> errors;
}

View File

@@ -0,0 +1,24 @@
package com.ycwl.basic.integration.device.dto.defaults;
import lombok.Data;
import java.util.List;
/**
* 冲突信息模型
*/
@Data
public class DefaultConfigConflict {
private String configKey;
private String conflictType;
private Integer deviceCount;
private String currentType;
private String proposedType;
private List<Long> conflictDevices;
}

View File

@@ -0,0 +1,26 @@
package com.ycwl.basic.integration.device.dto.defaults;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 默认配置请求模型
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DefaultConfigRequest {
@NotBlank(message = "配置键不能为空")
private String configKey;
@NotBlank(message = "配置值不能为空")
private String configValue;
@NotBlank(message = "配置类型不能为空")
private String configType;
@NotBlank(message = "配置描述不能为空")
private String description;
}

View File

@@ -0,0 +1,28 @@
package com.ycwl.basic.integration.device.dto.defaults;
import lombok.Data;
/**
* 默认配置响应模型
*/
@Data
public class DefaultConfigResponse {
private String id;
private String configKey;
private String configValue;
private String configType;
private String description;
private Integer isActive;
private String createTime;
private String updateTime;
private Integer usageCount;
}

View File

@@ -0,0 +1,275 @@
package com.ycwl.basic.integration.device.example;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.defaults.*;
import com.ycwl.basic.integration.device.service.DefaultConfigIntegrationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 默认配置集成服务使用示例
*
* 通过在 application.yml 中设置 integration.device.example.default-config.enabled=true 来启用
*/
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(name = "integration.device.example.default-config.enabled", havingValue = "true")
public class DefaultConfigIntegrationExample implements CommandLineRunner {
private final DefaultConfigIntegrationService defaultConfigService;
@Override
public void run(String... args) throws Exception {
log.info("=== 默认配置集成服务使用示例 ===");
try {
// 1. 基础查询操作示例(支持自动 Fallback)
basicQueryExamples();
// 2. 配置管理操作示例(直接操作)
configManagementExamples();
// 3. 批量操作示例
batchOperationExamples();
// 4. 高级使用模式示例
advancedUsageExamples();
} catch (Exception e) {
log.error("默认配置集成示例执行失败", e);
}
}
/**
* 基础查询操作示例(支持自动 Fallback)
*/
private void basicQueryExamples() {
log.info("--- 基础查询操作示例(支持自动 Fallback)---");
try {
// 获取默认配置列表(自动缓存,服务不可用时返回缓存数据)
PageResponse<DefaultConfigResponse> configList = defaultConfigService.listDefaultConfigs(1, 10);
log.info("默认配置列表: 总数={}, 当前页配置数={}",
configList.getData().getTotal(), configList.getData().getList().size());
// 显示配置详情
for (DefaultConfigResponse config : configList.getData().getList()) {
log.info("配置详情: key={}, value={}, type={}, description={}",
config.getConfigKey(), config.getConfigValue(),
config.getConfigType(), config.getDescription());
// 获取单个配置详情(自动缓存,服务不可用时返回缓存数据)
DefaultConfigResponse detailConfig = defaultConfigService.getDefaultConfig(config.getConfigKey());
if (detailConfig != null) {
log.info("配置详情获取成功: usageCount={}, isActive={}",
detailConfig.getUsageCount(), detailConfig.getIsActive());
}
break; // 只展示第一个
}
} catch (Exception e) {
log.error("基础查询操作失败", e);
}
}
/**
* 配置管理操作示例(直接操作)
*/
private void configManagementExamples() {
log.info("--- 配置管理操作示例(直接操作)---");
String testConfigKey = "example_test_config";
try {
// 1. 创建默认配置(直接操作,失败时立即报错)
DefaultConfigRequest createRequest = new DefaultConfigRequest();
createRequest.setConfigKey(testConfigKey);
createRequest.setConfigValue("1920x1080");
createRequest.setConfigType("string");
createRequest.setDescription("示例测试配置 - 默认分辨率");
boolean createResult = defaultConfigService.createDefaultConfig(createRequest);
log.info("创建默认配置结果: {}", createResult ? "成功" : "失败");
// 2. 更新默认配置(直接操作,可能返回冲突信息)
Map<String, Object> updates = new HashMap<>();
updates.put("configValue", "3840x2160");
updates.put("description", "更新后的默认分辨率 - 4K");
DefaultConfigConflict conflict = defaultConfigService.updateDefaultConfig(testConfigKey, updates);
if (conflict != null) {
log.warn("更新配置存在冲突: configKey={}, conflictType={}, deviceCount={}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
} else {
log.info("配置更新成功,无冲突");
}
// 3. 验证更新结果
DefaultConfigResponse updatedConfig = defaultConfigService.getDefaultConfig(testConfigKey);
if (updatedConfig != null) {
log.info("更新后配置值: {}", updatedConfig.getConfigValue());
}
// 4. 删除测试配置(直接操作)
boolean deleteResult = defaultConfigService.deleteDefaultConfig(testConfigKey);
log.info("删除默认配置结果: {}", deleteResult ? "成功" : "失败");
} catch (Exception e) {
log.error("配置管理操作失败", e);
}
}
/**
* 批量操作示例
*/
private void batchOperationExamples() {
log.info("--- 批量操作示例 ---");
try {
// 1. 使用构建器模式创建批量配置
BatchDefaultConfigRequest batchRequest = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 30, "H264") // 添加视频配置组
.addNetworkConfig("192.168.1.100", 554, "RTSP") // 添加网络配置组
.addConfig("recording_enabled", "true", "bool", "是否启用录制")
.addConfig("storage_path", "/data/recordings", "string", "录制存储路径")
.addConfig("max_file_size", "1024", "int", "最大文件大小(MB)")
.build();
log.info("准备批量创建 {} 个默认配置", batchRequest.getConfigs().size());
// 2. 执行批量更新(直接操作)
BatchDefaultConfigResponse batchResult = defaultConfigService.batchUpdateDefaultConfigs(batchRequest);
// 3. 处理批量结果
log.info("批量操作结果: 成功={}, 失败={}", batchResult.getSuccess(), batchResult.getFailed());
if (batchResult.getConflicts() != null && !batchResult.getConflicts().isEmpty()) {
log.warn("发现 {} 个配置冲突:", batchResult.getConflicts().size());
for (DefaultConfigConflict conflict : batchResult.getConflicts()) {
log.warn("冲突配置: key={}, type={}, deviceCount={}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
}
}
if (batchResult.getProcessedItems() != null) {
log.info("处理详情:");
batchResult.getProcessedItems().forEach(item ->
log.info(" 配置 {}: status={}, action={}, finalType={}",
item.getConfigKey(), item.getStatus(), item.getAction(), item.getFinalType())
);
}
if (batchResult.getErrors() != null && !batchResult.getErrors().isEmpty()) {
log.error("批量操作错误:");
batchResult.getErrors().forEach(error -> log.error(" {}", error));
}
} catch (Exception e) {
log.error("批量操作失败", e);
}
}
/**
* 高级使用模式示例
*/
private void advancedUsageExamples() {
log.info("--- 高级使用模式示例 ---");
try {
// 1. 设备类型特定的默认配置模式
createDeviceTypeSpecificConfigs();
// 2. 配置验证和完整性检查模式
validateConfigCompleteness();
// 3. 配置迁移和批量更新模式
configMigrationPattern();
} catch (Exception e) {
log.error("高级使用模式示例失败", e);
}
}
/**
* 创建设备类型特定的默认配置
*/
private void createDeviceTypeSpecificConfigs() {
log.info("创建设备类型特定的默认配置...");
// IPC摄像头默认配置
BatchDefaultConfigRequest ipcDefaults = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 25, "H264")
.addConfig("night_vision", "true", "bool", "夜视功能")
.addConfig("motion_detection", "true", "bool", "移动检测")
.addConfig("stream_profile", "main", "string", "码流类型")
.build();
// NVR设备默认配置
BatchDefaultConfigRequest nvrDefaults = defaultConfigService.createBatchConfigBuilder()
.addConfig("max_channels", "16", "int", "最大通道数")
.addConfig("storage_mode", "continuous", "string", "存储模式")
.addConfig("backup_enabled", "true", "bool", "备份启用")
.build();
log.info("IPC默认配置项数: {}, NVR默认配置项数: {}",
ipcDefaults.getConfigs().size(), nvrDefaults.getConfigs().size());
}
/**
* 配置验证和完整性检查
*/
private void validateConfigCompleteness() {
log.info("验证配置完整性...");
// 获取所有默认配置
PageResponse<DefaultConfigResponse> allConfigs = defaultConfigService.listDefaultConfigs(1, 100);
// 检查必需的基础配置是否存在
String[] requiredConfigs = {"resolution", "frameRate", "codec", "protocol"};
for (String requiredConfig : requiredConfigs) {
boolean exists = allConfigs.getData().getList().stream()
.anyMatch(config -> requiredConfig.equals(config.getConfigKey()));
log.info("必需配置 {} 存在: {}", requiredConfig, exists ? "" : "");
}
// 统计配置类型分布
Map<String, Long> typeDistribution = new HashMap<>();
allConfigs.getData().getList().forEach(config ->
typeDistribution.merge(config.getConfigType(), 1L, Long::sum)
);
log.info("配置类型分布: {}", typeDistribution);
}
/**
* 配置迁移和批量更新模式
*/
private void configMigrationPattern() {
log.info("配置迁移模式示例...");
try {
// 1. 获取需要升级的配置
PageResponse<DefaultConfigResponse> oldConfigs = defaultConfigService.listDefaultConfigs(1, 50);
// 2. 创建升级配置批次
BatchDefaultConfigRequest upgradeRequest = defaultConfigService.createBatchConfigBuilder()
.addConfig("api_version", "v2", "string", "API版本")
.addConfig("security_mode", "enhanced", "string", "安全模式")
.build();
// 3. 执行批量升级
BatchDefaultConfigResponse upgradeResult = defaultConfigService.batchUpdateDefaultConfigs(upgradeRequest);
log.info("配置升级结果: 成功={}, 失败={}", upgradeResult.getSuccess(), upgradeResult.getFailed());
} catch (Exception e) {
log.warn("配置迁移示例执行失败(这是正常的,因为是示例代码)", e);
}
}
}

View File

@@ -0,0 +1,230 @@
package com.ycwl.basic.integration.device.service;
import com.ycwl.basic.integration.common.exception.IntegrationException;
import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
import com.ycwl.basic.integration.device.client.DefaultConfigClient;
import com.ycwl.basic.integration.device.dto.defaults.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 默认配置集成服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DefaultConfigIntegrationService {
private final DefaultConfigClient defaultConfigClient;
private final IntegrationFallbackService fallbackService;
private static final String SERVICE_NAME = "zt-device";
/**
* 获取默认配置列表(支持 Fallback)
*/
public PageResponse<DefaultConfigResponse> listDefaultConfigs(int page, int pageSize) {
return fallbackService.executeWithFallback(
SERVICE_NAME,
"defaults:list:" + page + ":" + pageSize,
() -> {
PageResponse<DefaultConfigResponse> response = defaultConfigClient.listDefaultConfigs(page, pageSize);
return handlePageResponse(response, "获取默认配置列表失败");
},
PageResponse.class
);
}
/**
* 根据配置键获取默认配置(支持 Fallback)
*/
public DefaultConfigResponse getDefaultConfig(String configKey) {
return fallbackService.executeWithFallback(
SERVICE_NAME,
"defaults:config:" + configKey,
() -> {
log.info("获取默认配置, configKey: {}", configKey);
CommonResponse<DefaultConfigResponse> response = defaultConfigClient.getDefaultConfig(configKey);
return handleResponse(response, "获取默认配置失败");
},
DefaultConfigResponse.class
);
}
/**
* 创建默认配置(直接操作,不支持 Fallback)
*/
public boolean createDefaultConfig(DefaultConfigRequest request) {
log.info("创建默认配置, configKey: {}", request.getConfigKey());
CommonResponse<String> response = defaultConfigClient.createDefaultConfig(request);
String result = handleResponse(response, "创建默认配置失败");
return result != null;
}
/**
* 更新默认配置(直接操作,不支持 Fallback)
*/
public DefaultConfigConflict updateDefaultConfig(String configKey, Map<String, Object> updates) {
log.info("更新默认配置, configKey: {}, updates: {}", configKey, updates);
CommonResponse<Map<String, Object>> response = defaultConfigClient.updateDefaultConfig(configKey, updates);
Map<String, Object> result = handleResponse(response, "更新默认配置失败");
// 检查是否有冲突信息
if (result != null && result.containsKey("conflict")) {
Object conflictObj = result.get("conflict");
if (conflictObj instanceof Map) {
// 将Map转换为DefaultConfigConflict对象
return mapToDefaultConfigConflict((Map<String, Object>) conflictObj);
}
}
return null;
}
/**
* 删除默认配置(直接操作,不支持 Fallback)
*/
public boolean deleteDefaultConfig(String configKey) {
log.info("删除默认配置, configKey: {}", configKey);
CommonResponse<String> response = defaultConfigClient.deleteDefaultConfig(configKey);
String result = handleResponse(response, "删除默认配置失败");
return result != null;
}
/**
* 批量更新默认配置(直接操作,不支持 Fallback)
*/
public BatchDefaultConfigResponse batchUpdateDefaultConfigs(BatchDefaultConfigRequest request) {
log.info("批量更新默认配置, configs count: {}", request.getConfigs().size());
CommonResponse<BatchDefaultConfigResponse> response = defaultConfigClient.batchUpdateDefaultConfigs(request);
return handleResponse(response, "批量更新默认配置失败");
}
/**
* 创建批量配置请求构建器
*/
public BatchDefaultConfigRequestBuilder createBatchConfigBuilder() {
return new BatchDefaultConfigRequestBuilder();
}
/**
* 批量配置请求构建器
*/
public static class BatchDefaultConfigRequestBuilder {
private final BatchDefaultConfigRequest request = new BatchDefaultConfigRequest();
public BatchDefaultConfigRequestBuilder() {
request.setConfigs(new ArrayList<>());
}
/**
* 添加配置项
*/
public BatchDefaultConfigRequestBuilder addConfig(String configKey, String configValue,
String configType, String description) {
DefaultConfigRequest item = new DefaultConfigRequest();
item.setConfigKey(configKey);
item.setConfigValue(configValue);
item.setConfigType(configType);
item.setDescription(description);
request.getConfigs().add(item);
return this;
}
/**
* 添加视频配置
*/
public BatchDefaultConfigRequestBuilder addVideoConfig(String resolution, Integer frameRate, String codec) {
addConfig("resolution", resolution, "string", "视频分辨率");
addConfig("frameRate", frameRate.toString(), "int", "视频帧率");
addConfig("codec", codec, "string", "视频编码格式");
return this;
}
/**
* 添加网络配置
*/
public BatchDefaultConfigRequestBuilder addNetworkConfig(String ipAddress, Integer port, String protocol) {
addConfig("ipAddress", ipAddress, "string", "IP地址");
addConfig("port", port.toString(), "int", "端口号");
addConfig("protocol", protocol, "string", "网络协议");
return this;
}
/**
* 构建请求对象
*/
public BatchDefaultConfigRequest build() {
return request;
}
}
/**
* 将Map转换为DefaultConfigConflict对象
*/
@SuppressWarnings("unchecked")
private DefaultConfigConflict mapToDefaultConfigConflict(Map<String, Object> map) {
DefaultConfigConflict conflict = new DefaultConfigConflict();
if (map.containsKey("configKey")) {
conflict.setConfigKey((String) map.get("configKey"));
}
if (map.containsKey("conflictType")) {
conflict.setConflictType((String) map.get("conflictType"));
}
if (map.containsKey("deviceCount")) {
Object deviceCount = map.get("deviceCount");
if (deviceCount instanceof Number) {
conflict.setDeviceCount(((Number) deviceCount).intValue());
}
}
if (map.containsKey("currentType")) {
conflict.setCurrentType((String) map.get("currentType"));
}
if (map.containsKey("proposedType")) {
conflict.setProposedType((String) map.get("proposedType"));
}
if (map.containsKey("conflictDevices")) {
Object conflictDevices = map.get("conflictDevices");
if (conflictDevices instanceof Iterable) {
conflict.setConflictDevices(new ArrayList<>());
for (Object deviceId : (Iterable<?>) conflictDevices) {
if (deviceId instanceof Number) {
conflict.getConflictDevices().add(((Number) deviceId).longValue());
}
}
}
}
return conflict;
}
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
if (response == null || !response.isSuccess()) {
String msg = response != null && response.getMessage() != null
? response.getMessage()
: errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
return response.getData();
}
private <T> PageResponse<T> handlePageResponse(PageResponse<T> response, String errorMessage) {
if (response == null || !response.isSuccess()) {
String msg = response != null && response.getMessage() != null
? response.getMessage()
: errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
return response;
}
}