You've already forked FrameTour-BE
Merge branch 'device-microservice'
All checks were successful
ZhenTu-BE/pipeline/head This commit looks good
All checks were successful
ZhenTu-BE/pipeline/head This commit looks good
# Conflicts: # src/main/java/com/ycwl/basic/integration/scenic/service/ScenicConfigIntegrationService.java # src/main/java/com/ycwl/basic/integration/scenic/service/ScenicIntegrationService.java
This commit is contained in:
104
CLAUDE.md
104
CLAUDE.md
@@ -122,6 +122,15 @@ mvn test -DskipTests=false
|
||||
3. 使用 `@Scheduled` 进行基于 cron 的执行
|
||||
4. 遵循现有的错误处理和日志记录模式
|
||||
|
||||
### 多端API架构
|
||||
应用程序通过路径前缀区分不同的客户端:
|
||||
- **移动端**: `/api/mobile/*` - 针对移动应用优化的接口
|
||||
- **PC管理端**: `/api/*` - Web管理面板接口
|
||||
- **任务处理**: `/task/*` - 后台任务和渲染服务接口
|
||||
- **外部集成**: 专用集成接口(打印机、代理、viid、vpt、wvp等)
|
||||
|
||||
每个端点都有对应的Controller包结构,确保API的职责分离和维护性。
|
||||
|
||||
## 价格查询系统 (Pricing Module)
|
||||
|
||||
### 核心架构
|
||||
@@ -187,4 +196,97 @@ ProductType枚举定义了支持的商品类型:
|
||||
- 单元测试:每个服务类都有对应测试类
|
||||
- 配置验证测试:DefaultConfigValidationTest验证default配置
|
||||
- JSON序列化测试:验证复杂对象的数据库存储
|
||||
- 分页功能测试:验证PageHelper集成
|
||||
- 分页功能测试:验证PageHelper集成
|
||||
|
||||
## 关键架构模式
|
||||
|
||||
### Repository 层模式
|
||||
项目使用Repository层抽象数据访问逻辑:
|
||||
- Repository接口定义数据访问契约
|
||||
- Mapper接口处理MyBatis Plus的数据库映射
|
||||
- Service层通过Repository访问数据,避免直接依赖Mapper
|
||||
|
||||
### 异常处理架构
|
||||
- **全局异常处理**: `CustomExceptionHandle` 提供统一的异常处理和响应格式
|
||||
- **业务异常**: 自定义异常类继承RuntimeException,携带业务错误码
|
||||
- **集成异常**: `IntegrationException` 专门处理外部服务调用异常
|
||||
|
||||
### 配置驱动的扩展性
|
||||
通过配置文件驱动的多供应商支持:
|
||||
- 存储:本地、AWS S3、阿里云 OSS
|
||||
- 支付:微信支付、聪明支付
|
||||
- 人脸识别:阿里云、百度
|
||||
每个供应商通过统一接口访问,配置切换无需代码修改。
|
||||
|
||||
### 业务层架构
|
||||
- **Service层**: 核心业务逻辑实现
|
||||
- **Biz层**: 高级业务流程编排,组合多个Service
|
||||
- **Controller层**: HTTP请求处理和响应转换
|
||||
- **Repository层**: 数据访问抽象
|
||||
|
||||
### 认证和会话管理
|
||||
- **JWT**: 使用jjwt库进行身份验证
|
||||
- **Redis**: 存储会话信息和缓存
|
||||
- **BaseContextHandler**: 提供当前用户上下文访问
|
||||
|
||||
## 微服务集成架构 (Integration Package)
|
||||
|
||||
### 核心架构
|
||||
位于 `com.ycwl.basic.integration` 包,使用 Spring Cloud OpenFeign 和 Nacos 实现外部微服务集成。
|
||||
|
||||
#### 通用基础设施
|
||||
- **IntegrationProperties**: 所有集成的集中配置管理
|
||||
- **FeignErrorDecoder**: 自定义错误解码器,统一错误处理
|
||||
- **IntegrationException**: 标准化集成异常
|
||||
- **CommonResponse/PageResponse**: 外部服务响应包装器
|
||||
|
||||
#### 已实现的服务集成
|
||||
- **Scenic Integration** (`integration.scenic`): ZT-Scenic 微服务集成
|
||||
- **Device Integration** (`integration.device`): ZT-Device 微服务集成
|
||||
|
||||
#### 集成模式
|
||||
每个外部服务按以下结构组织:
|
||||
```
|
||||
service/
|
||||
├── client/ # Feign 客户端
|
||||
├── config/ # 服务特定配置
|
||||
├── dto/ # 数据传输对象
|
||||
├── service/ # 业务逻辑层
|
||||
└── example/ # 使用示例
|
||||
```
|
||||
|
||||
### 配置管理
|
||||
```yaml
|
||||
integration:
|
||||
scenic:
|
||||
enabled: true
|
||||
serviceName: zt-scenic
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
device:
|
||||
enabled: true
|
||||
serviceName: zt-device
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
```
|
||||
|
||||
### 使用模式
|
||||
所有集成服务使用统一的 `handleResponse` 模式进行错误处理,确保一致的异常包装和日志记录。
|
||||
|
||||
### 测试集成服务
|
||||
```bash
|
||||
# 运行特定集成测试
|
||||
mvn test -Dtest=ScenicIntegrationServiceTest
|
||||
mvn test -Dtest=DeviceIntegrationServiceTest
|
||||
|
||||
# 运行所有集成测试
|
||||
mvn test -Dtest="com.ycwl.basic.integration.*Test"
|
||||
```
|
||||
|
||||
### 调试集成问题
|
||||
启用 Feign 客户端日志:
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
com.ycwl.basic.integration: DEBUG
|
||||
```
|
@@ -11,7 +11,7 @@ import com.ycwl.basic.pricing.service.ICouponService;
|
||||
import com.ycwl.basic.pricing.service.VoucherPrintService;
|
||||
import com.ycwl.basic.repository.FaceRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
@@ -1,79 +0,0 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceBatchSortRequest;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceSortRequest;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.model.pc.template.req.TemplateSortRequest;
|
||||
import com.ycwl.basic.service.pc.DeviceService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/2 16:13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/device/v1")
|
||||
// 设备管理
|
||||
public class DeviceController {
|
||||
@Autowired
|
||||
private DeviceService deviceService;
|
||||
|
||||
// 设备分页查询
|
||||
@PostMapping("/page")
|
||||
public ApiResponse<PageInfo<DeviceRespVO>> pageQuery(@RequestBody DeviceReqQuery deviceReqQuery) {
|
||||
return deviceService.pageQuery(deviceReqQuery);
|
||||
}
|
||||
// 设备列表查询
|
||||
@PostMapping("/list")
|
||||
public ApiResponse list(@RequestBody DeviceReqQuery deviceReqQuery) {
|
||||
return deviceService.list(deviceReqQuery);
|
||||
}
|
||||
// 设备详情查询
|
||||
@GetMapping("/getDetails/{id}")
|
||||
public ApiResponse<DeviceRespVO> getDetails(@PathVariable("id") Long id) {
|
||||
return deviceService.getById(id);
|
||||
}
|
||||
// 新增或修改设备
|
||||
@PostMapping("/addOrUpdate")
|
||||
public ApiResponse addOrUpdate(@RequestBody DeviceAddOrUpdateReq deviceReqQuery) {
|
||||
return deviceService.addOrUpdate(deviceReqQuery);
|
||||
}
|
||||
// 删除设备
|
||||
@DeleteMapping("/delete/{id}")
|
||||
public ApiResponse delete(@PathVariable("id") Long id) {
|
||||
return deviceService.deleteById(id);
|
||||
}
|
||||
// 修改设备状态
|
||||
@PutMapping("/updateStatus/{id}")
|
||||
public ApiResponse updateStatus(@PathVariable("id") Long id) {
|
||||
return deviceService.updateStatus(id);
|
||||
}
|
||||
|
||||
// 排序设备
|
||||
@PostMapping("/sort")
|
||||
public ApiResponse<Boolean> sortDevice(@RequestBody DeviceSortRequest request) {
|
||||
return deviceService.sortDevice(request.getDeviceId(), request.getAfterDeviceId());
|
||||
}
|
||||
|
||||
@PostMapping("/scenic/{scenicId}/sortBatch")
|
||||
public ApiResponse<Boolean> sortDeviceBatch(@PathVariable("scenicId") Long scenicId, @RequestBody DeviceBatchSortRequest request) {
|
||||
return deviceService.batchSort(scenicId, request);
|
||||
}
|
||||
|
||||
@GetMapping("/config/{id}")
|
||||
public ApiResponse<DeviceConfigEntity> getConfig(@PathVariable("id") Long id) {
|
||||
return ApiResponse.success(deviceService.getConfig(id));
|
||||
}
|
||||
|
||||
@PostMapping("/saveConfig/{configId}")
|
||||
public ApiResponse saveConfig(@PathVariable("configId") Long configId, @RequestBody DeviceConfigEntity deviceConfigEntity) {
|
||||
deviceService.saveConfig(configId, deviceConfigEntity);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,487 @@
|
||||
package com.ycwl.basic.controller.pc;
|
||||
|
||||
import com.ycwl.basic.integration.device.dto.config.*;
|
||||
import com.ycwl.basic.integration.device.dto.device.*;
|
||||
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备管理 V2 版本控制器 - 基于 zt-device 集成服务
|
||||
*
|
||||
* @author Claude Code
|
||||
* @date 2025-09-01
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/device/v2")
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceV2Controller {
|
||||
|
||||
private final DeviceIntegrationService deviceIntegrationService;
|
||||
private final DeviceConfigIntegrationService deviceConfigIntegrationService;
|
||||
|
||||
// ========== 设备基础 CRUD 操作 ==========
|
||||
|
||||
/**
|
||||
* 设备V2核心信息分页列表
|
||||
*/
|
||||
@GetMapping("/")
|
||||
public ApiResponse<DeviceV2ListResponse> listDevices(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(required = false) String no,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) Integer isActive,
|
||||
@RequestParam(required = false) Long scenicId) {
|
||||
log.info("分页查询设备核心信息列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
|
||||
// 参数验证:限制pageSize最大值为100
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
|
||||
try {
|
||||
DeviceV2ListResponse response = deviceIntegrationService.listDevices(page, pageSize, name, no, type, isActive, scenicId);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询设备核心信息列表失败", e);
|
||||
return ApiResponse.fail("分页查询设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备V2带配置信息分页列表
|
||||
*/
|
||||
@GetMapping("/with-config")
|
||||
public ApiResponse<DeviceV2WithConfigListResponse> listDevicesWithConfig(@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(required = false) String no,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) Integer isActive,
|
||||
@RequestParam(required = false) Long scenicId) {
|
||||
log.info("分页查询设备带配置信息列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
|
||||
// 参数验证:限制pageSize最大值为100
|
||||
if (pageSize > 100) {
|
||||
pageSize = 100;
|
||||
}
|
||||
|
||||
try {
|
||||
DeviceV2WithConfigListResponse response = deviceIntegrationService.listDevicesWithConfig(page, pageSize, name, no, type, isActive, scenicId);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询设备带配置信息列表失败", e);
|
||||
return ApiResponse.fail("分页查询设备带配置信息列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取设备信息
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public ApiResponse<DeviceV2DTO> getDevice(@PathVariable Long id) {
|
||||
log.info("获取设备信息, id: {}", id);
|
||||
try {
|
||||
DeviceV2DTO device = deviceIntegrationService.getDevice(id);
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("获取设备信息失败, id: {}", id, e);
|
||||
return ApiResponse.fail("获取设备信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取设备带配置信息
|
||||
*/
|
||||
@GetMapping("/{id}/with-config")
|
||||
public ApiResponse<DeviceV2WithConfigDTO> getDeviceWithConfig(@PathVariable Long id) {
|
||||
log.info("获取设备配置信息, id: {}", id);
|
||||
try {
|
||||
DeviceV2WithConfigDTO device = deviceIntegrationService.getDeviceWithConfig(id);
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("获取设备配置信息失败, id: {}", id, e);
|
||||
return ApiResponse.fail("获取设备配置信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备信息
|
||||
*/
|
||||
@GetMapping("/no/{no}")
|
||||
public ApiResponse<DeviceV2DTO> getDeviceByNo(@PathVariable String no) {
|
||||
log.info("根据设备编号获取设备信息, no: {}", no);
|
||||
try {
|
||||
DeviceV2DTO device = deviceIntegrationService.getDeviceByNo(no);
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("根据设备编号获取设备信息失败, no: {}", no, e);
|
||||
return ApiResponse.fail("根据设备编号获取设备信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备带配置信息
|
||||
*/
|
||||
@GetMapping("/no/{no}/with-config")
|
||||
public ApiResponse<DeviceV2WithConfigDTO> getDeviceWithConfigByNo(@PathVariable String no) {
|
||||
log.info("根据设备编号获取设备配置信息, no: {}", no);
|
||||
try {
|
||||
DeviceV2WithConfigDTO device = deviceIntegrationService.getDeviceWithConfigByNo(no);
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("根据设备编号获取设备配置信息失败, no: {}", no, e);
|
||||
return ApiResponse.fail("根据设备编号获取设备配置信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备
|
||||
*/
|
||||
@PostMapping("/")
|
||||
public ApiResponse<DeviceV2DTO> createDevice(@Valid @RequestBody CreateDeviceRequest request) {
|
||||
log.info("创建设备, name: {}, no: {}, type: {}, sort: {}",
|
||||
request.getName(), request.getNo(), request.getType(), request.getSort());
|
||||
try {
|
||||
DeviceV2DTO device = deviceIntegrationService.createDevice(request);
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("创建设备失败", e);
|
||||
return ApiResponse.fail("创建设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建IPC摄像头设备(快捷方法)
|
||||
*/
|
||||
@PostMapping("/ipc")
|
||||
public ApiResponse<DeviceV2DTO> createIpcDevice(@RequestBody Map<String, Object> request) {
|
||||
String name = (String) request.get("name");
|
||||
String deviceNo = (String) request.get("no");
|
||||
Long scenicId = Long.valueOf(request.get("scenicId").toString());
|
||||
Integer sort = request.get("sort") != null ? Integer.valueOf(request.get("sort").toString()) : null;
|
||||
|
||||
log.info("创建IPC摄像头设备, name: {}, no: {}, scenicId: {}, sort: {}", name, deviceNo, scenicId, sort);
|
||||
try {
|
||||
DeviceV2DTO device;
|
||||
if (sort != null) {
|
||||
device = deviceIntegrationService.createIpcDeviceWithSort(name, deviceNo, scenicId, sort);
|
||||
} else {
|
||||
device = deviceIntegrationService.createIpcDevice(name, deviceNo, scenicId);
|
||||
}
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("创建IPC摄像头设备失败", e);
|
||||
return ApiResponse.fail("创建IPC摄像头设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义设备(快捷方法)
|
||||
*/
|
||||
@PostMapping("/custom")
|
||||
public ApiResponse<DeviceV2DTO> createCustomDevice(@RequestBody Map<String, Object> request) {
|
||||
String name = (String) request.get("name");
|
||||
String deviceNo = (String) request.get("no");
|
||||
Long scenicId = Long.valueOf(request.get("scenicId").toString());
|
||||
Integer sort = request.get("sort") != null ? Integer.valueOf(request.get("sort").toString()) : null;
|
||||
|
||||
log.info("创建自定义设备, name: {}, no: {}, scenicId: {}, sort: {}", name, deviceNo, scenicId, sort);
|
||||
try {
|
||||
DeviceV2DTO device;
|
||||
if (sort != null) {
|
||||
device = deviceIntegrationService.createCustomDeviceWithSort(name, deviceNo, scenicId, sort);
|
||||
} else {
|
||||
device = deviceIntegrationService.createCustomDevice(name, deviceNo, scenicId);
|
||||
}
|
||||
return ApiResponse.success(device);
|
||||
} catch (Exception e) {
|
||||
log.error("创建自定义设备失败", e);
|
||||
return ApiResponse.fail("创建自定义设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备信息
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
public ApiResponse<String> updateDevice(@PathVariable Long id, @Valid @RequestBody UpdateDeviceRequest request) {
|
||||
log.info("更新设备信息, id: {}", id);
|
||||
try {
|
||||
deviceIntegrationService.updateDevice(id, request);
|
||||
return ApiResponse.success("设备信息更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新设备信息失败, id: {}", id, e);
|
||||
return ApiResponse.fail("更新设备信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备排序
|
||||
*/
|
||||
@PutMapping("/{id}/sort")
|
||||
public ApiResponse<String> updateDeviceSort(@PathVariable Long id, @RequestBody Map<String, Integer> request) {
|
||||
Integer sort = request.get("sort");
|
||||
log.info("更新设备排序, id: {}, sort: {}", id, sort);
|
||||
try {
|
||||
deviceIntegrationService.updateDeviceSort(id, sort);
|
||||
return ApiResponse.success("设备排序更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新设备排序失败, id: {}, sort: {}", id, sort, e);
|
||||
return ApiResponse.fail("更新设备排序失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用设备
|
||||
*/
|
||||
@PutMapping("/{id}/enable")
|
||||
public ApiResponse<String> enableDevice(@PathVariable Long id) {
|
||||
log.info("启用设备, id: {}", id);
|
||||
try {
|
||||
deviceIntegrationService.enableDevice(id);
|
||||
return ApiResponse.success("设备启用成功");
|
||||
} catch (Exception e) {
|
||||
log.error("启用设备失败, id: {}", id, e);
|
||||
return ApiResponse.fail("启用设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用设备
|
||||
*/
|
||||
@PutMapping("/{id}/disable")
|
||||
public ApiResponse<String> disableDevice(@PathVariable Long id) {
|
||||
log.info("禁用设备, id: {}", id);
|
||||
try {
|
||||
deviceIntegrationService.disableDevice(id);
|
||||
return ApiResponse.success("设备禁用成功");
|
||||
} catch (Exception e) {
|
||||
log.error("禁用设备失败, id: {}", id, e);
|
||||
return ApiResponse.fail("禁用设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResponse<String> deleteDevice(@PathVariable Long id) {
|
||||
log.info("删除设备, id: {}", id);
|
||||
try {
|
||||
deviceIntegrationService.deleteDevice(id);
|
||||
return ApiResponse.success("设备删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除设备失败, id: {}", id, e);
|
||||
return ApiResponse.fail("删除设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 设备配置管理操作 ==========
|
||||
|
||||
/**
|
||||
* 获取设备配置列表
|
||||
*/
|
||||
@GetMapping("/{id}/config")
|
||||
public ApiResponse<List<DeviceConfigV2DTO>> getDeviceConfigs(@PathVariable Long id) {
|
||||
log.info("获取设备配置列表, deviceId: {}", id);
|
||||
try {
|
||||
List<DeviceConfigV2DTO> configs = deviceConfigIntegrationService.getDeviceConfigs(id);
|
||||
return ApiResponse.success(configs);
|
||||
} catch (Exception e) {
|
||||
log.error("获取设备配置列表失败, deviceId: {}", id, e);
|
||||
return ApiResponse.fail("获取设备配置列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备扁平化配置
|
||||
*/
|
||||
@GetMapping("/{id}/flat-config")
|
||||
public ApiResponse<Map<String, Object>> getDeviceFlatConfig(@PathVariable Long id) {
|
||||
log.info("获取设备扁平化配置, deviceId: {}", id);
|
||||
try {
|
||||
Map<String, Object> config = deviceConfigIntegrationService.getDeviceFlatConfig(id);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("获取设备扁平化配置失败, deviceId: {}", id, e);
|
||||
return ApiResponse.fail("获取设备扁平化配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键获取配置
|
||||
*/
|
||||
@GetMapping("/{id}/config/{configKey}")
|
||||
public ApiResponse<DeviceConfigV2DTO> getDeviceConfigByKey(@PathVariable Long id,
|
||||
@PathVariable String configKey) {
|
||||
log.info("根据键获取设备配置, deviceId: {}, configKey: {}", id, configKey);
|
||||
try {
|
||||
DeviceConfigV2DTO config = deviceConfigIntegrationService.getDeviceConfigByKey(id, configKey);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("根据键获取设备配置失败, deviceId: {}, configKey: {}", id, configKey, e);
|
||||
return ApiResponse.fail("根据键获取设备配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备编号获取配置列表
|
||||
*/
|
||||
@GetMapping("/no/{no}/config")
|
||||
public ApiResponse<List<DeviceConfigV2DTO>> getDeviceConfigsByNo(@PathVariable String no) {
|
||||
log.info("根据设备编号获取配置列表, deviceNo: {}", no);
|
||||
try {
|
||||
List<DeviceConfigV2DTO> configs = deviceConfigIntegrationService.getDeviceConfigsByNo(no);
|
||||
return ApiResponse.success(configs);
|
||||
} catch (Exception e) {
|
||||
log.error("根据设备编号获取配置列表失败, deviceNo: {}", no, e);
|
||||
return ApiResponse.fail("根据设备编号获取配置列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备编号获取扁平化配置
|
||||
*/
|
||||
@GetMapping("/no/{no}/flat-config")
|
||||
public ApiResponse<Map<String, Object>> getDeviceFlatConfigByNo(@PathVariable String no) {
|
||||
log.info("根据设备编号获取扁平化配置, deviceNo: {}", no);
|
||||
try {
|
||||
Map<String, Object> config = deviceConfigIntegrationService.getDeviceFlatConfigByNo(no);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("根据设备编号获取扁平化配置失败, deviceNo: {}", no, e);
|
||||
return ApiResponse.fail("根据设备编号获取扁平化配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备配置
|
||||
*/
|
||||
@PostMapping("/{id}/config")
|
||||
public ApiResponse<DeviceConfigV2DTO> createDeviceConfig(@PathVariable Long id,
|
||||
@Valid @RequestBody CreateDeviceConfigRequest request) {
|
||||
log.info("创建设备配置, deviceId: {}, configKey: {}", id, request.getConfigKey());
|
||||
try {
|
||||
DeviceConfigV2DTO config = deviceConfigIntegrationService.createDeviceConfig(id, request);
|
||||
return ApiResponse.success(config);
|
||||
} catch (Exception e) {
|
||||
log.error("创建设备配置失败, deviceId: {}, configKey: {}", id, request.getConfigKey(), e);
|
||||
return ApiResponse.fail("创建设备配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建/更新设备配置
|
||||
*/
|
||||
@PostMapping("/{id}/config/batch")
|
||||
public ApiResponse<BatchUpdateResponse> batchUpdateDeviceConfig(@PathVariable Long id,
|
||||
@Valid @RequestBody BatchDeviceConfigRequest request) {
|
||||
log.info("批量更新设备配置, deviceId: {}, configs count: {}", id, request.getConfigs().size());
|
||||
try {
|
||||
BatchUpdateResponse result = deviceConfigIntegrationService.batchUpdateDeviceConfig(id, request);
|
||||
return ApiResponse.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新设备配置失败, deviceId: {}", id, e);
|
||||
return ApiResponse.fail("批量更新设备配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新设备配置
|
||||
*/
|
||||
@PutMapping("/{id}/config/{configId}")
|
||||
public ApiResponse<String> updateDeviceConfig(@PathVariable Long id, @PathVariable Long configId,
|
||||
@Valid @RequestBody UpdateDeviceConfigRequest request) {
|
||||
log.info("更新设备配置, deviceId: {}, configId: {}", id, configId);
|
||||
try {
|
||||
deviceConfigIntegrationService.updateDeviceConfig(id, configId, request);
|
||||
return ApiResponse.success("设备配置更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新设备配置失败, deviceId: {}, configId: {}", id, configId, e);
|
||||
return ApiResponse.fail("更新设备配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除设备配置
|
||||
*/
|
||||
@DeleteMapping("/{id}/config/{configId}")
|
||||
public ApiResponse<String> deleteDeviceConfig(@PathVariable Long id, @PathVariable Long configId) {
|
||||
log.info("删除设备配置, deviceId: {}, configId: {}", id, configId);
|
||||
try {
|
||||
deviceConfigIntegrationService.deleteDeviceConfig(id, configId);
|
||||
return ApiResponse.success("设备配置删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除设备配置失败, deviceId: {}, configId: {}", id, configId, e);
|
||||
return ApiResponse.fail("删除设备配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 景区设备管理操作 ==========
|
||||
|
||||
/**
|
||||
* 获取景区IPC设备列表
|
||||
*/
|
||||
@GetMapping("/scenic/{scenicId}/ipc")
|
||||
public ApiResponse<DeviceV2ListResponse> getScenicIpcDevices(@PathVariable Long scenicId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
log.info("获取景区IPC设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
|
||||
try {
|
||||
DeviceV2ListResponse response = deviceIntegrationService.getScenicIpcDevices(scenicId, page, pageSize);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区IPC设备列表失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区IPC设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区激活设备列表
|
||||
*/
|
||||
@GetMapping("/scenic/{scenicId}/active")
|
||||
public ApiResponse<DeviceV2ListResponse> getScenicActiveDevices(@PathVariable Long scenicId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
log.info("获取景区激活设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
|
||||
try {
|
||||
DeviceV2ListResponse response = deviceIntegrationService.getScenicActiveDevices(scenicId, page, pageSize);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区激活设备列表失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区激活设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区所有设备列表
|
||||
*/
|
||||
@GetMapping("/scenic/{scenicId}/all")
|
||||
public ApiResponse<DeviceV2ListResponse> getScenicAllDevices(@PathVariable Long scenicId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
log.info("获取景区所有设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
|
||||
try {
|
||||
DeviceV2ListResponse response = deviceIntegrationService.listDevices(page, pageSize, null, null, null, null, scenicId);
|
||||
return ApiResponse.success(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取景区所有设备列表失败, scenicId: {}", scenicId, e);
|
||||
return ApiResponse.fail("获取景区所有设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,12 +4,16 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.thread.ThreadFactoryBuilder;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.annotation.IgnoreLogReq;
|
||||
import com.ycwl.basic.annotation.IgnoreToken;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.facebody.entity.AddFaceResp;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.CreateDeviceRequest;
|
||||
import com.ycwl.basic.integration.device.dto.device.UpdateDeviceRequest;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
@@ -33,7 +37,6 @@ import com.ycwl.basic.model.viid.req.UnRegisterReq;
|
||||
import com.ycwl.basic.model.viid.resp.SystemTimeResp;
|
||||
import com.ycwl.basic.model.viid.resp.VIIDBaseResp;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.service.pc.ScenicService;
|
||||
import com.ycwl.basic.service.task.TaskFaceService;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
@@ -80,15 +83,13 @@ import static com.ycwl.basic.constant.StorageConstant.VIID_FACE;
|
||||
@Slf4j
|
||||
public class ViidController {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
private static final String serverId = "00000000000000000001";
|
||||
@Autowired
|
||||
private SourceMapper sourceMapper;
|
||||
@Autowired
|
||||
private DeviceRepository deviceRepository;
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
@Autowired
|
||||
private TaskFaceService taskFaceService;
|
||||
private final Map<Long, ThreadPoolExecutor> executors = new ConcurrentHashMap<>();
|
||||
@Autowired
|
||||
@@ -129,12 +130,21 @@ public class ViidController {
|
||||
}
|
||||
device.setKeepaliveAt(new Date());
|
||||
device.setIpAddr(IpUtils.getIpAddr(request));
|
||||
if (device.getId() != null) {
|
||||
deviceMapper.updateEntity(device);
|
||||
} else {
|
||||
device.setId(SnowFlakeUtil.getLongId());
|
||||
deviceMapper.addEntity(device);
|
||||
deviceRepository.clearDeviceCache(deviceId);
|
||||
if (device.getId() == null) {
|
||||
// 通过zt-device服务创建新设备
|
||||
CreateDeviceRequest createRequest = new CreateDeviceRequest();
|
||||
createRequest.setName(device.getName());
|
||||
createRequest.setNo(device.getNo());
|
||||
createRequest.setType("IPC"); // 默认类型为IPC
|
||||
createRequest.setIsActive(0);
|
||||
createRequest.setScenicId(0L);
|
||||
createRequest.setSort(0);
|
||||
try {
|
||||
DeviceV2DTO createdDevice = deviceIntegrationService.createDevice(createRequest);
|
||||
device.setId(createdDevice.getId());
|
||||
} catch (Exception e) {
|
||||
log.warn("创建设备失败,设备编号: {}, 错误: {}", deviceId, e.getMessage());
|
||||
}
|
||||
}
|
||||
return new VIIDBaseResp(
|
||||
new ResponseStatusObject(serverId, "/VIID/System/Register", "0", "注册成功", sdfTime.format(new Date()))
|
||||
@@ -166,9 +176,20 @@ public class ViidController {
|
||||
device.setOnline(1);
|
||||
device.setKeepaliveAt(new Date());
|
||||
device.setIpAddr(IpUtils.getIpAddr(request));
|
||||
device.setId(SnowFlakeUtil.getLongId());
|
||||
deviceMapper.addEntity(device);
|
||||
deviceRepository.clearDeviceCache(deviceId);
|
||||
// 通过zt-device服务创建新设备
|
||||
CreateDeviceRequest createRequest = new CreateDeviceRequest();
|
||||
createRequest.setName(device.getName());
|
||||
createRequest.setNo(device.getNo());
|
||||
createRequest.setType("IPC"); // 默认类型为IPC
|
||||
createRequest.setIsActive(0);
|
||||
createRequest.setScenicId(0L);
|
||||
createRequest.setSort(0);
|
||||
try {
|
||||
DeviceV2DTO createdDevice = deviceIntegrationService.createDevice(createRequest);
|
||||
device.setId(createdDevice.getId());
|
||||
} catch (Exception e) {
|
||||
log.warn("创建设备失败,设备编号: {}, 错误: {}", deviceId, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
deviceRepository.updateOnlineStatus(device.getId(), IpUtils.getIpAddr(request), 1, new Date());
|
||||
}
|
||||
@@ -245,17 +266,15 @@ public class ViidController {
|
||||
if (device == null) {
|
||||
continue;
|
||||
}
|
||||
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(device.getId());
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(device.getId());
|
||||
DeviceConfigEntity deviceConfigEntity = deviceRepository.getDeviceConfig(device.getId());
|
||||
if (deviceConfig == null) {
|
||||
log.warn("设备配置不存在:" + deviceID);
|
||||
return new VIIDBaseResp(
|
||||
new ResponseStatusObject(faceId, "/VIID/Faces", "0", "OK", sdfTime.format(new Date()))
|
||||
);
|
||||
}
|
||||
int viidMode = 0;
|
||||
if (deviceConfig.getViidType() != null) {
|
||||
viidMode = deviceConfig.getViidType();
|
||||
}
|
||||
Integer viidMode = deviceConfig.getInteger("viid_mode", 0);
|
||||
Date shotTime = null;
|
||||
if (StringUtils.isNotBlank(face.getShotTime())) {
|
||||
try {
|
||||
@@ -333,7 +352,7 @@ public class ViidController {
|
||||
faceSampleMapper.updateScore(faceSample.getId(), addFaceResp.getScore());
|
||||
}
|
||||
}
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("enable_pre_book"))) {
|
||||
DynamicTaskGenerator.addTask(faceSample.getId());
|
||||
}
|
||||
});
|
||||
@@ -352,7 +371,7 @@ public class ViidController {
|
||||
MultipartFile _file = ImageUtils.base64ToMultipartFile(_subImage.getData());
|
||||
ThreadPoolExecutor executor = getExecutor(scenicId);
|
||||
executor.execute(() -> {
|
||||
List<DeviceCropConfig> cropConfigs = deviceConfig._getCropConfig();
|
||||
List<DeviceCropConfig> cropConfigs = deviceConfigEntity._getCropConfig();
|
||||
for (DeviceCropConfig cropConfig : cropConfigs) {
|
||||
source.setId(SnowFlakeUtil.getLongId());
|
||||
String filename = StorageUtil.joinPath(PHOTO_PATH, UUID.randomUUID() + "." + ext);
|
||||
@@ -425,7 +444,7 @@ public class ViidController {
|
||||
faceSampleMapper.updateScore(faceSample.getId(), addFaceResp.getScore());
|
||||
}
|
||||
}
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("enable_pre_book"))) {
|
||||
DynamicTaskGenerator.addTask(faceSample.getId());
|
||||
}
|
||||
});
|
||||
|
@@ -6,7 +6,6 @@ import com.ycwl.basic.constant.StorageConstant;
|
||||
import com.ycwl.basic.device.entity.common.FileObject;
|
||||
import com.ycwl.basic.device.operator.WvpPassiveStorageOperator;
|
||||
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
|
||||
import com.ycwl.basic.service.pc.DeviceService;
|
||||
import com.ycwl.basic.service.pc.ScenicService;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.storage.enums.StorageAcl;
|
||||
@@ -30,15 +29,12 @@ import java.util.List;
|
||||
@RequestMapping("/wvp/v1/")
|
||||
public class WvpController {
|
||||
|
||||
@Autowired
|
||||
private DeviceService deviceService;
|
||||
@Autowired
|
||||
private ScenicService scenicService;
|
||||
|
||||
@IgnoreLogReq
|
||||
@PostMapping("/scenic/{scenicId}/sync")
|
||||
public ApiResponse<List<WvpPassiveStorageOperator.Task>> sync(@PathVariable("scenicId") Long scenicId, @RequestBody WvpSyncReqVo reqVo) {
|
||||
deviceService.updateDevices(scenicId, reqVo);
|
||||
return ApiResponse.success(WvpPassiveStorageOperator.getTaskListByScenicId(scenicId));
|
||||
}
|
||||
|
||||
|
@@ -11,10 +11,13 @@ import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
|
||||
import com.ycwl.basic.device.operator.WvpActiveStorageOperator;
|
||||
import com.ycwl.basic.device.operator.WvpPassiveStorageOperator;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
|
||||
public class DeviceFactory {
|
||||
public static IDeviceStorageOperator getDeviceStorageOperator(DeviceEntity device, DeviceConfigEntity config) {
|
||||
public static IDeviceStorageOperator getDeviceStorageOperator(DeviceV2DTO device, DeviceConfigEntity config) {
|
||||
IDeviceStorageOperator operator = null;
|
||||
if (config == null) {
|
||||
return null;
|
||||
@@ -35,11 +38,34 @@ public class DeviceFactory {
|
||||
if (operator == null) {
|
||||
return null;
|
||||
}
|
||||
operator.setDevice(device);
|
||||
operator.setDevice(convertToEntity(device));
|
||||
operator.setDeviceConfig(config);
|
||||
return operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将DeviceV2DTO转换为DeviceEntity
|
||||
*/
|
||||
private static DeviceEntity convertToEntity(DeviceV2DTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
DeviceEntity entity = new DeviceEntity();
|
||||
entity.setId(dto.getId());
|
||||
entity.setName(dto.getName());
|
||||
entity.setNo(dto.getNo());
|
||||
entity.setScenicId(dto.getScenicId());
|
||||
entity.setStatus(dto.getIsActive());
|
||||
// 转换时间格式:LocalDateTime -> Date
|
||||
if (dto.getCreateTime() != null) {
|
||||
entity.setCreateAt(Date.from(dto.getCreateTime().atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
if (dto.getUpdateTime() != null) {
|
||||
entity.setUpdateAt(Date.from(dto.getUpdateTime().atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static IDeviceStatusChecker getDeviceStatusChecker(DeviceEntity device, DeviceConfigEntity config) {
|
||||
IDeviceStatusChecker checker = null;
|
||||
if (config.getOnlineCheck() <= 0) {
|
||||
|
@@ -84,7 +84,8 @@ public class VptPassiveStorageOperator extends ADeviceStorageOperator {
|
||||
if (StringUtils.isNotBlank(config.getDeviceNo())) {
|
||||
task.deviceNo = config.getDeviceNo();
|
||||
} else {
|
||||
task.deviceNo = device.getNo2();
|
||||
log.warn("设备未配置deviceNo:{}", device);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
task.startTime = startDate;
|
||||
task.endTime = endDate;
|
||||
|
@@ -81,7 +81,8 @@ public class WvpPassiveStorageOperator extends ADeviceStorageOperator {
|
||||
if (StringUtils.isNotBlank(config.getDeviceNo())) {
|
||||
task.deviceNo = config.getDeviceNo();
|
||||
} else {
|
||||
task.deviceNo = device.getNo2();
|
||||
log.warn("设备未配置deviceNo:{}", device);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
task.startTime = startDate;
|
||||
task.endTime = endDate;
|
||||
|
662
src/main/java/com/ycwl/basic/integration/CLAUDE.md
Normal file
662
src/main/java/com/ycwl/basic/integration/CLAUDE.md
Normal file
@@ -0,0 +1,662 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Integration Package Overview
|
||||
|
||||
The integration package (`com.ycwl.basic.integration`) is responsible for external microservice integrations using Spring Cloud OpenFeign and Nacos service discovery. It provides a standardized approach for calling external services with proper error handling, configuration management, and response processing.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
#### Common Infrastructure (`com.ycwl.basic.integration.common`)
|
||||
- **IntegrationProperties**: Centralized configuration properties for all integrations
|
||||
- **FeignConfig**: Global Feign configuration with error decoder and request interceptors
|
||||
- **FeignErrorDecoder**: Custom error decoder that converts Feign errors to IntegrationException
|
||||
- **IntegrationException**: Standardized exception for integration failures
|
||||
- **CommonResponse/PageResponse**: Standard response wrappers for external service calls
|
||||
- **ConfigValueUtil**: Utility for handling configuration values
|
||||
- **IntegrationFallbackService**: Universal fallback service for handling integration failures
|
||||
|
||||
#### Service-Specific Integrations
|
||||
Currently implemented:
|
||||
- **Scenic Integration** (`com.ycwl.basic.integration.scenic`): ZT-Scenic microservice integration
|
||||
- **Device Integration** (`com.ycwl.basic.integration.device`): ZT-Device microservice integration
|
||||
|
||||
### Integration Pattern
|
||||
|
||||
Each external service integration follows this structure:
|
||||
```
|
||||
service/
|
||||
├── client/ # Feign clients for HTTP calls
|
||||
├── config/ # Service-specific configuration
|
||||
├── dto/ # Data transfer objects
|
||||
├── service/ # Service layer with business logic
|
||||
└── 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)
|
||||
|
||||
### Key Components
|
||||
|
||||
#### Feign Clients
|
||||
- **ScenicV2Client**: Main scenic operations (CRUD, filtering, listing)
|
||||
- **ScenicConfigV2Client**: Scenic configuration management
|
||||
- **DefaultConfigClient**: Default configuration operations
|
||||
|
||||
#### Services
|
||||
- **ScenicIntegrationService**: High-level scenic operations (with automatic fallback)
|
||||
- **ScenicConfigIntegrationService**: Configuration management (with automatic fallback)
|
||||
- **DefaultConfigIntegrationService**: Default configuration handling
|
||||
|
||||
#### Configuration
|
||||
```yaml
|
||||
integration:
|
||||
scenic:
|
||||
enabled: true
|
||||
serviceName: zt-scenic
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
retryEnabled: false
|
||||
maxRetries: 3
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Basic Scenic Operations (with Automatic Fallback)
|
||||
```java
|
||||
@Autowired
|
||||
private ScenicIntegrationService scenicService;
|
||||
|
||||
// Get scenic with configuration (automatically falls back to cache on failure)
|
||||
ScenicV2WithConfigDTO scenic = scenicService.getScenicWithConfig(scenicId);
|
||||
|
||||
// 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();
|
||||
request.setName("Test Scenic");
|
||||
ScenicV2DTO result = scenicService.createScenic(request);
|
||||
|
||||
// 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();
|
||||
// configure filters...
|
||||
ScenicFilterPageResponse response = scenicService.filterScenics(filterRequest);
|
||||
```
|
||||
|
||||
#### Configuration Management (with Automatic Fallback)
|
||||
```java
|
||||
@Autowired
|
||||
private ScenicConfigIntegrationService configService;
|
||||
|
||||
// Get flat configuration (automatically falls back to cache on failure)
|
||||
Map<String, Object> config = configService.getFlatConfigs(scenicId);
|
||||
|
||||
// Batch update configurations (direct operation, fails immediately on error)
|
||||
BatchConfigRequest batchRequest = new BatchConfigRequest();
|
||||
// configure batch updates...
|
||||
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)
|
||||
|
||||
### Key Components
|
||||
|
||||
#### Feign Clients
|
||||
- **DeviceV2Client**: Main device operations (CRUD, filtering, listing)
|
||||
- **DeviceConfigV2Client**: Device configuration management
|
||||
|
||||
#### Services
|
||||
- **DeviceIntegrationService**: High-level device operations (with automatic fallback)
|
||||
- **DeviceConfigIntegrationService**: Device configuration management (with automatic fallback)
|
||||
|
||||
#### Configuration
|
||||
```yaml
|
||||
integration:
|
||||
device:
|
||||
enabled: true
|
||||
serviceName: zt-device
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
retryEnabled: false
|
||||
maxRetries: 3
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Basic Device Operations (with Automatic Fallback)
|
||||
```java
|
||||
@Autowired
|
||||
private DeviceIntegrationService deviceService;
|
||||
|
||||
// Create IPC camera device (operation tracked for future fallback)
|
||||
DeviceV2DTO ipcDevice = deviceService.createIpcDevice("前门摄像头", "CAM001", scenicId);
|
||||
|
||||
// Get device with configuration (automatically falls back to cache on failure)
|
||||
DeviceV2WithConfigDTO device = deviceService.getDeviceWithConfig(deviceId);
|
||||
|
||||
// Get device by number (automatically falls back to cache on failure)
|
||||
DeviceV2DTO deviceByNo = deviceService.getDeviceByNo("CAM001");
|
||||
|
||||
// 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);
|
||||
|
||||
// Enable/disable device (direct operation, fails immediately on error)
|
||||
deviceService.enableDevice(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 (with Automatic Fallback)
|
||||
```java
|
||||
@Autowired
|
||||
private DeviceConfigIntegrationService configService;
|
||||
|
||||
// Get device configurations (with fallback)
|
||||
List<DeviceConfigV2DTO> configs = configService.getDeviceConfigs(deviceId);
|
||||
|
||||
// Get flat configuration (automatically falls back to cache on failure)
|
||||
Map<String, Object> config = configService.getDeviceFlatConfig(deviceId);
|
||||
|
||||
// Get flat configuration by device number (with fallback)
|
||||
Map<String, Object> configByNo = configService.getDeviceFlatConfigByNo("CAM001");
|
||||
|
||||
// Batch update configurations (direct operation, fails immediately on error)
|
||||
BatchDeviceConfigRequest batchRequest = configService.createBatchConfigBuilder()
|
||||
.addConfig("brightness", "50")
|
||||
.addConfig("contrast", "80")
|
||||
.build();
|
||||
configService.batchUpdateDeviceConfig(deviceId, batchRequest);
|
||||
|
||||
// New batch configuration API with detailed results
|
||||
BatchDeviceConfigRequest request = configService.createBatchConfigBuilder()
|
||||
.addVideoConfig("4K", 30, "H265") // Add video configuration
|
||||
.addNetworkConfig("192.168.1.100", 554, "RTSP") // Add network configuration
|
||||
.addAuthConfig("admin", "password") // Add authentication configuration
|
||||
.addConfig("recording_enabled", "true") // Use default configuration
|
||||
.addConfig("custom_setting", "value", "string", "Custom setting") // Custom configuration
|
||||
.build();
|
||||
|
||||
BatchUpdateResponse result = configService.batchUpdateDeviceConfigWithResult(deviceId, request);
|
||||
if (result.getFailed() > 0) {
|
||||
// Handle partial failure
|
||||
result.getProcessedItems().stream()
|
||||
.filter(item -> "failed".equals(item.getStatus()))
|
||||
.forEach(item -> log.warn("Config {} failed: {}", item.getConfigKey(), item.getMessage()));
|
||||
}
|
||||
```
|
||||
|
||||
#### Device Management Patterns
|
||||
```java
|
||||
// Create and configure camera in one operation (both operations direct, no fallback)
|
||||
DeviceV2DTO camera = deviceService.createIpcDevice("摄像头1", "CAM001", scenicId);
|
||||
// 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
|
||||
DeviceV2WithConfigListResponse camerasWithConfig =
|
||||
deviceService.listDevicesWithConfig(1, 100, null, null, "IPC", 1, scenicId);
|
||||
|
||||
// Batch update camera resolution (direct operations, no fallback)
|
||||
for (DeviceV2WithConfigDTO device : camerasWithConfig.getList()) {
|
||||
BatchDeviceConfigRequest resolutionUpdate = configService.createBatchConfigBuilder()
|
||||
.addConfig("resolution", "2560x1440")
|
||||
.build();
|
||||
configService.batchUpdateDeviceConfig(device.getId(), resolutionUpdate);
|
||||
}
|
||||
|
||||
// Monitor device configuration completeness (with automatic fallback)
|
||||
for (DeviceV2DTO device : activeDevices.getList()) {
|
||||
Map<String, Object> config = configService.getDeviceFlatConfig(device.getId());
|
||||
boolean hasIpConfig = config.containsKey("ip_address");
|
||||
// log configuration status...
|
||||
}
|
||||
```
|
||||
|
||||
#### 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
|
||||
|
||||
Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules.
|
||||
|
||||
#### Default Configuration Rules
|
||||
The system uses `device_id = 0` configurations as default templates:
|
||||
- **Configurations with defaults**: System enforces default `config_type` and `description`, only `configValue` is updated
|
||||
- **Configurations without defaults**: Allows custom `config_type` and `description`
|
||||
|
||||
#### Usage Examples
|
||||
```java
|
||||
// 1. Using Builder Pattern
|
||||
BatchDeviceConfigRequest request = configService.createBatchConfigBuilder()
|
||||
.addVideoConfig("1920x1080", 30, "H264") // Video settings
|
||||
.addNetworkConfig("192.168.1.100", 554, "RTSP") // Network settings
|
||||
.addAuthConfig("admin", "password123") // Authentication
|
||||
.addConfig("recording_enabled", "true") // Use default config rule
|
||||
.addConfig("custom_path", "/data", "string", "Storage path") // Custom config
|
||||
.build();
|
||||
|
||||
BatchUpdateResponse result = configService.batchUpdateDeviceConfigWithResult(deviceId, request);
|
||||
|
||||
// 2. Processing Results
|
||||
log.info("Batch update: {} success, {} failed", result.getSuccess(), result.getFailed());
|
||||
|
||||
for (ProcessedConfigItem item : result.getProcessedItems()) {
|
||||
if ("success".equals(item.getStatus())) {
|
||||
log.info("✅ {} updated (action: {}, hasDefault: {})",
|
||||
item.getConfigKey(), item.getAction(), item.getHasDefault());
|
||||
} else {
|
||||
log.warn("❌ {} failed: {}", item.getConfigKey(), item.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Error Handling
|
||||
if (result.getFailed() > 0) {
|
||||
result.getErrors().forEach(error -> log.warn("Error: {}", error));
|
||||
}
|
||||
```
|
||||
|
||||
#### Response Format
|
||||
- **200 OK**: All configurations updated successfully
|
||||
- **202 Accepted**: Partial success (some configurations failed)
|
||||
- **400 Bad Request**: All configurations failed
|
||||
|
||||
Each processed item includes:
|
||||
- `status`: "success" or "failed"
|
||||
- `action`: "create" or "update"
|
||||
- `hasDefault`: Whether default configuration rules were applied
|
||||
- `finalType`/`finalDescription`: Actually used type and description
|
||||
|
||||
### Device Types
|
||||
- **IPC**: IP Camera devices for video monitoring
|
||||
- **CUSTOM**: Custom device types for sensors, controllers, etc.
|
||||
|
||||
### Common Configuration Keys
|
||||
- `ip_address`: Device IP address
|
||||
- `resolution`: Video resolution (e.g., "1920x1080", "3840x2160")
|
||||
- `framerate`: Video frame rate (integer)
|
||||
- `protocol`: Communication protocol (e.g., "RTSP", "HTTP")
|
||||
- `username`: Authentication username
|
||||
- `password`: Authentication password
|
||||
- `brightness`: Display brightness (0-100)
|
||||
- `contrast`: Display contrast (0-100)
|
||||
- `quality`: Video quality ("low", "medium", "high")
|
||||
|
||||
## Adding New Service Integrations
|
||||
|
||||
### 1. Create Package Structure
|
||||
```
|
||||
com.ycwl.basic.integration.{service-name}/
|
||||
├── client/
|
||||
├── config/
|
||||
├── dto/
|
||||
├── service/
|
||||
└── example/
|
||||
```
|
||||
|
||||
### 2. Add Configuration Properties
|
||||
Update `IntegrationProperties` to include new service configuration:
|
||||
```java
|
||||
@Data
|
||||
public static class NewServiceConfig {
|
||||
private boolean enabled = true;
|
||||
private String serviceName = "service-name";
|
||||
private int connectTimeout = 5000;
|
||||
private int readTimeout = 10000;
|
||||
// other configs...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Create Feign Client
|
||||
```java
|
||||
@FeignClient(name = "service-name", contextId = "service-context", path = "/api/path")
|
||||
public interface NewServiceClient {
|
||||
@GetMapping("/endpoint")
|
||||
CommonResponse<ResponseDTO> getData(@PathVariable Long id);
|
||||
// other endpoints...
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Implement Service Layer
|
||||
```java
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class NewServiceIntegrationService {
|
||||
private final NewServiceClient client;
|
||||
|
||||
public ResponseDTO getData(Long id) {
|
||||
CommonResponse<ResponseDTO> response = client.getData(id);
|
||||
return handleResponse(response, "Failed to get data");
|
||||
}
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "service-name");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### IntegrationException
|
||||
All integration failures are wrapped in `IntegrationException`:
|
||||
```java
|
||||
public class IntegrationException extends RuntimeException {
|
||||
private final Integer code;
|
||||
private final String serviceName;
|
||||
// constructors and getters...
|
||||
}
|
||||
```
|
||||
|
||||
### FeignErrorDecoder
|
||||
Automatically converts Feign errors to IntegrationException:
|
||||
- Parses CommonResponse error format
|
||||
- Extracts service name from method key
|
||||
- Provides meaningful error messages
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Properties Structure
|
||||
```yaml
|
||||
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:
|
||||
enabled: true
|
||||
serviceName: zt-scenic
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
retryEnabled: false
|
||||
maxRetries: 3
|
||||
device:
|
||||
enabled: true
|
||||
serviceName: zt-device
|
||||
connectTimeout: 5000
|
||||
readTimeout: 10000
|
||||
retryEnabled: false
|
||||
maxRetries: 3
|
||||
```
|
||||
|
||||
### Configuration Refresh
|
||||
Uses `@RefreshScope` to support dynamic configuration updates without restart.
|
||||
|
||||
## Testing Integration Services
|
||||
|
||||
### Unit Testing
|
||||
Test service layers by mocking Feign clients:
|
||||
```java
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ScenicIntegrationServiceTest {
|
||||
@Mock
|
||||
private ScenicV2Client scenicV2Client;
|
||||
|
||||
@InjectMocks
|
||||
private ScenicIntegrationService scenicService;
|
||||
|
||||
@Test
|
||||
void testGetScenic() {
|
||||
// Mock response
|
||||
CommonResponse<ScenicV2DTO> response = new CommonResponse<>();
|
||||
response.setSuccess(true);
|
||||
response.setData(new ScenicV2DTO());
|
||||
|
||||
when(scenicV2Client.getScenic(1L)).thenReturn(response);
|
||||
|
||||
// Test
|
||||
ScenicV2DTO result = scenicService.getScenic(1L);
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Testing
|
||||
Use `@SpringBootTest` with test profiles and mock external services.
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Running Integration Tests
|
||||
```bash
|
||||
# Run specific integration test class
|
||||
mvn test -Dtest=ScenicIntegrationServiceTest
|
||||
|
||||
# Run all integration tests
|
||||
mvn test -Dtest="com.ycwl.basic.integration.*Test"
|
||||
|
||||
# Run device integration tests
|
||||
mvn test -Dtest=DeviceIntegrationServiceTest
|
||||
mvn test -Dtest="com.ycwl.basic.integration.device.*Test"
|
||||
```
|
||||
|
||||
### Adding New DTOs
|
||||
1. Create DTO classes in the appropriate `dto` package
|
||||
2. Follow existing patterns with proper Jackson annotations
|
||||
3. Use `@JsonProperty` for field mapping when needed
|
||||
|
||||
### Debugging Integration Issues
|
||||
1. Enable Feign client logging in `application-dev.yml`:
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
com.ycwl.basic.integration: DEBUG
|
||||
```
|
||||
|
||||
2. Check Nacos service discovery in development
|
||||
3. Verify service configurations and timeouts
|
||||
4. Review FeignErrorDecoder logs for detailed error information
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Response Handling
|
||||
- Always use the `handleResponse` pattern for consistent error handling
|
||||
- Provide meaningful error messages for business context
|
||||
- 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
|
||||
- Use environment-specific configuration profiles
|
||||
- Set appropriate timeouts based on service characteristics
|
||||
- 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
|
||||
- Keep DTOs simple and focused on data transfer
|
||||
- Use proper Jackson annotations for field mapping
|
||||
- Separate request/response DTOs for clarity
|
||||
|
||||
### Service Layer Design
|
||||
- Keep integration services focused on external service calls
|
||||
- Handle response transformation and error conversion
|
||||
- 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,11 +11,21 @@ import org.springframework.stereotype.Component;
|
||||
@ConfigurationProperties(prefix = "integration")
|
||||
public class IntegrationProperties {
|
||||
|
||||
/**
|
||||
* 降级服务配置
|
||||
*/
|
||||
private FallbackConfig fallback = new FallbackConfig();
|
||||
|
||||
/**
|
||||
* 景区服务配置
|
||||
*/
|
||||
private ScenicConfig scenic = new ScenicConfig();
|
||||
|
||||
/**
|
||||
* 设备服务配置
|
||||
*/
|
||||
private DeviceConfig device = new DeviceConfig();
|
||||
|
||||
@Data
|
||||
public static class ScenicConfig {
|
||||
/**
|
||||
@@ -40,4 +50,71 @@ public class IntegrationProperties {
|
||||
private boolean retryEnabled = false;
|
||||
private int maxRetries = 3;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DeviceConfig {
|
||||
/**
|
||||
* 是否启用设备服务集成
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
private String serviceName = "zt-device";
|
||||
|
||||
/**
|
||||
* 超时配置(毫秒)
|
||||
*/
|
||||
private int connectTimeout = 5000;
|
||||
private int readTimeout = 10000;
|
||||
|
||||
/**
|
||||
* 重试配置
|
||||
*/
|
||||
private boolean retryEnabled = false;
|
||||
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,406 @@
|
||||
package com.ycwl.basic.integration.common.manager;
|
||||
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用配置管理器基类
|
||||
* 提供配置查找、类型转换等通用功能
|
||||
*
|
||||
* @param <T> 配置DTO类型
|
||||
*/
|
||||
public abstract class ConfigManager<T> {
|
||||
|
||||
protected List<T> configs;
|
||||
|
||||
public ConfigManager(List<T> configs) {
|
||||
this.configs = configs != null ? configs : new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置项的键
|
||||
* 子类需要实现此方法来提取配置键
|
||||
*/
|
||||
protected abstract String getConfigKey(T config);
|
||||
|
||||
/**
|
||||
* 获取配置项的值
|
||||
* 子类需要实现此方法来提取配置值
|
||||
*/
|
||||
protected abstract Object getConfigValue(T config);
|
||||
|
||||
/**
|
||||
* 根据键查找配置项
|
||||
*/
|
||||
protected T findConfigByKey(String key) {
|
||||
if (key == null || configs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return configs.stream()
|
||||
.filter(config -> key.equals(getConfigKey(config)))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串配置值
|
||||
*/
|
||||
public String getString(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public String getString(String key, String defaultValue) {
|
||||
String value = getString(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整型配置值
|
||||
*/
|
||||
public Integer getInteger(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Integer) return (Integer) value;
|
||||
if (value instanceof Number) return ((Number) value).intValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整型配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public Integer getInteger(String key, Integer defaultValue) {
|
||||
Integer value = getInteger(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取长整型配置值
|
||||
*/
|
||||
public Long getLong(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Long) return (Long) value;
|
||||
if (value instanceof Number) return ((Number) value).longValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取长整型配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
Long value = getLong(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取布尔型配置值
|
||||
*/
|
||||
public Boolean getBoolean(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Boolean) return (Boolean) value;
|
||||
if (value instanceof String) {
|
||||
String str = ((String) value).toLowerCase().trim();
|
||||
return "true".equals(str) || "1".equals(str) || "yes".equals(str);
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue() != 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取布尔型配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
Boolean value = getBoolean(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浮点型配置值
|
||||
*/
|
||||
public Float getFloat(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
if (value instanceof Float) return (Float) value;
|
||||
if (value instanceof Number) return ((Number) value).floatValue();
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Float.parseFloat((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浮点型配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public Float getFloat(String key, Float defaultValue) {
|
||||
Float value = getFloat(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取BigDecimal配置值
|
||||
*/
|
||||
public BigDecimal getBigDecimal(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
if (value instanceof BigDecimal) return (BigDecimal) value;
|
||||
if (value instanceof Number) return new BigDecimal(value.toString());
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return new BigDecimal((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取BigDecimal配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
|
||||
BigDecimal value = getBigDecimal(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举配置值
|
||||
*/
|
||||
public <E extends Enum<E>> E getEnum(String key, Class<E> enumClass) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
return parseEnum(value, enumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析枚举值
|
||||
*/
|
||||
private <E extends Enum<E>> E parseEnum(Object value, Class<E> enumClass) {
|
||||
if (value == null) return null;
|
||||
try {
|
||||
if (value instanceof String) {
|
||||
return Enum.valueOf(enumClass, (String) value);
|
||||
}
|
||||
return Enum.valueOf(enumClass, value.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举配置值,如果不存在则返回默认值
|
||||
*/
|
||||
public <E extends Enum<E>> E getEnum(String key, Class<E> enumClass, E defaultValue) {
|
||||
E value = getEnum(key, enumClass);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定键的配置是否存在
|
||||
*/
|
||||
public boolean hasConfig(String key) {
|
||||
return findConfigByKey(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有配置项的数量
|
||||
*/
|
||||
public int size() {
|
||||
return configs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有配置项
|
||||
*/
|
||||
public List<T> getAllConfigs() {
|
||||
return new ArrayList<>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始对象配置值
|
||||
*/
|
||||
public Object getObject(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
return getConfigValue(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并转换为指定类型的对象
|
||||
* 支持JSON字符串自动反序列化
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <R> R getObject(String key, Class<R> clazz) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
|
||||
// 如果类型匹配,直接返回
|
||||
if (clazz.isInstance(value)) {
|
||||
return (R) value;
|
||||
}
|
||||
|
||||
// 如果是String类型的JSON,尝试反序列化
|
||||
if (value instanceof String && !clazz.equals(String.class)) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, clazz);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果目标是String,直接转换
|
||||
if (clazz.equals(String.class)) {
|
||||
return (R) value.toString();
|
||||
}
|
||||
|
||||
// 其他情况尝试JSON转换
|
||||
try {
|
||||
String json = JacksonUtil.toJSONString(value);
|
||||
return JacksonUtil.parseObject(json, clazz);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Map类型的配置值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> getMap(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof Map) {
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, Map.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取List类型的配置值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof List) {
|
||||
return (List<Object>) value;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseObject((String) value, List.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定元素类型的List配置值
|
||||
*/
|
||||
public <R> List<R> getList(String key, Class<R> elementClass) {
|
||||
T config = findConfigByKey(key);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = getConfigValue(config);
|
||||
if (value == null) return null;
|
||||
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return JacksonUtil.parseArray((String) value, elementClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String json = JacksonUtil.toJSONString(value);
|
||||
return JacksonUtil.parseArray(json, elementClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.ycwl.basic.integration.common.manager;
|
||||
|
||||
import com.ycwl.basic.integration.device.dto.config.DeviceConfigV2DTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设备配置管理器
|
||||
* 基于通用ConfigManager实现设备配置的管理功能
|
||||
*/
|
||||
public class DeviceConfigManager extends ConfigManager<DeviceConfigV2DTO> {
|
||||
|
||||
public DeviceConfigManager(List<DeviceConfigV2DTO> configs) {
|
||||
super(configs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConfigKey(DeviceConfigV2DTO config) {
|
||||
return config != null ? config.getConfigKey() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getConfigValue(DeviceConfigV2DTO config) {
|
||||
return config != null ? config.getConfigValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备配置类型
|
||||
*/
|
||||
public String getConfigType(String key) {
|
||||
DeviceConfigV2DTO config = findConfigByKey(key);
|
||||
return config != null ? config.getConfigType() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备配置描述
|
||||
*/
|
||||
public String getConfigDescription(String key) {
|
||||
DeviceConfigV2DTO config = findConfigByKey(key);
|
||||
return config != null ? config.getDescription() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备配置ID
|
||||
*/
|
||||
public Long getConfigId(String key) {
|
||||
DeviceConfigV2DTO config = findConfigByKey(key);
|
||||
return config != null ? config.getId() : null;
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.ycwl.basic.util;
|
||||
package com.ycwl.basic.integration.common.manager;
|
||||
|
||||
import com.ycwl.basic.integration.common.util.ConfigValueUtil;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO;
|
||||
@@ -9,11 +9,12 @@ import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 景区配置管理器
|
||||
* 基于通用ConfigManager实现景区配置的管理功能
|
||||
*
|
||||
* 提供类型安全的配置值获取功能,支持多种数据类型的自动转换,
|
||||
* 当类型不兼容时返回null而不是抛出异常。
|
||||
*/
|
||||
public class ScenicConfigManager {
|
||||
public class ScenicConfigManager extends ConfigManager<ScenicConfigV2DTO> {
|
||||
|
||||
private final Map<String, Object> configMap;
|
||||
|
||||
@@ -23,6 +24,7 @@ public class ScenicConfigManager {
|
||||
* @param configList 配置项列表
|
||||
*/
|
||||
public ScenicConfigManager(List<ScenicConfigV2DTO> configList) {
|
||||
super(configList);
|
||||
this.configMap = new HashMap<>();
|
||||
if (configList != null) {
|
||||
for (ScenicConfigV2DTO config : configList) {
|
||||
@@ -39,50 +41,20 @@ public class ScenicConfigManager {
|
||||
* @param configMap 配置Map
|
||||
*/
|
||||
public ScenicConfigManager(Map<String, Object> configMap) {
|
||||
super(null); // 使用Map构造时,父类configs为null
|
||||
this.configMap = configMap != null ? new HashMap<>(configMap) : new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @return 字符串值,如果键不存在或转换失败返回null
|
||||
*/
|
||||
public String getString(String key) {
|
||||
return ConfigValueUtil.getStringValue(configMap, key);
|
||||
|
||||
@Override
|
||||
protected String getConfigKey(ScenicConfigV2DTO config) {
|
||||
return config != null ? config.getConfigKey() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getConfigValue(ScenicConfigV2DTO config) {
|
||||
return config != null ? config.getConfigValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串值,如果为null则返回默认值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return 字符串值或默认值
|
||||
*/
|
||||
public String getString(String key, String defaultValue) {
|
||||
return ConfigValueUtil.getStringValue(configMap, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整数值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @return Integer值,如果键不存在或转换失败返回null
|
||||
*/
|
||||
public Integer getInteger(String key) {
|
||||
return ConfigValueUtil.getIntValue(configMap, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整数值,如果为null则返回默认值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return Integer值或默认值
|
||||
*/
|
||||
public Integer getInteger(String key, Integer defaultValue) {
|
||||
return ConfigValueUtil.getIntValue(configMap, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取长整数值
|
||||
@@ -193,85 +165,7 @@ public class ScenicConfigManager {
|
||||
return ConfigValueUtil.getBooleanValue(configMap, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param enumClass 枚举类型
|
||||
* @param <T> 枚举类型泛型
|
||||
* @return 枚举值,如果键不存在或转换失败返回null
|
||||
*/
|
||||
public <T extends Enum<T>> T getEnum(String key, Class<T> enumClass) {
|
||||
return ConfigValueUtil.getEnumValue(configMap, key, enumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举值,如果为null则返回默认值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param enumClass 枚举类型
|
||||
* @param defaultValue 默认值
|
||||
* @param <T> 枚举类型泛型
|
||||
* @return 枚举值或默认值
|
||||
*/
|
||||
public <T extends Enum<T>> T getEnum(String key, Class<T> enumClass, T defaultValue) {
|
||||
T value = ConfigValueUtil.getEnumValue(configMap, key, enumClass);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始对象值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @return 原始Object值
|
||||
*/
|
||||
public Object getObject(String key) {
|
||||
return ConfigValueUtil.getObjectValue(configMap, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并转换为指定类型的对象
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param clazz 目标类型
|
||||
* @param <T> 目标类型泛型
|
||||
* @return 转换后的对象,如果转换失败返回null
|
||||
*/
|
||||
public <T> T getObject(String key, Class<T> clazz) {
|
||||
return ConfigValueUtil.getObjectValue(configMap, key, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Map类型的值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @return Map值,如果转换失败返回null
|
||||
*/
|
||||
public Map<String, Object> getMap(String key) {
|
||||
return ConfigValueUtil.getMapValue(configMap, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取List类型的值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @return List值,如果转换失败返回null
|
||||
*/
|
||||
public List<Object> getList(String key) {
|
||||
return ConfigValueUtil.getListValue(configMap, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定元素类型的List值
|
||||
*
|
||||
* @param key 配置键
|
||||
* @param elementClass List元素类型
|
||||
* @param <T> List元素类型泛型
|
||||
* @return 指定类型的List,如果转换失败返回null
|
||||
*/
|
||||
public <T> List<T> getList(String key, Class<T> elementClass) {
|
||||
return ConfigValueUtil.getListValue(configMap, key, elementClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置键是否存在
|
||||
@@ -307,6 +201,7 @@ public class ScenicConfigManager {
|
||||
*
|
||||
* @return 配置项数量
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return configMap.size();
|
||||
}
|
||||
@@ -325,7 +220,7 @@ public class ScenicConfigManager {
|
||||
*
|
||||
* @return 配置Map的拷贝
|
||||
*/
|
||||
public Map<String, Object> getAllConfigs() {
|
||||
public Map<String, Object> getAllConfigsAsMap() {
|
||||
return new HashMap<>(configMap);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package com.ycwl.basic.integration.device.client;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.device.dto.config.*;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "zt-device", contextId = "device-config-v2", path = "/api/device/config/v2")
|
||||
public interface DeviceConfigV2Client {
|
||||
|
||||
/**
|
||||
* 获取设备所有配置
|
||||
*/
|
||||
@GetMapping("/{deviceId}")
|
||||
CommonResponse<List<DeviceConfigV2DTO>> getDeviceConfigs(@PathVariable("deviceId") Long deviceId);
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备所有配置
|
||||
*/
|
||||
@GetMapping("/no/{no}")
|
||||
CommonResponse<List<DeviceConfigV2DTO>> getDeviceConfigsByNo(@PathVariable("no") String no);
|
||||
|
||||
/**
|
||||
* 获取设备特定配置
|
||||
*/
|
||||
@GetMapping("/{deviceId}/key/{configKey}")
|
||||
CommonResponse<DeviceConfigV2DTO> getDeviceConfigByKey(@PathVariable("deviceId") Long deviceId,
|
||||
@PathVariable("configKey") String configKey);
|
||||
|
||||
/**
|
||||
* 获取设备扁平化配置
|
||||
*/
|
||||
@GetMapping("/{deviceId}/flat")
|
||||
CommonResponse<Map<String, Object>> getDeviceFlatConfig(@PathVariable("deviceId") Long deviceId);
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备扁平化配置
|
||||
*/
|
||||
@GetMapping("/no/{no}/flat")
|
||||
CommonResponse<Map<String, Object>> getDeviceFlatConfigByNo(@PathVariable("no") String no);
|
||||
|
||||
/**
|
||||
* 创建设备配置
|
||||
*/
|
||||
@PostMapping("/{deviceId}")
|
||||
CommonResponse<DeviceConfigV2DTO> createDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
||||
@RequestBody CreateDeviceConfigRequest request);
|
||||
|
||||
/**
|
||||
* 更新设备配置
|
||||
*/
|
||||
@PutMapping("/{deviceId}/{id}")
|
||||
CommonResponse<String> updateDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
||||
@PathVariable("id") Long id,
|
||||
@RequestBody UpdateDeviceConfigRequest request);
|
||||
|
||||
/**
|
||||
* 删除设备配置
|
||||
*/
|
||||
@DeleteMapping("/{deviceId}/{id}")
|
||||
CommonResponse<String> deleteDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
||||
@PathVariable("id") Long id);
|
||||
|
||||
/**
|
||||
* 批量更新设备配置
|
||||
*/
|
||||
@PostMapping("/{deviceId}/batch")
|
||||
CommonResponse<BatchUpdateResponse> batchUpdateDeviceConfig(@PathVariable("deviceId") Long deviceId,
|
||||
@RequestBody BatchDeviceConfigRequest request);
|
||||
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package com.ycwl.basic.integration.device.client;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.device.dto.device.*;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@FeignClient(name = "zt-device", contextId = "device-v2", path = "/api/device/v2")
|
||||
public interface DeviceV2Client {
|
||||
|
||||
/**
|
||||
* 获取设备核心信息
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
CommonResponse<DeviceV2DTO> getDevice(@PathVariable("id") Long id);
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备核心信息
|
||||
*/
|
||||
@GetMapping("/no/{no}")
|
||||
CommonResponse<DeviceV2DTO> getDeviceByNo(@PathVariable("no") String no);
|
||||
|
||||
/**
|
||||
* 获取设备详细信息(含配置)
|
||||
*/
|
||||
@GetMapping("/{id}/with-config")
|
||||
CommonResponse<DeviceV2WithConfigDTO> getDeviceWithConfig(@PathVariable("id") Long id);
|
||||
|
||||
/**
|
||||
* 根据设备编号获取设备详细信息(含配置)
|
||||
*/
|
||||
@GetMapping("/no/{no}/with-config")
|
||||
CommonResponse<DeviceV2WithConfigDTO> getDeviceByNoWithConfig(@PathVariable("no") String no);
|
||||
|
||||
/**
|
||||
* 创建设备
|
||||
*/
|
||||
@PostMapping("/")
|
||||
CommonResponse<DeviceV2DTO> createDevice(@RequestBody CreateDeviceRequest request);
|
||||
|
||||
/**
|
||||
* 更新设备
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
CommonResponse<String> updateDevice(@PathVariable("id") Long id,
|
||||
@RequestBody UpdateDeviceRequest request);
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
CommonResponse<String> deleteDevice(@PathVariable("id") Long id);
|
||||
|
||||
/**
|
||||
* 分页获取设备列表(核心信息)
|
||||
*/
|
||||
@GetMapping("/")
|
||||
CommonResponse<DeviceV2ListResponse> listDevices(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(value = "name", required = false) String name,
|
||||
@RequestParam(value = "no", required = false) String no,
|
||||
@RequestParam(value = "type", required = false) String type,
|
||||
@RequestParam(value = "isActive", required = false) Integer isActive,
|
||||
@RequestParam(value = "scenicId", required = false) Long scenicId);
|
||||
|
||||
/**
|
||||
* 分页获取设备列表(含配置)
|
||||
*/
|
||||
@GetMapping("/with-config")
|
||||
CommonResponse<DeviceV2WithConfigListResponse> listDevicesWithConfig(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(value = "name", required = false) String name,
|
||||
@RequestParam(value = "no", required = false) String no,
|
||||
@RequestParam(value = "type", required = false) String type,
|
||||
@RequestParam(value = "isActive", required = false) Integer isActive,
|
||||
@RequestParam(value = "scenicId", required = false) Long scenicId);
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.ycwl.basic.integration.device.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "integration.device")
|
||||
public class DeviceIntegrationConfig {
|
||||
|
||||
public DeviceIntegrationConfig() {
|
||||
log.info("ZT-Device集成配置初始化完成");
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BatchDeviceConfigRequest {
|
||||
private List<BatchDeviceConfigItem> configs;
|
||||
|
||||
@Data
|
||||
public static class BatchDeviceConfigItem {
|
||||
private String configKey;
|
||||
private String configValue;
|
||||
private String configType;
|
||||
private String description;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量配置更新响应
|
||||
*/
|
||||
@Data
|
||||
public class BatchUpdateResponse {
|
||||
/**
|
||||
* 成功处理的配置数量
|
||||
*/
|
||||
@JsonProperty("success")
|
||||
private Integer success;
|
||||
|
||||
/**
|
||||
* 失败的配置数量
|
||||
*/
|
||||
@JsonProperty("failed")
|
||||
private Integer failed;
|
||||
|
||||
/**
|
||||
* 处理详情列表
|
||||
*/
|
||||
@JsonProperty("processedItems")
|
||||
private List<ProcessedConfigItem> processedItems;
|
||||
|
||||
/**
|
||||
* 错误信息列表
|
||||
*/
|
||||
@JsonProperty("errors")
|
||||
private List<String> errors;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreateDeviceConfigRequest {
|
||||
private String configKey;
|
||||
private String configValue;
|
||||
private String configType;
|
||||
private String description;
|
||||
private Integer isActive;
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class DeviceConfigV2DTO {
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("deviceId")
|
||||
private Long deviceId;
|
||||
|
||||
@JsonProperty("configKey")
|
||||
private String configKey;
|
||||
|
||||
@JsonProperty("configValue")
|
||||
private String configValue;
|
||||
|
||||
@JsonProperty("configType")
|
||||
private String configType;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("isActive")
|
||||
private Integer isActive;
|
||||
|
||||
@JsonProperty("createTime")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@JsonProperty("updateTime")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 处理的配置项详情
|
||||
*/
|
||||
@Data
|
||||
public class ProcessedConfigItem {
|
||||
/**
|
||||
* 配置键名
|
||||
*/
|
||||
@JsonProperty("configKey")
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 处理状态: success/failed
|
||||
*/
|
||||
@JsonProperty("status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 执行动作: create/update
|
||||
*/
|
||||
@JsonProperty("action")
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 是否有默认配置
|
||||
*/
|
||||
@JsonProperty("hasDefault")
|
||||
private Boolean hasDefault;
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*/
|
||||
@JsonProperty("message")
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 最终使用的类型
|
||||
*/
|
||||
@JsonProperty("finalType")
|
||||
private String finalType;
|
||||
|
||||
/**
|
||||
* 最终使用的描述
|
||||
*/
|
||||
@JsonProperty("finalDescription")
|
||||
private String finalDescription;
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.ycwl.basic.integration.device.dto.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class UpdateDeviceConfigRequest {
|
||||
private String configKey;
|
||||
private String configValue;
|
||||
private String configType;
|
||||
private String description;
|
||||
private Integer isActive;
|
||||
|
||||
// 支持灵活的字段更新
|
||||
public static UpdateDeviceConfigRequest fromMap(Map<String, Object> updates) {
|
||||
UpdateDeviceConfigRequest request = new UpdateDeviceConfigRequest();
|
||||
if (updates.containsKey("configKey")) {
|
||||
request.setConfigKey((String) updates.get("configKey"));
|
||||
}
|
||||
if (updates.containsKey("configValue")) {
|
||||
request.setConfigValue((String) updates.get("configValue"));
|
||||
}
|
||||
if (updates.containsKey("configType")) {
|
||||
request.setConfigType((String) updates.get("configType"));
|
||||
}
|
||||
if (updates.containsKey("description")) {
|
||||
request.setDescription((String) updates.get("description"));
|
||||
}
|
||||
if (updates.containsKey("isActive")) {
|
||||
request.setIsActive((Integer) updates.get("isActive"));
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreateDeviceRequest {
|
||||
private String name;
|
||||
private String no;
|
||||
private String type;
|
||||
private Integer isActive;
|
||||
private Long scenicId;
|
||||
private Integer sort;
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class DeviceV2DTO {
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("no")
|
||||
private String no;
|
||||
|
||||
@JsonProperty("type")
|
||||
private String type; // IPC, CUSTOM
|
||||
|
||||
@JsonProperty("isActive")
|
||||
private Integer isActive;
|
||||
|
||||
@JsonProperty("scenicId")
|
||||
private Long scenicId;
|
||||
|
||||
@JsonProperty("sort")
|
||||
private Integer sort;
|
||||
|
||||
@JsonProperty("createTime")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@JsonProperty("updateTime")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceV2ListResponse {
|
||||
private List<DeviceV2DTO> list;
|
||||
private Long total;
|
||||
private Integer page;
|
||||
private Integer pageSize;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeviceV2WithConfigDTO extends DeviceV2DTO {
|
||||
private Map<String, Object> config;
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeviceV2WithConfigListResponse {
|
||||
private List<DeviceV2WithConfigDTO> list;
|
||||
private Long total;
|
||||
private Integer page;
|
||||
private Integer pageSize;
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.ycwl.basic.integration.device.dto.device;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateDeviceRequest {
|
||||
private String name;
|
||||
private String no;
|
||||
private String type;
|
||||
private Integer isActive;
|
||||
private Long scenicId;
|
||||
private Integer sort;
|
||||
}
|
@@ -0,0 +1,184 @@
|
||||
package com.ycwl.basic.integration.device.example;
|
||||
|
||||
import com.ycwl.basic.integration.device.dto.device.*;
|
||||
import com.ycwl.basic.integration.device.dto.config.*;
|
||||
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Device Integration 使用示例
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceIntegrationExample {
|
||||
|
||||
private final DeviceIntegrationService deviceService;
|
||||
private final DeviceConfigIntegrationService deviceConfigService;
|
||||
|
||||
/**
|
||||
* 基本设备操作
|
||||
*/
|
||||
public void basicDeviceOperations() {
|
||||
log.info("=== 基本设备操作 ===");
|
||||
|
||||
// 创建IPC摄像头设备(默认排序)
|
||||
DeviceV2DTO ipcDevice = deviceService.createIpcDevice(
|
||||
"前门摄像头", "CAM001", 1001L);
|
||||
log.info("创建IPC设备: {}, 排序值: {}", ipcDevice.getName(), ipcDevice.getSort());
|
||||
|
||||
// 根据ID获取设备信息
|
||||
DeviceV2DTO device = deviceService.getDevice(ipcDevice.getId());
|
||||
log.info("获取设备信息: {}, 排序值: {}", device.getName(), device.getSort());
|
||||
|
||||
// 根据设备编号获取设备信息
|
||||
DeviceV2DTO deviceByNo = deviceService.getDeviceByNo("CAM001");
|
||||
log.info("根据编号获取设备: {}", deviceByNo.getName());
|
||||
|
||||
// 获取设备详细信息(含配置)
|
||||
DeviceV2WithConfigDTO deviceWithConfig = deviceService.getDeviceWithConfig(ipcDevice.getId());
|
||||
log.info("获取设备配置: {}", deviceWithConfig.getName());
|
||||
|
||||
// 分页查询景区设备列表
|
||||
DeviceV2ListResponse deviceList = deviceService.getScenicIpcDevices(1001L, 1, 10);
|
||||
log.info("景区设备列表: 总数={}", deviceList.getTotal());
|
||||
|
||||
// 启用设备
|
||||
deviceService.enableDevice(ipcDevice.getId());
|
||||
log.info("设备已启用");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备排序功能演示
|
||||
*/
|
||||
public void deviceSortingOperations() {
|
||||
log.info("=== 设备排序功能演示 ===");
|
||||
|
||||
Long scenicId = 1001L;
|
||||
|
||||
// 创建带排序的设备
|
||||
DeviceV2DTO camera1 = deviceService.createIpcDeviceWithSort(
|
||||
"大门摄像头", "CAM_GATE", scenicId, 10);
|
||||
log.info("创建摄像头1: {}, 排序: {}", camera1.getName(), camera1.getSort());
|
||||
|
||||
DeviceV2DTO camera2 = deviceService.createIpcDeviceWithSort(
|
||||
"后门摄像头", "CAM_BACK", scenicId, 20);
|
||||
log.info("创建摄像头2: {}, 排序: {}", camera2.getName(), camera2.getSort());
|
||||
|
||||
DeviceV2DTO sensor1 = deviceService.createCustomDeviceWithSort(
|
||||
"温度传感器", "TEMP_01", scenicId, 5);
|
||||
log.info("创建传感器: {}, 排序: {}", sensor1.getName(), sensor1.getSort());
|
||||
|
||||
// 更新设备排序
|
||||
deviceService.updateDeviceSort(camera1.getId(), 1);
|
||||
log.info("更新摄像头1排序为1(置顶)");
|
||||
|
||||
// 获取排序后的设备列表
|
||||
DeviceV2ListResponse sortedList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId);
|
||||
log.info("排序后的设备列表:");
|
||||
for (DeviceV2DTO device : sortedList.getList()) {
|
||||
log.info(" - {}: 排序={}, 类型={}", device.getName(), device.getSort(), device.getType());
|
||||
}
|
||||
|
||||
// 批量调整排序演示
|
||||
log.info("--- 批量调整排序演示 ---");
|
||||
deviceService.updateDeviceSort(sensor1.getId(), 15); // 传感器排到中间
|
||||
deviceService.updateDeviceSort(camera2.getId(), 30); // 后门摄像头排到最后
|
||||
log.info("批量排序调整完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备配置管理
|
||||
*/
|
||||
public void deviceConfigurationOperations() {
|
||||
log.info("=== 设备配置管理 ===");
|
||||
|
||||
Long deviceId = 1L;
|
||||
|
||||
// 获取设备所有配置
|
||||
List<DeviceConfigV2DTO> configs = deviceConfigService.getDeviceConfigs(deviceId);
|
||||
log.info("设备配置数量: {}", configs.size());
|
||||
|
||||
// 获取扁平化配置
|
||||
Map<String, Object> flatConfig = deviceConfigService.getDeviceFlatConfig(deviceId);
|
||||
log.info("扁平化配置项数: {}", flatConfig.size());
|
||||
|
||||
// 使用批量配置API
|
||||
BatchDeviceConfigRequest builderRequest = deviceConfigService.createBatchConfigBuilder()
|
||||
.build();
|
||||
|
||||
BatchUpdateResponse result = deviceConfigService.batchUpdateDeviceConfig(deviceId, builderRequest);
|
||||
log.info("批量配置更新结果: 成功={}, 失败={}", result.getSuccess(), result.getFailed());
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序最佳实践演示
|
||||
*/
|
||||
public void sortingBestPractices() {
|
||||
log.info("=== 排序最佳实践演示 ===");
|
||||
|
||||
Long scenicId = 1001L;
|
||||
|
||||
// 推荐使用10的倍数作为排序值
|
||||
DeviceV2DTO device1 = deviceService.createIpcDeviceWithSort(
|
||||
"重要摄像头", "CAM_IMPORTANT", scenicId, 10);
|
||||
|
||||
DeviceV2DTO device2 = deviceService.createIpcDeviceWithSort(
|
||||
"普通摄像头", "CAM_NORMAL", scenicId, 20);
|
||||
|
||||
DeviceV2DTO device3 = deviceService.createIpcDeviceWithSort(
|
||||
"备用摄像头", "CAM_BACKUP", scenicId, 30);
|
||||
|
||||
log.info("使用10的倍数创建设备排序: 10, 20, 30");
|
||||
|
||||
// 在中间插入新设备
|
||||
DeviceV2DTO insertDevice = deviceService.createIpcDeviceWithSort(
|
||||
"中间摄像头", "CAM_MIDDLE", scenicId, 25);
|
||||
log.info("在20和30之间插入设备,排序值: 25");
|
||||
|
||||
// 置顶操作
|
||||
deviceService.updateDeviceSort(device2.getId(), 1);
|
||||
log.info("将普通摄像头置顶(排序值: 1)");
|
||||
|
||||
// 查看最终排序结果
|
||||
DeviceV2ListResponse finalList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId);
|
||||
log.info("最终排序结果:");
|
||||
for (DeviceV2DTO device : finalList.getList()) {
|
||||
log.info(" - {}: 排序={}", device.getName(), device.getSort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行所有示例
|
||||
*/
|
||||
public void runAllExamples() {
|
||||
try {
|
||||
basicDeviceOperations();
|
||||
deviceSortingOperations();
|
||||
sortingBestPractices();
|
||||
deviceConfigurationOperations();
|
||||
log.info("=== 所有示例执行完成 ===");
|
||||
} catch (Exception e) {
|
||||
log.error("示例执行过程中发生错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行基础示例(简化版)
|
||||
*/
|
||||
public void runBasicExamples() {
|
||||
try {
|
||||
basicDeviceOperations();
|
||||
deviceConfigurationOperations();
|
||||
log.info("=== 基础示例执行完成 ===");
|
||||
} catch (Exception e) {
|
||||
log.error("示例执行过程中发生错误", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
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.DeviceConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备集成示例(包含降级机制)
|
||||
* 演示设备集成和失败降级策略的使用
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceIntegrationFallbackExample {
|
||||
|
||||
private final DeviceIntegrationService deviceService;
|
||||
private final DeviceConfigIntegrationService configService;
|
||||
private final IntegrationFallbackService fallbackService;
|
||||
|
||||
private static final String SERVICE_NAME = "zt-device";
|
||||
|
||||
/**
|
||||
* 演示设备信息获取的降级机制
|
||||
*/
|
||||
public void deviceInfoFallbackExample() {
|
||||
log.info("=== 设备信息获取降级示例 ===");
|
||||
|
||||
Long deviceId = 1001L;
|
||||
String deviceNo = "CAM001";
|
||||
|
||||
try {
|
||||
// 获取设备信息 - 自动降级
|
||||
DeviceV2DTO device = deviceService.getDevice(deviceId);
|
||||
log.info("获取设备成功: {}", device.getName());
|
||||
|
||||
// 根据设备号获取设备 - 自动降级
|
||||
DeviceV2DTO deviceByNo = deviceService.getDeviceByNo(deviceNo);
|
||||
log.info("根据设备号获取设备成功: {}", deviceByNo.getName());
|
||||
|
||||
// 获取设备配置 - 自动降级
|
||||
DeviceV2WithConfigDTO deviceWithConfig = deviceService.getDeviceWithConfig(deviceId);
|
||||
log.info("获取设备配置成功,配置数量: {}", deviceWithConfig.getConfig().size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("所有降级策略失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示设备操作(无降级机制)
|
||||
*/
|
||||
public void deviceOperationExample() {
|
||||
log.info("=== 设备操作示例 ===");
|
||||
|
||||
Long deviceId = 1001L;
|
||||
|
||||
try {
|
||||
// 设备更新操作 - 直接操作,失败时抛出异常
|
||||
UpdateDeviceRequest updateRequest = new UpdateDeviceRequest();
|
||||
updateRequest.setName("更新后的摄像头");
|
||||
deviceService.updateDevice(deviceId, updateRequest);
|
||||
log.info("设备更新操作完成");
|
||||
|
||||
// 设备排序更新 - 直接操作,失败时抛出异常
|
||||
deviceService.updateDeviceSort(deviceId, 5);
|
||||
log.info("设备排序更新完成");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("设备操作失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示降级缓存管理
|
||||
*/
|
||||
public void fallbackCacheManagementExample() {
|
||||
log.info("=== 降级缓存管理示例 ===");
|
||||
|
||||
String deviceCacheKey = "device:1001";
|
||||
String configCacheKey = "device:flat:config:1001";
|
||||
|
||||
// 检查降级缓存状态
|
||||
boolean hasDeviceCache = fallbackService.hasFallbackCache(SERVICE_NAME, deviceCacheKey);
|
||||
boolean hasConfigCache = fallbackService.hasFallbackCache(SERVICE_NAME, configCacheKey);
|
||||
|
||||
log.info("设备降级缓存存在: {}", hasDeviceCache);
|
||||
log.info("配置降级缓存存在: {}", hasConfigCache);
|
||||
|
||||
// 清理特定的降级缓存
|
||||
if (hasDeviceCache) {
|
||||
fallbackService.clearFallbackCache(SERVICE_NAME, deviceCacheKey);
|
||||
log.info("已清理设备降级缓存");
|
||||
}
|
||||
|
||||
// 获取降级缓存统计信息
|
||||
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats(SERVICE_NAME);
|
||||
log.info("设备服务降级缓存统计: {}", stats);
|
||||
|
||||
// 批量清理所有设备降级缓存
|
||||
if (stats.getTotalCacheCount() > 10) {
|
||||
fallbackService.clearAllFallbackCache(SERVICE_NAME);
|
||||
log.info("已批量清理所有设备降级缓存");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行所有示例
|
||||
*/
|
||||
public void runAllExamples() {
|
||||
log.info("开始运行设备集成示例...");
|
||||
|
||||
deviceInfoFallbackExample();
|
||||
deviceOperationExample();
|
||||
fallbackCacheManagementExample();
|
||||
|
||||
log.info("设备集成示例运行完成");
|
||||
}
|
||||
}
|
@@ -0,0 +1,167 @@
|
||||
package com.ycwl.basic.integration.device.service;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||
import com.ycwl.basic.integration.device.client.DeviceConfigV2Client;
|
||||
import com.ycwl.basic.integration.device.dto.config.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceConfigIntegrationService {
|
||||
|
||||
private final DeviceConfigV2Client deviceConfigV2Client;
|
||||
private final IntegrationFallbackService fallbackService;
|
||||
|
||||
private static final String SERVICE_NAME = "zt-device";
|
||||
|
||||
public List<DeviceConfigV2DTO> getDeviceConfigs(Long deviceId) {
|
||||
log.info("获取设备配置列表, deviceId: {}", deviceId);
|
||||
CommonResponse<List<DeviceConfigV2DTO>> response = deviceConfigV2Client.getDeviceConfigs(deviceId);
|
||||
return handleResponse(response, "获取设备配置列表失败");
|
||||
}
|
||||
|
||||
public List<DeviceConfigV2DTO> getDeviceConfigsByNo(String deviceNo) {
|
||||
log.info("根据设备编号获取配置列表, deviceNo: {}", deviceNo);
|
||||
CommonResponse<List<DeviceConfigV2DTO>> response = deviceConfigV2Client.getDeviceConfigsByNo(deviceNo);
|
||||
return handleResponse(response, "根据设备编号获取配置列表失败");
|
||||
}
|
||||
|
||||
public DeviceConfigV2DTO getDeviceConfigByKey(Long deviceId, String configKey) {
|
||||
log.info("根据键获取设备配置, deviceId: {}, configKey: {}", deviceId, configKey);
|
||||
CommonResponse<DeviceConfigV2DTO> response = deviceConfigV2Client.getDeviceConfigByKey(deviceId, configKey);
|
||||
return handleResponse(response, "根据键获取设备配置失败");
|
||||
}
|
||||
|
||||
public Map<String, Object> getDeviceFlatConfig(Long deviceId) {
|
||||
log.info("获取设备扁平化配置, deviceId: {}", deviceId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:flat:config:" + deviceId,
|
||||
() -> {
|
||||
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfig(deviceId);
|
||||
return handleResponse(response, "获取设备扁平化配置失败");
|
||||
},
|
||||
Map.class
|
||||
);
|
||||
}
|
||||
|
||||
public Map<String, Object> getDeviceFlatConfigByNo(String deviceNo) {
|
||||
log.info("根据设备编号获取扁平化配置, deviceNo: {}", deviceNo);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:flat:config:no:" + deviceNo,
|
||||
() -> {
|
||||
CommonResponse<Map<String, Object>> response = deviceConfigV2Client.getDeviceFlatConfigByNo(deviceNo);
|
||||
return handleResponse(response, "根据设备编号获取扁平化配置失败");
|
||||
},
|
||||
Map.class
|
||||
);
|
||||
}
|
||||
|
||||
public DeviceConfigV2DTO createDeviceConfig(Long deviceId, CreateDeviceConfigRequest request) {
|
||||
log.info("创建设备配置, deviceId: {}, configKey: {}", deviceId, request.getConfigKey());
|
||||
CommonResponse<DeviceConfigV2DTO> response = deviceConfigV2Client.createDeviceConfig(deviceId, request);
|
||||
return handleResponse(response, "创建设备配置失败");
|
||||
}
|
||||
|
||||
public void updateDeviceConfig(Long deviceId, Long configId, UpdateDeviceConfigRequest request) {
|
||||
log.info("更新设备配置, deviceId: {}, configId: {}", deviceId, configId);
|
||||
CommonResponse<String> response = deviceConfigV2Client.updateDeviceConfig(deviceId, configId, request);
|
||||
handleResponse(response, "更新设备配置失败");
|
||||
}
|
||||
|
||||
public void deleteDeviceConfig(Long deviceId, Long configId) {
|
||||
log.info("删除设备配置, deviceId: {}, configId: {}", deviceId, configId);
|
||||
CommonResponse<String> response = deviceConfigV2Client.deleteDeviceConfig(deviceId, configId);
|
||||
handleResponse(response, "删除设备配置失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新设备配置
|
||||
*/
|
||||
public BatchUpdateResponse batchUpdateDeviceConfig(Long deviceId, BatchDeviceConfigRequest request) {
|
||||
log.info("批量更新设备配置, deviceId: {}, configs count: {}", deviceId, request.getConfigs().size());
|
||||
CommonResponse<BatchUpdateResponse> response = deviceConfigV2Client.batchUpdateDeviceConfig(deviceId, request);
|
||||
return handleResponse(response, "批量更新设备配置失败");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备特定配置值
|
||||
*/
|
||||
public String getDeviceConfigValue(Long deviceId, String configKey) {
|
||||
DeviceConfigV2DTO config = getDeviceConfigByKey(deviceId, configKey);
|
||||
return config != null ? config.getConfigValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建批量配置请求构建器
|
||||
*/
|
||||
public BatchDeviceConfigRequestBuilder createBatchConfigBuilder() {
|
||||
return new BatchDeviceConfigRequestBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量配置请求构建器
|
||||
*/
|
||||
public static class BatchDeviceConfigRequestBuilder {
|
||||
private final BatchDeviceConfigRequest request = new BatchDeviceConfigRequest();
|
||||
|
||||
public BatchDeviceConfigRequestBuilder() {
|
||||
request.setConfigs(new java.util.ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加配置项(有默认配置的情况,只需提供key和value)
|
||||
*/
|
||||
public BatchDeviceConfigRequestBuilder addConfig(String configKey, String configValue) {
|
||||
BatchDeviceConfigRequest.BatchDeviceConfigItem item = new BatchDeviceConfigRequest.BatchDeviceConfigItem();
|
||||
item.setConfigKey(configKey);
|
||||
item.setConfigValue(configValue);
|
||||
request.getConfigs().add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加配置项(完整参数,用于自定义配置)
|
||||
*/
|
||||
public BatchDeviceConfigRequestBuilder addConfig(String configKey, String configValue,
|
||||
String configType, String description) {
|
||||
BatchDeviceConfigRequest.BatchDeviceConfigItem item = new BatchDeviceConfigRequest.BatchDeviceConfigItem();
|
||||
item.setConfigKey(configKey);
|
||||
item.setConfigValue(configValue);
|
||||
item.setConfigType(configType);
|
||||
item.setDescription(description);
|
||||
request.getConfigs().add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求对象
|
||||
*/
|
||||
public BatchDeviceConfigRequest build() {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "zt-device");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
@@ -0,0 +1,218 @@
|
||||
package com.ycwl.basic.integration.device.service;
|
||||
|
||||
import com.ycwl.basic.integration.common.exception.IntegrationException;
|
||||
import com.ycwl.basic.integration.common.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||
import com.ycwl.basic.integration.device.client.DeviceV2Client;
|
||||
import com.ycwl.basic.integration.device.dto.device.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceIntegrationService {
|
||||
|
||||
private final DeviceV2Client deviceV2Client;
|
||||
private final IntegrationFallbackService fallbackService;
|
||||
|
||||
private static final String SERVICE_NAME = "zt-device";
|
||||
|
||||
public DeviceV2DTO getDevice(Long deviceId) {
|
||||
log.info("获取设备信息, deviceId: {}", deviceId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:" + deviceId,
|
||||
() -> {
|
||||
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDevice(deviceId);
|
||||
return handleResponse(response, "获取设备信息失败");
|
||||
},
|
||||
DeviceV2DTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public DeviceV2DTO getDeviceByNo(String deviceNo) {
|
||||
log.info("根据设备编号获取设备信息, deviceNo: {}", deviceNo);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:no:" + deviceNo,
|
||||
() -> {
|
||||
CommonResponse<DeviceV2DTO> response = deviceV2Client.getDeviceByNo(deviceNo);
|
||||
return handleResponse(response, "根据设备编号获取设备信息失败");
|
||||
},
|
||||
DeviceV2DTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public DeviceV2WithConfigDTO getDeviceWithConfig(Long deviceId) {
|
||||
log.info("获取设备配置信息, deviceId: {}", deviceId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:config:" + deviceId,
|
||||
() -> {
|
||||
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceWithConfig(deviceId);
|
||||
return handleResponse(response, "获取设备配置信息失败");
|
||||
},
|
||||
DeviceV2WithConfigDTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public DeviceV2WithConfigDTO getDeviceWithConfigByNo(String deviceNo) {
|
||||
log.info("根据设备编号获取设备配置信息, deviceNo: {}", deviceNo);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"device:config:no:" + deviceNo,
|
||||
() -> {
|
||||
CommonResponse<DeviceV2WithConfigDTO> response = deviceV2Client.getDeviceByNoWithConfig(deviceNo);
|
||||
return handleResponse(response, "根据设备编号获取设备配置信息失败");
|
||||
},
|
||||
DeviceV2WithConfigDTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public DeviceV2DTO createDevice(CreateDeviceRequest request) {
|
||||
log.info("创建设备, name: {}, no: {}, type: {}", request.getName(), request.getNo(), request.getType());
|
||||
CommonResponse<DeviceV2DTO> response = deviceV2Client.createDevice(request);
|
||||
return handleResponse(response, "创建设备失败");
|
||||
}
|
||||
|
||||
public void updateDevice(Long deviceId, UpdateDeviceRequest request) {
|
||||
log.info("更新设备信息, deviceId: {}", deviceId);
|
||||
CommonResponse<String> response = deviceV2Client.updateDevice(deviceId, request);
|
||||
handleResponse(response, "更新设备信息失败");
|
||||
}
|
||||
|
||||
public void deleteDevice(Long deviceId) {
|
||||
log.info("删除设备, deviceId: {}", deviceId);
|
||||
CommonResponse<String> response = deviceV2Client.deleteDevice(deviceId);
|
||||
handleResponse(response, "删除设备失败");
|
||||
}
|
||||
|
||||
public DeviceV2ListResponse listDevices(Integer page, Integer pageSize, String name, String no,
|
||||
String type, Integer isActive, Long scenicId) {
|
||||
log.info("分页查询设备列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
CommonResponse<DeviceV2ListResponse> response = deviceV2Client.listDevices(
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
return handleResponse(response, "分页查询设备列表失败");
|
||||
}
|
||||
|
||||
public DeviceV2WithConfigListResponse listDevicesWithConfig(Integer page, Integer pageSize, String name, String no,
|
||||
String type, Integer isActive, Long scenicId) {
|
||||
log.info("分页查询设备带配置列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
CommonResponse<DeviceV2WithConfigListResponse> response = deviceV2Client.listDevicesWithConfig(
|
||||
page, pageSize, name, no, type, isActive, scenicId);
|
||||
return handleResponse(response, "分页查询设备带配置列表失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建IPC摄像头设备
|
||||
*/
|
||||
public DeviceV2DTO createIpcDevice(String name, String deviceNo, Long scenicId) {
|
||||
CreateDeviceRequest request = new CreateDeviceRequest();
|
||||
request.setName(name);
|
||||
request.setNo(deviceNo);
|
||||
request.setType("IPC");
|
||||
request.setIsActive(1);
|
||||
request.setScenicId(scenicId);
|
||||
request.setSort(0);
|
||||
return createDevice(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建IPC摄像头设备(带排序)
|
||||
*/
|
||||
public DeviceV2DTO createIpcDeviceWithSort(String name, String deviceNo, Long scenicId, Integer sort) {
|
||||
CreateDeviceRequest request = new CreateDeviceRequest();
|
||||
request.setName(name);
|
||||
request.setNo(deviceNo);
|
||||
request.setType("IPC");
|
||||
request.setIsActive(1);
|
||||
request.setScenicId(scenicId);
|
||||
request.setSort(sort != null ? sort : 0);
|
||||
return createDevice(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义设备
|
||||
*/
|
||||
public DeviceV2DTO createCustomDevice(String name, String deviceNo, Long scenicId) {
|
||||
CreateDeviceRequest request = new CreateDeviceRequest();
|
||||
request.setName(name);
|
||||
request.setNo(deviceNo);
|
||||
request.setType("CUSTOM");
|
||||
request.setIsActive(1);
|
||||
request.setScenicId(scenicId);
|
||||
request.setSort(0);
|
||||
return createDevice(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义设备(带排序)
|
||||
*/
|
||||
public DeviceV2DTO createCustomDeviceWithSort(String name, String deviceNo, Long scenicId, Integer sort) {
|
||||
CreateDeviceRequest request = new CreateDeviceRequest();
|
||||
request.setName(name);
|
||||
request.setNo(deviceNo);
|
||||
request.setType("CUSTOM");
|
||||
request.setIsActive(1);
|
||||
request.setScenicId(scenicId);
|
||||
request.setSort(sort != null ? sort : 0);
|
||||
return createDevice(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用设备
|
||||
*/
|
||||
public void enableDevice(Long deviceId) {
|
||||
UpdateDeviceRequest request = new UpdateDeviceRequest();
|
||||
request.setIsActive(1);
|
||||
updateDevice(deviceId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用设备
|
||||
*/
|
||||
public void disableDevice(Long deviceId) {
|
||||
UpdateDeviceRequest request = new UpdateDeviceRequest();
|
||||
request.setIsActive(0);
|
||||
updateDevice(deviceId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备排序
|
||||
*/
|
||||
public void updateDeviceSort(Long deviceId, Integer sort) {
|
||||
log.info("更新设备排序, deviceId: {}, sort: {}", deviceId, sort);
|
||||
UpdateDeviceRequest request = new UpdateDeviceRequest();
|
||||
request.setSort(sort);
|
||||
updateDevice(deviceId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区的IPC设备列表
|
||||
*/
|
||||
public DeviceV2ListResponse getScenicIpcDevices(Long scenicId, Integer page, Integer pageSize) {
|
||||
return listDevices(page, pageSize, null, null, "IPC", 1, scenicId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取景区的所有激活设备
|
||||
*/
|
||||
public DeviceV2ListResponse getScenicActiveDevices(Long scenicId, Integer page, Integer pageSize) {
|
||||
return listDevices(page, pageSize, null, null, null, 1, scenicId);
|
||||
}
|
||||
|
||||
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
|
||||
if (response == null || !response.isSuccess()) {
|
||||
String msg = response != null && response.getMessage() != null
|
||||
? response.getMessage()
|
||||
: errorMessage;
|
||||
Integer code = response != null ? response.getCode() : 5000;
|
||||
throw new IntegrationException(code, msg, "zt-device");
|
||||
}
|
||||
return response.getData();
|
||||
}
|
||||
}
|
@@ -13,4 +13,12 @@ public class BatchUpdateResponse {
|
||||
|
||||
@JsonProperty("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;
|
||||
|
||||
import com.ycwl.basic.integration.scenic.dto.config.CreateConfigRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.FilterCondition;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
|
||||
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.*;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.*;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -11,10 +11,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ZT-Scenic集成服务使用示例
|
||||
* 仅供参考,实际使用时根据业务需要调用相应的服务方法
|
||||
* 景区集成示例(包含降级机制)
|
||||
* 演示景区集成和失败降级策略的使用
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -23,6 +25,9 @@ public class ScenicIntegrationExample {
|
||||
|
||||
private final ScenicIntegrationService scenicIntegrationService;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示基础景区操作的降级机制
|
||||
*/
|
||||
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.response.CommonResponse;
|
||||
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
|
||||
import com.ycwl.basic.integration.scenic.client.ScenicConfigV2Client;
|
||||
import com.ycwl.basic.integration.scenic.dto.config.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -17,43 +18,61 @@ import java.util.Map;
|
||||
public class ScenicConfigIntegrationService {
|
||||
|
||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||
private final IntegrationFallbackService fallbackService;
|
||||
|
||||
private static final String SERVICE_NAME = "zt-scenic";
|
||||
|
||||
public List<ScenicConfigV2DTO> listConfigs(Long scenicId) {
|
||||
log.info("获取景区配置列表, scenicId: {}", scenicId);
|
||||
CommonResponse<List<ScenicConfigV2DTO>> response = scenicConfigV2Client.listConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区配置列表失败");
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO getConfigByKey(Long scenicId, String configKey) {
|
||||
log.info("根据键获取景区配置, scenicId: {}, configKey: {}", scenicId, configKey);
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.getConfigByKey(scenicId, configKey);
|
||||
return handleResponse(response, "根据键获取景区配置失败");
|
||||
}
|
||||
|
||||
public Map<String, Object> getFlatConfigs(Long scenicId) {
|
||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区扁平化配置失败");
|
||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||
return fallbackService.executeWithFallback(
|
||||
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) {
|
||||
log.info("创建景区配置, scenicId: {}, configKey: {}", scenicId, request.getConfigKey());
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.createConfig(scenicId, request);
|
||||
return handleResponse(response, "创建景区配置失败");
|
||||
}
|
||||
|
||||
public ScenicConfigV2DTO updateConfig(Long scenicId, String id, UpdateConfigRequest request) {
|
||||
log.info("更新景区配置, scenicId: {}, id: {}", scenicId, id);
|
||||
CommonResponse<ScenicConfigV2DTO> response = scenicConfigV2Client.updateConfig(scenicId, id, request);
|
||||
return handleResponse(response, "更新景区配置失败");
|
||||
}
|
||||
|
||||
public void deleteConfig(Long scenicId, String id) {
|
||||
log.info("删除景区配置, scenicId: {}, id: {}", scenicId, id);
|
||||
CommonResponse<Void> response = scenicConfigV2Client.deleteConfig(scenicId, id);
|
||||
handleResponse(response, "删除景区配置失败");
|
||||
}
|
||||
|
||||
public BatchUpdateResponse batchUpdateConfigs(Long scenicId, BatchConfigRequest request) {
|
||||
log.info("批量更新景区配置, scenicId: {}, configs count: {}", scenicId, request.getConfigs().size());
|
||||
CommonResponse<BatchUpdateResponse> response = scenicConfigV2Client.batchUpdateConfigs(scenicId, request);
|
||||
return handleResponse(response, "批量更新景区配置失败");
|
||||
}
|
||||
|
||||
public BatchUpdateResponse batchFlatUpdateConfigs(Long scenicId, Map<String, Object> configs) {
|
||||
log.info("扁平化批量更新景区配置, scenicId: {}, configs count: {}", scenicId, configs.size());
|
||||
CommonResponse<BatchUpdateResponse> response = scenicConfigV2Client.batchFlatUpdateConfigs(scenicId, configs);
|
||||
return handleResponse(response, "扁平化批量更新景区配置失败");
|
||||
}
|
||||
|
@@ -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.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.ScenicV2Client;
|
||||
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
|
||||
@@ -25,48 +26,81 @@ public class ScenicIntegrationService {
|
||||
|
||||
private final ScenicV2Client scenicV2Client;
|
||||
private final ScenicConfigV2Client scenicConfigV2Client;
|
||||
private final IntegrationFallbackService fallbackService;
|
||||
|
||||
private static final String SERVICE_NAME = "zt-scenic";
|
||||
|
||||
public ScenicV2DTO getScenic(Long scenicId) {
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.getScenic(scenicId);
|
||||
return handleResponse(response, "获取景区信息失败");
|
||||
log.info("获取景区信息, scenicId: {}", scenicId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"scenic:" + scenicId,
|
||||
() -> {
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.getScenic(scenicId);
|
||||
return handleResponse(response, "获取景区信息失败");
|
||||
},
|
||||
ScenicV2DTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public ScenicV2WithConfigDTO getScenicWithConfig(Long scenicId) {
|
||||
CommonResponse<ScenicV2WithConfigDTO> response = scenicV2Client.getScenicWithConfig(scenicId);
|
||||
return handleResponse(response, "获取景区配置信息失败");
|
||||
log.info("获取景区配置信息, scenicId: {}", scenicId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"scenic:config:" + scenicId,
|
||||
() -> {
|
||||
CommonResponse<ScenicV2WithConfigDTO> response = scenicV2Client.getScenicWithConfig(scenicId);
|
||||
return handleResponse(response, "获取景区配置信息失败");
|
||||
},
|
||||
ScenicV2WithConfigDTO.class
|
||||
);
|
||||
}
|
||||
|
||||
public Map<String, Object> getScenicFlatConfig(Long scenicId) {
|
||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区扁平化配置失败");
|
||||
log.info("获取景区扁平化配置, scenicId: {}", scenicId);
|
||||
return fallbackService.executeWithFallback(
|
||||
SERVICE_NAME,
|
||||
"scenic:flat:config:" + scenicId,
|
||||
() -> {
|
||||
CommonResponse<Map<String, Object>> response = scenicConfigV2Client.getFlatConfigs(scenicId);
|
||||
return handleResponse(response, "获取景区扁平化配置失败");
|
||||
},
|
||||
Map.class
|
||||
);
|
||||
}
|
||||
|
||||
public ScenicV2DTO createScenic(CreateScenicRequest request) {
|
||||
log.info("创建景区, name: {}", request.getName());
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.createScenic(request);
|
||||
return handleResponse(response, "创建景区失败");
|
||||
}
|
||||
|
||||
public ScenicV2DTO updateScenic(Long scenicId, UpdateScenicRequest request) {
|
||||
log.info("更新景区信息, scenicId: {}", scenicId);
|
||||
CommonResponse<ScenicV2DTO> response = scenicV2Client.updateScenic(scenicId, request);
|
||||
return handleResponse(response, "更新景区信息失败");
|
||||
}
|
||||
|
||||
public void deleteScenic(Long scenicId) {
|
||||
log.info("删除景区, scenicId: {}", scenicId);
|
||||
CommonResponse<Void> response = scenicV2Client.deleteScenic(scenicId);
|
||||
handleResponse(response, "删除景区失败");
|
||||
}
|
||||
|
||||
public ScenicFilterPageResponse filterScenics(ScenicFilterRequest request) {
|
||||
log.info("筛选景区, filters: {}", request.getFilters().size());
|
||||
CommonResponse<ScenicFilterPageResponse> response = scenicV2Client.filterScenics(request);
|
||||
return handleResponse(response, "筛选景区失败");
|
||||
}
|
||||
|
||||
public ScenicV2ListResponse listScenics(Integer page, Integer pageSize, Integer status, String name) {
|
||||
log.info("分页查询景区列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
CommonResponse<ScenicV2ListResponse> response = scenicV2Client.listScenics(page, pageSize, status, name);
|
||||
return handleResponse(response, "分页查询景区列表失败");
|
||||
}
|
||||
|
||||
public ScenicV2WithConfigListResponse listScenicsWithConfig(Integer page, Integer pageSize, Integer status, String name) {
|
||||
log.info("分页查询景区带配置列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
|
||||
CommonResponse<ScenicV2WithConfigListResponse> response = scenicV2Client.listScenicsWithConfig(page, pageSize, status, name);
|
||||
return handleResponse(response, "分页查询景区带配置列表失败");
|
||||
}
|
||||
|
@@ -1,52 +0,0 @@
|
||||
package com.ycwl.basic.mapper;
|
||||
|
||||
import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/11/29 14:48
|
||||
* device(设备管理)
|
||||
*/
|
||||
@Mapper
|
||||
public interface DeviceMapper {
|
||||
List<DeviceRespVO> list(DeviceReqQuery deviceReqQuery);
|
||||
List<DeviceEntity> listAll();
|
||||
DeviceRespVO getById(Long id);
|
||||
int add(DeviceAddOrUpdateReq deviceReqQuery);
|
||||
int deleteById(Long id);
|
||||
int update(DeviceAddOrUpdateReq deviceReqQuery);
|
||||
int updateStatus(Long id);
|
||||
|
||||
DeviceEntity getByDeviceId(Long deviceId);
|
||||
List<DeviceRespVO> listByScenicIdWithWVP(Long scenicId);
|
||||
|
||||
ScenicDeviceCountVO deviceCountByScenicId(@Param("scenicId") Long scenicId);
|
||||
|
||||
DeviceConfigEntity getConfigByDeviceId(Long deviceId);
|
||||
int addConfig(DeviceConfigEntity deviceConfigEntity);
|
||||
int updateConfig(DeviceConfigEntity deviceConfigEntity);
|
||||
|
||||
DeviceEntity getByDeviceNo(String deviceNo);
|
||||
|
||||
int updateEntity(DeviceEntity device);
|
||||
|
||||
int addEntity(DeviceEntity device);
|
||||
|
||||
int updateOnlineStatus(Long id, String ipAddr, int online, Date keepaliveAt);
|
||||
|
||||
DeviceEntity getByDeviceNo2(String deviceNo);
|
||||
|
||||
List<Long> listAllByScenicId(Long scenicId);
|
||||
|
||||
int updateSort(Long id, Integer sort);
|
||||
}
|
@@ -28,7 +28,6 @@ public class DeviceEntity {
|
||||
* 设备编号
|
||||
*/
|
||||
private String no;
|
||||
private String no2;
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
|
@@ -16,7 +16,7 @@ import com.ycwl.basic.pricing.service.VoucherPrintService;
|
||||
import com.ycwl.basic.printer.ticket.FeiETicketPrinter;
|
||||
import com.ycwl.basic.repository.FaceRepository;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.utils.WxMpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
@@ -1,98 +1,111 @@
|
||||
package com.ycwl.basic.repository;
|
||||
|
||||
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.config.DeviceConfigV2DTO;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.utils.SnowFlakeUtil;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeviceRepository {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
|
||||
public static final String DEVICE_ONLINE_CACHE_KEY = "device:online_status:%s";
|
||||
public static final String DEVICE_CACHE_KEY = "device:%s";
|
||||
public static final String DEVICE_CONFIG_CACHE_KEY = "device:%s:config";
|
||||
@Autowired
|
||||
private DeviceConfigIntegrationService deviceConfigIntegrationService;
|
||||
|
||||
/**
|
||||
* 将DeviceV2DTO转换为DeviceEntity
|
||||
*/
|
||||
private DeviceEntity convertToEntity(DeviceV2DTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
DeviceEntity entity = new DeviceEntity();
|
||||
entity.setId(dto.getId());
|
||||
entity.setName(dto.getName());
|
||||
entity.setNo(dto.getNo());
|
||||
entity.setScenicId(dto.getScenicId());
|
||||
// DeviceV2DTO中的isActive对应DeviceEntity中的status
|
||||
entity.setStatus(dto.getIsActive());
|
||||
// 转换时间格式:LocalDateTime -> Date
|
||||
if (dto.getCreateTime() != null) {
|
||||
entity.setCreateAt(Date.from(dto.getCreateTime().atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
if (dto.getUpdateTime() != null) {
|
||||
entity.setUpdateAt(Date.from(dto.getUpdateTime().atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public DeviceEntity getDevice(Long deviceId) {
|
||||
if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceId))) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CACHE_KEY, deviceId)), DeviceEntity.class);
|
||||
}
|
||||
DeviceEntity device = deviceMapper.getByDeviceId(deviceId);
|
||||
if (null != device) {
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceId), JacksonUtil.toJSONString(device), 3, TimeUnit.DAYS);
|
||||
}
|
||||
log.debug("获取设备信息, deviceId: {}", deviceId);
|
||||
DeviceV2DTO deviceDto = deviceIntegrationService.getDevice(deviceId);
|
||||
DeviceEntity device = convertToEntity(deviceDto);
|
||||
return device;
|
||||
}
|
||||
|
||||
public DeviceEntity getDeviceByDeviceNo(String deviceNo) {
|
||||
if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceNo))) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CACHE_KEY, deviceNo)), DeviceEntity.class);
|
||||
}
|
||||
DeviceEntity device = deviceMapper.getByDeviceNo(deviceNo);
|
||||
if (null == device) {
|
||||
device = deviceMapper.getByDeviceNo2(deviceNo);
|
||||
}
|
||||
if (null != device) {
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceNo), JacksonUtil.toJSONString(device), 3, TimeUnit.DAYS);
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, device.getId()), JacksonUtil.toJSONString(device), 3, TimeUnit.DAYS);
|
||||
} else {
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceNo), "null", 60L, TimeUnit.SECONDS);
|
||||
}
|
||||
log.debug("根据设备编号获取设备信息, deviceNo: {}", deviceNo);
|
||||
DeviceV2DTO deviceDto = deviceIntegrationService.getDeviceByNo(deviceNo);
|
||||
DeviceEntity device = convertToEntity(deviceDto);
|
||||
return device;
|
||||
}
|
||||
|
||||
public DeviceConfigEntity getDeviceConfig(Long deviceId) {
|
||||
if (redisTemplate.hasKey(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId))) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId)), DeviceConfigEntity.class);
|
||||
List<DeviceConfigV2DTO> configList = deviceConfigIntegrationService.getDeviceConfigs(deviceId);
|
||||
if (configList != null && !configList.isEmpty()) {
|
||||
return convertToDeviceConfigEntity(configList, deviceId);
|
||||
}
|
||||
DeviceConfigEntity deviceConfig = deviceMapper.getConfigByDeviceId(deviceId);
|
||||
if (null == deviceConfig) {
|
||||
deviceConfig = new DeviceConfigEntity();
|
||||
deviceConfig.setId(SnowFlakeUtil.getLongId());
|
||||
deviceConfig.setDeviceId(deviceId);
|
||||
deviceMapper.addConfig(deviceConfig);
|
||||
}
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId), JacksonUtil.toJSONString(deviceConfig), 3, TimeUnit.DAYS);
|
||||
return deviceConfig;
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean clearDeviceCache(String deviceNo) {
|
||||
if (deviceNo == null) {
|
||||
return true;
|
||||
/**
|
||||
* 获取设备配置管理器
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return DeviceConfigManager实例,如果获取失败返回null
|
||||
*/
|
||||
public DeviceConfigManager getDeviceConfigManager(Long deviceId) {
|
||||
try {
|
||||
List<DeviceConfigV2DTO> configList = deviceConfigIntegrationService.getDeviceConfigs(deviceId);
|
||||
return new DeviceConfigManager(configList);
|
||||
} catch (Exception e) {
|
||||
log.warn("获取设备配置管理器失败, deviceId: {}", deviceId, e);
|
||||
return null;
|
||||
}
|
||||
if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceNo))) {
|
||||
DeviceEntity device = getDeviceByDeviceNo(deviceNo);
|
||||
if (device != null) {
|
||||
redisTemplate.delete(String.format(DEVICE_CACHE_KEY, device.getNo()));
|
||||
clearDeviceCache(device.getId());
|
||||
} else {
|
||||
redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceNo));
|
||||
}
|
||||
}
|
||||
redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceNo));
|
||||
return true;
|
||||
}
|
||||
public boolean clearDeviceCache(Long deviceId) {
|
||||
if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceId))) {
|
||||
DeviceEntity device = getDevice(deviceId);
|
||||
redisTemplate.delete(String.format(DEVICE_CACHE_KEY, device.getNo()));
|
||||
redisTemplate.delete(String.format(DEVICE_CONFIG_CACHE_KEY, device.getNo()));
|
||||
}
|
||||
redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceId));
|
||||
redisTemplate.delete(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId));
|
||||
return true;
|
||||
|
||||
/**
|
||||
* 将DeviceConfigV2DTO列表转换为DeviceConfigEntity(为了兼容性)
|
||||
*/
|
||||
private DeviceConfigEntity convertToDeviceConfigEntity(List<DeviceConfigV2DTO> configList, Long deviceId) {
|
||||
DeviceConfigEntity entity = new DeviceConfigEntity();
|
||||
entity.setId(SnowFlakeUtil.getLongId());
|
||||
entity.setDeviceId(deviceId);
|
||||
|
||||
// 由于DeviceConfigEntity没有通用的configJson字段,这里只设置基本信息
|
||||
// 具体的配置项需要通过DeviceConfigManager来访问
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void updateOnlineStatus(String deviceNo, int online, Date keepaliveAt) {
|
||||
@@ -129,8 +142,15 @@ public class DeviceRepository {
|
||||
redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, device.getNo()), JacksonUtil.toJSONString(device));
|
||||
}
|
||||
|
||||
public List<DeviceEntity> getAllDeviceByScenicId(Long scenicId) {
|
||||
List<Long> deviceIdList = deviceMapper.listAllByScenicId(scenicId);
|
||||
return deviceIdList.stream().map(this::getDevice).collect(Collectors.toList());
|
||||
public List<DeviceV2DTO> getAllDeviceByScenicId(Long scenicId) {
|
||||
try {
|
||||
var deviceListResponse = deviceIntegrationService.getScenicActiveDevices(scenicId, 1, 1000);
|
||||
if (deviceListResponse != null && deviceListResponse.getList() != null) {
|
||||
return deviceListResponse.getList();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取景区设备列表失败,景区ID: {}, 错误: {}", scenicId, e.getMessage());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
@@ -19,13 +19,12 @@ import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.pay.enums.PayAdapterType;
|
||||
import com.ycwl.basic.storage.enums.StorageType;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ScenicRepository {
|
||||
@@ -49,51 +48,15 @@ public class ScenicRepository {
|
||||
|
||||
public ScenicV2DTO getScenicBasic(Long id) {
|
||||
String key = String.format(SCENIC_BASIC_CACHE_KEY, id);
|
||||
try {
|
||||
ScenicV2DTO scenicDTO = scenicIntegrationService.getScenic(id);
|
||||
|
||||
// 请求成功,写入缓存
|
||||
if (scenicDTO != null) {
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(scenicDTO)
|
||||
);
|
||||
}
|
||||
return scenicDTO;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
String cacheKey = key;
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(cacheKey), ScenicV2DTO.class);
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
ScenicV2DTO scenicDTO = scenicIntegrationService.getScenic(id);
|
||||
return scenicDTO;
|
||||
}
|
||||
|
||||
public ScenicEntity getScenic(Long id) {
|
||||
String key = String.format(SCENIC_CACHE_KEY, id);
|
||||
try {
|
||||
ScenicV2WithConfigDTO scenicDTO = scenicIntegrationService.getScenicWithConfig(id);
|
||||
ScenicEntity scenicEntity = convertToScenicEntity(scenicDTO);
|
||||
|
||||
// 请求成功,写入缓存
|
||||
if (scenicEntity != null) {
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(scenicEntity)
|
||||
);
|
||||
}
|
||||
return scenicEntity;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
String cacheKey = key;
|
||||
if (redisTemplate.hasKey(cacheKey)) {
|
||||
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(cacheKey), ScenicEntity.class);
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
}
|
||||
ScenicV2WithConfigDTO scenicDTO = scenicIntegrationService.getScenicWithConfig(id);
|
||||
ScenicEntity scenicEntity = convertToScenicEntity(scenicDTO);
|
||||
return scenicEntity;
|
||||
}
|
||||
|
||||
public ScenicConfigEntity getScenicConfig(Long scenicId) {
|
||||
@@ -384,38 +347,13 @@ public class ScenicRepository {
|
||||
public ScenicConfigManager getScenicConfigManagerWithCache(Long scenicId) {
|
||||
String key = String.format(SCENIC_CONFIG_CACHE_KEY + ":manager", scenicId);
|
||||
|
||||
try {
|
||||
List<com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO> configList =
|
||||
scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
if (configList != null) {
|
||||
ScenicConfigManager manager = new ScenicConfigManager(configList);
|
||||
|
||||
// 请求成功,写入缓存(将配置列表序列化存储)
|
||||
redisTemplate.opsForValue().set(
|
||||
key,
|
||||
JacksonUtil.toJSONString(configList)
|
||||
);
|
||||
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
// 请求失败,尝试从缓存获取历史成功数据
|
||||
if (redisTemplate.hasKey(key)) {
|
||||
try {
|
||||
String cachedConfigJson = redisTemplate.opsForValue().get(key);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<ScenicConfigV2DTO> cachedConfigList =
|
||||
JacksonUtil.parseArray(cachedConfigJson, ScenicConfigV2DTO.class);
|
||||
return new ScenicConfigManager(cachedConfigList);
|
||||
} catch (Exception cacheException) {
|
||||
// 缓存解析失败,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 缓存也没有,返回null
|
||||
return null;
|
||||
List<com.ycwl.basic.integration.scenic.dto.config.ScenicConfigV2DTO> configList =
|
||||
scenicConfigIntegrationService.listConfigs(scenicId);
|
||||
if (configList != null) {
|
||||
ScenicConfigManager manager = new ScenicConfigManager(configList);
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package com.ycwl.basic.service.custom;
|
||||
|
||||
import com.aliyun.credentials.Client;
|
||||
import com.aliyun.credentials.models.Config;
|
||||
import com.aliyun.mts20140618.models.QuerySmarttagJobRequest;
|
||||
import com.aliyun.mts20140618.models.QuerySmarttagJobResponse;
|
||||
import com.aliyun.mts20140618.models.QuerySmarttagJobResponseBody;
|
||||
@@ -10,11 +8,11 @@ import com.aliyun.mts20140618.models.SubmitSmarttagJobResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.facebody.entity.AddFaceResp;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import com.ycwl.basic.mapper.CustomUploadTaskMapper;
|
||||
import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.custom.entity.CustomUploadTaskEntity;
|
||||
import com.ycwl.basic.model.custom.entity.FaceData;
|
||||
import com.ycwl.basic.model.custom.req.CreateUploadTaskReq;
|
||||
@@ -30,20 +28,15 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.ycwl.basic.constant.StorageConstant.VIID_FACE;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CustomUploadTaskService {
|
||||
@@ -52,7 +45,7 @@ public class CustomUploadTaskService {
|
||||
private CustomUploadTaskMapper customUploadTaskMapper;
|
||||
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
|
||||
@Autowired
|
||||
private ScenicService scenicService;
|
||||
@@ -80,7 +73,7 @@ public class CustomUploadTaskService {
|
||||
}
|
||||
|
||||
// 验证设备访问权限
|
||||
DeviceEntity device = validateDeviceAccess(req.getAccessKey(), req.getType());
|
||||
DeviceV2DTO device = validateDeviceAccess(req.getAccessKey(), req.getType());
|
||||
|
||||
long taskId = SnowFlakeUtil.getLongId();
|
||||
String savePath = generateSavePath(device.getScenicId(), req.getFileName());
|
||||
@@ -108,7 +101,7 @@ public class CustomUploadTaskService {
|
||||
|
||||
public void completeUpload(String accessKey, Long taskId) {
|
||||
// 验证设备访问权限
|
||||
DeviceEntity device = validateDeviceAccess(accessKey, null);
|
||||
DeviceV2DTO device = validateDeviceAccess(accessKey, null);
|
||||
|
||||
CustomUploadTaskEntity task = customUploadTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
@@ -146,7 +139,7 @@ public class CustomUploadTaskService {
|
||||
|
||||
public void markTaskFailed(String accessKey, Long taskId, String errorMsg) {
|
||||
// 验证设备访问权限
|
||||
DeviceEntity device = validateDeviceAccess(accessKey, null);
|
||||
DeviceV2DTO device = validateDeviceAccess(accessKey, null);
|
||||
|
||||
CustomUploadTaskEntity task = customUploadTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
@@ -214,17 +207,18 @@ public class CustomUploadTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceEntity validateDeviceAccess(String accessKey, String type) {
|
||||
private DeviceV2DTO validateDeviceAccess(String accessKey, String type) {
|
||||
if (StringUtils.isBlank(accessKey)) {
|
||||
throw new RuntimeException("设备访问密钥不能为空");
|
||||
}
|
||||
|
||||
DeviceEntity device = deviceMapper.getByDeviceNo(accessKey);
|
||||
if (device == null || device.getStatus() != 1) {
|
||||
// 通过zt-device服务获取设备信息
|
||||
DeviceV2DTO deviceV2DTO = deviceIntegrationService.getDeviceByNo(accessKey);
|
||||
if (deviceV2DTO == null || deviceV2DTO.getIsActive() != 1) {
|
||||
throw new RuntimeException("无效的设备访问密钥或设备已被禁用");
|
||||
}
|
||||
|
||||
return device;
|
||||
return deviceV2DTO;
|
||||
}
|
||||
|
||||
private String createAliyunMtsTask(String inputPath) {
|
||||
|
@@ -2,8 +2,10 @@ package com.ycwl.basic.service.mobile.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2ListResponse;
|
||||
import com.ycwl.basic.mapper.ExtraDeviceMapper;
|
||||
import com.ycwl.basic.mapper.ScenicAccountMapper;
|
||||
import com.ycwl.basic.model.jwt.JwtInfo;
|
||||
@@ -34,8 +36,10 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
|
||||
|
||||
@@ -48,7 +52,7 @@ import static com.ycwl.basic.constant.JwtRoleConstant.MERCHANT;
|
||||
public class AppScenicServiceImpl implements AppScenicService {
|
||||
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
@Autowired
|
||||
private ScenicAccountMapper scenicAccountMapper;
|
||||
@Autowired
|
||||
@@ -80,7 +84,14 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
@Override
|
||||
public ApiResponse<ScenicDeviceCountVO> deviceCountByScenicId(Long scenicId) {
|
||||
JwtInfo worker = JwtTokenUtil.getWorker();
|
||||
ScenicDeviceCountVO scenicDeviceCountVO = deviceMapper.deviceCountByScenicId(scenicId);
|
||||
// 通过zt-device服务获取设备统计
|
||||
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.getScenicActiveDevices(scenicId, 1, 1000);
|
||||
ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO();
|
||||
if (deviceListResponse != null && deviceListResponse.getList() != null) {
|
||||
scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size());
|
||||
} else {
|
||||
scenicDeviceCountVO.setTotalDeviceCount(0);
|
||||
}
|
||||
return ApiResponse.success(scenicDeviceCountVO);
|
||||
}
|
||||
|
||||
@@ -107,7 +118,14 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
scenicRespVO.setKfCodeUrl(scenic.getKfCodeUrl());
|
||||
}
|
||||
|
||||
ScenicDeviceCountVO scenicDeviceCountVO = deviceMapper.deviceCountByScenicId(id);
|
||||
// 通过zt-device服务获取设备统计
|
||||
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.getScenicActiveDevices(id, 1, 1000);
|
||||
ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO();
|
||||
if (deviceListResponse != null && deviceListResponse.getList() != null) {
|
||||
scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size());
|
||||
} else {
|
||||
scenicDeviceCountVO.setTotalDeviceCount(0);
|
||||
}
|
||||
scenicRespVO.setLensNum(scenicDeviceCountVO.getTotalDeviceCount());
|
||||
return ApiResponse.success(scenicRespVO);
|
||||
}
|
||||
@@ -257,7 +275,18 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
|
||||
@Override
|
||||
public ApiResponse<List<DeviceRespVO>> getDevices(Long scenicId) {
|
||||
List<DeviceRespVO> deviceRespVOList = deviceMapper.listByScenicIdWithWVP(scenicId);
|
||||
DeviceV2ListResponse deviceV2ListResponse = deviceIntegrationService.listDevices(1, 1000, null, null, null, 1, scenicId);
|
||||
List<DeviceRespVO> deviceRespVOList = deviceV2ListResponse.getList().stream().map(device -> {
|
||||
DeviceRespVO deviceRespVO = new DeviceRespVO();
|
||||
deviceRespVO.setId(device.getId());
|
||||
deviceRespVO.setName(device.getName());
|
||||
deviceRespVO.setNo(device.getNo());
|
||||
|
||||
DeviceConfigManager config = deviceRepository.getDeviceConfigManager(device.getId());
|
||||
deviceRespVO.setDeviceNo(device.getNo());
|
||||
deviceRespVO.setChannelNo(config.getString("channel_no"));
|
||||
return deviceRespVO;
|
||||
}).collect(Collectors.toList());
|
||||
for (DeviceRespVO deviceRespVO : deviceRespVOList) {
|
||||
DeviceEntity onlineStatus = deviceRepository.getOnlineStatus(deviceRespVO.getId());
|
||||
if (onlineStatus != null) {
|
||||
@@ -282,7 +311,7 @@ public class AppScenicServiceImpl implements AppScenicService {
|
||||
deviceRespVO.setOnline(0);
|
||||
continue;
|
||||
}
|
||||
Long ts = Long.parseLong(onlineTs);
|
||||
long ts = Long.parseLong(onlineTs);
|
||||
Date keepaliveAt = new Date(ts*1000);
|
||||
deviceRespVO.setUpdateAt(keepaliveAt);
|
||||
deviceRespVO.setKeepaliveAt(keepaliveAt);
|
||||
|
@@ -1,35 +0,0 @@
|
||||
package com.ycwl.basic.service.pc;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceBatchSortRequest;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/2 16:14
|
||||
* 设备管理
|
||||
*/
|
||||
public interface DeviceService {
|
||||
ApiResponse<PageInfo<DeviceRespVO>> pageQuery(DeviceReqQuery deviceReqQuery);
|
||||
ApiResponse<List<DeviceRespVO>> list(DeviceReqQuery deviceReqQuery);
|
||||
ApiResponse<DeviceRespVO> getById(Long id);
|
||||
ApiResponse addOrUpdate(DeviceAddOrUpdateReq deviceReqQuery);
|
||||
ApiResponse deleteById(Long id);
|
||||
ApiResponse updateStatus(Long id);
|
||||
|
||||
DeviceConfigEntity getConfig(Long id);
|
||||
void saveConfig(Long configId, DeviceConfigEntity config);
|
||||
|
||||
void updateDevices(Long scenicId, WvpSyncReqVo reqVo);
|
||||
|
||||
ApiResponse<Boolean> sortDevice(Long deviceId, Long afterDeviceId);
|
||||
|
||||
ApiResponse<Boolean> batchSort(Long scenicId, DeviceBatchSortRequest request);
|
||||
}
|
@@ -1,176 +0,0 @@
|
||||
package com.ycwl.basic.service.pc.impl;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceBatchSortRequest;
|
||||
import com.ycwl.basic.model.wvp.WvpSyncReqVo;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceAddOrUpdateReq;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.service.pc.DeviceService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.SnowFlakeUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/2 16:18
|
||||
*/
|
||||
@Service
|
||||
public class DeviceServiceImpl implements DeviceService {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
@Autowired
|
||||
private DeviceRepository deviceRepository;
|
||||
@Override
|
||||
public ApiResponse<PageInfo<DeviceRespVO>> pageQuery(DeviceReqQuery deviceReqQuery) {
|
||||
PageHelper.startPage(deviceReqQuery.getPageNum(), deviceReqQuery.getPageSize());
|
||||
List<DeviceRespVO> list = deviceMapper.list(deviceReqQuery);
|
||||
for (DeviceRespVO deviceRespVO : list) {
|
||||
DeviceEntity onlineStatus = deviceRepository.getOnlineStatus(deviceRespVO.getId());
|
||||
if (onlineStatus != null) {
|
||||
deviceRespVO.setKeepaliveAt(onlineStatus.getKeepaliveAt());
|
||||
if (new Date().getTime() - onlineStatus.getKeepaliveAt().getTime() > 300000) {
|
||||
deviceRespVO.setOnline(0);
|
||||
} else {
|
||||
deviceRespVO.setOnline(onlineStatus.getOnline());
|
||||
}
|
||||
} else {
|
||||
deviceRespVO.setOnline(0);
|
||||
deviceRespVO.setKeepaliveAt(null);
|
||||
}
|
||||
}
|
||||
PageInfo<DeviceRespVO> pageInfo = new PageInfo<>(list);
|
||||
return ApiResponse.success(pageInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<List<DeviceRespVO>> list(DeviceReqQuery deviceReqQuery) {
|
||||
return ApiResponse.success(deviceMapper.list(deviceReqQuery));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<DeviceRespVO> getById(Long id) {
|
||||
return ApiResponse.success(deviceMapper.getById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse addOrUpdate(DeviceAddOrUpdateReq deviceReqQuery) {
|
||||
Long id = deviceReqQuery.getId();
|
||||
if (id == null) {
|
||||
deviceReqQuery.setId(SnowFlakeUtil.getLongId());
|
||||
if (StringUtils.isBlank(deviceReqQuery.getNo())) {
|
||||
deviceReqQuery.setNo(deviceReqQuery.getId().toString());
|
||||
}
|
||||
deviceReqQuery.setStatus(0);
|
||||
return ApiResponse.success(deviceMapper.add(deviceReqQuery));
|
||||
} else {
|
||||
deviceRepository.clearDeviceCache(deviceReqQuery.getId());
|
||||
deviceMapper.update(deviceReqQuery);
|
||||
deviceRepository.clearDeviceCache(deviceReqQuery.getId());
|
||||
return ApiResponse.success(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse deleteById(Long id) {
|
||||
return ApiResponse.success(deviceMapper.deleteById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse updateStatus(Long id) {
|
||||
deviceRepository.clearDeviceCache(id);
|
||||
deviceMapper.updateStatus(id);
|
||||
deviceRepository.clearDeviceCache(id);
|
||||
return ApiResponse.success(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceConfigEntity getConfig(Long id) {
|
||||
DeviceConfigEntity config = deviceMapper.getConfigByDeviceId(id);
|
||||
if (config == null) {
|
||||
config = new DeviceConfigEntity();
|
||||
config.setId(SnowFlakeUtil.getLongId());
|
||||
config.setDeviceId(id);
|
||||
deviceMapper.addConfig(config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig(Long configId, DeviceConfigEntity config) {
|
||||
config.setId(configId);
|
||||
deviceMapper.updateConfig(config);
|
||||
deviceRepository.clearDeviceCache(config.getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDevices(Long scenicId, WvpSyncReqVo reqVo) {
|
||||
if (reqVo == null) {
|
||||
return;
|
||||
}
|
||||
if (reqVo.getDevices() != null && !reqVo.getDevices().isEmpty()) {
|
||||
for (WvpSyncReqVo.DeviceItem deviceItem : reqVo.getDevices()) {
|
||||
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceItem.getDeviceNo());
|
||||
if (device != null) {
|
||||
device.setOnline(deviceItem.getOnline());
|
||||
device.setKeepaliveAt(deviceItem.getKeepaliveAt());
|
||||
deviceRepository.updateOnlineStatus(device.getId(), deviceItem.getIp(), 1, deviceItem.getKeepaliveAt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> sortDevice(Long deviceId, Long afterDeviceId) {
|
||||
DeviceEntity device = deviceRepository.getDevice(deviceId);
|
||||
if (device == null) {
|
||||
return ApiResponse.fail("设备不存在");
|
||||
}
|
||||
List<DeviceEntity> scenicDeviceList = deviceRepository.getAllDeviceByScenicId(device.getScenicId());
|
||||
AtomicInteger sortNum = new AtomicInteger(0);
|
||||
for (DeviceEntity item : scenicDeviceList) {
|
||||
item.setSort(sortNum.addAndGet(1));
|
||||
}
|
||||
Optional<DeviceEntity> templateOptional = scenicDeviceList.stream().filter(item -> item.getId().equals(deviceId)).findAny();
|
||||
if (templateOptional.isEmpty()) {
|
||||
return ApiResponse.fail("设备不存在");
|
||||
}
|
||||
Optional<DeviceEntity> afterTemplateOptional = scenicDeviceList.stream().filter(item -> item.getId().equals(afterDeviceId)).findAny();
|
||||
if (afterTemplateOptional.isPresent()) {
|
||||
DeviceEntity afterTemplate = afterTemplateOptional.get();
|
||||
Integer newSort = afterTemplate.getSort();
|
||||
DeviceEntity oldTemplate = templateOptional.get();
|
||||
Integer oldSort = oldTemplate.getSort();
|
||||
afterTemplate.setSort(oldSort);
|
||||
oldTemplate.setSort(newSort);
|
||||
}
|
||||
scenicDeviceList.forEach(item -> {
|
||||
deviceMapper.updateSort(item.getId(), item.getSort());
|
||||
deviceRepository.clearDeviceCache(item.getId());
|
||||
deviceRepository.clearDeviceCache(item.getNo());
|
||||
deviceRepository.clearDeviceCache(item.getNo2());
|
||||
});
|
||||
return ApiResponse.success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse<Boolean> batchSort(Long scenicId, DeviceBatchSortRequest request) {
|
||||
for (DeviceBatchSortRequest.SortItem item : request.getList()) {
|
||||
deviceMapper.updateSort(item.getId(), item.getSort());
|
||||
deviceRepository.clearDeviceCache(item.getId());
|
||||
}
|
||||
return ApiResponse.success(true);
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.enums.StatisticEnum;
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.mapper.StatisticsMapper;
|
||||
import com.ycwl.basic.mapper.FaceMapper;
|
||||
@@ -290,7 +291,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {// 匹配原片:照片
|
||||
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
|
||||
List<MemberSourceEntity> memberSourceEntityList = sourceEntities.stream().map(sourceEntity -> {
|
||||
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(sourceEntity.getDeviceId());
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(sourceEntity.getDeviceId());
|
||||
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
|
||||
memberSourceEntity.setScenicId(face.getScenicId());
|
||||
memberSourceEntity.setFaceId(face.getId());
|
||||
@@ -300,11 +301,11 @@ public class FaceServiceImpl implements FaceService {
|
||||
memberSourceEntity.setIsFree(0);
|
||||
if (deviceConfig != null) {
|
||||
if (sourceEntity.getType() == 1) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getVideoFree())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("video_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
} else if (sourceEntity.getType() == 2) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getImageFree())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("image_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package com.ycwl.basic.service.pc.impl;
|
||||
import com.ycwl.basic.facebody.FaceBodyFactory;
|
||||
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
|
||||
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
|
||||
import com.ycwl.basic.pay.PayFactory;
|
||||
import com.ycwl.basic.pay.adapter.IPayAdapter;
|
||||
@@ -12,10 +11,9 @@ import com.ycwl.basic.service.pc.ScenicService;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
import com.ycwl.basic.storage.adapters.IStorageAdapter;
|
||||
import com.ycwl.basic.storage.exceptions.StorageUnsupportedException;
|
||||
import com.ycwl.basic.util.ScenicConfigManager;
|
||||
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
|
||||
import com.ycwl.basic.util.TtlCacheMap;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.ycwl.basic.service.task.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.utils.JacksonUtil;
|
||||
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
|
||||
import com.ycwl.basic.biz.OrderBiz;
|
||||
@@ -115,7 +116,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
if (sampleListIds != null && !sampleListIds.isEmpty()) {// 匹配原片:照片
|
||||
List<SourceEntity> sourceEntities = sourceMapper.listBySampleIds(sampleListIds);
|
||||
List<MemberSourceEntity> memberSourceEntityList = sourceEntities.stream().map(sourceEntity -> {
|
||||
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(sourceEntity.getDeviceId());
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(sourceEntity.getDeviceId());
|
||||
MemberSourceEntity memberSourceEntity = new MemberSourceEntity();
|
||||
memberSourceEntity.setScenicId(scenicId);
|
||||
memberSourceEntity.setFaceId(faceEntity.getId());
|
||||
@@ -125,11 +126,11 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
memberSourceEntity.setIsFree(0);
|
||||
if (deviceConfig != null) {
|
||||
if (sourceEntity.getType() == 1) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getVideoFree())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("video_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
} else if (sourceEntity.getType() == 2) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getImageFree())) {
|
||||
if (Integer.valueOf(1).equals(deviceConfig.getInteger("image_free"))) {
|
||||
memberSourceEntity.setIsFree(1);
|
||||
}
|
||||
}
|
||||
|
@@ -3,11 +3,16 @@ package com.ycwl.basic.task;
|
||||
|
||||
import com.ycwl.basic.device.DeviceFactory;
|
||||
import com.ycwl.basic.device.operator.IDeviceStorageOperator;
|
||||
import com.ycwl.basic.mapper.DeviceMapper;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2ListResponse;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import java.util.stream.Collectors;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.pc.device.req.DeviceReqQuery;
|
||||
import com.ycwl.basic.model.pc.device.resp.DeviceRespVO;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
@@ -24,29 +29,33 @@ import java.util.List;
|
||||
@Profile("prod")
|
||||
public class VideoPieceCleaner {
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
@Autowired
|
||||
private DeviceRepository deviceRepository;
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void clean() {
|
||||
log.info("开始删除视频文件");
|
||||
List<DeviceEntity> deviceList = deviceMapper.listAll();
|
||||
for (DeviceEntity device : deviceList) {
|
||||
DeviceConfigEntity config = deviceMapper.getConfigByDeviceId(device.getId());
|
||||
if (config == null) {
|
||||
// 通过zt-device服务获取所有激活设备
|
||||
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.listDevices(1, 10000, null, null, null, 1, null);
|
||||
List<DeviceEntity> deviceList;
|
||||
if (deviceListResponse == null) {
|
||||
return;
|
||||
}
|
||||
for (DeviceV2DTO device : deviceListResponse.getList()) {
|
||||
DeviceConfigManager config = deviceRepository.getDeviceConfigManager(device.getId());
|
||||
DeviceConfigEntity dConfig = deviceRepository.getDeviceConfig(device.getId());
|
||||
Integer storeExpireDay = config.getInteger("store_expire_day");
|
||||
if (storeExpireDay == null || storeExpireDay <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (config.getStoreExpireDay() == null) {
|
||||
continue;
|
||||
}
|
||||
if (config.getStoreExpireDay() <= 0) {
|
||||
continue;
|
||||
}
|
||||
IDeviceStorageOperator storageOperator = DeviceFactory.getDeviceStorageOperator(device, config);
|
||||
IDeviceStorageOperator storageOperator = DeviceFactory.getDeviceStorageOperator(device, dConfig);
|
||||
if (storageOperator == null) {
|
||||
continue;
|
||||
}
|
||||
storageOperator.removeFilesBeforeDate(new Date(System.currentTimeMillis() - config.getStoreExpireDay() * 24 * 60 * 60 * 1000));
|
||||
storageOperator.removeFilesBeforeDate(new Date(System.currentTimeMillis() - storeExpireDay * 24 * 60 * 60 * 1000));
|
||||
log.info("删除视频文件完成");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import com.ycwl.basic.constant.StorageConstant;
|
||||
import com.ycwl.basic.device.DeviceFactory;
|
||||
import com.ycwl.basic.device.entity.common.FileObject;
|
||||
import com.ycwl.basic.device.operator.IDeviceStorageOperator;
|
||||
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
|
||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
|
||||
@@ -15,6 +16,8 @@ import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.SourceMapper;
|
||||
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
|
||||
import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
|
||||
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
|
||||
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
|
||||
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
|
||||
@@ -76,6 +79,8 @@ public class VideoPieceGetter {
|
||||
private VideoReUploader videoReUploader;
|
||||
@Autowired
|
||||
private ScenicRepository scenicRepository;
|
||||
@Autowired
|
||||
private DeviceIntegrationService deviceIntegrationService;
|
||||
|
||||
@Data
|
||||
public static class Task {
|
||||
@@ -144,12 +149,13 @@ public class VideoPieceGetter {
|
||||
Map<Long, Long> pairDeviceMap = new ConcurrentHashMap<>();
|
||||
if (!list.isEmpty()) {
|
||||
Long scenicId = list.getFirst().getScenicId();
|
||||
List<DeviceEntity> allDeviceByScenicId = deviceRepository.getAllDeviceByScenicId(scenicId);
|
||||
List<DeviceV2DTO> allDeviceByScenicId = deviceRepository.getAllDeviceByScenicId(scenicId);
|
||||
allDeviceByScenicId.forEach(device -> {
|
||||
Long deviceId = device.getId();
|
||||
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(deviceId);
|
||||
if (deviceConfig != null && deviceConfig.getPairDevice() != null) {
|
||||
pairDeviceMap.putIfAbsent(deviceId, deviceConfig.getPairDevice());
|
||||
DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(deviceId);
|
||||
Long pairDevice = deviceConfig.getLong("pair_device");
|
||||
if (pairDevice != null) {
|
||||
pairDeviceMap.putIfAbsent(deviceId, pairDevice);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -254,24 +260,21 @@ public class VideoPieceGetter {
|
||||
}
|
||||
|
||||
private boolean doCut(Long deviceId, Long faceSampleId, Date baseTime, Task task) {
|
||||
DeviceEntity device = deviceRepository.getDevice(deviceId);
|
||||
DeviceConfigEntity config = deviceRepository.getDeviceConfig(deviceId);
|
||||
// 通过zt-device服务获取设备信息
|
||||
DeviceV2DTO deviceV2 = deviceIntegrationService.getDevice(deviceId);
|
||||
if (deviceV2 == null) {
|
||||
log.warn("设备不存在,设备ID: {}", deviceId);
|
||||
return false;
|
||||
}
|
||||
DeviceConfigManager config = deviceRepository.getDeviceConfigManager(deviceId);
|
||||
DeviceConfigEntity dConfig = deviceRepository.getDeviceConfig(deviceId);
|
||||
|
||||
SourceEntity source = sourceMapper.querySameVideo(faceSampleId, device.getId());
|
||||
SourceEntity source = sourceMapper.querySameVideo(faceSampleId, deviceV2.getId());
|
||||
|
||||
if (source == null || task.force) {
|
||||
BigDecimal cutPre = BigDecimal.valueOf(5L);
|
||||
BigDecimal cutPost = BigDecimal.valueOf(4L);
|
||||
if (config != null) {
|
||||
// 有配置
|
||||
if (config.getCutPre() != null) {
|
||||
cutPre = config.getCutPre();
|
||||
}
|
||||
if (config.getCutPost() != null) {
|
||||
cutPost = config.getCutPost();
|
||||
}
|
||||
}
|
||||
IDeviceStorageOperator pieceGetter = DeviceFactory.getDeviceStorageOperator(device, config);
|
||||
BigDecimal cutPre = config.getBigDecimal("cut_pre", BigDecimal.valueOf(5L));
|
||||
BigDecimal cutPost = config.getBigDecimal("cut_post", BigDecimal.valueOf(5L));
|
||||
IDeviceStorageOperator pieceGetter = DeviceFactory.getDeviceStorageOperator(deviceV2, dConfig);
|
||||
if (pieceGetter == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -317,12 +320,12 @@ public class VideoPieceGetter {
|
||||
sourceEntity.setUrl(imgSource.getUrl());
|
||||
sourceEntity.setPosJson(imgSource.getPosJson());
|
||||
}
|
||||
if (StringUtils.isNotBlank(config.getVideoCrop())) {
|
||||
sourceEntity.setPosJson(config.getVideoCrop());
|
||||
if (StringUtils.isNotBlank(config.getString("video_crop"))) {
|
||||
sourceEntity.setPosJson(config.getString("video_crop"));
|
||||
}
|
||||
sourceEntity.setVideoUrl(url);
|
||||
sourceEntity.setFaceSampleId(faceSampleId);
|
||||
sourceEntity.setScenicId(device.getScenicId());
|
||||
sourceEntity.setScenicId(deviceV2.getScenicId());
|
||||
sourceEntity.setDeviceId(deviceId);
|
||||
sourceEntity.setType(1);
|
||||
if (task.memberId != null && task.faceId != null) {
|
||||
@@ -330,9 +333,9 @@ public class VideoPieceGetter {
|
||||
videoSource.setMemberId(task.getMemberId());
|
||||
videoSource.setType(1);
|
||||
videoSource.setFaceId(task.getFaceId());
|
||||
videoSource.setScenicId(device.getScenicId());
|
||||
videoSource.setScenicId(deviceV2.getScenicId());
|
||||
videoSource.setSourceId(sourceEntity.getId());
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), device.getScenicId(), 1, task.getFaceId());
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), deviceV2.getScenicId(), 1, task.getFaceId());
|
||||
if (isBuy.isBuy()) { // 如果用户买过
|
||||
videoSource.setIsBuy(1);
|
||||
} else if (isBuy.isFree()) { // 全免费逻辑
|
||||
@@ -346,8 +349,8 @@ public class VideoPieceGetter {
|
||||
videoReUploader.addTask(sourceEntity.getId());
|
||||
} else {
|
||||
source.setVideoUrl(url);
|
||||
if (StringUtils.isNotBlank(config.getVideoCrop())) {
|
||||
source.setPosJson(config.getVideoCrop());
|
||||
if (StringUtils.isNotBlank(config.getString("video_crop"))) {
|
||||
source.setPosJson(config.getString("video_crop"));
|
||||
}
|
||||
sourceMapper.update(source);
|
||||
videoReUploader.addTask(source.getId());
|
||||
@@ -358,10 +361,10 @@ public class VideoPieceGetter {
|
||||
int count = sourceMapper.hasRelationTo(task.getMemberId(), source.getId(), 1);
|
||||
if (count <= 0) {
|
||||
// 没有关联
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), device.getScenicId(), 1, task.getFaceId());
|
||||
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), deviceV2.getScenicId(), 1, task.getFaceId());
|
||||
MemberSourceEntity videoSource = new MemberSourceEntity();
|
||||
videoSource.setId(SnowFlakeUtil.getLongId());
|
||||
videoSource.setScenicId(device.getScenicId());
|
||||
videoSource.setScenicId(deviceV2.getScenicId());
|
||||
videoSource.setFaceId(task.getFaceId());
|
||||
videoSource.setMemberId(task.getMemberId());
|
||||
videoSource.setType(1);
|
||||
|
@@ -1,153 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ycwl.basic.mapper.DeviceMapper">
|
||||
<insert id="add">
|
||||
insert into device(id, scenic_id, name, no, latitude, longitude) values (#{id}, #{scenicId}, #{name}, #{no}, #{latitude}, #{longitude})
|
||||
</insert>
|
||||
<insert id="addConfig">
|
||||
insert into device_config(id, device_id, create_time)
|
||||
values (#{id}, #{deviceId}, now())
|
||||
</insert>
|
||||
<insert id="addEntity">
|
||||
insert into device(id, name, no, status, online, ip_addr, keepalive_at, create_at, update_at)
|
||||
values (#{id}, #{name}, #{no}, 0, #{online}, #{ipAddr}, #{keepaliveAt}, now(), now())
|
||||
</insert>
|
||||
<update id="update">
|
||||
update device set scenic_id = #{scenicId}, name = #{name}, no = #{no}, longitude = #{longitude}, latitude = #{latitude}, update_at = now() where id = #{id}
|
||||
</update>
|
||||
<update id="updateStatus">
|
||||
update device
|
||||
set status = (CASE
|
||||
status
|
||||
WHEN 1 THEN
|
||||
0
|
||||
WHEN 0 THEN
|
||||
1
|
||||
ELSE null
|
||||
END)
|
||||
where id = #{id}
|
||||
</update>
|
||||
<update id="updateConfig">
|
||||
update device_config
|
||||
set viid_type = #{viidType},
|
||||
store_type = #{storeType},
|
||||
store_config_json = #{storeConfigJson},
|
||||
store_expire_day = #{storeExpireDay},
|
||||
online_check = #{onlineCheck},
|
||||
online_max_interval = #{onlineMaxInterval},
|
||||
cut_pre = #{cutPre},
|
||||
cut_post = #{cutPost},
|
||||
enable_pre_book = #{enablePreBook},
|
||||
image_free = #{imageFree},
|
||||
video_free = #{videoFree},
|
||||
pair_device = #{pairDevice},
|
||||
video_crop = #{videoCrop}
|
||||
where id = #{id}
|
||||
</update>
|
||||
<update id="updateEntity">
|
||||
update device
|
||||
set name = #{name},
|
||||
no = #{no},
|
||||
online = #{online},
|
||||
ip_addr = #{ipAddr},
|
||||
keepalive_at = #{keepaliveAt},
|
||||
update_at = now()
|
||||
where id = #{id}
|
||||
</update>
|
||||
<update id="updateOnlineStatus">
|
||||
update device
|
||||
set online = #{online},
|
||||
<if test="ipAddr != null">ip_addr = #{ipAddr},</if>
|
||||
keepalive_at = #{keepaliveAt},
|
||||
update_at = now()
|
||||
where id = #{id}
|
||||
</update>
|
||||
<update id="updateSort">
|
||||
update device
|
||||
set sort = #{sort}
|
||||
where id = #{id}
|
||||
</update>
|
||||
<delete id="deleteById">
|
||||
delete from device where id = #{id}
|
||||
</delete>
|
||||
<select id="list" resultType="com.ycwl.basic.model.pc.device.resp.DeviceRespVO">
|
||||
select d.id, scenic_id, d.name, no, d.longitude, d.latitude, d.status, create_at, d.update_at, s.name scenic_name, d.keepalive_at, d.online, p.wvp_device_no as device_no, p.wvp_channel_no channel_no,
|
||||
(select s.url from source s where s.device_id=d.id order by id desc limit 1) coverUrl
|
||||
from device d
|
||||
left join scenic s on d.scenic_id = s.id
|
||||
left join device_preview_config p on d.id = p.device_id and p.status = 1
|
||||
<where>
|
||||
<if test="name!= null and name!= ''">
|
||||
and d.`name` like concat('%', #{name}, '%')
|
||||
</if>
|
||||
<if test="no!= null and no!= ''">
|
||||
and `no` like concat('%', #{no}, '%')
|
||||
</if>
|
||||
<if test="status!= null">
|
||||
and d.`status` = #{status}
|
||||
</if>
|
||||
<if test="scenicId!= null and scenicId!= ''">
|
||||
and scenic_id = #{scenicId}
|
||||
</if>
|
||||
<if test="startTime!=null">
|
||||
and d.create_at >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime!=null">
|
||||
and d.create_at <= #{endTime}
|
||||
</if>
|
||||
</where>
|
||||
order by d.scenic_id desc, sort asc
|
||||
</select>
|
||||
<select id="getById" resultType="com.ycwl.basic.model.pc.device.resp.DeviceRespVO">
|
||||
select d.id, scenic_id, d.name, no, d.longitude, d.latitude, d.status, create_at, d.update_at, s.name scenic_name
|
||||
from device d
|
||||
left join scenic s on d.scenic_id = s.id
|
||||
where d.id = #{id}
|
||||
</select>
|
||||
<select id="listByScenicIdWithWVP" resultType="com.ycwl.basic.model.pc.device.resp.DeviceRespVO">
|
||||
select d.id, d.name, no, d.status, create_at, d.update_at, p.wvp_device_no as device_no, p.wvp_channel_no channel_no
|
||||
from device d
|
||||
left join device_preview_config p on d.id = p.device_id and p.status = 1
|
||||
where d.scenic_id = #{scenicId}
|
||||
order by d.sort
|
||||
</select>
|
||||
<select id="deviceCountByScenicId" resultType="com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO">
|
||||
select count(1) totalDeviceCount
|
||||
from device
|
||||
where scenic_id = #{scenicId} and status = 1
|
||||
</select>
|
||||
<select id="getConfigByDeviceId" resultType="com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity">
|
||||
select *
|
||||
from device_config
|
||||
where device_id = #{deviceId}
|
||||
limit 1
|
||||
</select>
|
||||
<select id="getByDeviceId" resultType="com.ycwl.basic.model.pc.device.entity.DeviceEntity">
|
||||
select *
|
||||
from device
|
||||
where id = #{deviceId}
|
||||
</select>
|
||||
<select id="getByDeviceNo" resultType="com.ycwl.basic.model.pc.device.entity.DeviceEntity">
|
||||
select *
|
||||
from device
|
||||
where no = #{deviceNo}
|
||||
</select>
|
||||
<select id="listAll" resultType="com.ycwl.basic.model.pc.device.entity.DeviceEntity">
|
||||
select *
|
||||
from device
|
||||
where status = 1
|
||||
order by sort
|
||||
</select>
|
||||
<select id="getByDeviceNo2" resultType="com.ycwl.basic.model.pc.device.entity.DeviceEntity">
|
||||
select *
|
||||
from device
|
||||
where no_2 = #{deviceNo}
|
||||
limit 1
|
||||
</select>
|
||||
<select id="listAllByScenicId" resultType="java.lang.Long">
|
||||
select id
|
||||
from device
|
||||
where scenic_id = #{scenicId}
|
||||
order by sort
|
||||
</select>
|
||||
</mapper>
|
Reference in New Issue
Block a user