You've already forked FrameTour-BE
refactor(integration): 重构集成服务的降级机制
-移除各服务自定义的降级服务类,统一降级逻辑 - 新增 IntegrationFallbackService作为通用降级服务 - 更新设备和景区服务的降级处理方式 - 优化降级缓存管理,增加统计信息和批量清理功能 - 调整 API 接口,移除扁平化批量更新等相关方法
This commit is contained in:
@@ -399,21 +399,6 @@ public class DeviceV2Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 扁平化批量更新设备配置
|
|
||||||
*/
|
|
||||||
@PostMapping("/{id}/config/flat-batch")
|
|
||||||
public ApiResponse<String> batchFlatUpdateDeviceConfig(@PathVariable Long id,
|
|
||||||
@RequestBody Map<String, Object> configs) {
|
|
||||||
log.info("扁平化批量更新设备配置, deviceId: {}, configs count: {}", id, configs.size());
|
|
||||||
try {
|
|
||||||
deviceConfigIntegrationService.batchFlatUpdateDeviceConfig(id, configs);
|
|
||||||
return ApiResponse.success("设备配置批量更新成功");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("扁平化批量更新设备配置失败, deviceId: {}", id, e);
|
|
||||||
return ApiResponse.fail("扁平化批量更新设备配置失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新设备配置
|
* 更新设备配置
|
||||||
@@ -431,27 +416,6 @@ public class DeviceV2Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置摄像头参数(快捷方法)
|
|
||||||
*/
|
|
||||||
@PutMapping("/{id}/config/camera")
|
|
||||||
public ApiResponse<String> configureCameraParams(@PathVariable Long id, @RequestBody Map<String, Object> request) {
|
|
||||||
String ipAddress = (String) request.get("ipAddress");
|
|
||||||
String resolution = (String) request.get("resolution");
|
|
||||||
Integer framerate = request.get("framerate") != null ? Integer.valueOf(request.get("framerate").toString()) : null;
|
|
||||||
String protocol = (String) request.get("protocol");
|
|
||||||
String username = (String) request.get("username");
|
|
||||||
String password = (String) request.get("password");
|
|
||||||
|
|
||||||
log.info("配置摄像头参数, deviceId: {}, ipAddress: {}, resolution: {}", id, ipAddress, resolution);
|
|
||||||
try {
|
|
||||||
deviceConfigIntegrationService.configureCameraParams(id, ipAddress, resolution, framerate, protocol, username, password);
|
|
||||||
return ApiResponse.success("摄像头参数配置成功");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("配置摄像头参数失败, deviceId: {}", id, e);
|
|
||||||
return ApiResponse.fail("配置摄像头参数失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除设备配置
|
* 删除设备配置
|
||||||
|
@@ -17,6 +17,7 @@ The integration package (`com.ycwl.basic.integration`) is responsible for extern
|
|||||||
- **IntegrationException**: Standardized exception for integration failures
|
- **IntegrationException**: Standardized exception for integration failures
|
||||||
- **CommonResponse/PageResponse**: Standard response wrappers for external service calls
|
- **CommonResponse/PageResponse**: Standard response wrappers for external service calls
|
||||||
- **ConfigValueUtil**: Utility for handling configuration values
|
- **ConfigValueUtil**: Utility for handling configuration values
|
||||||
|
- **IntegrationFallbackService**: Universal fallback service for handling integration failures
|
||||||
|
|
||||||
#### Service-Specific Integrations
|
#### Service-Specific Integrations
|
||||||
Currently implemented:
|
Currently implemented:
|
||||||
@@ -35,6 +36,91 @@ service/
|
|||||||
└── example/ # Usage examples
|
└── example/ # Usage examples
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Integration Fallback Mechanism
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
The integration package includes a comprehensive fallback mechanism that provides automatic degradation when external microservices are unavailable or return errors. This mechanism ensures system resilience and maintains service availability even when dependencies fail.
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
#### IntegrationFallbackService
|
||||||
|
Universal fallback service that handles failure degradation for all microservice integrations:
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- **Automatic Fallback**: Transparently handles service failures and returns cached results
|
||||||
|
- **Configurable TTL**: Service-specific cache TTL configuration (default: 7 days)
|
||||||
|
- **Cache Management**: Comprehensive cache statistics, cleanup, and monitoring
|
||||||
|
- **Service Isolation**: Per-service cache namespacing and configuration
|
||||||
|
- **Operation Types**: Supports both query operations (return cached data) and mutation operations (ignore failures with history)
|
||||||
|
|
||||||
|
**Core Methods:**
|
||||||
|
```java
|
||||||
|
// Query operations with fallback
|
||||||
|
<T> T executeWithFallback(String serviceName, String cacheKey, Supplier<T> operation, Class<T> resultClass)
|
||||||
|
|
||||||
|
// Mutation operations with fallback
|
||||||
|
void executeWithFallback(String serviceName, String cacheKey, Runnable operation)
|
||||||
|
|
||||||
|
// Cache management
|
||||||
|
boolean hasFallbackCache(String serviceName, String cacheKey)
|
||||||
|
void clearFallbackCache(String serviceName, String cacheKey)
|
||||||
|
void clearAllFallbackCache(String serviceName)
|
||||||
|
FallbackCacheStats getFallbackCacheStats(String serviceName)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fallback Strategy
|
||||||
|
|
||||||
|
#### Query Operations (GET methods) - WITH FALLBACK
|
||||||
|
1. **Normal Execution**: Execute the primary operation
|
||||||
|
2. **Success Handling**: Store successful result in fallback cache for future degradation
|
||||||
|
3. **Failure Handling**: On failure, attempt to retrieve cached result from previous success
|
||||||
|
4. **Final Fallback**: If no cached result exists, propagate the original exception
|
||||||
|
|
||||||
|
#### Mutation Operations (PUT/POST/DELETE methods) - NO FALLBACK
|
||||||
|
1. **Direct Execution**: Execute the primary operation directly without fallback
|
||||||
|
2. **Success Handling**: Operation completes successfully
|
||||||
|
3. **Failure Handling**: Immediately propagate the original exception to caller
|
||||||
|
4. **Rationale**: Users need to know if their create/update/delete operations truly succeeded or failed
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
#### Properties Structure
|
||||||
|
```yaml
|
||||||
|
integration:
|
||||||
|
fallback:
|
||||||
|
enabled: true # Global fallback switch
|
||||||
|
cachePrefix: "integration:fallback:" # Global cache prefix
|
||||||
|
defaultTtlDays: 7 # Default cache TTL in days
|
||||||
|
|
||||||
|
# Service-specific configurations
|
||||||
|
scenic:
|
||||||
|
enabled: true # Service-specific fallback switch
|
||||||
|
ttlDays: 10 # Custom TTL for scenic service
|
||||||
|
cachePrefix: "scenic:fallback:" # Custom cache prefix (optional)
|
||||||
|
|
||||||
|
device:
|
||||||
|
enabled: true
|
||||||
|
ttlDays: 5 # Custom TTL for device service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configuration Priority
|
||||||
|
1. **Service-specific settings**: Override global defaults when specified
|
||||||
|
2. **Global defaults**: Used when service-specific settings are not provided
|
||||||
|
3. **Hardcoded defaults**: Final fallback when no configuration is available
|
||||||
|
|
||||||
|
### Cache Key Strategy
|
||||||
|
|
||||||
|
Cache keys follow a standardized naming convention:
|
||||||
|
```
|
||||||
|
{cachePrefix}{serviceName}:{operationType}:{resourceId}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `integration:fallback:zt-device:device:1001` - Device info cache
|
||||||
|
- `integration:fallback:zt-device:device:config:1001` - Device config cache
|
||||||
|
- `integration:fallback:zt-scenic:scenic:2001` - Scenic info cache
|
||||||
|
- `integration:fallback:zt-scenic:scenic:flat:config:2001` - Scenic flat config cache
|
||||||
|
|
||||||
## Scenic Integration (ZT-Scenic Microservice)
|
## Scenic Integration (ZT-Scenic Microservice)
|
||||||
|
|
||||||
### Key Components
|
### Key Components
|
||||||
@@ -45,8 +131,8 @@ service/
|
|||||||
- **DefaultConfigClient**: Default configuration operations
|
- **DefaultConfigClient**: Default configuration operations
|
||||||
|
|
||||||
#### Services
|
#### Services
|
||||||
- **ScenicIntegrationService**: High-level scenic operations
|
- **ScenicIntegrationService**: High-level scenic operations (with automatic fallback)
|
||||||
- **ScenicConfigIntegrationService**: Configuration management
|
- **ScenicConfigIntegrationService**: Configuration management (with automatic fallback)
|
||||||
- **DefaultConfigIntegrationService**: Default configuration handling
|
- **DefaultConfigIntegrationService**: Default configuration handling
|
||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
@@ -63,37 +149,74 @@ integration:
|
|||||||
|
|
||||||
### Usage Examples
|
### Usage Examples
|
||||||
|
|
||||||
#### Basic Scenic Operations
|
#### Basic Scenic Operations (with Automatic Fallback)
|
||||||
```java
|
```java
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScenicIntegrationService scenicService;
|
private ScenicIntegrationService scenicService;
|
||||||
|
|
||||||
// Get scenic with configuration
|
// Get scenic with configuration (automatically falls back to cache on failure)
|
||||||
ScenicV2WithConfigDTO scenic = scenicService.getScenicWithConfig(scenicId);
|
ScenicV2WithConfigDTO scenic = scenicService.getScenicWithConfig(scenicId);
|
||||||
|
|
||||||
// Create new scenic
|
// Get scenic basic info (automatically falls back to cache on failure)
|
||||||
|
ScenicV2DTO scenicInfo = scenicService.getScenic(scenicId);
|
||||||
|
|
||||||
|
// Get flat configuration (automatically falls back to cache on failure)
|
||||||
|
Map<String, Object> config = scenicService.getScenicFlatConfig(scenicId);
|
||||||
|
|
||||||
|
// Create new scenic (direct operation, fails immediately on error)
|
||||||
CreateScenicRequest request = new CreateScenicRequest();
|
CreateScenicRequest request = new CreateScenicRequest();
|
||||||
request.setName("Test Scenic");
|
request.setName("Test Scenic");
|
||||||
ScenicV2DTO result = scenicService.createScenic(request);
|
ScenicV2DTO result = scenicService.createScenic(request);
|
||||||
|
|
||||||
// Filter scenics
|
// Update scenic (direct operation, fails immediately on error)
|
||||||
|
UpdateScenicRequest updateRequest = new UpdateScenicRequest();
|
||||||
|
updateRequest.setName("Updated Scenic");
|
||||||
|
ScenicV2DTO updated = scenicService.updateScenic(scenicId, updateRequest);
|
||||||
|
|
||||||
|
// Filter scenics (no fallback for complex queries)
|
||||||
ScenicFilterRequest filterRequest = new ScenicFilterRequest();
|
ScenicFilterRequest filterRequest = new ScenicFilterRequest();
|
||||||
// configure filters...
|
// configure filters...
|
||||||
ScenicFilterPageResponse response = scenicService.filterScenics(filterRequest);
|
ScenicFilterPageResponse response = scenicService.filterScenics(filterRequest);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Configuration Management
|
#### Configuration Management (with Automatic Fallback)
|
||||||
```java
|
```java
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScenicConfigIntegrationService configService;
|
private ScenicConfigIntegrationService configService;
|
||||||
|
|
||||||
// Get flat configuration
|
// Get flat configuration (automatically falls back to cache on failure)
|
||||||
Map<String, Object> config = scenicService.getScenicFlatConfig(scenicId);
|
Map<String, Object> config = configService.getFlatConfigs(scenicId);
|
||||||
|
|
||||||
// Batch update configurations
|
// Batch update configurations (direct operation, fails immediately on error)
|
||||||
BatchConfigRequest batchRequest = new BatchConfigRequest();
|
BatchConfigRequest batchRequest = new BatchConfigRequest();
|
||||||
// configure batch updates...
|
// configure batch updates...
|
||||||
configService.batchUpdateConfigs(scenicId, batchRequest);
|
BatchUpdateResponse result = configService.batchUpdateConfigs(scenicId, batchRequest);
|
||||||
|
|
||||||
|
// Batch flat update configurations (direct operation, fails immediately on error)
|
||||||
|
Map<String, Object> flatUpdates = new HashMap<>();
|
||||||
|
flatUpdates.put("max_visitors", "5000");
|
||||||
|
flatUpdates.put("opening_hours", "08:00-18:00");
|
||||||
|
BatchUpdateResponse flatResult = configService.batchFlatUpdateConfigs(scenicId, flatUpdates);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fallback Cache Management for Scenics
|
||||||
|
```java
|
||||||
|
@Autowired
|
||||||
|
private IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
// Check fallback cache status
|
||||||
|
boolean hasScenicCache = fallbackService.hasFallbackCache("zt-scenic", "scenic:2001");
|
||||||
|
boolean hasConfigCache = fallbackService.hasFallbackCache("zt-scenic", "scenic:flat:configs:2001");
|
||||||
|
|
||||||
|
// Get cache statistics
|
||||||
|
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats("zt-scenic");
|
||||||
|
log.info("Scenic fallback cache: {} items, TTL: {} days", stats.getTotalCacheCount(), stats.getFallbackTtlDays());
|
||||||
|
|
||||||
|
// Clear specific cache
|
||||||
|
fallbackService.clearFallbackCache("zt-scenic", "scenic:2001");
|
||||||
|
|
||||||
|
// Clear all scenic caches
|
||||||
|
fallbackService.clearAllFallbackCache("zt-scenic");
|
||||||
```
|
```
|
||||||
|
|
||||||
## Device Integration (ZT-Device Microservice)
|
## Device Integration (ZT-Device Microservice)
|
||||||
@@ -105,8 +228,8 @@ configService.batchUpdateConfigs(scenicId, batchRequest);
|
|||||||
- **DeviceConfigV2Client**: Device configuration management
|
- **DeviceConfigV2Client**: Device configuration management
|
||||||
|
|
||||||
#### Services
|
#### Services
|
||||||
- **DeviceIntegrationService**: High-level device operations
|
- **DeviceIntegrationService**: High-level device operations (with automatic fallback)
|
||||||
- **DeviceConfigIntegrationService**: Device configuration management
|
- **DeviceConfigIntegrationService**: Device configuration management (with automatic fallback)
|
||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
```yaml
|
```yaml
|
||||||
@@ -122,52 +245,56 @@ integration:
|
|||||||
|
|
||||||
### Usage Examples
|
### Usage Examples
|
||||||
|
|
||||||
#### Basic Device Operations
|
#### Basic Device Operations (with Automatic Fallback)
|
||||||
```java
|
```java
|
||||||
@Autowired
|
@Autowired
|
||||||
private DeviceIntegrationService deviceService;
|
private DeviceIntegrationService deviceService;
|
||||||
|
|
||||||
// Create IPC camera device
|
// Create IPC camera device (operation tracked for future fallback)
|
||||||
DeviceV2DTO ipcDevice = deviceService.createIpcDevice("前门摄像头", "CAM001", scenicId);
|
DeviceV2DTO ipcDevice = deviceService.createIpcDevice("前门摄像头", "CAM001", scenicId);
|
||||||
|
|
||||||
// Get device with configuration
|
// Get device with configuration (automatically falls back to cache on failure)
|
||||||
DeviceV2WithConfigDTO device = deviceService.getDeviceWithConfig(deviceId);
|
DeviceV2WithConfigDTO device = deviceService.getDeviceWithConfig(deviceId);
|
||||||
|
|
||||||
// Get device by number
|
// Get device by number (automatically falls back to cache on failure)
|
||||||
DeviceV2DTO deviceByNo = deviceService.getDeviceByNo("CAM001");
|
DeviceV2DTO deviceByNo = deviceService.getDeviceByNo("CAM001");
|
||||||
|
|
||||||
// List scenic devices
|
// Get basic device info (automatically falls back to cache on failure)
|
||||||
|
DeviceV2DTO basicDevice = deviceService.getDevice(deviceId);
|
||||||
|
|
||||||
|
// List scenic devices (no fallback for list operations)
|
||||||
DeviceV2ListResponse deviceList = deviceService.getScenicIpcDevices(scenicId, 1, 10);
|
DeviceV2ListResponse deviceList = deviceService.getScenicIpcDevices(scenicId, 1, 10);
|
||||||
|
|
||||||
// Enable/disable device
|
// Enable/disable device (direct operation, fails immediately on error)
|
||||||
deviceService.enableDevice(deviceId);
|
deviceService.enableDevice(deviceId);
|
||||||
deviceService.disableDevice(deviceId);
|
deviceService.disableDevice(deviceId);
|
||||||
|
|
||||||
|
// Update device (direct operation, fails immediately on error)
|
||||||
|
UpdateDeviceRequest updateRequest = new UpdateDeviceRequest();
|
||||||
|
updateRequest.setName("更新后的摄像头");
|
||||||
|
deviceService.updateDevice(deviceId, updateRequest);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Device Configuration Management
|
#### Device Configuration Management (with Automatic Fallback)
|
||||||
```java
|
```java
|
||||||
@Autowired
|
@Autowired
|
||||||
private DeviceConfigIntegrationService configService;
|
private DeviceConfigIntegrationService configService;
|
||||||
|
|
||||||
// Configure camera basic parameters
|
// Get device configurations (with fallback)
|
||||||
configService.configureCameraBasicParams(deviceId, "192.168.1.100", "1920x1080", 30, "RTSP");
|
List<DeviceConfigV2DTO> configs = configService.getDeviceConfigs(deviceId);
|
||||||
|
|
||||||
// Configure camera with authentication
|
// Get flat configuration (automatically falls back to cache on failure)
|
||||||
configService.configureCameraFullParams(deviceId, "192.168.1.100", "1920x1080", 30, "RTSP", "admin", "password");
|
|
||||||
|
|
||||||
// Set specific configuration
|
|
||||||
configService.setDeviceIpAddress(deviceId, "192.168.1.101");
|
|
||||||
configService.setDeviceResolution(deviceId, "2560x1440");
|
|
||||||
configService.setDeviceFramerate(deviceId, 60);
|
|
||||||
|
|
||||||
// Get flat configuration
|
|
||||||
Map<String, Object> config = configService.getDeviceFlatConfig(deviceId);
|
Map<String, Object> config = configService.getDeviceFlatConfig(deviceId);
|
||||||
|
|
||||||
// Batch update configurations (simple way)
|
// Get flat configuration by device number (with fallback)
|
||||||
Map<String, Object> batchConfigs = new HashMap<>();
|
Map<String, Object> configByNo = configService.getDeviceFlatConfigByNo("CAM001");
|
||||||
batchConfigs.put("brightness", "50");
|
|
||||||
batchConfigs.put("contrast", "80");
|
// Batch update configurations (direct operation, fails immediately on error)
|
||||||
configService.batchFlatUpdateDeviceConfig(deviceId, batchConfigs);
|
BatchDeviceConfigRequest batchRequest = configService.createBatchConfigBuilder()
|
||||||
|
.addConfig("brightness", "50")
|
||||||
|
.addConfig("contrast", "80")
|
||||||
|
.build();
|
||||||
|
configService.batchUpdateDeviceConfig(deviceId, batchRequest);
|
||||||
|
|
||||||
// New batch configuration API with detailed results
|
// New batch configuration API with detailed results
|
||||||
BatchDeviceConfigRequest request = configService.createBatchConfigBuilder()
|
BatchDeviceConfigRequest request = configService.createBatchConfigBuilder()
|
||||||
@@ -189,20 +316,32 @@ if (result.getFailed() > 0) {
|
|||||||
|
|
||||||
#### Device Management Patterns
|
#### Device Management Patterns
|
||||||
```java
|
```java
|
||||||
// Create and configure camera in one operation
|
// Create and configure camera in one operation (both operations direct, no fallback)
|
||||||
DeviceV2DTO camera = deviceService.createIpcDevice("摄像头1", "CAM001", scenicId);
|
DeviceV2DTO camera = deviceService.createIpcDevice("摄像头1", "CAM001", scenicId);
|
||||||
configService.configureCameraFullParams(camera.getId(), "192.168.1.100", "1920x1080", 30, "RTSP", "admin", "password");
|
// Use batch configuration to set camera parameters (direct operation)
|
||||||
|
BatchDeviceConfigRequest cameraConfig = configService.createBatchConfigBuilder()
|
||||||
|
.addConfig("ip_address", "192.168.1.100")
|
||||||
|
.addConfig("resolution", "1920x1080")
|
||||||
|
.addConfig("framerate", "30")
|
||||||
|
.addConfig("protocol", "RTSP")
|
||||||
|
.addConfig("username", "admin")
|
||||||
|
.addConfig("password", "password")
|
||||||
|
.build();
|
||||||
|
configService.batchUpdateDeviceConfig(camera.getId(), cameraConfig);
|
||||||
|
|
||||||
// Get scenic camera status
|
// Get scenic camera status
|
||||||
DeviceV2WithConfigListResponse camerasWithConfig =
|
DeviceV2WithConfigListResponse camerasWithConfig =
|
||||||
deviceService.listDevicesWithConfig(1, 100, null, null, "IPC", 1, scenicId);
|
deviceService.listDevicesWithConfig(1, 100, null, null, "IPC", 1, scenicId);
|
||||||
|
|
||||||
// Batch update camera resolution
|
// Batch update camera resolution (direct operations, no fallback)
|
||||||
for (DeviceV2WithConfigDTO device : camerasWithConfig.getList()) {
|
for (DeviceV2WithConfigDTO device : camerasWithConfig.getList()) {
|
||||||
configService.setDeviceResolution(device.getId(), "2560x1440");
|
BatchDeviceConfigRequest resolutionUpdate = configService.createBatchConfigBuilder()
|
||||||
|
.addConfig("resolution", "2560x1440")
|
||||||
|
.build();
|
||||||
|
configService.batchUpdateDeviceConfig(device.getId(), resolutionUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monitor device configuration completeness
|
// Monitor device configuration completeness (with automatic fallback)
|
||||||
for (DeviceV2DTO device : activeDevices.getList()) {
|
for (DeviceV2DTO device : activeDevices.getList()) {
|
||||||
Map<String, Object> config = configService.getDeviceFlatConfig(device.getId());
|
Map<String, Object> config = configService.getDeviceFlatConfig(device.getId());
|
||||||
boolean hasIpConfig = config.containsKey("ip_address");
|
boolean hasIpConfig = config.containsKey("ip_address");
|
||||||
@@ -210,6 +349,26 @@ for (DeviceV2DTO device : activeDevices.getList()) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Fallback Cache Management for Devices
|
||||||
|
```java
|
||||||
|
@Autowired
|
||||||
|
private IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
// Check fallback cache status
|
||||||
|
boolean hasDeviceCache = fallbackService.hasFallbackCache("zt-device", "device:1001");
|
||||||
|
boolean hasConfigCache = fallbackService.hasFallbackCache("zt-device", "device:flat:config:1001");
|
||||||
|
|
||||||
|
// Get cache statistics
|
||||||
|
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats("zt-device");
|
||||||
|
log.info("Device fallback cache: {} items, TTL: {} days", stats.getTotalCacheCount(), stats.getFallbackTtlDays());
|
||||||
|
|
||||||
|
// Clear specific cache
|
||||||
|
fallbackService.clearFallbackCache("zt-device", "device:1001");
|
||||||
|
|
||||||
|
// Clear all device caches
|
||||||
|
fallbackService.clearAllFallbackCache("zt-device");
|
||||||
|
```
|
||||||
|
|
||||||
### Enhanced Batch Configuration API
|
### Enhanced Batch Configuration API
|
||||||
|
|
||||||
Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules.
|
Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules.
|
||||||
@@ -359,6 +518,24 @@ Automatically converts Feign errors to IntegrationException:
|
|||||||
### Properties Structure
|
### Properties Structure
|
||||||
```yaml
|
```yaml
|
||||||
integration:
|
integration:
|
||||||
|
# Fallback configuration
|
||||||
|
fallback:
|
||||||
|
enabled: true # Global fallback switch
|
||||||
|
cachePrefix: "integration:fallback:" # Global cache prefix
|
||||||
|
defaultTtlDays: 7 # Default cache TTL in days
|
||||||
|
|
||||||
|
# Service-specific fallback configurations
|
||||||
|
scenic:
|
||||||
|
enabled: true # Enable fallback for scenic service
|
||||||
|
ttlDays: 10 # Custom TTL for scenic (longer due to stable data)
|
||||||
|
# cachePrefix: "scenic:fallback:" # Optional custom prefix
|
||||||
|
|
||||||
|
device:
|
||||||
|
enabled: true # Enable fallback for device service
|
||||||
|
ttlDays: 5 # Custom TTL for device (shorter due to dynamic data)
|
||||||
|
# cachePrefix: "device:fallback:" # Optional custom prefix
|
||||||
|
|
||||||
|
# Service configurations
|
||||||
scenic:
|
scenic:
|
||||||
enabled: true
|
enabled: true
|
||||||
serviceName: zt-scenic
|
serviceName: zt-scenic
|
||||||
@@ -449,10 +626,23 @@ logging:
|
|||||||
- Provide meaningful error messages for business context
|
- Provide meaningful error messages for business context
|
||||||
- Include service name in IntegrationException for debugging
|
- Include service name in IntegrationException for debugging
|
||||||
|
|
||||||
|
### Fallback Strategy Best Practices
|
||||||
|
- **Query operations only**: Only apply fallback to GET operations (data retrieval)
|
||||||
|
- **No fallback for mutations**: CREATE/UPDATE/DELETE operations should fail immediately to provide clear feedback
|
||||||
|
- **Cache key design**: Use clear, hierarchical cache keys for easy identification and cleanup
|
||||||
|
- **TTL management**: Set appropriate TTL based on data freshness requirements
|
||||||
|
- **Monitoring**: Regularly check fallback cache statistics and cleanup stale entries
|
||||||
|
- **Service isolation**: Configure service-specific fallback settings based on reliability needs
|
||||||
|
- **Error transparency**: Let users know exactly when their modification operations fail
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
- Use environment-specific configuration profiles
|
- Use environment-specific configuration profiles
|
||||||
- Set appropriate timeouts based on service characteristics
|
- Set appropriate timeouts based on service characteristics
|
||||||
- Enable retries only when safe (idempotent operations)
|
- Enable retries only when safe (idempotent operations)
|
||||||
|
- Configure fallback TTL based on business requirements:
|
||||||
|
- **Critical data**: Longer TTL (7-14 days) for essential operations
|
||||||
|
- **Volatile data**: Shorter TTL (1-3 days) for frequently changing data
|
||||||
|
- **Configuration data**: Medium TTL (5-7 days) for semi-static settings
|
||||||
|
|
||||||
### DTOs and Mapping
|
### DTOs and Mapping
|
||||||
- Keep DTOs simple and focused on data transfer
|
- Keep DTOs simple and focused on data transfer
|
||||||
@@ -463,3 +653,10 @@ logging:
|
|||||||
- Keep integration services focused on external service calls
|
- Keep integration services focused on external service calls
|
||||||
- Handle response transformation and error conversion
|
- Handle response transformation and error conversion
|
||||||
- Avoid business logic in integration services
|
- Avoid business logic in integration services
|
||||||
|
- Use fallback service for resilience without changing business logic
|
||||||
|
|
||||||
|
### Cache Management Strategies
|
||||||
|
- **Proactive cleanup**: Monitor cache size and clean up periodically
|
||||||
|
- **Service-specific management**: Separate cache management per service
|
||||||
|
- **Debugging support**: Use cache statistics for troubleshooting
|
||||||
|
- **Configuration validation**: Ensure fallback configuration matches service requirements
|
@@ -11,6 +11,11 @@ import org.springframework.stereotype.Component;
|
|||||||
@ConfigurationProperties(prefix = "integration")
|
@ConfigurationProperties(prefix = "integration")
|
||||||
public class IntegrationProperties {
|
public class IntegrationProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降级服务配置
|
||||||
|
*/
|
||||||
|
private FallbackConfig fallback = new FallbackConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 景区服务配置
|
* 景区服务配置
|
||||||
*/
|
*/
|
||||||
@@ -70,4 +75,46 @@ public class IntegrationProperties {
|
|||||||
private boolean retryEnabled = false;
|
private boolean retryEnabled = false;
|
||||||
private int maxRetries = 3;
|
private int maxRetries = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class FallbackConfig {
|
||||||
|
/**
|
||||||
|
* 是否启用降级功能
|
||||||
|
*/
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降级缓存前缀
|
||||||
|
*/
|
||||||
|
private String cachePrefix = "integration:fallback:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认降级缓存TTL(天)
|
||||||
|
*/
|
||||||
|
private long defaultTtlDays = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务特定的降级配置
|
||||||
|
*/
|
||||||
|
private ServiceFallbackConfig scenic = new ServiceFallbackConfig();
|
||||||
|
private ServiceFallbackConfig device = new ServiceFallbackConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ServiceFallbackConfig {
|
||||||
|
/**
|
||||||
|
* 是否启用此服务的降级功能
|
||||||
|
*/
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务特定的缓存TTL(天),如果为0则使用默认TTL
|
||||||
|
*/
|
||||||
|
private long ttlDays = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务特定的缓存前缀,如果为空则使用默认前缀
|
||||||
|
*/
|
||||||
|
private String cachePrefix;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,257 @@
|
|||||||
|
package com.ycwl.basic.integration.common.service;
|
||||||
|
|
||||||
|
import com.ycwl.basic.integration.common.config.IntegrationProperties;
|
||||||
|
import com.ycwl.basic.utils.JacksonUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 集成服务通用失败降级处理
|
||||||
|
* 提供统一的降级策略,支持所有微服务集成
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IntegrationFallbackService {
|
||||||
|
|
||||||
|
private final RedisTemplate<String, String> redisTemplate;
|
||||||
|
private final IntegrationProperties integrationProperties;
|
||||||
|
|
||||||
|
// 默认降级缓存配置
|
||||||
|
private static final String DEFAULT_FALLBACK_PREFIX = "integration:fallback:";
|
||||||
|
private static final long DEFAULT_FALLBACK_TTL = 7; // 7天
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行操作,失败时降级到缓存结果
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称 (如: zt-device, zt-scenic)
|
||||||
|
* @param cacheKey 缓存键
|
||||||
|
* @param operation 主要操作
|
||||||
|
* @param resultClass 结果类型
|
||||||
|
* @param <T> 结果类型
|
||||||
|
* @return 操作结果或缓存的结果
|
||||||
|
*/
|
||||||
|
public <T> T executeWithFallback(String serviceName, String cacheKey, Supplier<T> operation, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
T result = operation.get();
|
||||||
|
if (result != null) {
|
||||||
|
// 操作成功,保存结果用于将来的降级
|
||||||
|
storeFallbackCache(serviceName, cacheKey, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[{}] 操作失败,尝试降级到缓存结果, cacheKey: {}", serviceName, cacheKey, e);
|
||||||
|
T fallbackResult = getFallbackFromCache(serviceName, cacheKey, resultClass);
|
||||||
|
if (fallbackResult == null) {
|
||||||
|
log.error("[{}] 操作失败且无缓存数据, cacheKey: {}", serviceName, cacheKey);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
log.info("[{}] 成功从降级缓存获取结果, cacheKey: {}", serviceName, cacheKey);
|
||||||
|
return fallbackResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行操作,失败时降级到缓存结果,无返回值版本
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称
|
||||||
|
* @param cacheKey 缓存键
|
||||||
|
* @param operation 主要操作
|
||||||
|
*/
|
||||||
|
public void executeWithFallback(String serviceName, String cacheKey, Runnable operation) {
|
||||||
|
try {
|
||||||
|
operation.run();
|
||||||
|
// 操作成功,记录成功状态
|
||||||
|
storeFallbackCache(serviceName, cacheKey + ":success", "true");
|
||||||
|
log.debug("[{}] 操作成功,已记录成功状态, cacheKey: {}", serviceName, cacheKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[{}] 操作失败,检查是否有历史成功记录, cacheKey: {}", serviceName, cacheKey, e);
|
||||||
|
String successRecord = getFallbackFromCache(serviceName, cacheKey + ":success", String.class);
|
||||||
|
if (successRecord == null) {
|
||||||
|
log.error("[{}] 操作失败且无历史成功记录, cacheKey: {}", serviceName, cacheKey);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
log.info("[{}] 操作失败但有历史成功记录,忽略此次失败, cacheKey: {}", serviceName, cacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储降级缓存
|
||||||
|
*/
|
||||||
|
private void storeFallbackCache(String serviceName, String cacheKey, Object value) {
|
||||||
|
try {
|
||||||
|
String fullKey = buildFullCacheKey(serviceName, cacheKey);
|
||||||
|
String jsonValue = JacksonUtil.toJSONString(value);
|
||||||
|
long ttl = getFallbackTtl(serviceName);
|
||||||
|
redisTemplate.opsForValue().set(fullKey, jsonValue, ttl, TimeUnit.DAYS);
|
||||||
|
log.debug("[{}] 存储降级缓存成功, key: {}", serviceName, fullKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[{}] 存储降级缓存失败, cacheKey: {}", serviceName, cacheKey, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从降级缓存获取结果
|
||||||
|
*/
|
||||||
|
private <T> T getFallbackFromCache(String serviceName, String cacheKey, Class<T> resultClass) {
|
||||||
|
try {
|
||||||
|
String fullKey = buildFullCacheKey(serviceName, cacheKey);
|
||||||
|
String cachedValue = redisTemplate.opsForValue().get(fullKey);
|
||||||
|
if (cachedValue != null) {
|
||||||
|
log.debug("[{}] 从降级缓存获取结果, key: {}", serviceName, fullKey);
|
||||||
|
if (resultClass == String.class) {
|
||||||
|
return resultClass.cast(cachedValue);
|
||||||
|
}
|
||||||
|
return JacksonUtil.parseObject(cachedValue, resultClass);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[{}] 从降级缓存获取结果失败, cacheKey: {}", serviceName, cacheKey, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除降级缓存
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称
|
||||||
|
* @param cacheKey 缓存键
|
||||||
|
*/
|
||||||
|
public void clearFallbackCache(String serviceName, String cacheKey) {
|
||||||
|
String fullKey = buildFullCacheKey(serviceName, cacheKey);
|
||||||
|
redisTemplate.delete(fullKey);
|
||||||
|
log.debug("[{}] 清除降级缓存, key: {}", serviceName, fullKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量清除服务的所有降级缓存
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称
|
||||||
|
*/
|
||||||
|
public void clearAllFallbackCache(String serviceName) {
|
||||||
|
String pattern = buildFullCacheKey(serviceName, "*");
|
||||||
|
Set<String> keys = redisTemplate.keys(pattern);
|
||||||
|
if (keys != null && !keys.isEmpty()) {
|
||||||
|
redisTemplate.delete(keys);
|
||||||
|
log.info("[{}] 批量清除降级缓存,共删除 {} 个缓存项", serviceName, keys.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有降级缓存
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称
|
||||||
|
* @param cacheKey 缓存键
|
||||||
|
* @return 是否存在降级缓存
|
||||||
|
*/
|
||||||
|
public boolean hasFallbackCache(String serviceName, String cacheKey) {
|
||||||
|
String fullKey = buildFullCacheKey(serviceName, cacheKey);
|
||||||
|
return Boolean.TRUE.equals(redisTemplate.hasKey(fullKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务的降级缓存统计信息
|
||||||
|
*
|
||||||
|
* @param serviceName 服务名称
|
||||||
|
* @return 缓存统计信息
|
||||||
|
*/
|
||||||
|
public FallbackCacheStats getFallbackCacheStats(String serviceName) {
|
||||||
|
String pattern = buildFullCacheKey(serviceName, "*");
|
||||||
|
Set<String> keys = redisTemplate.keys(pattern);
|
||||||
|
int totalCount = keys != null ? keys.size() : 0;
|
||||||
|
|
||||||
|
return FallbackCacheStats.builder()
|
||||||
|
.serviceName(serviceName)
|
||||||
|
.totalCacheCount(totalCount)
|
||||||
|
.cacheKeyPattern(pattern)
|
||||||
|
.fallbackTtlDays(getFallbackTtl(serviceName))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整的缓存键
|
||||||
|
*/
|
||||||
|
private String buildFullCacheKey(String serviceName, String cacheKey) {
|
||||||
|
String prefix = getFallbackPrefix(serviceName);
|
||||||
|
return prefix + serviceName + ":" + cacheKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务的降级缓存前缀
|
||||||
|
*/
|
||||||
|
private String getFallbackPrefix(String serviceName) {
|
||||||
|
if (!integrationProperties.getFallback().isEnabled()) {
|
||||||
|
return DEFAULT_FALLBACK_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务特定的缓存前缀
|
||||||
|
IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
|
||||||
|
if (serviceConfig != null && serviceConfig.getCachePrefix() != null) {
|
||||||
|
return serviceConfig.getCachePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用全局配置的前缀
|
||||||
|
return integrationProperties.getFallback().getCachePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务的降级缓存TTL
|
||||||
|
*/
|
||||||
|
private long getFallbackTtl(String serviceName) {
|
||||||
|
if (!integrationProperties.getFallback().isEnabled()) {
|
||||||
|
return DEFAULT_FALLBACK_TTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务特定的TTL
|
||||||
|
IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
|
||||||
|
if (serviceConfig != null && serviceConfig.getTtlDays() > 0) {
|
||||||
|
return serviceConfig.getTtlDays();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用全局配置的TTL
|
||||||
|
return integrationProperties.getFallback().getDefaultTtlDays();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务特定的降级配置
|
||||||
|
*/
|
||||||
|
private IntegrationProperties.ServiceFallbackConfig getServiceFallbackConfig(String serviceName) {
|
||||||
|
switch (serviceName.toLowerCase()) {
|
||||||
|
case "zt-scenic":
|
||||||
|
return integrationProperties.getFallback().getScenic();
|
||||||
|
case "zt-device":
|
||||||
|
return integrationProperties.getFallback().getDevice();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查服务是否启用降级功能
|
||||||
|
*/
|
||||||
|
public boolean isFallbackEnabled(String serviceName) {
|
||||||
|
if (!integrationProperties.getFallback().isEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
|
||||||
|
return serviceConfig == null || serviceConfig.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降级缓存统计信息
|
||||||
|
*/
|
||||||
|
@lombok.Builder
|
||||||
|
@lombok.Data
|
||||||
|
public static class FallbackCacheStats {
|
||||||
|
private String serviceName;
|
||||||
|
private int totalCacheCount;
|
||||||
|
private String cacheKeyPattern;
|
||||||
|
private long fallbackTtlDays;
|
||||||
|
}
|
||||||
|
}
|
@@ -71,10 +71,4 @@ public interface DeviceConfigV2Client {
|
|||||||
CommonResponse<BatchUpdateResponse> batchUpdateDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
CommonResponse<BatchUpdateResponse> batchUpdateDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
||||||
@RequestBody BatchDeviceConfigRequest request);
|
@RequestBody BatchDeviceConfigRequest request);
|
||||||
|
|
||||||
/**
|
|
||||||
* 扁平化批量更新设备配置
|
|
||||||
*/
|
|
||||||
@PostMapping("/{deviceId}/batch-flat")
|
|
||||||
CommonResponse<String> batchFlatUpdateDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
|
||||||
@RequestBody Map<String, Object> configs);
|
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
package com.ycwl.basic.integration.device.example;
|
package com.ycwl.basic.integration.device.example;
|
||||||
|
|
||||||
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||||
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
|
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
|
||||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationFallbackService;
|
|
||||||
import com.ycwl.basic.integration.device.dto.device.*;
|
import com.ycwl.basic.integration.device.dto.device.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -11,8 +11,8 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备集成降级机制示例
|
* 设备集成示例(包含降级机制)
|
||||||
* 演示失败降级策略的使用
|
* 演示设备集成和失败降级策略的使用
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -21,7 +21,9 @@ public class DeviceIntegrationFallbackExample {
|
|||||||
|
|
||||||
private final DeviceIntegrationService deviceService;
|
private final DeviceIntegrationService deviceService;
|
||||||
private final DeviceConfigIntegrationService configService;
|
private final DeviceConfigIntegrationService configService;
|
||||||
private final DeviceIntegrationFallbackService fallbackService;
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-device";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 演示设备信息获取的降级机制
|
* 演示设备信息获取的降级机制
|
||||||
@@ -51,26 +53,26 @@ public class DeviceIntegrationFallbackExample {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 演示设备操作的降级机制
|
* 演示设备操作(无降级机制)
|
||||||
*/
|
*/
|
||||||
public void deviceOperationFallbackExample() {
|
public void deviceOperationExample() {
|
||||||
log.info("=== 设备操作降级示例 ===");
|
log.info("=== 设备操作示例 ===");
|
||||||
|
|
||||||
Long deviceId = 1001L;
|
Long deviceId = 1001L;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 设备更新操作 - 自动降级
|
// 设备更新操作 - 直接操作,失败时抛出异常
|
||||||
UpdateDeviceRequest updateRequest = new UpdateDeviceRequest();
|
UpdateDeviceRequest updateRequest = new UpdateDeviceRequest();
|
||||||
updateRequest.setName("更新后的摄像头");
|
updateRequest.setName("更新后的摄像头");
|
||||||
deviceService.updateDevice(deviceId, updateRequest);
|
deviceService.updateDevice(deviceId, updateRequest);
|
||||||
log.info("设备更新操作完成");
|
log.info("设备更新操作完成");
|
||||||
|
|
||||||
// 设备排序更新 - 自动降级
|
// 设备排序更新 - 直接操作,失败时抛出异常
|
||||||
deviceService.updateDeviceSort(deviceId, 5);
|
deviceService.updateDeviceSort(deviceId, 5);
|
||||||
log.info("设备排序更新完成");
|
log.info("设备排序更新完成");
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("设备操作降级失败", e);
|
log.error("设备操作失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,29 +86,39 @@ public class DeviceIntegrationFallbackExample {
|
|||||||
String configCacheKey = "device:flat:config:1001";
|
String configCacheKey = "device:flat:config:1001";
|
||||||
|
|
||||||
// 检查降级缓存状态
|
// 检查降级缓存状态
|
||||||
boolean hasDeviceCache = fallbackService.hasFallbackCache(deviceCacheKey);
|
boolean hasDeviceCache = fallbackService.hasFallbackCache(SERVICE_NAME, deviceCacheKey);
|
||||||
boolean hasConfigCache = fallbackService.hasFallbackCache(configCacheKey);
|
boolean hasConfigCache = fallbackService.hasFallbackCache(SERVICE_NAME, configCacheKey);
|
||||||
|
|
||||||
log.info("设备降级缓存存在: {}", hasDeviceCache);
|
log.info("设备降级缓存存在: {}", hasDeviceCache);
|
||||||
log.info("配置降级缓存存在: {}", hasConfigCache);
|
log.info("配置降级缓存存在: {}", hasConfigCache);
|
||||||
|
|
||||||
// 清理特定的降级缓存
|
// 清理特定的降级缓存
|
||||||
if (hasDeviceCache) {
|
if (hasDeviceCache) {
|
||||||
fallbackService.clearFallbackCache(deviceCacheKey);
|
fallbackService.clearFallbackCache(SERVICE_NAME, deviceCacheKey);
|
||||||
log.info("已清理设备降级缓存");
|
log.info("已清理设备降级缓存");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取降级缓存统计信息
|
||||||
|
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats(SERVICE_NAME);
|
||||||
|
log.info("设备服务降级缓存统计: {}", stats);
|
||||||
|
|
||||||
|
// 批量清理所有设备降级缓存
|
||||||
|
if (stats.getTotalCacheCount() > 10) {
|
||||||
|
fallbackService.clearAllFallbackCache(SERVICE_NAME);
|
||||||
|
log.info("已批量清理所有设备降级缓存");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 运行所有降级示例
|
* 运行所有示例
|
||||||
*/
|
*/
|
||||||
public void runAllFallbackExamples() {
|
public void runAllExamples() {
|
||||||
log.info("开始运行设备集成降级示例...");
|
log.info("开始运行设备集成示例...");
|
||||||
|
|
||||||
deviceInfoFallbackExample();
|
deviceInfoFallbackExample();
|
||||||
deviceOperationFallbackExample();
|
deviceOperationExample();
|
||||||
fallbackCacheManagementExample();
|
fallbackCacheManagementExample();
|
||||||
|
|
||||||
log.info("设备集成降级示例运行完成");
|
log.info("设备集成示例运行完成");
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@ package com.ycwl.basic.integration.device.service;
|
|||||||
|
|
||||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||||
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.device.client.DeviceConfigV2Client;
|
import com.ycwl.basic.integration.device.client.DeviceConfigV2Client;
|
||||||
import com.ycwl.basic.integration.device.dto.config.*;
|
import com.ycwl.basic.integration.device.dto.config.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -18,7 +19,9 @@ import java.util.Map;
|
|||||||
public class DeviceConfigIntegrationService {
|
public class DeviceConfigIntegrationService {
|
||||||
|
|
||||||
private final DeviceConfigV2Client deviceConfigV2Client;
|
private final DeviceConfigV2Client deviceConfigV2Client;
|
||||||
private final DeviceIntegrationFallbackService fallbackService;
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-device";
|
||||||
|
|
||||||
public List<DeviceConfigV2DTO> getDeviceConfigs(Long deviceId) {
|
public List<DeviceConfigV2DTO> getDeviceConfigs(Long deviceId) {
|
||||||
log.info("获取设备配置列表, deviceId: {}", deviceId);
|
log.info("获取设备配置列表, deviceId: {}", deviceId);
|
||||||
@@ -41,6 +44,7 @@ public class DeviceConfigIntegrationService {
|
|||||||
public Map<String, Object> getDeviceFlatConfig(Long deviceId) {
|
public Map<String, Object> getDeviceFlatConfig(Long deviceId) {
|
||||||
log.info("获取设备扁平化配置, deviceId: {}", deviceId);
|
log.info("获取设备扁平化配置, deviceId: {}", deviceId);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:flat:config:" + deviceId,
|
"device:flat:config:" + deviceId,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfig(deviceId);
|
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfig(deviceId);
|
||||||
@@ -53,6 +57,7 @@ public class DeviceConfigIntegrationService {
|
|||||||
public Map<String, Object> getDeviceFlatConfigByNo(String deviceNo) {
|
public Map<String, Object> getDeviceFlatConfigByNo(String deviceNo) {
|
||||||
log.info("根据设备编号获取扁平化配置, deviceNo: {}", deviceNo);
|
log.info("根据设备编号获取扁平化配置, deviceNo: {}", deviceNo);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:flat:config:no:" + deviceNo,
|
"device:flat:config:no:" + deviceNo,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfigByNo(deviceNo);
|
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfigByNo(deviceNo);
|
||||||
@@ -89,6 +94,8 @@ public class DeviceConfigIntegrationService {
|
|||||||
return handleResponse(response, "批量更新设备配置失败");
|
return handleResponse(response, "批量更新设备配置失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取设备特定配置值
|
* 获取设备特定配置值
|
||||||
*/
|
*/
|
||||||
|
@@ -1,128 +0,0 @@
|
|||||||
package com.ycwl.basic.integration.device.service;
|
|
||||||
|
|
||||||
import com.ycwl.basic.integration.device.dto.device.*;
|
|
||||||
import com.ycwl.basic.utils.JacksonUtil;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备集成服务失败降级处理
|
|
||||||
* 提供失败时的降级策略,不使用常规缓存
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class DeviceIntegrationFallbackService {
|
|
||||||
|
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
|
||||||
|
|
||||||
// 降级缓存键前缀
|
|
||||||
private static final String FALLBACK_CACHE_PREFIX = "device:fallback:";
|
|
||||||
private static final long FALLBACK_CACHE_TTL = 7; // 7天
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行操作,失败时降级到缓存结果
|
|
||||||
*
|
|
||||||
* @param cacheKey 缓存键
|
|
||||||
* @param operation 主要操作
|
|
||||||
* @param resultClass 结果类型
|
|
||||||
* @param <T> 结果类型
|
|
||||||
* @return 操作结果或缓存的结果
|
|
||||||
*/
|
|
||||||
public <T> T executeWithFallback(String cacheKey, Supplier<T> operation, Class<T> resultClass) {
|
|
||||||
try {
|
|
||||||
T result = operation.get();
|
|
||||||
if (result != null) {
|
|
||||||
// 操作成功,保存结果用于将来的降级
|
|
||||||
storeFallbackCache(cacheKey, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("操作失败,尝试降级到缓存结果, cacheKey: {}", cacheKey, e);
|
|
||||||
return getFallbackFromCache(cacheKey, resultClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行操作,失败时降级到缓存结果,无返回值版本
|
|
||||||
*
|
|
||||||
* @param cacheKey 缓存键
|
|
||||||
* @param operation 主要操作
|
|
||||||
*/
|
|
||||||
public void executeWithFallback(String cacheKey, Runnable operation) {
|
|
||||||
try {
|
|
||||||
operation.run();
|
|
||||||
// 操作成功,记录成功状态
|
|
||||||
storeFallbackCache(cacheKey + ":success", "true");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("操作失败,检查是否有历史成功记录, cacheKey: {}", cacheKey, e);
|
|
||||||
String successRecord = getFallbackFromCache(cacheKey + ":success", String.class);
|
|
||||||
if (successRecord == null) {
|
|
||||||
log.error("操作失败且无历史成功记录, cacheKey: {}", cacheKey);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
log.info("操作失败但有历史成功记录,忽略此次失败, cacheKey: {}", cacheKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储降级缓存
|
|
||||||
*/
|
|
||||||
private void storeFallbackCache(String cacheKey, Object value) {
|
|
||||||
try {
|
|
||||||
String key = FALLBACK_CACHE_PREFIX + cacheKey;
|
|
||||||
String jsonValue = JacksonUtil.toJSONString(value);
|
|
||||||
redisTemplate.opsForValue().set(key, jsonValue, FALLBACK_CACHE_TTL, TimeUnit.DAYS);
|
|
||||||
log.debug("存储降级缓存成功, key: {}", key);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("存储降级缓存失败, cacheKey: {}", cacheKey, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从降级缓存获取结果
|
|
||||||
*/
|
|
||||||
private <T> T getFallbackFromCache(String cacheKey, Class<T> resultClass) {
|
|
||||||
try {
|
|
||||||
String key = FALLBACK_CACHE_PREFIX + cacheKey;
|
|
||||||
String cachedValue = redisTemplate.opsForValue().get(key);
|
|
||||||
if (cachedValue != null) {
|
|
||||||
log.info("从降级缓存获取结果, key: {}", key);
|
|
||||||
if (resultClass == String.class) {
|
|
||||||
return resultClass.cast(cachedValue);
|
|
||||||
}
|
|
||||||
return JacksonUtil.parseObject(cachedValue, resultClass);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("从降级缓存获取结果失败, cacheKey: {}", cacheKey, e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除降级缓存
|
|
||||||
*
|
|
||||||
* @param cacheKey 缓存键
|
|
||||||
*/
|
|
||||||
public void clearFallbackCache(String cacheKey) {
|
|
||||||
String key = FALLBACK_CACHE_PREFIX + cacheKey;
|
|
||||||
redisTemplate.delete(key);
|
|
||||||
log.debug("清除降级缓存, key: {}", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否有降级缓存
|
|
||||||
*
|
|
||||||
* @param cacheKey 缓存键
|
|
||||||
* @return 是否存在降级缓存
|
|
||||||
*/
|
|
||||||
public boolean hasFallbackCache(String cacheKey) {
|
|
||||||
String key = FALLBACK_CACHE_PREFIX + cacheKey;
|
|
||||||
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,6 +2,7 @@ package com.ycwl.basic.integration.device.service;
|
|||||||
|
|
||||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||||
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.device.client.DeviceV2Client;
|
import com.ycwl.basic.integration.device.client.DeviceV2Client;
|
||||||
import com.ycwl.basic.integration.device.dto.device.*;
|
import com.ycwl.basic.integration.device.dto.device.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -14,11 +15,14 @@ import org.springframework.stereotype.Service;
|
|||||||
public class DeviceIntegrationService {
|
public class DeviceIntegrationService {
|
||||||
|
|
||||||
private final DeviceV2Client deviceV2Client;
|
private final DeviceV2Client deviceV2Client;
|
||||||
private final DeviceIntegrationFallbackService fallbackService;
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-device";
|
||||||
|
|
||||||
public DeviceV2DTO getDevice(Long deviceId) {
|
public DeviceV2DTO getDevice(Long deviceId) {
|
||||||
log.info("获取设备信息, deviceId: {}", deviceId);
|
log.info("获取设备信息, deviceId: {}", deviceId);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:" + deviceId,
|
"device:" + deviceId,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDevice(deviceId);
|
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDevice(deviceId);
|
||||||
@@ -31,6 +35,7 @@ public class DeviceIntegrationService {
|
|||||||
public DeviceV2DTO getDeviceByNo(String deviceNo) {
|
public DeviceV2DTO getDeviceByNo(String deviceNo) {
|
||||||
log.info("根据设备编号获取设备信息, deviceNo: {}", deviceNo);
|
log.info("根据设备编号获取设备信息, deviceNo: {}", deviceNo);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:no:" + deviceNo,
|
"device:no:" + deviceNo,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDeviceByNo(deviceNo);
|
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDeviceByNo(deviceNo);
|
||||||
@@ -43,6 +48,7 @@ public class DeviceIntegrationService {
|
|||||||
public DeviceV2WithConfigDTO getDeviceWithConfig(Long deviceId) {
|
public DeviceV2WithConfigDTO getDeviceWithConfig(Long deviceId) {
|
||||||
log.info("获取设备配置信息, deviceId: {}", deviceId);
|
log.info("获取设备配置信息, deviceId: {}", deviceId);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:config:" + deviceId,
|
"device:config:" + deviceId,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceWithConfig(deviceId);
|
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceWithConfig(deviceId);
|
||||||
@@ -55,6 +61,7 @@ public class DeviceIntegrationService {
|
|||||||
public DeviceV2WithConfigDTO getDeviceWithConfigByNo(String deviceNo) {
|
public DeviceV2WithConfigDTO getDeviceWithConfigByNo(String deviceNo) {
|
||||||
log.info("根据设备编号获取设备配置信息, deviceNo: {}", deviceNo);
|
log.info("根据设备编号获取设备配置信息, deviceNo: {}", deviceNo);
|
||||||
return fallbackService.executeWithFallback(
|
return fallbackService.executeWithFallback(
|
||||||
|
SERVICE_NAME,
|
||||||
"device:config:no:" + deviceNo,
|
"device:config:no:" + deviceNo,
|
||||||
() -> {
|
() -> {
|
||||||
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceByNoWithConfig(deviceNo);
|
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceByNoWithConfig(deviceNo);
|
||||||
@@ -72,24 +79,14 @@ public class DeviceIntegrationService {
|
|||||||
|
|
||||||
public void updateDevice(Long deviceId, UpdateDeviceRequest request) {
|
public void updateDevice(Long deviceId, UpdateDeviceRequest request) {
|
||||||
log.info("更新设备信息, deviceId: {}", deviceId);
|
log.info("更新设备信息, deviceId: {}", deviceId);
|
||||||
fallbackService.executeWithFallback(
|
CommonResponse<String> response = deviceV2Client.updateDevice(deviceId, request);
|
||||||
"device:update:" + deviceId,
|
handleResponse(response, "更新设备信息失败");
|
||||||
() -> {
|
|
||||||
CommonResponse<String> response = deviceV2Client.updateDevice(deviceId, request);
|
|
||||||
handleResponse(response, "更新设备信息失败");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteDevice(Long deviceId) {
|
public void deleteDevice(Long deviceId) {
|
||||||
log.info("删除设备, deviceId: {}", deviceId);
|
log.info("删除设备, deviceId: {}", deviceId);
|
||||||
fallbackService.executeWithFallback(
|
CommonResponse<String> response = deviceV2Client.deleteDevice(deviceId);
|
||||||
"device:delete:" + deviceId,
|
handleResponse(response, "删除设备失败");
|
||||||
() -> {
|
|
||||||
CommonResponse<String> response = deviceV2Client.deleteDevice(deviceId);
|
|
||||||
handleResponse(response, "删除设备失败");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceV2ListResponse listDevices(Integer page, Integer pageSize, String name, String no,
|
public DeviceV2ListResponse listDevices(Integer page, Integer pageSize, String name, String no,
|
||||||
|
@@ -13,4 +13,12 @@ public class BatchUpdateResponse {
|
|||||||
|
|
||||||
@JsonProperty("message")
|
@JsonProperty("message")
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
|
public Integer getSuccess() {
|
||||||
|
return (updatedCount != null ? updatedCount : 0) + (createdCount != null ? createdCount : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getFailed() {
|
||||||
|
return 0; // 当前响应格式不包含失败计数,返回0作为默认值
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,9 @@
|
|||||||
package com.ycwl.basic.integration.scenic.example;
|
package com.ycwl.basic.integration.scenic.example;
|
||||||
|
|
||||||
import com.ycwl.basic.integration.scenic.dto.config.CreateConfigRequest;
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.scenic.dto.filter.FilterCondition;
|
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
import com.ycwl.basic.integration.scenic.dto.filter.*;
|
||||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
import com.ycwl.basic.integration.scenic.dto.scenic.*;
|
||||||
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
||||||
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -11,10 +11,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ZT-Scenic集成服务使用示例
|
* 景区集成示例(包含降级机制)
|
||||||
* 仅供参考,实际使用时根据业务需要调用相应的服务方法
|
* 演示景区集成和失败降级策略的使用
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -23,6 +25,9 @@ public class ScenicIntegrationExample {
|
|||||||
|
|
||||||
private final ScenicIntegrationService scenicIntegrationService;
|
private final ScenicIntegrationService scenicIntegrationService;
|
||||||
private final ScenicConfigIntegrationService scenicConfigIntegrationService;
|
private final ScenicConfigIntegrationService scenicConfigIntegrationService;
|
||||||
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-scenic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 示例:创建景区并设置配置
|
* 示例:创建景区并设置配置
|
||||||
@@ -75,4 +80,105 @@ public class ScenicIntegrationExample {
|
|||||||
log.error("筛选景区失败", e);
|
log.error("筛选景区失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示基础景区操作的降级机制
|
||||||
|
*/
|
||||||
|
public void basicScenicOperationsExample() {
|
||||||
|
log.info("=== 基础景区操作示例(含降级机制) ===");
|
||||||
|
|
||||||
|
Long scenicId = 2001L;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取景区信息 - 自动降级
|
||||||
|
ScenicV2DTO scenic = scenicIntegrationService.getScenic(scenicId);
|
||||||
|
log.info("获取景区成功: {}", scenic.getName());
|
||||||
|
|
||||||
|
// 获取景区配置信息 - 自动降级
|
||||||
|
ScenicV2WithConfigDTO scenicWithConfig = scenicIntegrationService.getScenicWithConfig(scenicId);
|
||||||
|
log.info("获取景区配置成功,配置数量: {}", scenicWithConfig.getConfig().size());
|
||||||
|
|
||||||
|
// 获取扁平化配置 - 自动降级
|
||||||
|
Map<String, Object> flatConfig = scenicIntegrationService.getScenicFlatConfig(scenicId);
|
||||||
|
log.info("获取扁平化配置成功,配置项数量: {}", flatConfig.size());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("景区操作降级失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示景区配置管理的降级机制
|
||||||
|
*/
|
||||||
|
public void scenicConfigManagementFallbackExample() {
|
||||||
|
log.info("=== 景区配置管理示例(含降级机制) ===");
|
||||||
|
|
||||||
|
Long scenicId = 2001L;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取扁平化配置 - 自动降级
|
||||||
|
Map<String, Object> flatConfigs = scenicConfigIntegrationService.getFlatConfigs(scenicId);
|
||||||
|
log.info("获取扁平化配置成功,配置项数量: {}", flatConfigs.size());
|
||||||
|
|
||||||
|
// 批量更新配置 - 直接操作,失败时抛出异常
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("max_visitors", "5000");
|
||||||
|
updates.put("opening_hours", "08:00-18:00");
|
||||||
|
|
||||||
|
BatchUpdateResponse result = scenicConfigIntegrationService.batchFlatUpdateConfigs(scenicId, updates);
|
||||||
|
log.info("批量更新配置完成: 成功 {}, 失败 {}", result.getSuccess(), result.getFailed());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("景区配置管理操作失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示降级缓存管理
|
||||||
|
*/
|
||||||
|
public void fallbackCacheManagementExample() {
|
||||||
|
log.info("=== 景区降级缓存管理示例 ===");
|
||||||
|
|
||||||
|
String scenicCacheKey = "scenic:2001";
|
||||||
|
String configCacheKey = "scenic:flat:configs:2001";
|
||||||
|
|
||||||
|
// 检查降级缓存状态
|
||||||
|
boolean hasScenicCache = fallbackService.hasFallbackCache(SERVICE_NAME, scenicCacheKey);
|
||||||
|
boolean hasConfigCache = fallbackService.hasFallbackCache(SERVICE_NAME, configCacheKey);
|
||||||
|
|
||||||
|
log.info("景区降级缓存存在: {}", hasScenicCache);
|
||||||
|
log.info("配置降级缓存存在: {}", hasConfigCache);
|
||||||
|
|
||||||
|
// 获取降级缓存统计信息
|
||||||
|
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats(SERVICE_NAME);
|
||||||
|
log.info("景区服务降级缓存统计: 缓存数量={}, TTL={}天",
|
||||||
|
stats.getTotalCacheCount(), stats.getFallbackTtlDays());
|
||||||
|
|
||||||
|
// 清理特定的降级缓存
|
||||||
|
if (hasScenicCache) {
|
||||||
|
fallbackService.clearFallbackCache(SERVICE_NAME, scenicCacheKey);
|
||||||
|
log.info("已清理景区降级缓存");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果缓存过多,批量清理
|
||||||
|
if (stats.getTotalCacheCount() > 50) {
|
||||||
|
fallbackService.clearAllFallbackCache(SERVICE_NAME);
|
||||||
|
log.info("已批量清理所有景区降级缓存");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行所有示例
|
||||||
|
*/
|
||||||
|
public void runAllExamples() {
|
||||||
|
log.info("开始运行景区集成示例(包含降级机制)...");
|
||||||
|
|
||||||
|
createScenicWithConfig();
|
||||||
|
filterScenics();
|
||||||
|
basicScenicOperationsExample();
|
||||||
|
scenicConfigManagementFallbackExample();
|
||||||
|
fallbackCacheManagementExample();
|
||||||
|
|
||||||
|
log.info("景区集成示例运行完成");
|
||||||
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@ package com.ycwl.basic.integration.scenic.service;
|
|||||||
|
|
||||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||||
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
||||||
import com.ycwl.basic.integration.scenic.dto.config.*;
|
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -17,6 +18,9 @@ import java.util.Map;
|
|||||||
public class ScenicConfigIntegrationService {
|
public class ScenicConfigIntegrationService {
|
||||||
|
|
||||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||||
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-scenic";
|
||||||
|
|
||||||
public List<ScenicConfigV2DTO> listConfigs(Long scenicId) {
|
public List<ScenicConfigV2DTO> listConfigs(Long scenicId) {
|
||||||
log.info("获取景区配置列表, scenicId: {}", scenicId);
|
log.info("获取景区配置列表, scenicId: {}", scenicId);
|
||||||
@@ -32,8 +36,15 @@ public class ScenicConfigIntegrationService {
|
|||||||
|
|
||||||
public Map<String, Object> getFlatConfigs(Long scenicId) {
|
public Map<String, Object> getFlatConfigs(Long scenicId) {
|
||||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
return fallbackService.executeWithFallback(
|
||||||
return handleResponse(response, "获取景区扁平化配置失败");
|
SERVICE_NAME,
|
||||||
|
"scenic:flat:configs:" + scenicId,
|
||||||
|
() -> {
|
||||||
|
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||||
|
return handleResponse(response, "获取景区扁平化配置失败");
|
||||||
|
},
|
||||||
|
Map.class
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScenicConfigV2DTO createConfig(Long scenicId, CreateConfigRequest request) {
|
public ScenicConfigV2DTO createConfig(Long scenicId, CreateConfigRequest request) {
|
||||||
|
@@ -2,6 +2,7 @@ package com.ycwl.basic.integration.scenic.service;
|
|||||||
|
|
||||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||||
|
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||||
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
||||||
import com.ycwl.basic.integration.scenic.client.ScenicV2Client;
|
import com.ycwl.basic.integration.scenic.client.ScenicV2Client;
|
||||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
||||||
@@ -25,23 +26,47 @@ public class ScenicIntegrationService {
|
|||||||
|
|
||||||
private final ScenicV2Client scenicV2Client;
|
private final ScenicV2Client scenicV2Client;
|
||||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||||
|
private final IntegrationFallbackService fallbackService;
|
||||||
|
|
||||||
|
private static final String SERVICE_NAME = "zt-scenic";
|
||||||
|
|
||||||
public ScenicV2DTO getScenic(Long scenicId) {
|
public ScenicV2DTO getScenic(Long scenicId) {
|
||||||
log.info("获取景区信息, scenicId: {}", scenicId);
|
log.info("获取景区信息, scenicId: {}", scenicId);
|
||||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.getScenic(scenicId);
|
return fallbackService.executeWithFallback(
|
||||||
return handleResponse(response, "获取景区信息失败");
|
SERVICE_NAME,
|
||||||
|
"scenic:" + scenicId,
|
||||||
|
() -> {
|
||||||
|
CommonResponse<ScenicV2DTO> response = scenicV2Client.getScenic(scenicId);
|
||||||
|
return handleResponse(response, "获取景区信息失败");
|
||||||
|
},
|
||||||
|
ScenicV2DTO.class
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScenicV2WithConfigDTO getScenicWithConfig(Long scenicId) {
|
public ScenicV2WithConfigDTO getScenicWithConfig(Long scenicId) {
|
||||||
log.info("获取景区配置信息, scenicId: {}", scenicId);
|
log.info("获取景区配置信息, scenicId: {}", scenicId);
|
||||||
CommonResponse<ScenicV2WithConfigDTO> response = scenicV2Client.getScenicWithConfig(scenicId);
|
return fallbackService.executeWithFallback(
|
||||||
return handleResponse(response, "获取景区配置信息失败");
|
SERVICE_NAME,
|
||||||
|
"scenic:config:" + scenicId,
|
||||||
|
() -> {
|
||||||
|
CommonResponse<ScenicV2WithConfigDTO> response = scenicV2Client.getScenicWithConfig(scenicId);
|
||||||
|
return handleResponse(response, "获取景区配置信息失败");
|
||||||
|
},
|
||||||
|
ScenicV2WithConfigDTO.class
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getScenicFlatConfig(Long scenicId) {
|
public Map<String, Object> getScenicFlatConfig(Long scenicId) {
|
||||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
return fallbackService.executeWithFallback(
|
||||||
return handleResponse(response, "获取景区扁平化配置失败");
|
SERVICE_NAME,
|
||||||
|
"scenic:flat:config:" + scenicId,
|
||||||
|
() -> {
|
||||||
|
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||||
|
return handleResponse(response, "获取景区扁平化配置失败");
|
||||||
|
},
|
||||||
|
Map.class
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScenicV2DTO createScenic(CreateScenicRequest request) {
|
public ScenicV2DTO createScenic(CreateScenicRequest request) {
|
||||||
|
Reference in New Issue
Block a user