refactor(integration): 重构集成服务的降级机制

-移除各服务自定义的降级服务类,统一降级逻辑
- 新增 IntegrationFallbackService作为通用降级服务
- 更新设备和景区服务的降级处理方式
- 优化降级缓存管理,增加统计信息和批量清理功能
- 调整 API 接口,移除扁平化批量更新等相关方法
This commit is contained in:
2025-09-02 12:24:55 +08:00
parent d35a1facbd
commit 8c8a6baa5e
13 changed files with 757 additions and 260 deletions

View File

@@ -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());
}
}
/** /**
* 删除设备配置 * 删除设备配置

View File

@@ -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

View File

@@ -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;
}
} }

View File

@@ -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;
}
}

View File

@@ -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);
} }

View File

@@ -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("设备集成示例运行完成");
} }
} }

View File

@@ -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, "批量更新设备配置失败");
} }
/** /**
* 获取设备特定配置值 * 获取设备特定配置值
*/ */

View File

@@ -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));
}
}

View File

@@ -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,

View File

@@ -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作为默认值
}
} }

View File

@@ -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("景区集成示例运行完成");
}
} }

View File

@@ -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) {

View File

@@ -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) {