Merge branch 'render-worker-microservice'

# Conflicts:
#	src/main/java/com/ycwl/basic/integration/scenic/service/ScenicIntegrationService.java
#	src/main/java/com/ycwl/basic/service/task/impl/TaskTaskServiceImpl.java
This commit is contained in:
2025-09-09 11:00:10 +08:00
52 changed files with 2711 additions and 630 deletions

View File

@@ -1,7 +1,7 @@
package com.ycwl.basic.controller.pc; package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO; import com.ycwl.basic.integration.scenic.dto.config.DefaultConfigDTO;
import com.ycwl.basic.integration.scenic.service.DefaultConfigIntegrationService; import com.ycwl.basic.integration.scenic.service.ScenicDefaultConfigIntegrationService;
import com.ycwl.basic.utils.ApiConst; import com.ycwl.basic.utils.ApiConst;
import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -20,7 +20,7 @@ import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
public class DefaultConfigController { public class DefaultConfigController {
private final DefaultConfigIntegrationService defaultConfigIntegrationService; private final ScenicDefaultConfigIntegrationService scenicDefaultConfigIntegrationService;
/** /**
* 获取默认配置列表 * 获取默认配置列表
@@ -29,7 +29,7 @@ public class DefaultConfigController {
public ApiResponse<List<DefaultConfigDTO>> listDefaultConfigs() { public ApiResponse<List<DefaultConfigDTO>> listDefaultConfigs() {
log.info("获取默认配置列表"); log.info("获取默认配置列表");
try { try {
List<DefaultConfigDTO> configs = defaultConfigIntegrationService.listDefaultConfigs(); List<DefaultConfigDTO> configs = scenicDefaultConfigIntegrationService.listDefaultConfigs();
return ApiResponse.success(configs); return ApiResponse.success(configs);
} catch (Exception e) { } catch (Exception e) {
log.error("获取默认配置列表失败", e); log.error("获取默认配置列表失败", e);
@@ -44,7 +44,7 @@ public class DefaultConfigController {
public ApiResponse<DefaultConfigDTO> getDefaultConfig(@PathVariable String configKey) { public ApiResponse<DefaultConfigDTO> getDefaultConfig(@PathVariable String configKey) {
log.info("获取默认配置, configKey: {}", configKey); log.info("获取默认配置, configKey: {}", configKey);
try { try {
DefaultConfigDTO config = defaultConfigIntegrationService.getDefaultConfig(configKey); DefaultConfigDTO config = scenicDefaultConfigIntegrationService.getDefaultConfig(configKey);
return ApiResponse.success(config); return ApiResponse.success(config);
} catch (Exception e) { } catch (Exception e) {
log.error("获取默认配置失败, configKey: {}", configKey, e); log.error("获取默认配置失败, configKey: {}", configKey, e);
@@ -59,7 +59,7 @@ public class DefaultConfigController {
public ApiResponse<DefaultConfigDTO> createDefaultConfig(@RequestBody DefaultConfigDTO request) { public ApiResponse<DefaultConfigDTO> createDefaultConfig(@RequestBody DefaultConfigDTO request) {
log.info("创建默认配置, configKey: {}", request.getConfigKey()); log.info("创建默认配置, configKey: {}", request.getConfigKey());
try { try {
DefaultConfigDTO config = defaultConfigIntegrationService.createDefaultConfig(request); DefaultConfigDTO config = scenicDefaultConfigIntegrationService.createDefaultConfig(request);
return ApiResponse.success(config); return ApiResponse.success(config);
} catch (Exception e) { } catch (Exception e) {
log.error("创建默认配置失败, configKey: {}", request.getConfigKey(), e); log.error("创建默认配置失败, configKey: {}", request.getConfigKey(), e);
@@ -75,7 +75,7 @@ public class DefaultConfigController {
@RequestBody DefaultConfigDTO request) { @RequestBody DefaultConfigDTO request) {
log.info("更新默认配置, configKey: {}", configKey); log.info("更新默认配置, configKey: {}", configKey);
try { try {
DefaultConfigDTO config = defaultConfigIntegrationService.updateDefaultConfig(configKey, request); DefaultConfigDTO config = scenicDefaultConfigIntegrationService.updateDefaultConfig(configKey, request);
return ApiResponse.success(config); return ApiResponse.success(config);
} catch (Exception e) { } catch (Exception e) {
log.error("更新默认配置失败, configKey: {}", configKey, e); log.error("更新默认配置失败, configKey: {}", configKey, e);
@@ -90,7 +90,7 @@ public class DefaultConfigController {
public ApiResponse<Void> deleteDefaultConfig(@PathVariable String configKey) { public ApiResponse<Void> deleteDefaultConfig(@PathVariable String configKey) {
log.info("删除默认配置, configKey: {}", configKey); log.info("删除默认配置, configKey: {}", configKey);
try { try {
defaultConfigIntegrationService.deleteDefaultConfig(configKey); scenicDefaultConfigIntegrationService.deleteDefaultConfig(configKey);
return ApiResponse.buildResponse(ApiConst.Code.CODE_SUCCESS.code(), null, "删除成功"); return ApiResponse.buildResponse(ApiConst.Code.CODE_SUCCESS.code(), null, "删除成功");
} catch (Exception e) { } catch (Exception e) {
log.error("删除默认配置失败, configKey: {}", configKey, e); log.error("删除默认配置失败, configKey: {}", configKey, e);

View File

@@ -1,6 +1,7 @@
package com.ycwl.basic.controller.pc; package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.device.dto.config.*; import com.ycwl.basic.integration.device.dto.config.*;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.*; import com.ycwl.basic.integration.device.dto.device.*;
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService; import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
import com.ycwl.basic.integration.device.service.DeviceIntegrationService; import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
@@ -35,7 +36,7 @@ public class DeviceV2Controller {
* 设备V2核心信息分页列表 * 设备V2核心信息分页列表
*/ */
@GetMapping("/") @GetMapping("/")
public ApiResponse<DeviceV2ListResponse> listDevices(@RequestParam(defaultValue = "1") Integer page, public ApiResponse<PageResponse<DeviceV2DTO>> listDevices(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name, @RequestParam(required = false) String name,
@RequestParam(required = false) String no, @RequestParam(required = false) String no,
@@ -51,7 +52,7 @@ public class DeviceV2Controller {
} }
try { try {
DeviceV2ListResponse response = deviceIntegrationService.listDevices(page, pageSize, name, no, type, isActive, scenicId); PageResponse<DeviceV2DTO> response = deviceIntegrationService.listDevices(page, pageSize, name, no, type, isActive, scenicId);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("分页查询设备核心信息列表失败", e); log.error("分页查询设备核心信息列表失败", e);
@@ -63,7 +64,7 @@ public class DeviceV2Controller {
* 设备V2带配置信息分页列表 * 设备V2带配置信息分页列表
*/ */
@GetMapping("/with-config") @GetMapping("/with-config")
public ApiResponse<DeviceV2WithConfigListResponse> listDevicesWithConfig(@RequestParam(defaultValue = "1") Integer page, public ApiResponse<PageResponse<DeviceV2WithConfigDTO>> listDevicesWithConfig(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name, @RequestParam(required = false) String name,
@RequestParam(required = false) String no, @RequestParam(required = false) String no,
@@ -79,7 +80,7 @@ public class DeviceV2Controller {
} }
try { try {
DeviceV2WithConfigListResponse response = deviceIntegrationService.listDevicesWithConfig(page, pageSize, name, no, type, isActive, scenicId); PageResponse<DeviceV2WithConfigDTO> response = deviceIntegrationService.listDevicesWithConfig(page, pageSize, name, no, type, isActive, scenicId);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("分页查询设备带配置信息列表失败", e); log.error("分页查询设备带配置信息列表失败", e);
@@ -431,12 +432,12 @@ public class DeviceV2Controller {
* 获取景区IPC设备列表 * 获取景区IPC设备列表
*/ */
@GetMapping("/scenic/{scenicId}/ipc") @GetMapping("/scenic/{scenicId}/ipc")
public ApiResponse<DeviceV2ListResponse> getScenicIpcDevices(@PathVariable Long scenicId, public ApiResponse<PageResponse<DeviceV2DTO>> getScenicIpcDevices(@PathVariable Long scenicId,
@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) { @RequestParam(defaultValue = "10") Integer pageSize) {
log.info("获取景区IPC设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize); log.info("获取景区IPC设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
try { try {
DeviceV2ListResponse response = deviceIntegrationService.getScenicIpcDevices(scenicId, page, pageSize); PageResponse<DeviceV2DTO> response = deviceIntegrationService.getScenicIpcDevices(scenicId, page, pageSize);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("获取景区IPC设备列表失败, scenicId: {}", scenicId, e); log.error("获取景区IPC设备列表失败, scenicId: {}", scenicId, e);
@@ -448,12 +449,12 @@ public class DeviceV2Controller {
* 获取景区激活设备列表 * 获取景区激活设备列表
*/ */
@GetMapping("/scenic/{scenicId}/active") @GetMapping("/scenic/{scenicId}/active")
public ApiResponse<DeviceV2ListResponse> getScenicActiveDevices(@PathVariable Long scenicId, public ApiResponse<PageResponse<DeviceV2DTO>> getScenicActiveDevices(@PathVariable Long scenicId,
@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) { @RequestParam(defaultValue = "10") Integer pageSize) {
log.info("获取景区激活设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize); log.info("获取景区激活设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
try { try {
DeviceV2ListResponse response = deviceIntegrationService.getScenicActiveDevices(scenicId, page, pageSize); PageResponse<DeviceV2DTO> response = deviceIntegrationService.getScenicActiveDevices(scenicId, page, pageSize);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("获取景区激活设备列表失败, scenicId: {}", scenicId, e); log.error("获取景区激活设备列表失败, scenicId: {}", scenicId, e);
@@ -465,12 +466,12 @@ public class DeviceV2Controller {
* 获取景区所有设备列表 * 获取景区所有设备列表
*/ */
@GetMapping("/scenic/{scenicId}/all") @GetMapping("/scenic/{scenicId}/all")
public ApiResponse<DeviceV2ListResponse> getScenicAllDevices(@PathVariable Long scenicId, public ApiResponse<PageResponse<DeviceV2DTO>> getScenicAllDevices(@PathVariable Long scenicId,
@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) { @RequestParam(defaultValue = "10") Integer pageSize) {
log.info("获取景区所有设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize); log.info("获取景区所有设备列表, scenicId: {}, page: {}, pageSize: {}", scenicId, page, pageSize);
try { try {
DeviceV2ListResponse response = deviceIntegrationService.listDevices(page, pageSize, null, null, null, null, scenicId); PageResponse<DeviceV2DTO> response = deviceIntegrationService.listDevices(page, pageSize, null, null, null, null, scenicId);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("获取景区所有设备列表失败, scenicId: {}", scenicId, e); log.error("获取景区所有设备列表失败, scenicId: {}", scenicId, e);

View File

@@ -0,0 +1,196 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.render.dto.config.BatchRenderWorkerConfigRequest;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import com.ycwl.basic.integration.render.service.RenderWorkerConfigIntegrationService;
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.List;
import java.util.Map;
/**
* 渲染工作器配置管理 V2 版本控制器
* 基于 zt-render-worker 微服务标准接口实现
*
* @author Claude Code
* @date 2025-09-06
*/
@Slf4j
@RestController
@RequestMapping("/api/render/worker/config/v2")
@RequiredArgsConstructor
public class RenderWorkerConfigV2Controller {
private final RenderWorkerConfigIntegrationService configIntegrationService;
/**
* 获取工作器所有配置
*
* @param workerId 工作器ID
* @return 工作器配置列表
*/
@GetMapping("/{workerId}")
public ApiResponse<List<RenderWorkerConfigV2DTO>> getWorkerConfigs(@PathVariable Long workerId) {
log.info("获取渲染工作器配置列表, workerId: {}", workerId);
try {
List<RenderWorkerConfigV2DTO> configs = configIntegrationService.getWorkerConfigs(workerId);
return ApiResponse.success(configs);
} catch (Exception e) {
log.error("获取渲染工作器配置列表失败, workerId: {}", workerId, e);
return ApiResponse.fail("获取渲染工作器配置列表失败: " + e.getMessage());
}
}
/**
* 获取工作器平铺配置
*
* @param workerId 工作器ID
* @return 平铺配置Map
*/
@GetMapping("/{workerId}/flat")
public ApiResponse<Map<String, Object>> getWorkerFlatConfig(@PathVariable Long workerId) {
log.info("获取渲染工作器平铺配置, workerId: {}", workerId);
try {
Map<String, Object> flatConfig = configIntegrationService.getWorkerFlatConfig(workerId);
return ApiResponse.success(flatConfig);
} catch (Exception e) {
log.error("获取渲染工作器平铺配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("获取渲染工作器平铺配置失败: " + e.getMessage());
}
}
/**
* 根据配置键获取特定配置
*
* @param workerId 工作器ID
* @param configKey 配置键
* @return 配置信息
*/
@GetMapping("/{workerId}/key/{configKey}")
public ApiResponse<RenderWorkerConfigV2DTO> getWorkerConfigByKey(@PathVariable Long workerId,
@PathVariable String configKey) {
log.info("根据配置键获取渲染工作器配置, workerId: {}, configKey: {}", workerId, configKey);
try {
RenderWorkerConfigV2DTO config = configIntegrationService.getWorkerConfigByKey(workerId, configKey);
return ApiResponse.success(config);
} catch (Exception e) {
log.error("根据配置键获取渲染工作器配置失败, workerId: {}, configKey: {}", workerId, configKey, e);
return ApiResponse.fail("根据配置键获取渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 创建工作器配置
*
* @param workerId 工作器ID
* @param config 配置信息
* @return 创建的配置信息
*/
@PostMapping("/{workerId}")
public ApiResponse<RenderWorkerConfigV2DTO> createWorkerConfig(@PathVariable Long workerId,
@Valid @RequestBody RenderWorkerConfigV2DTO config) {
log.info("创建渲染工作器配置, workerId: {}, configKey: {}", workerId, config.getConfigKey());
try {
RenderWorkerConfigV2DTO result = configIntegrationService.createWorkerConfig(workerId, config);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("创建渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("创建渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 更新工作器配置
*
* @param workerId 工作器ID
* @param configId 配置ID
* @param updates 更新内容
* @return 操作结果
*/
@PutMapping("/{workerId}/{configId}")
public ApiResponse<Void> updateWorkerConfig(@PathVariable Long workerId,
@PathVariable Long configId,
@Valid @RequestBody Map<String, Object> updates) {
log.info("更新渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
try {
configIntegrationService.updateWorkerConfig(workerId, configId, updates);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("更新渲染工作器配置失败, workerId: {}, configId: {}", workerId, configId, e);
return ApiResponse.fail("更新渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 删除工作器配置
*
* @param workerId 工作器ID
* @param configId 配置ID
* @return 操作结果
*/
@DeleteMapping("/{workerId}/{configId}")
public ApiResponse<Void> deleteWorkerConfig(@PathVariable Long workerId,
@PathVariable Long configId) {
log.info("删除渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
try {
configIntegrationService.deleteWorkerConfig(workerId, configId);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("删除渲染工作器配置失败, workerId: {}, configId: {}", workerId, configId, e);
return ApiResponse.fail("删除渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 批量更新工作器配置
*
* @param workerId 工作器ID
* @param request 批量配置请求
* @return 操作结果
*/
@PostMapping("/{workerId}/batch")
public ApiResponse<Void> batchUpdateWorkerConfigs(@PathVariable Long workerId,
@Valid @RequestBody BatchRenderWorkerConfigRequest request) {
log.info("批量更新渲染工作器配置, workerId: {}, configCount: {}",
workerId, request.getConfigs() != null ? request.getConfigs().size() : 0);
try {
configIntegrationService.batchUpdateWorkerConfigs(workerId, request);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("批量更新渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("批量更新渲染工作器配置失败: " + e.getMessage());
}
}
/**
* 批量平铺更新工作器配置
*
* @param workerId 工作器ID
* @param flatConfigs 平铺配置Map
* @return 操作结果
*/
@PostMapping("/{workerId}/flat-batch")
public ApiResponse<Void> batchFlatUpdateWorkerConfigs(@PathVariable Long workerId,
@Valid @RequestBody Map<String, Object> flatConfigs) {
log.info("批量平铺更新渲染工作器配置, workerId: {}, configCount: {}", workerId, flatConfigs.size());
try {
configIntegrationService.batchFlatUpdateWorkerConfigs(workerId, flatConfigs);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("批量平铺更新渲染工作器配置失败, workerId: {}", workerId, e);
return ApiResponse.fail("批量平铺更新渲染工作器配置失败: " + e.getMessage());
}
}
}

View File

@@ -1,61 +0,0 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.renderWorker.req.RenderWorkerReqQuery;
import com.ycwl.basic.service.pc.RenderWorkerService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Author:longbinbin
* @Date:2024/12/3 14:59
*/
@RestController
@RequestMapping("/api/renderWorker/v1")
// 渲染机管理
public class RenderWorkerController {
@Autowired
private RenderWorkerService renderWorkerService;
// 分页查询渲染机
@PostMapping("/page")
public ApiResponse pageQuery(@RequestBody RenderWorkerReqQuery renderWorkerReqQuery){
return renderWorkerService.pageQuery(renderWorkerReqQuery);
}
// 渲染机列表查询
@PostMapping("/list")
public ApiResponse list(@RequestBody RenderWorkerReqQuery renderWorkerReqQuery){
return renderWorkerService.list(renderWorkerReqQuery);
}
// 渲染机详情查询
@GetMapping("/detail/{id}")
public ApiResponse detail(@PathVariable Long id){
return renderWorkerService.detail(id);
}
// 渲染机新增
@PostMapping("/add")
public ApiResponse add(@RequestBody RenderWorkerEntity renderWorker){
return renderWorkerService.add(renderWorker);
}
// 渲染机删除
@DeleteMapping("/delete/{id}")
public ApiResponse deleteById(@PathVariable Long id){
return renderWorkerService.deleteById(id);
}
// 渲染机修改
@PostMapping("/update")
public ApiResponse update(@RequestBody RenderWorkerEntity renderWorker){
return renderWorkerService.update(renderWorker);
}
// 渲染机修改状态
@PutMapping("/updateStatus/{id}")
public ApiResponse updateStatus(@PathVariable Long id) {
return renderWorkerService.updateStatus(id);
}
}

View File

@@ -0,0 +1,142 @@
package com.ycwl.basic.controller.pc;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.render.dto.worker.CreateRenderWorkerRequest;
import com.ycwl.basic.integration.render.dto.worker.RenderWorkerV2DTO;
import com.ycwl.basic.integration.render.dto.worker.UpdateRenderWorkerRequest;
import com.ycwl.basic.integration.render.service.RenderWorkerIntegrationService;
import com.ycwl.basic.utils.ApiResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 渲染工作器管理 V2 版本控制器
* 基于 zt-render-worker 微服务标准接口实现
*
* @author Claude Code
* @date 2025-09-06
*/
@Slf4j
@RestController
@RequestMapping("/api/render/worker/v2")
@RequiredArgsConstructor
public class RenderWorkerV2Controller {
private final RenderWorkerIntegrationService renderWorkerIntegrationService;
/**
* 分页查询渲染工作器列表
*
* @param page 页码,从1开始
* @param pageSize 每页大小,默认10,最大100
* @param isEnabled 是否启用(0-禁用,1-启用)
* @param name 工作器名称(模糊搜索)
* @return 分页查询结果
*/
@GetMapping
public ApiResponse<PageResponse<RenderWorkerV2DTO>> listWorkers(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer isEnabled,
@RequestParam(required = false) String name) {
log.info("分页查询渲染工作器列表, page: {}, pageSize: {}, isEnabled: {}, name: {}",
page, pageSize, isEnabled, name);
// 参数验证:限制pageSize最大值为100
if (pageSize > 100) {
pageSize = 100;
}
try {
PageResponse<RenderWorkerV2DTO> result = renderWorkerIntegrationService.listWorkers(
page, pageSize, isEnabled, name);
return ApiResponse.success(result);
} catch (Exception e) {
log.error("分页查询渲染工作器列表失败", e);
return ApiResponse.fail("分页查询渲染工作器列表失败: " + e.getMessage());
}
}
/**
* 根据ID获取渲染工作器详情
*
* @param id 工作器ID
* @return 工作器详情
*/
@GetMapping("/{id}")
public ApiResponse<RenderWorkerV2DTO> getWorker(@PathVariable Long id) {
log.info("获取渲染工作器详情, id: {}", id);
try {
RenderWorkerV2DTO worker = renderWorkerIntegrationService.getWorker(id);
return ApiResponse.success(worker);
} catch (Exception e) {
log.error("获取渲染工作器详情失败, id: {}", id, e);
return ApiResponse.fail("获取渲染工作器详情失败: " + e.getMessage());
}
}
/**
* 创建渲染工作器
*
* @param request 创建请求
* @return 创建的工作器信息
*/
@PostMapping
public ApiResponse<RenderWorkerV2DTO> createWorker(@Valid @RequestBody CreateRenderWorkerRequest request) {
log.info("创建渲染工作器, name: {}, key: {}, isActive: {}",
request.getName(), request.getKey(), request.getIsActive());
try {
RenderWorkerV2DTO worker = renderWorkerIntegrationService.createWorker(request);
return ApiResponse.success(worker);
} catch (Exception e) {
log.error("创建渲染工作器失败", e);
return ApiResponse.fail("创建渲染工作器失败: " + e.getMessage());
}
}
/**
* 更新渲染工作器
*
* @param id 工作器ID
* @param request 更新请求
* @return 操作结果
*/
@PutMapping("/{id}")
public ApiResponse<Void> updateWorker(@PathVariable Long id,
@Valid @RequestBody UpdateRenderWorkerRequest request) {
log.info("更新渲染工作器, id: {}, name: {}, isActive: {}",
id, request.getName(), request.getIsActive());
try {
renderWorkerIntegrationService.updateWorker(id, request);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("更新渲染工作器失败, id: {}", id, e);
return ApiResponse.fail("更新渲染工作器失败: " + e.getMessage());
}
}
/**
* 删除渲染工作器
*
* @param id 工作器ID
* @return 操作结果
*/
@DeleteMapping("/{id}")
public ApiResponse<Void> deleteWorker(@PathVariable Long id) {
log.info("删除渲染工作器, id: {}", id);
try {
renderWorkerIntegrationService.deleteWorker(id);
return ApiResponse.success(null);
} catch (Exception e) {
log.error("删除渲染工作器失败, id: {}", id, e);
return ApiResponse.fail("删除渲染工作器失败: " + e.getMessage());
}
}
}

View File

@@ -9,9 +9,8 @@ import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterPageResponse;
import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest; import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse; import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService; import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService; import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
@@ -52,7 +51,7 @@ public class ScenicV2Controller {
* 景区V2核心信息分页列表 * 景区V2核心信息分页列表
*/ */
@GetMapping("/") @GetMapping("/")
public ApiResponse<ScenicV2ListResponse> listScenics(@RequestParam(defaultValue = "1") Integer page, public ApiResponse<PageResponse<ScenicV2DTO>> listScenics(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer status, @RequestParam(required = false) Integer status,
@RequestParam(required = false) String name) { @RequestParam(required = false) String name) {
@@ -64,7 +63,7 @@ public class ScenicV2Controller {
} }
try { try {
ScenicV2ListResponse response = scenicIntegrationService.listScenics(page, pageSize, status, name); PageResponse<ScenicV2DTO> response = scenicIntegrationService.listScenics(page, pageSize, status, name);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("分页查询景区核心信息列表失败", e); log.error("分页查询景区核心信息列表失败", e);
@@ -76,7 +75,7 @@ public class ScenicV2Controller {
* 景区V2带配置信息分页列表 * 景区V2带配置信息分页列表
*/ */
@GetMapping("/with-config") @GetMapping("/with-config")
public ApiResponse<ScenicV2WithConfigListResponse> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page, public ApiResponse<PageResponse<ScenicV2WithConfigDTO>> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer status, @RequestParam(required = false) Integer status,
@RequestParam(required = false) String name) { @RequestParam(required = false) String name) {
@@ -88,7 +87,7 @@ public class ScenicV2Controller {
} }
try { try {
ScenicV2WithConfigListResponse response = scenicIntegrationService.listScenicsWithConfig(page, pageSize, status, name); PageResponse<ScenicV2WithConfigDTO> response = scenicIntegrationService.listScenicsWithConfig(page, pageSize, status, name);
return ApiResponse.success(response); return ApiResponse.success(response);
} catch (Exception e) { } catch (Exception e) {
log.error("分页查询景区带配置信息列表失败", e); log.error("分页查询景区带配置信息列表失败", e);
@@ -178,11 +177,11 @@ public class ScenicV2Controller {
* 只支持根据状态筛选 * 只支持根据状态筛选
*/ */
@GetMapping("/list") @GetMapping("/list")
public ApiResponse<ScenicV2ListResponse> listScenicsByStatus(@RequestParam(required = false) Integer status) { public ApiResponse<PageResponse<ScenicV2DTO>> listScenicsByStatus(@RequestParam(required = false) Integer status) {
log.info("查询景区列表, status: {}", status); log.info("查询景区列表, status: {}", status);
try { try {
// 默认查询1000条数据,第1页 // 默认查询1000条数据,第1页
ScenicV2ListResponse scenics = scenicIntegrationService.listScenics(1, 1000, status, null); PageResponse<ScenicV2DTO> scenics = scenicIntegrationService.listScenics(1, 1000, status, null);
return ApiResponse.success(scenics); return ApiResponse.success(scenics);
} catch (Exception e) { } catch (Exception e) {
log.error("查询景区列表失败, status: {}", status, e); log.error("查询景区列表失败, status: {}", status, e);

View File

@@ -23,6 +23,7 @@ The integration package (`com.ycwl.basic.integration`) is responsible for extern
Currently implemented: Currently implemented:
- **Scenic Integration** (`com.ycwl.basic.integration.scenic`): ZT-Scenic microservice integration - **Scenic Integration** (`com.ycwl.basic.integration.scenic`): ZT-Scenic microservice integration
- **Device Integration** (`com.ycwl.basic.integration.device`): ZT-Device microservice integration - **Device Integration** (`com.ycwl.basic.integration.device`): ZT-Device microservice integration
- **Render Worker Integration** (`com.ycwl.basic.integration.render`): ZT-Render-Worker microservice integration
### Integration Pattern ### Integration Pattern
@@ -226,10 +227,12 @@ fallbackService.clearAllFallbackCache("zt-scenic");
#### Feign Clients #### Feign Clients
- **DeviceV2Client**: Main device operations (CRUD, filtering, listing) - **DeviceV2Client**: Main device operations (CRUD, filtering, listing)
- **DeviceConfigV2Client**: Device configuration management - **DeviceConfigV2Client**: Device configuration management
- **DefaultConfigClient**: Default configuration management
#### Services #### Services
- **DeviceIntegrationService**: High-level device operations (with automatic fallback) - **DeviceIntegrationService**: High-level device operations (with automatic fallback)
- **DeviceConfigIntegrationService**: Device configuration management (with automatic fallback) - **DeviceConfigIntegrationService**: Device configuration management (with automatic fallback)
- **DefaultConfigIntegrationService**: Default configuration management (with automatic fallback)
#### Configuration #### Configuration
```yaml ```yaml
@@ -241,6 +244,13 @@ integration:
readTimeout: 10000 readTimeout: 10000
retryEnabled: false retryEnabled: false
maxRetries: 3 maxRetries: 3
render:
enabled: true
serviceName: zt-render-worker
connectTimeout: 5000
readTimeout: 10000
retryEnabled: false
maxRetries: 3
``` ```
### Usage Examples ### Usage Examples
@@ -369,6 +379,355 @@ fallbackService.clearFallbackCache("zt-device", "device:1001");
fallbackService.clearAllFallbackCache("zt-device"); fallbackService.clearAllFallbackCache("zt-device");
``` ```
## Default Configuration Management (ZT-Device Microservice)
### Key Components
#### Default Configuration API
The zt-device microservice provides a comprehensive default configuration management API at `/api/device/config/v2/defaults` that allows:
- Creating and managing default configuration templates
- Batch operations with conflict detection
- Configuration type validation and enforcement
- Usage tracking and analytics
#### Feign Client
- **DefaultConfigClient**: Default configuration management operations
#### Service
- **DefaultConfigIntegrationService**: High-level default configuration operations (with automatic fallback for queries)
### Usage Examples
#### Basic Default Configuration Operations (with Automatic Fallback)
```java
@Autowired
private DefaultConfigIntegrationService defaultConfigService;
// Get default configuration list (automatically falls back to cache on failure)
PageResponse<DefaultConfigResponse> configList = defaultConfigService.listDefaultConfigs(1, 10);
log.info("Default configs: total={}, current page={}",
configList.getData().getTotal(), configList.getData().getList().size());
// Get specific default configuration (automatically falls back to cache on failure)
DefaultConfigResponse config = defaultConfigService.getDefaultConfig("resolution");
if (config != null) {
log.info("Default resolution: {}", config.getConfigValue());
}
// Create new default configuration (direct operation, fails immediately on error)
DefaultConfigRequest newConfig = new DefaultConfigRequest();
newConfig.setConfigKey("bitrate");
newConfig.setConfigValue("2000000");
newConfig.setConfigType("int");
newConfig.setDescription("Default video bitrate in bps");
boolean created = defaultConfigService.createDefaultConfig(newConfig);
// Update default configuration with conflict detection (direct operation)
Map<String, Object> updates = new HashMap<>();
updates.put("configValue", "4000000");
updates.put("description", "Updated default bitrate - high quality");
DefaultConfigConflict conflict = defaultConfigService.updateDefaultConfig("bitrate", updates);
if (conflict != null) {
log.warn("Configuration update conflict detected:");
log.warn(" Key: {}, Type: {}, Affected devices: {}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
log.warn(" Current type: {}, Proposed type: {}",
conflict.getCurrentType(), conflict.getProposedType());
// Handle conflict - show affected device IDs
if (conflict.getConflictDevices() != null) {
log.warn(" Conflicted device IDs: {}", conflict.getConflictDevices());
}
} else {
log.info("Configuration updated successfully without conflicts");
}
// Delete default configuration (direct operation, fails immediately on error)
boolean deleted = defaultConfigService.deleteDefaultConfig("bitrate");
```
#### Batch Default Configuration Operations
```java
// Using Builder Pattern for batch operations
BatchDefaultConfigRequest batchRequest = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 30, "H265") // Add video configuration group
.addNetworkConfig("192.168.1.100", 554, "RTSP") // Add network configuration group
.addConfig("recording_enabled", "true", "bool", "Enable recording by default")
.addConfig("storage_retention", "30", "int", "Storage retention in days")
.addConfig("quality_profile", "high", "string", "Default video quality profile")
.build();
// Execute batch update (direct operation, no fallback)
BatchDefaultConfigResponse result = defaultConfigService.batchUpdateDefaultConfigs(batchRequest);
// Process batch results
log.info("Batch operation completed: {} success, {} failed", result.getSuccess(), result.getFailed());
// Handle conflicts
if (result.getConflicts() != null && !result.getConflicts().isEmpty()) {
log.warn("Configuration conflicts detected:");
for (DefaultConfigConflict conflict : result.getConflicts()) {
log.warn(" Key: {}, Type: {}, Devices affected: {}",
conflict.getConfigKey(), conflict.getConflictType(), conflict.getDeviceCount());
}
}
// Review processed items
if (result.getProcessedItems() != null) {
result.getProcessedItems().forEach(item -> {
if ("success".equals(item.getStatus())) {
log.info("✅ {} {} successfully (type: {})",
item.getConfigKey(), item.getAction(), item.getFinalType());
} else {
log.warn("❌ {} failed: {}", item.getConfigKey(), item.getMessage());
}
});
}
// Handle errors
if (result.getErrors() != null && !result.getErrors().isEmpty()) {
result.getErrors().forEach(error -> log.error("Batch operation error: {}", error));
}
```
#### Device Type Specific Default Configuration Patterns
```java
// Create IPC camera default configurations
BatchDefaultConfigRequest ipcDefaults = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("1920x1080", 25, "H264") // Standard HD resolution
.addConfig("night_vision", "true", "bool", "Enable night vision")
.addConfig("motion_detection", "true", "bool", "Enable motion detection")
.addConfig("stream_profile", "main", "string", "Default stream profile")
.addConfig("ptz_enabled", "false", "bool", "PTZ control enabled")
.addConfig("audio_enabled", "true", "bool", "Audio recording enabled")
.build();
// Create NVR default configurations
BatchDefaultConfigRequest nvrDefaults = defaultConfigService.createBatchConfigBuilder()
.addConfig("max_channels", "16", "int", "Maximum supported channels")
.addConfig("storage_mode", "continuous", "string", "Recording storage mode")
.addConfig("backup_enabled", "true", "bool", "Enable automatic backup")
.addConfig("raid_level", "5", "int", "Default RAID level")
.addConfig("disk_quota", "80", "int", "Disk usage quota percentage")
.build();
// Apply device-type-specific defaults
BatchDefaultConfigResponse ipcResult = defaultConfigService.batchUpdateDefaultConfigs(ipcDefaults);
BatchDefaultConfigResponse nvrResult = defaultConfigService.batchUpdateDefaultConfigs(nvrDefaults);
log.info("IPC defaults: {} success, {} failed", ipcResult.getSuccess(), ipcResult.getFailed());
log.info("NVR defaults: {} success, {} failed", nvrResult.getSuccess(), nvrResult.getFailed());
```
#### Configuration Validation and Management Patterns
```java
// Validate system configuration completeness
PageResponse<DefaultConfigResponse> allDefaults = defaultConfigService.listDefaultConfigs(1, 100);
// Check for required default configurations
String[] requiredDefaults = {
"resolution", "frameRate", "codec", "bitrate",
"protocol", "port", "username", "password"
};
Map<String, Boolean> configStatus = new HashMap<>();
for (String required : requiredDefaults) {
boolean exists = allDefaults.getData().getList().stream()
.anyMatch(config -> required.equals(config.getConfigKey()));
configStatus.put(required, exists);
if (!exists) {
log.warn("Missing required default configuration: {}", required);
}
}
// Generate configuration completeness report
long totalConfigs = allDefaults.getData().getTotal();
long completeConfigs = configStatus.values().stream().mapToLong(exists -> exists ? 1 : 0).sum();
double completeness = (double) completeConfigs / requiredDefaults.length * 100;
log.info("Configuration completeness: {:.1f}% ({}/{} required configs present)",
completeness, completeConfigs, requiredDefaults.length);
log.info("Total default configurations in system: {}", totalConfigs);
// Analyze configuration type distribution
Map<String, Long> typeDistribution = allDefaults.getData().getList().stream()
.collect(Collectors.groupingBy(
DefaultConfigResponse::getConfigType,
Collectors.counting()
));
log.info("Configuration type distribution: {}", typeDistribution);
// Find most and least used configurations
DefaultConfigResponse mostUsed = allDefaults.getData().getList().stream()
.max(Comparator.comparing(DefaultConfigResponse::getUsageCount))
.orElse(null);
DefaultConfigResponse leastUsed = allDefaults.getData().getList().stream()
.min(Comparator.comparing(DefaultConfigResponse::getUsageCount))
.orElse(null);
if (mostUsed != null) {
log.info("Most used default config: {} (used {} times)",
mostUsed.getConfigKey(), mostUsed.getUsageCount());
}
if (leastUsed != null) {
log.info("Least used default config: {} (used {} times)",
leastUsed.getConfigKey(), leastUsed.getUsageCount());
}
```
#### Advanced Batch Configuration with Error Handling
```java
// Complex batch operation with comprehensive error handling
try {
BatchDefaultConfigRequest complexBatch = defaultConfigService.createBatchConfigBuilder()
.addVideoConfig("3840x2160", 60, "H265") // 4K configuration
.addConfig("hdr_enabled", "true", "bool", "HDR video support")
.addConfig("ai_analysis", "enabled", "string", "AI analysis features")
.addConfig("edge_processing", "true", "bool", "Edge computing enabled")
.addConfig("cloud_sync", "auto", "string", "Cloud synchronization mode")
.build();
BatchDefaultConfigResponse result = defaultConfigService.batchUpdateDefaultConfigs(complexBatch);
// Detailed result analysis
if (result.getSuccess() == complexBatch.getConfigs().size()) {
log.info("✅ All {} configurations processed successfully", result.getSuccess());
} else if (result.getSuccess() > 0) {
log.warn("⚠️ Partial success: {} succeeded, {} failed", result.getSuccess(), result.getFailed());
// Analyze what succeeded vs failed
if (result.getProcessedItems() != null) {
Map<String, Long> statusCounts = result.getProcessedItems().stream()
.collect(Collectors.groupingBy(
ProcessedConfigItem::getStatus,
Collectors.counting()
));
log.info("Processing breakdown: {}", statusCounts);
}
} else {
log.error("❌ All configurations failed to process");
}
// Handle different types of conflicts
if (result.getConflicts() != null) {
Map<String, List<DefaultConfigConflict>> conflictsByType = result.getConflicts().stream()
.collect(Collectors.groupingBy(DefaultConfigConflict::getConflictType));
conflictsByType.forEach((conflictType, conflicts) -> {
log.warn("Conflict type '{}' affects {} configurations:", conflictType, conflicts.size());
conflicts.forEach(conflict ->
log.warn(" {} affects {} devices", conflict.getConfigKey(), conflict.getDeviceCount())
);
});
}
} catch (Exception e) {
log.error("Batch default configuration operation failed", e);
// Implement retry logic or fallback behavior as needed
}
```
#### Fallback Cache Management for Default Configurations
```java
@Autowired
private IntegrationFallbackService fallbackService;
// Check fallback cache status for default configurations
boolean hasDefaultListCache = fallbackService.hasFallbackCache("zt-device", "defaults:list:1:10");
boolean hasSpecificConfigCache = fallbackService.hasFallbackCache("zt-device", "defaults:config:resolution");
// Get cache statistics
IntegrationFallbackService.FallbackCacheStats stats = fallbackService.getFallbackCacheStats("zt-device");
log.info("Default config fallback cache: {} items, TTL: {} days",
stats.getTotalCacheCount(), stats.getFallbackTtlDays());
// Clear specific default configuration cache
fallbackService.clearFallbackCache("zt-device", "defaults:config:resolution");
// Clear all default configuration caches
fallbackService.clearAllFallbackCache("zt-device");
```
### Default Configuration Types and Common Keys
#### Video Configuration Defaults
- `resolution`: Video resolution ("1920x1080", "3840x2160", "1280x720")
- `frameRate`: Video frame rate (integer: 15, 25, 30, 60)
- `codec`: Video codec ("H264", "H265", "MJPEG")
- `bitrate`: Video bitrate in bps (integer)
- `quality`: Video quality level ("low", "medium", "high", "ultra")
#### Network Configuration Defaults
- `protocol`: Network protocol ("RTSP", "HTTP", "ONVIF")
- `port`: Network port (integer: 554, 80, 8080)
- `timeout`: Connection timeout in seconds (integer)
- `retry_count`: Maximum retry attempts (integer)
#### Authentication Defaults
- `username`: Default username (string)
- `password`: Default password (string)
- `auth_method`: Authentication method ("basic", "digest", "none")
#### Storage and Recording Defaults
- `storage_path`: Default storage path (string)
- `recording_enabled`: Enable recording (boolean)
- `retention_days`: Storage retention period (integer)
- `max_file_size`: Maximum file size in MB (integer)
#### Feature Control Defaults
- `motion_detection`: Enable motion detection (boolean)
- `night_vision`: Enable night vision (boolean)
- `audio_enabled`: Enable audio recording (boolean)
- `ptz_enabled`: Enable PTZ control (boolean)
- `ai_analysis`: AI analysis features ("enabled", "disabled", "auto")
### Error Handling and Responses
#### HTTP Status Codes
- **200 OK**: Configuration retrieved/updated successfully
- **201 Created**: New configuration created successfully
- **202 Accepted**: Operation completed with conflicts (check response for details)
- **400 Bad Request**: Invalid request parameters or validation errors
- **404 Not Found**: Configuration key not found
- **409 Conflict**: Configuration conflicts prevent operation (includes conflict details)
#### Conflict Resolution
When updating default configurations that are actively used by devices, the API performs conflict detection:
1. **Type Conflicts**: Changing configuration type when devices use different types
2. **Value Validation**: Ensuring new values are compatible with existing device configurations
3. **Dependency Conflicts**: Checking for configuration dependencies and relationships
The response includes detailed conflict information to help resolve issues:
```java
// Example conflict handling
DefaultConfigConflict conflict = updateResult;
if (conflict != null) {
switch (conflict.getConflictType()) {
case "TYPE_MISMATCH":
log.warn("Type conflict: {} devices using '{}' type, proposed '{}'type",
conflict.getDeviceCount(), conflict.getCurrentType(), conflict.getProposedType());
// Handle type conversion or device updates
break;
case "VALUE_RANGE":
log.warn("Value range conflict for key '{}' affects {} devices",
conflict.getConfigKey(), conflict.getDeviceCount());
// Handle value range adjustments
break;
case "DEPENDENCY":
log.warn("Dependency conflict detected for '{}'", conflict.getConfigKey());
// Handle dependency resolution
break;
}
}
```
### Enhanced Batch Configuration API ### Enhanced Batch Configuration API
Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules. Device Integration now supports an enhanced batch configuration API that provides detailed processing results and supports default configuration rules.
@@ -424,6 +783,23 @@ Each processed item includes:
- **IPC**: IP Camera devices for video monitoring - **IPC**: IP Camera devices for video monitoring
- **CUSTOM**: Custom device types for sensors, controllers, etc. - **CUSTOM**: Custom device types for sensors, controllers, etc.
### Testing Default Configuration Integration
```bash
# Run default configuration integration tests
mvn test -Dtest=DefaultConfigIntegrationServiceTest
# Run all device integration tests (including default configs)
mvn test -Dtest="com.ycwl.basic.integration.device.*Test"
# Enable example runner in application-dev.yml
integration:
device:
example:
default-config:
enabled: true
```
### Common Configuration Keys ### Common Configuration Keys
- `ip_address`: Device IP address - `ip_address`: Device IP address
- `resolution`: Video resolution (e.g., "1920x1080", "3840x2160") - `resolution`: Video resolution (e.g., "1920x1080", "3840x2160")
@@ -535,6 +911,11 @@ integration:
ttlDays: 5 # Custom TTL for device (shorter due to dynamic data) ttlDays: 5 # Custom TTL for device (shorter due to dynamic data)
# cachePrefix: "device:fallback:" # Optional custom prefix # cachePrefix: "device:fallback:" # Optional custom prefix
render:
enabled: true # Enable fallback for render worker service
ttlDays: 7 # Custom TTL for render worker (medium due to stable worker data)
# cachePrefix: "render:fallback:" # Optional custom prefix
# Service configurations # Service configurations
scenic: scenic:
enabled: true enabled: true
@@ -587,6 +968,205 @@ class ScenicIntegrationServiceTest {
### Integration Testing ### Integration Testing
Use `@SpringBootTest` with test profiles and mock external services. Use `@SpringBootTest` with test profiles and mock external services.
## Render Worker Integration (ZT-Render-Worker Microservice)
### Key Components
#### Feign Clients
- **RenderWorkerV2Client**: Main render worker operations (CRUD, listing)
- **RenderWorkerConfigV2Client**: Render worker configuration management
#### Services
- **RenderWorkerIntegrationService**: High-level render worker operations (with automatic fallback)
- **RenderWorkerConfigIntegrationService**: Configuration management (with automatic fallback)
#### Configuration
```yaml
integration:
render:
enabled: true
serviceName: zt-render-worker
connectTimeout: 5000
readTimeout: 10000
retryEnabled: false
maxRetries: 3
```
### Usage Examples
#### Basic Render Worker Operations (with Automatic Fallback)
```java
@Autowired
private RenderWorkerIntegrationService renderWorkerService;
// Get render worker basic info (automatically falls back to cache on failure)
RenderWorkerV2DTO worker = renderWorkerService.getWorker(workerId);
// Get render worker with configuration (automatically falls back to cache on failure)
RenderWorkerV2WithConfigDTO workerWithConfig = renderWorkerService.getWorkerWithConfig(workerId);
// Get render worker by key (automatically falls back to cache on failure)
RenderWorkerV2DTO workerByKey = renderWorkerService.getWorkerByKey("video-renderer-001");
// Get render worker with config by key (automatically falls back to cache on failure)
RenderWorkerV2WithConfigDTO workerWithConfigByKey = renderWorkerService.getWorkerWithConfigByKey("video-renderer-001");
// Create new render worker (direct operation, fails immediately on error)
CreateRenderWorkerRequest request = new CreateRenderWorkerRequest();
request.setName("Video Renderer");
request.setKey("video-renderer-001");
request.setIsActive(1);
RenderWorkerV2DTO newWorker = renderWorkerService.createWorker(request);
// Update render worker (direct operation, fails immediately on error)
UpdateRenderWorkerRequest updateRequest = new UpdateRenderWorkerRequest();
updateRequest.setName("Updated Video Renderer");
renderWorkerService.updateWorker(workerId, updateRequest);
// Delete render worker (direct operation, fails immediately on error)
renderWorkerService.deleteWorker(workerId);
// List render workers (no fallback for list operations)
List<RenderWorkerV2DTO> workers = renderWorkerService.listWorkers(1, 10, 1, null);
// List render workers with config (no fallback for list operations)
List<RenderWorkerV2WithConfigDTO> workersWithConfig =
renderWorkerService.listWorkersWithConfig(1, 10, 1, null);
```
#### Configuration Management (with Automatic Fallback)
```java
@Autowired
private RenderWorkerConfigIntegrationService configService;
// Get worker configurations (with fallback)
List<RenderWorkerConfigV2DTO> configs = configService.getWorkerConfigs(workerId);
// Get flat configuration (automatically falls back to cache on failure)
Map<String, Object> flatConfig = configService.getWorkerFlatConfig(workerId);
// Get specific configuration by key (with fallback)
RenderWorkerConfigV2DTO config = configService.getWorkerConfigByKey(workerId, "render_quality");
// Create configuration (direct operation, fails immediately on error)
RenderWorkerConfigV2DTO newConfig = new RenderWorkerConfigV2DTO();
newConfig.setConfigKey("max_concurrent_tasks");
newConfig.setConfigValue("4");
newConfig.setConfigType("int");
newConfig.setDescription("Maximum concurrent render tasks");
newConfig.setIsActive(1);
RenderWorkerConfigV2DTO created = configService.createWorkerConfig(workerId, newConfig);
// Update configuration (direct operation, fails immediately on error)
Map<String, Object> updates = new HashMap<>();
updates.put("configValue", "8");
configService.updateWorkerConfig(workerId, configId, updates);
// Delete configuration (direct operation, fails immediately on error)
configService.deleteWorkerConfig(workerId, configId);
```
#### Enhanced Batch Configuration API
```java
// Using Builder Pattern for batch configuration
BatchRenderWorkerConfigRequest request = configService.createBatchConfigBuilder()
.addRenderConfig("high", "mp4", "1920x1080") // Render settings
.addPerformanceConfig(8, 3600) // Performance settings
.addConfig("output_path", "/data/renders", "string", "Output directory")
.addConfig("enable_gpu", "true", "bool", "Enable GPU acceleration")
.build();
// Batch update configurations (direct operation, fails immediately on error)
configService.batchUpdateWorkerConfigs(workerId, request);
// Batch flat update configurations (direct operation, fails immediately on error)
Map<String, Object> flatUpdates = new HashMap<>();
flatUpdates.put("render_quality", "ultra");
flatUpdates.put("max_concurrent_tasks", 12);
flatUpdates.put("enable_preview", true);
configService.batchFlatUpdateWorkerConfigs(workerId, flatUpdates);
```
#### Render Worker Management Patterns
```java
// Create and configure render worker in one operation
CreateRenderWorkerRequest workerRequest = new CreateRenderWorkerRequest();
workerRequest.setName("4K Video Renderer");
workerRequest.setKey("4k-video-renderer");
RenderWorkerV2DTO worker = renderWorkerService.createWorker(workerRequest);
// Configure the worker for 4K video rendering
BatchRenderWorkerConfigRequest config = configService.createBatchConfigBuilder()
.addRenderConfig("ultra", "mp4", "3840x2160")
.addPerformanceConfig(4, 7200) // 4 concurrent tasks, 2 hour timeout
.addConfig("gpu_memory_limit", "8192", "int", "GPU memory limit in MB")
.addConfig("codec", "h265", "string", "Video codec")
.addConfig("bitrate", "50000", "int", "Video bitrate in kbps")
.build();
configService.batchUpdateWorkerConfigs(worker.getId(), config);
// Monitor worker configuration completeness (with automatic fallback)
Map<String, Object> workerConfig = configService.getWorkerFlatConfig(worker.getId());
boolean hasRenderConfig = workerConfig.containsKey("render_quality");
boolean hasPerformanceConfig = workerConfig.containsKey("max_concurrent_tasks");
// log configuration status...
// Get all active workers for monitoring
List<RenderWorkerV2WithConfigDTO> activeWorkers =
renderWorkerService.listWorkersWithConfig(1, 100, 1, null);
// Check worker health and configuration
for (RenderWorkerV2WithConfigDTO activeWorker : activeWorkers) {
Map<String, Object> config = activeWorker.getConfig();
boolean isConfigured = config.containsKey("render_quality") &&
config.containsKey("max_concurrent_tasks");
// log worker status...
}
```
#### Fallback Cache Management for Render Workers
```java
@Autowired
private IntegrationFallbackService fallbackService;
// Check fallback cache status
boolean hasWorkerCache = fallbackService.hasFallbackCache("zt-render-worker", "worker:1001");
boolean hasWorkerByKeyCache = fallbackService.hasFallbackCache("zt-render-worker", "worker:key:video-renderer-001");
boolean hasConfigCache = fallbackService.hasFallbackCache("zt-render-worker", "worker:flat:config:1001");
boolean hasConfigByKeyCache = fallbackService.hasFallbackCache("zt-render-worker", "worker:key:config:video-renderer-001");
// Get cache statistics
IntegrationFallbackService.FallbackCacheStats stats =
fallbackService.getFallbackCacheStats("zt-render-worker");
log.info("Render worker fallback cache: {} items, TTL: {} days",
stats.getTotalCacheCount(), stats.getFallbackTtlDays());
// Clear specific cache
fallbackService.clearFallbackCache("zt-render-worker", "worker:1001");
// Clear all render worker caches
fallbackService.clearAllFallbackCache("zt-render-worker");
```
### Render Worker Types and Configuration
#### Common Configuration Keys
- `render_quality`: Render quality level ("low", "medium", "high", "ultra")
- `render_format`: Output format ("mp4", "avi", "mov", "webm")
- `render_resolution`: Video resolution ("1920x1080", "3840x2160", "1280x720")
- `max_concurrent_tasks`: Maximum concurrent render tasks (integer)
- `task_timeout`: Task timeout in seconds (integer)
- `output_path`: Render output directory path (string)
- `enable_gpu`: Enable GPU acceleration (boolean)
- `gpu_memory_limit`: GPU memory limit in MB (integer)
- `codec`: Video codec ("h264", "h265", "vp9")
- `bitrate`: Video bitrate in kbps (integer)
- `enable_preview`: Enable preview generation (boolean)
#### Worker Status
- **Active (isActive=1)**: Worker is available for tasks
- **Inactive (isActive=0)**: Worker is disabled
## Common Development Tasks ## Common Development Tasks
### Running Integration Tests ### Running Integration Tests
@@ -600,6 +1180,10 @@ mvn test -Dtest="com.ycwl.basic.integration.*Test"
# Run device integration tests # Run device integration tests
mvn test -Dtest=DeviceIntegrationServiceTest mvn test -Dtest=DeviceIntegrationServiceTest
mvn test -Dtest="com.ycwl.basic.integration.device.*Test" mvn test -Dtest="com.ycwl.basic.integration.device.*Test"
# Run render worker integration tests
mvn test -Dtest=RenderWorkerIntegrationServiceTest
mvn test -Dtest="com.ycwl.basic.integration.render.*Test"
``` ```
### Adding New DTOs ### Adding New DTOs

View File

@@ -26,6 +26,11 @@ public class IntegrationProperties {
*/ */
private DeviceConfig device = new DeviceConfig(); private DeviceConfig device = new DeviceConfig();
/**
* 渲染工作器服务配置
*/
private RenderWorkerConfig render = new RenderWorkerConfig();
@Data @Data
public static class ScenicConfig { public static class ScenicConfig {
/** /**
@@ -98,6 +103,32 @@ public class IntegrationProperties {
*/ */
private ServiceFallbackConfig scenic = new ServiceFallbackConfig(); private ServiceFallbackConfig scenic = new ServiceFallbackConfig();
private ServiceFallbackConfig device = new ServiceFallbackConfig(); private ServiceFallbackConfig device = new ServiceFallbackConfig();
private ServiceFallbackConfig render = new ServiceFallbackConfig();
}
@Data
public static class RenderWorkerConfig {
/**
* 是否启用渲染工作器服务集成
*/
private boolean enabled = true;
/**
* 服务名称
*/
private String serviceName = "zt-render-worker";
/**
* 超时配置(毫秒)
*/
private int connectTimeout = 5000;
private int readTimeout = 10000;
/**
* 重试配置
*/
private boolean retryEnabled = false;
private int maxRetries = 3;
} }
@Data @Data

View File

@@ -0,0 +1,82 @@
package com.ycwl.basic.integration.common.manager;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import java.util.List;
/**
* 渲染工作器配置管理器
* 基于通用ConfigManager实现渲染工作器配置的管理功能
*/
public class RenderWorkerConfigManager extends ConfigManager<RenderWorkerConfigV2DTO> {
public RenderWorkerConfigManager(List<RenderWorkerConfigV2DTO> configs) {
super(configs);
}
@Override
protected String getConfigKey(RenderWorkerConfigV2DTO config) {
return config != null ? config.getConfigKey() : null;
}
@Override
protected Object getConfigValue(RenderWorkerConfigV2DTO config) {
return config != null ? config.getConfigValue() : null;
}
/**
* 获取渲染工作器配置类型
*/
public String getConfigType(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getConfigType() : null;
}
/**
* 获取渲染工作器配置描述
*/
public String getConfigDescription(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getDescription() : null;
}
/**
* 获取渲染工作器配置ID
*/
public Long getConfigId(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getId() : null;
}
/**
* 获取工作器ID
*/
public Long getWorkerId(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getWorkerId() : null;
}
/**
* 检查配置是否启用
*/
public boolean isActive(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null && config.getIsActive() != null && config.getIsActive() == 1;
}
/**
* 获取配置的创建时间
*/
public String getCreateTime(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getCreateTime() : null;
}
/**
* 获取配置的更新时间
*/
public String getUpdateTime(String key) {
RenderWorkerConfigV2DTO config = findConfigByKey(key);
return config != null ? config.getUpdateTime() : null;
}
}

View File

@@ -12,29 +12,33 @@ import lombok.AllArgsConstructor;
public class CommonResponse<T> { public class CommonResponse<T> {
private Integer code; private Integer code;
private String message; private String message;
private Boolean success;
private T data; private T data;
public static <T> CommonResponse<T> success() { public static <T> CommonResponse<T> success() {
return new CommonResponse<>(200, "OK", null); return new CommonResponse<>(200, "OK", true, null);
} }
public static <T> CommonResponse<T> success(T data) { public static <T> CommonResponse<T> success(T data) {
return new CommonResponse<>(200, "OK", data); return new CommonResponse<>(200, "OK", true, data);
} }
public static <T> CommonResponse<T> success(String message, T data) { public static <T> CommonResponse<T> success(String message, T data) {
return new CommonResponse<>(200, message, data); return new CommonResponse<>(200, message, true, data);
} }
public static <T> CommonResponse<T> error(Integer code, String message) { public static <T> CommonResponse<T> error(Integer code, String message) {
return new CommonResponse<>(code, message, null); return new CommonResponse<>(code, message, false, null);
} }
public static <T> CommonResponse<T> error(String message) { public static <T> CommonResponse<T> error(String message) {
return new CommonResponse<>(5000, message, null); return new CommonResponse<>(5000, message, false, null);
} }
public boolean isSuccess() { public boolean isSuccess() {
return code != null && code == 200; if (success == null) { // compatible
return code != null && code == 200;
}
return success;
} }
} }

View File

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

View File

@@ -1,6 +1,7 @@
package com.ycwl.basic.integration.device.client; package com.ycwl.basic.integration.device.client;
import com.ycwl.basic.integration.common.response.CommonResponse; import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.*; import com.ycwl.basic.integration.device.dto.device.*;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -55,7 +56,7 @@ public interface DeviceV2Client {
* 分页获取设备列表(核心信息) * 分页获取设备列表(核心信息)
*/ */
@GetMapping("/") @GetMapping("/")
CommonResponse<DeviceV2ListResponse> listDevices( CommonResponse<PageResponse<DeviceV2DTO>> listDevices(
@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "name", required = false) String name, @RequestParam(value = "name", required = false) String name,
@@ -68,7 +69,7 @@ public interface DeviceV2Client {
* 分页获取设备列表(含配置) * 分页获取设备列表(含配置)
*/ */
@GetMapping("/with-config") @GetMapping("/with-config")
CommonResponse<DeviceV2WithConfigListResponse> listDevicesWithConfig( CommonResponse<PageResponse<DeviceV2WithConfigDTO>> listDevicesWithConfig(
@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "name", required = false) String name, @RequestParam(value = "name", required = false) String name,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +0,0 @@
package com.ycwl.basic.integration.device.dto.device;
import lombok.Data;
import java.util.List;
@Data
public class DeviceV2ListResponse {
private List<DeviceV2DTO> list;
private Integer total;
private Integer page;
private Integer pageSize;
}

View File

@@ -1,13 +0,0 @@
package com.ycwl.basic.integration.device.dto.device;
import lombok.Data;
import java.util.List;
@Data
public class DeviceV2WithConfigListResponse {
private List<DeviceV2WithConfigDTO> list;
private Integer total;
private Integer page;
private Integer pageSize;
}

View File

@@ -17,7 +17,7 @@ public class FilterDevicesByConfigsResponse {
/** /**
* 总数 * 总数
*/ */
private String total; private Integer total;
/** /**
* 页码 * 页码

View File

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

View File

@@ -1,5 +1,6 @@
package com.ycwl.basic.integration.device.example; package com.ycwl.basic.integration.device.example;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.*; import com.ycwl.basic.integration.device.dto.device.*;
import com.ycwl.basic.integration.device.dto.config.*; import com.ycwl.basic.integration.device.dto.config.*;
import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService; import com.ycwl.basic.integration.device.service.DeviceConfigIntegrationService;
@@ -46,7 +47,7 @@ public class DeviceIntegrationExample {
log.info("获取设备配置: {}", deviceWithConfig.getName()); log.info("获取设备配置: {}", deviceWithConfig.getName());
// 分页查询景区设备列表 // 分页查询景区设备列表
DeviceV2ListResponse deviceList = deviceService.getScenicIpcDevices(1001L, 1, 10); PageResponse<DeviceV2DTO> deviceList = deviceService.getScenicIpcDevices(1001L, 1, 10);
log.info("景区设备列表: 总数={}", deviceList.getTotal()); log.info("景区设备列表: 总数={}", deviceList.getTotal());
// 启用设备 // 启用设备
@@ -80,7 +81,7 @@ public class DeviceIntegrationExample {
log.info("更新摄像头1排序为1(置顶)"); log.info("更新摄像头1排序为1(置顶)");
// 获取排序后的设备列表 // 获取排序后的设备列表
DeviceV2ListResponse sortedList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId); PageResponse<DeviceV2DTO> sortedList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId);
log.info("排序后的设备列表:"); log.info("排序后的设备列表:");
for (DeviceV2DTO device : sortedList.getList()) { for (DeviceV2DTO device : sortedList.getList()) {
log.info(" - {}: 排序={}, 类型={}", device.getName(), device.getSort(), device.getType()); log.info(" - {}: 排序={}, 类型={}", device.getName(), device.getSort(), device.getType());
@@ -147,7 +148,7 @@ public class DeviceIntegrationExample {
log.info("将普通摄像头置顶(排序值: 1)"); log.info("将普通摄像头置顶(排序值: 1)");
// 查看最终排序结果 // 查看最终排序结果
DeviceV2ListResponse finalList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId); PageResponse<DeviceV2DTO> finalList = deviceService.listDevices(1, 10, null, null, null, 1, scenicId);
log.info("最终排序结果:"); log.info("最终排序结果:");
for (DeviceV2DTO device : finalList.getList()) { for (DeviceV2DTO device : finalList.getList()) {
log.info(" - {}: 排序={}", device.getName(), device.getSort()); log.info(" - {}: 排序={}", device.getName(), device.getSort());

View File

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

View File

@@ -4,6 +4,7 @@ import com.ycwl.basic.integration.common.exception.IntegrationException;
import com.ycwl.basic.integration.common.response.CommonResponse; import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.service.IntegrationFallbackService; import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
import com.ycwl.basic.integration.device.client.DeviceV2Client; import com.ycwl.basic.integration.device.client.DeviceV2Client;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.*; import com.ycwl.basic.integration.device.dto.device.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -91,20 +92,20 @@ public class DeviceIntegrationService {
handleResponse(response, "删除设备失败"); handleResponse(response, "删除设备失败");
} }
public DeviceV2ListResponse listDevices(Integer page, Integer pageSize, String name, String no, public PageResponse<DeviceV2DTO> listDevices(Integer page, Integer pageSize, String name, String no,
String type, Integer isActive, Long scenicId) { String type, Integer isActive, Long scenicId) {
log.debug("分页查询设备列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}", log.debug("分页查询设备列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
page, pageSize, name, no, type, isActive, scenicId); page, pageSize, name, no, type, isActive, scenicId);
CommonResponse<DeviceV2ListResponse> response = deviceV2Client.listDevices( CommonResponse<PageResponse<DeviceV2DTO>> response = deviceV2Client.listDevices(
page, pageSize, name, no, type, isActive, scenicId); page, pageSize, name, no, type, isActive, scenicId);
return handleResponse(response, "分页查询设备列表失败"); return handleResponse(response, "分页查询设备列表失败");
} }
public DeviceV2WithConfigListResponse listDevicesWithConfig(Integer page, Integer pageSize, String name, String no, public PageResponse<DeviceV2WithConfigDTO> listDevicesWithConfig(Integer page, Integer pageSize, String name, String no,
String type, Integer isActive, Long scenicId) { String type, Integer isActive, Long scenicId) {
log.debug("分页查询设备带配置列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}", log.debug("分页查询设备带配置列表, page: {}, pageSize: {}, name: {}, no: {}, type: {}, isActive: {}, scenicId: {}",
page, pageSize, name, no, type, isActive, scenicId); page, pageSize, name, no, type, isActive, scenicId);
CommonResponse<DeviceV2WithConfigListResponse> response = deviceV2Client.listDevicesWithConfig( CommonResponse<PageResponse<DeviceV2WithConfigDTO>> response = deviceV2Client.listDevicesWithConfig(
page, pageSize, name, no, type, isActive, scenicId); page, pageSize, name, no, type, isActive, scenicId);
return handleResponse(response, "分页查询设备带配置列表失败"); return handleResponse(response, "分页查询设备带配置列表失败");
} }
@@ -196,14 +197,14 @@ public class DeviceIntegrationService {
/** /**
* 获取景区的IPC设备列表 * 获取景区的IPC设备列表
*/ */
public DeviceV2ListResponse getScenicIpcDevices(Long scenicId, Integer page, Integer pageSize) { public PageResponse<DeviceV2DTO> getScenicIpcDevices(Long scenicId, Integer page, Integer pageSize) {
return listDevices(page, pageSize, null, null, "IPC", 1, scenicId); return listDevices(page, pageSize, null, null, "IPC", 1, scenicId);
} }
/** /**
* 获取景区的所有激活设备 * 获取景区的所有激活设备
*/ */
public DeviceV2ListResponse getScenicActiveDevices(Long scenicId, Integer page, Integer pageSize) { public PageResponse<DeviceV2DTO> getScenicActiveDevices(Long scenicId, Integer page, Integer pageSize) {
return listDevices(page, pageSize, null, null, null, 1, scenicId); return listDevices(page, pageSize, null, null, null, 1, scenicId);
} }

View File

@@ -0,0 +1,59 @@
package com.ycwl.basic.integration.render.client;
import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.render.dto.config.BatchRenderWorkerConfigRequest;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 渲染工作器配置V2客户端
*/
@FeignClient(name = "zt-render-worker", contextId = "render-worker-config-v2", path = "/api/render/worker/config/v2")
public interface RenderWorkerConfigV2Client {
/**
* 获取工作器所有配置
*/
@GetMapping("/{workerId}")
CommonResponse<List<RenderWorkerConfigV2DTO>> getWorkerConfigs(@PathVariable("workerId") Long workerId);
/**
* 根据配置键获取特定配置
*/
@GetMapping("/{workerId}/key/{configKey}")
CommonResponse<RenderWorkerConfigV2DTO> getWorkerConfigByKey(@PathVariable("workerId") Long workerId,
@PathVariable("configKey") String configKey);
/**
* 创建配置
*/
@PostMapping("/{workerId}")
CommonResponse<RenderWorkerConfigV2DTO> createWorkerConfig(@PathVariable("workerId") Long workerId,
@RequestBody RenderWorkerConfigV2DTO config);
/**
* 更新配置
*/
@PutMapping("/{workerId}/{id}")
CommonResponse<Void> updateWorkerConfig(@PathVariable("workerId") Long workerId,
@PathVariable("id") Long id,
@RequestBody Map<String, Object> updates);
/**
* 删除配置
*/
@DeleteMapping("/{workerId}/{id}")
CommonResponse<Void> deleteWorkerConfig(@PathVariable("workerId") Long workerId,
@PathVariable("id") Long id);
/**
* 批量更新配置
*/
@PostMapping("/{workerId}/batch")
CommonResponse<Void> batchUpdateWorkerConfigs(@PathVariable("workerId") Long workerId,
@RequestBody BatchRenderWorkerConfigRequest request);
}

View File

@@ -0,0 +1,76 @@
package com.ycwl.basic.integration.render.client;
import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.render.dto.worker.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
/**
* 渲染工作器V2客户端
*/
@FeignClient(name = "zt-render-worker", contextId = "render-worker-v2", path = "/api/render/worker/v2")
public interface RenderWorkerV2Client {
/**
* 获取工作器核心信息
*/
@GetMapping("/{id}")
CommonResponse<RenderWorkerV2DTO> getWorker(@PathVariable("id") Long id);
/**
* 获取工作器含配置信息
*/
@GetMapping("/{id}/with-config")
CommonResponse<RenderWorkerV2WithConfigDTO> getWorkerWithConfig(@PathVariable("id") Long id);
/**
* 创建工作器
*/
@PostMapping
CommonResponse<RenderWorkerV2DTO> createWorker(@RequestBody CreateRenderWorkerRequest request);
/**
* 更新工作器
*/
@PutMapping("/{id}")
CommonResponse<Void> updateWorker(@PathVariable("id") Long id,
@RequestBody UpdateRenderWorkerRequest request);
/**
* 删除工作器
*/
@DeleteMapping("/{id}")
CommonResponse<Void> deleteWorker(@PathVariable("id") Long id);
/**
* 分页查询工作器列表(核心信息)
*/
@GetMapping
CommonResponse<PageResponse<RenderWorkerV2DTO>> listWorkers(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer isEnabled,
@RequestParam(required = false) String name);
/**
* 分页查询工作器列表(含配置信息)
*/
@GetMapping("/with-config")
CommonResponse<PageResponse<RenderWorkerV2WithConfigDTO>> listWorkersWithConfig(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer isEnabled,
@RequestParam(required = false) String name);
/**
* 根据key获取工作器核心信息
*/
@GetMapping("/key/{key}")
CommonResponse<RenderWorkerV2DTO> getWorkerByKey(@PathVariable("key") String key);
/**
* 根据key获取工作器完整信息(含配置)
*/
@GetMapping("/key/{key}/with-config")
CommonResponse<RenderWorkerV2WithConfigDTO> getWorkerWithConfigByKey(@PathVariable("key") String key);
}

View File

@@ -0,0 +1,17 @@
package com.ycwl.basic.integration.render.config;
import com.ycwl.basic.integration.common.config.IntegrationProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
/**
* 渲染工作器集成配置
*/
@Configuration
@ConditionalOnProperty(prefix = "integration.render", name = "enabled", havingValue = "true", matchIfMissing = true)
@RequiredArgsConstructor
public class RenderWorkerIntegrationConfig {
private final IntegrationProperties integrationProperties;
}

View File

@@ -0,0 +1,17 @@
package com.ycwl.basic.integration.render.dto.config;
import lombok.Data;
import java.util.List;
/**
* 批量更新渲染工作器配置请求DTO
*/
@Data
public class BatchRenderWorkerConfigRequest {
/**
* 配置列表
*/
private List<RenderWorkerConfigV2DTO> configs;
}

View File

@@ -0,0 +1,63 @@
package com.ycwl.basic.integration.render.dto.config;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 渲染工作器配置DTO
*/
@Data
public class RenderWorkerConfigV2DTO {
/**
* 配置ID
*/
private Long id;
/**
* 工作器ID
*/
@JsonProperty("workerId")
private Long workerId;
/**
* 配置键
*/
@JsonProperty("configKey")
private String configKey;
/**
* 配置值
*/
@JsonProperty("configValue")
private String configValue;
/**
* 配置类型 (string/int/float/bool/json)
*/
@JsonProperty("configType")
private String configType;
/**
* 描述
*/
private String description;
/**
* 是否启用 (0-禁用,1-启用)
*/
@JsonProperty("isActive")
private Integer isActive;
/**
* 创建时间
*/
@JsonProperty("createTime")
private String createTime;
/**
* 更新时间
*/
@JsonProperty("updateTime")
private String updateTime;
}

View File

@@ -0,0 +1,31 @@
package com.ycwl.basic.integration.render.dto.worker;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
/**
* 创建渲染工作器请求DTO
*/
@Data
public class CreateRenderWorkerRequest {
/**
* 工作器名称
*/
@NotBlank(message = "工作器名称不能为空")
private String name;
/**
* 工作器标识
*/
@NotBlank(message = "工作器标识不能为空")
private String key;
/**
* 是否启用 (0-禁用,1-启用)
*/
@JsonProperty("isActive")
private Integer isActive = 1;
}

View File

@@ -0,0 +1,49 @@
package com.ycwl.basic.integration.render.dto.worker;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
/**
* 渲染工作器核心信息DTO
*/
@Data
public class RenderWorkerV2DTO {
/**
* 工作器ID
*/
private Long id;
/**
* 工作器名称
*/
private String name;
/**
* 工作器标识
*/
private String key;
/**
* 是否启用 (0-禁用,1-启用)
*/
@JsonProperty("isActive")
private Integer isActive;
/**
* 创建时间
*/
@JsonProperty("createTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* 更新时间
*/
@JsonProperty("updateTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

View File

@@ -0,0 +1,55 @@
package com.ycwl.basic.integration.render.dto.worker;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.Map;
/**
* 带配置的渲染工作器DTO
*/
@Data
public class RenderWorkerV2WithConfigDTO {
/**
* 工作器ID
*/
private Long id;
/**
* 工作器名称
*/
private String name;
/**
* 工作器标识
*/
private String key;
/**
* 是否启用 (0-禁用,1-启用)
*/
@JsonProperty("isActive")
private Integer isActive;
/**
* 创建时间
*/
@JsonProperty("createTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* 更新时间
*/
@JsonProperty("updateTime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
/**
* 动态配置信息
*/
private Map<String, Object> config;
}

View File

@@ -0,0 +1,27 @@
package com.ycwl.basic.integration.render.dto.worker;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 更新渲染工作器请求DTO
*/
@Data
public class UpdateRenderWorkerRequest {
/**
* 工作器名称
*/
private String name;
/**
* 工作器标识
*/
private String key;
/**
* 是否启用 (0-禁用,1-启用)
*/
@JsonProperty("isActive")
private Integer isActive;
}

View File

@@ -0,0 +1,287 @@
package com.ycwl.basic.integration.render.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.render.client.RenderWorkerConfigV2Client;
import com.ycwl.basic.integration.render.dto.config.BatchRenderWorkerConfigRequest;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 渲染工作器配置集成服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RenderWorkerConfigIntegrationService {
private final RenderWorkerConfigV2Client renderWorkerConfigV2Client;
private final IntegrationFallbackService fallbackService;
private static final String SERVICE_NAME = "zt-render-worker";
/**
* 获取工作器所有配置(带降级)
*/
public List<RenderWorkerConfigV2DTO> getWorkerConfigs(Long workerId) {
log.info("获取渲染工作器配置列表, workerId: {}", workerId);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:configs:" + workerId,
() -> {
CommonResponse<List<RenderWorkerConfigV2DTO>> response =
renderWorkerConfigV2Client.getWorkerConfigs(workerId);
List<RenderWorkerConfigV2DTO> configs = handleResponse(response, "获取渲染工作器配置列表失败");
return configs != null ? configs : Collections.emptyList();
},
List.class
);
}
/**
* 根据配置键获取特定配置(带降级)
*/
public RenderWorkerConfigV2DTO getWorkerConfigByKey(Long workerId, String configKey) {
log.info("根据配置键获取渲染工作器配置, workerId: {}, configKey: {}", workerId, configKey);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:config:" + workerId + ":" + configKey,
() -> {
CommonResponse<RenderWorkerConfigV2DTO> response =
renderWorkerConfigV2Client.getWorkerConfigByKey(workerId, configKey);
return handleResponse(response, "根据配置键获取渲染工作器配置失败");
},
RenderWorkerConfigV2DTO.class
);
}
/**
* 获取工作器平铺配置(带降级)
*/
public Map<String, Object> getWorkerFlatConfig(Long workerId) {
log.info("获取渲染工作器平铺配置, workerId: {}", workerId);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:flat:config:" + workerId,
() -> {
List<RenderWorkerConfigV2DTO> configs = getWorkerConfigsInternal(workerId);
return flattenConfigs(configs);
},
Map.class
);
}
/**
* 创建配置(直接调用,不降级)
*/
public RenderWorkerConfigV2DTO createWorkerConfig(Long workerId, RenderWorkerConfigV2DTO config) {
log.info("创建渲染工作器配置, workerId: {}, configKey: {}", workerId, config.getConfigKey());
CommonResponse<RenderWorkerConfigV2DTO> response =
renderWorkerConfigV2Client.createWorkerConfig(workerId, config);
return handleResponse(response, "创建渲染工作器配置失败");
}
/**
* 更新配置(直接调用,不降级)
*/
public void updateWorkerConfig(Long workerId, Long configId, Map<String, Object> updates) {
log.info("更新渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
CommonResponse<Void> response =
renderWorkerConfigV2Client.updateWorkerConfig(workerId, configId, updates);
handleVoidResponse(response, "更新渲染工作器配置失败");
}
/**
* 删除配置(直接调用,不降级)
*/
public void deleteWorkerConfig(Long workerId, Long configId) {
log.info("删除渲染工作器配置, workerId: {}, configId: {}", workerId, configId);
CommonResponse<Void> response =
renderWorkerConfigV2Client.deleteWorkerConfig(workerId, configId);
handleVoidResponse(response, "删除渲染工作器配置失败");
}
/**
* 批量更新配置(直接调用,不降级)
*/
public void batchUpdateWorkerConfigs(Long workerId, BatchRenderWorkerConfigRequest request) {
log.info("批量更新渲染工作器配置, workerId: {}, configCount: {}",
workerId, request.getConfigs() != null ? request.getConfigs().size() : 0);
CommonResponse<Void> response =
renderWorkerConfigV2Client.batchUpdateWorkerConfigs(workerId, request);
handleVoidResponse(response, "批量更新渲染工作器配置失败");
}
/**
* 批量平铺更新配置(直接调用,不降级)
*/
public void batchFlatUpdateWorkerConfigs(Long workerId, Map<String, Object> flatConfigs) {
log.info("批量平铺更新渲染工作器配置, workerId: {}, configCount: {}",
workerId, flatConfigs.size());
BatchRenderWorkerConfigRequest request = new BatchRenderWorkerConfigRequest();
List<RenderWorkerConfigV2DTO> configs = flatConfigs.entrySet().stream()
.map(entry -> {
RenderWorkerConfigV2DTO config = new RenderWorkerConfigV2DTO();
config.setConfigKey(entry.getKey());
config.setConfigValue(String.valueOf(entry.getValue()));
config.setConfigType(determineConfigType(entry.getValue()));
config.setIsActive(1);
return config;
})
.toList();
request.setConfigs(configs);
batchUpdateWorkerConfigs(workerId, request);
}
/**
* 创建批量配置构建器
*/
public BatchConfigBuilder createBatchConfigBuilder() {
return new BatchConfigBuilder();
}
/**
* 内部获取配置方法(不使用降级)
*/
private List<RenderWorkerConfigV2DTO> getWorkerConfigsInternal(Long workerId) {
CommonResponse<List<RenderWorkerConfigV2DTO>> response =
renderWorkerConfigV2Client.getWorkerConfigs(workerId);
List<RenderWorkerConfigV2DTO> configs = handleResponse(response, "获取渲染工作器配置列表失败");
return configs != null ? configs : Collections.emptyList();
}
/**
* 将配置列表转换为平铺Map
*/
private Map<String, Object> flattenConfigs(List<RenderWorkerConfigV2DTO> configs) {
Map<String, Object> flatConfig = new HashMap<>();
for (RenderWorkerConfigV2DTO config : configs) {
if (config.getIsActive() == 1) {
Object value = convertConfigValue(config.getConfigValue(), config.getConfigType());
flatConfig.put(config.getConfigKey(), value);
}
}
return flatConfig;
}
/**
* 根据配置类型转换配置值
*/
private Object convertConfigValue(String configValue, String configType) {
if (configValue == null) return null;
return switch (configType) {
case "int" -> Integer.parseInt(configValue);
case "float" -> Double.parseDouble(configValue);
case "bool" -> Boolean.parseBoolean(configValue);
case "json" -> configValue; // 保持JSON字符串格式
default -> configValue; // string类型或其他
};
}
/**
* 自动判断配置类型
*/
private String determineConfigType(Object value) {
if (value instanceof Integer) return "int";
if (value instanceof Double || value instanceof Float) return "float";
if (value instanceof Boolean) return "bool";
return "string";
}
/**
* 处理通用响应
*/
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
if (response == null || !response.getSuccess()) {
String msg = response != null && response.getMessage() != null ?
response.getMessage() : errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
return response.getData();
}
/**
* 处理空响应
*/
private void handleVoidResponse(CommonResponse<Void> response, String errorMessage) {
if (response == null || !response.getSuccess()) {
String msg = response != null && response.getMessage() != null ?
response.getMessage() : errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
}
/**
* 批量配置构建器
*/
public static class BatchConfigBuilder {
private final List<RenderWorkerConfigV2DTO> configs = new java.util.ArrayList<>();
/**
* 添加配置项
*/
public BatchConfigBuilder addConfig(String key, String value) {
return addConfig(key, value, "string", null);
}
/**
* 添加配置项(指定类型)
*/
public BatchConfigBuilder addConfig(String key, String value, String type) {
return addConfig(key, value, type, null);
}
/**
* 添加配置项(完整)
*/
public BatchConfigBuilder addConfig(String key, String value, String type, String description) {
RenderWorkerConfigV2DTO config = new RenderWorkerConfigV2DTO();
config.setConfigKey(key);
config.setConfigValue(value);
config.setConfigType(type);
config.setDescription(description);
config.setIsActive(1);
configs.add(config);
return this;
}
/**
* 添加渲染配置
*/
public BatchConfigBuilder addRenderConfig(String quality, String format, String resolution) {
return addConfig("render_quality", quality, "string", "渲染质量")
.addConfig("render_format", format, "string", "渲染格式")
.addConfig("render_resolution", resolution, "string", "渲染分辨率");
}
/**
* 添加性能配置
*/
public BatchConfigBuilder addPerformanceConfig(Integer maxTasks, Integer timeout) {
return addConfig("max_concurrent_tasks", String.valueOf(maxTasks), "int", "最大并发任务数")
.addConfig("task_timeout", String.valueOf(timeout), "int", "任务超时时间(秒)");
}
/**
* 构建批量配置请求
*/
public BatchRenderWorkerConfigRequest build() {
BatchRenderWorkerConfigRequest request = new BatchRenderWorkerConfigRequest();
request.setConfigs(configs);
return request;
}
}
}

View File

@@ -0,0 +1,168 @@
package com.ycwl.basic.integration.render.service;
import com.ycwl.basic.integration.common.exception.IntegrationException;
import com.ycwl.basic.integration.common.response.CommonResponse;
import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.common.service.IntegrationFallbackService;
import com.ycwl.basic.integration.render.client.RenderWorkerV2Client;
import com.ycwl.basic.integration.render.dto.worker.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
/**
* 渲染工作器集成服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RenderWorkerIntegrationService {
private final RenderWorkerV2Client renderWorkerV2Client;
private final IntegrationFallbackService fallbackService;
private static final String SERVICE_NAME = "zt-render-worker";
/**
* 获取工作器核心信息(带降级)
*/
public RenderWorkerV2DTO getWorker(Long id) {
log.info("获取渲染工作器信息, id: {}", id);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:" + id,
() -> {
CommonResponse<RenderWorkerV2DTO> response = renderWorkerV2Client.getWorker(id);
return handleResponse(response, "获取渲染工作器信息失败");
},
RenderWorkerV2DTO.class
);
}
/**
* 获取工作器详细信息(含配置)(带降级)
*/
public RenderWorkerV2WithConfigDTO getWorkerWithConfig(Long id) {
log.info("获取渲染工作器详细信息, id: {}", id);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:config:" + id,
() -> {
CommonResponse<RenderWorkerV2WithConfigDTO> response = renderWorkerV2Client.getWorkerWithConfig(id);
return handleResponse(response, "获取渲染工作器详细信息失败");
},
RenderWorkerV2WithConfigDTO.class
);
}
/**
* 创建工作器(直接调用,不降级)
*/
public RenderWorkerV2DTO createWorker(CreateRenderWorkerRequest request) {
log.info("创建渲染工作器, name: {}, key: {}", request.getName(), request.getKey());
CommonResponse<RenderWorkerV2DTO> response = renderWorkerV2Client.createWorker(request);
return handleResponse(response, "创建渲染工作器失败");
}
/**
* 更新工作器(直接调用,不降级)
*/
public void updateWorker(Long id, UpdateRenderWorkerRequest request) {
log.info("更新渲染工作器, id: {}, name: {}", id, request.getName());
CommonResponse<Void> response = renderWorkerV2Client.updateWorker(id, request);
handleVoidResponse(response, "更新渲染工作器失败");
}
/**
* 删除工作器(直接调用,不降级)
*/
public void deleteWorker(Long id) {
log.info("删除渲染工作器, id: {}", id);
CommonResponse<Void> response = renderWorkerV2Client.deleteWorker(id);
handleVoidResponse(response, "删除渲染工作器失败");
}
/**
* 分页查询工作器列表(核心信息)(不降级)
*/
public PageResponse<RenderWorkerV2DTO> listWorkers(Integer page, Integer pageSize, Integer isEnabled, String name) {
log.info("分页查询渲染工作器列表, page: {}, pageSize: {}, isEnabled: {}, name: {}",
page, pageSize, isEnabled, name);
CommonResponse<PageResponse<RenderWorkerV2DTO>> response =
renderWorkerV2Client.listWorkers(page, pageSize, isEnabled, name);
return handleResponse(response, "查询渲染工作器列表失败");
}
/**
* 分页查询工作器列表(含配置信息)(不降级)
*/
public PageResponse<RenderWorkerV2WithConfigDTO> listWorkersWithConfig(Integer page, Integer pageSize,
Integer isEnabled, String name) {
log.info("分页查询渲染工作器列表(含配置), page: {}, pageSize: {}, isEnabled: {}, name: {}",
page, pageSize, isEnabled, name);
CommonResponse<PageResponse<RenderWorkerV2WithConfigDTO>> response =
renderWorkerV2Client.listWorkersWithConfig(page, pageSize, isEnabled, name);
return handleResponse(response, "查询渲染工作器列表(含配置)失败");
}
/**
* 根据key获取工作器核心信息(带降级)
*/
public RenderWorkerV2DTO getWorkerByKey(String key) {
log.info("根据key获取渲染工作器信息, key: {}", key);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:key:" + key,
() -> {
CommonResponse<RenderWorkerV2DTO> response = renderWorkerV2Client.getWorkerByKey(key);
return handleResponse(response, "根据key获取渲染工作器信息失败");
},
RenderWorkerV2DTO.class
);
}
/**
* 根据key获取工作器详细信息(含配置)(带降级)
*/
public RenderWorkerV2WithConfigDTO getWorkerWithConfigByKey(String key) {
log.info("根据key获取渲染工作器详细信息, key: {}", key);
return fallbackService.executeWithFallback(
SERVICE_NAME,
"worker:key:config:" + key,
() -> {
CommonResponse<RenderWorkerV2WithConfigDTO> response = renderWorkerV2Client.getWorkerWithConfigByKey(key);
return handleResponse(response, "根据key获取渲染工作器详细信息失败");
},
RenderWorkerV2WithConfigDTO.class
);
}
/**
* 处理通用响应
*/
private <T> T handleResponse(CommonResponse<T> response, String errorMessage) {
if (response == null || !response.getSuccess()) {
String msg = response != null && response.getMessage() != null ?
response.getMessage() : errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
return response.getData();
}
/**
* 处理空响应
*/
private void handleVoidResponse(CommonResponse<Void> response, String errorMessage) {
if (response == null || !response.getSuccess()) {
String msg = response != null && response.getMessage() != null ?
response.getMessage() : errorMessage;
Integer code = response != null ? response.getCode() : 5000;
throw new IntegrationException(code, msg, SERVICE_NAME);
}
}
}

View File

@@ -6,8 +6,7 @@ import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse; import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -38,13 +37,13 @@ public interface ScenicV2Client {
CommonResponse<ScenicFilterPageResponse> filterScenics(@RequestBody ScenicFilterRequest request); CommonResponse<ScenicFilterPageResponse> filterScenics(@RequestBody ScenicFilterRequest request);
@GetMapping("/") @GetMapping("/")
CommonResponse<ScenicV2ListResponse> listScenics(@RequestParam(defaultValue = "1") Integer page, CommonResponse<PageResponse<ScenicV2DTO>> listScenics(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer status, @RequestParam(required = false) Integer status,
@RequestParam(required = false) String name); @RequestParam(required = false) String name);
@GetMapping("/with-config") @GetMapping("/with-config")
CommonResponse<ScenicV2WithConfigListResponse> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page, CommonResponse<PageResponse<ScenicV2WithConfigDTO>> listScenicsWithConfig(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer status, @RequestParam(required = false) Integer status,
@RequestParam(required = false) String name); @RequestParam(required = false) String name);

View File

@@ -1,21 +0,0 @@
package com.ycwl.basic.integration.scenic.dto.scenic;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class ScenicV2ListResponse {
@JsonProperty("list")
private List<ScenicV2DTO> list;
@JsonProperty("total")
private Integer total;
@JsonProperty("page")
private Integer page;
@JsonProperty("pageSize")
private Integer pageSize;
}

View File

@@ -1,21 +0,0 @@
package com.ycwl.basic.integration.scenic.dto.scenic;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class ScenicV2WithConfigListResponse {
@JsonProperty("list")
private List<ScenicV2WithConfigDTO> list;
@JsonProperty("total")
private Integer total;
@JsonProperty("page")
private Integer page;
@JsonProperty("pageSize")
private Integer pageSize;
}

View File

@@ -13,7 +13,7 @@ import java.util.List;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class DefaultConfigIntegrationService { public class ScenicDefaultConfigIntegrationService {
private final DefaultConfigClient defaultConfigClient; private final DefaultConfigClient defaultConfigClient;

View File

@@ -10,8 +10,7 @@ import com.ycwl.basic.integration.scenic.dto.filter.ScenicFilterRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.CreateScenicRequest;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse; import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigListResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest; import com.ycwl.basic.integration.scenic.dto.scenic.UpdateScenicRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -93,15 +92,15 @@ public class ScenicIntegrationService {
return handleResponse(response, "筛选景区失败"); return handleResponse(response, "筛选景区失败");
} }
public ScenicV2ListResponse listScenics(Integer page, Integer pageSize, Integer status, String name) { public PageResponse<ScenicV2DTO> listScenics(Integer page, Integer pageSize, Integer status, String name) {
log.debug("分页查询景区列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name); log.debug("分页查询景区列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
CommonResponse<ScenicV2ListResponse> response = scenicV2Client.listScenics(page, pageSize, status, name); CommonResponse<PageResponse<ScenicV2DTO>> response = scenicV2Client.listScenics(page, pageSize, status, name);
return handleResponse(response, "分页查询景区列表失败"); return handleResponse(response, "分页查询景区列表失败");
} }
public ScenicV2WithConfigListResponse listScenicsWithConfig(Integer page, Integer pageSize, Integer status, String name) { public PageResponse<ScenicV2WithConfigDTO> listScenicsWithConfig(Integer page, Integer pageSize, Integer status, String name) {
log.debug("分页查询景区带配置列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name); log.debug("分页查询景区带配置列表, page: {}, pageSize: {}, status: {}, name: {}", page, pageSize, status, name);
CommonResponse<ScenicV2WithConfigListResponse> response = scenicV2Client.listScenicsWithConfig(page, pageSize, status, name); CommonResponse<PageResponse<ScenicV2WithConfigDTO>> response = scenicV2Client.listScenicsWithConfig(page, pageSize, status, name);
return handleResponse(response, "分页查询景区带配置列表失败"); return handleResponse(response, "分页查询景区带配置列表失败");
} }

View File

@@ -1,26 +0,0 @@
package com.ycwl.basic.mapper;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.renderWorker.req.RenderWorkerReqQuery;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/11/29 17:22
* 渲染机管理表
*/
@Mapper
public interface RenderWorkerMapper {
List<RenderWorkerEntity> list(RenderWorkerReqQuery renderWorkerReqQuery);
RenderWorkerEntity getById(Long id);
int add(RenderWorkerEntity renderWorker);
int deleteById(Long id);
int update(RenderWorkerEntity renderWorker);
int updateStatus(Long id);
RenderWorkerEntity findByAccessKey(String accessKey);
int updateHost(Long id, RenderWorkerEntity status);
}

View File

@@ -14,50 +14,16 @@ import java.util.Date;
* 渲染机管理表 * 渲染机管理表
*/ */
@Data @Data
@TableName("render_worker")
public class RenderWorkerEntity { public class RenderWorkerEntity {
@TableId
private Long id; private Long id;
/** /**
* 渲染机名称 * 渲染机名称
*/ */
private String name; private String name;
/**
* 系统
*/
private String platform;
/**
* 运行环境
*/
private String runtimeVersion;
/**
* 版本
*/
private String version;
/** /**
* 访问秘钥 * 访问秘钥
*/ */
private String accessKey; private String accessKey;
/**
* cpu数量
*/
private Integer cpuCount;
/**
* cpu使用率
*/
private BigDecimal cpuUsage;
/**
* 内存总量,MB
*/
private BigDecimal memoryTotal;
/**
* 内存余量,MB
*/
private BigDecimal memoryAvailable;
/**
* 支持的功能,逗号隔开
*/
private String supportFeature;
/** /**
* 是否仅用于指定景区,空或0不适用,否则为景区ID * 是否仅用于指定景区,空或0不适用,否则为景区ID
*/ */
@@ -66,24 +32,10 @@ public class RenderWorkerEntity {
* 是否仅用于测试,0不是,1是 * 是否仅用于测试,0不是,1是
*/ */
private Integer testOnly; private Integer testOnly;
/**
* 是否在线,0不在,1在
*/
private Integer online;
/** /**
* 状态,0禁用,1启用 * 状态,0禁用,1启用
*/ */
private Integer status; private Integer status;
private Date createAt; private Date createAt;
private Date updateAt; private Date updateAt;
/**
* 存储类型
*/
private StorageType storeType;
/**
* 存储配置
*/
private String storeConfigJson;
} }

View File

@@ -1,50 +1,50 @@
package com.ycwl.basic.repository; package com.ycwl.basic.repository;
import com.ycwl.basic.utils.JacksonUtil; import com.ycwl.basic.utils.JacksonUtil;
import com.ycwl.basic.mapper.RenderWorkerMapper;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity; import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.task.req.ClientStatusReqVo; import com.ycwl.basic.model.task.req.ClientStatusReqVo;
import com.ycwl.basic.integration.render.dto.worker.RenderWorkerV2DTO;
import com.ycwl.basic.integration.render.service.RenderWorkerIntegrationService;
import com.ycwl.basic.integration.render.service.RenderWorkerConfigIntegrationService;
import com.ycwl.basic.integration.render.dto.config.RenderWorkerConfigV2DTO;
import com.ycwl.basic.integration.common.manager.RenderWorkerConfigManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Slf4j
@Component @Component
public class RenderWorkerRepository { public class RenderWorkerRepository {
@Autowired @Autowired
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
public static final String RENDER_WORKER_CACHE_KEY = "render_worker:%s";
public static final String RENDER_WORKER_STATUS_CACHE_KEY = "render_worker:host_status:%s"; public static final String RENDER_WORKER_STATUS_CACHE_KEY = "render_worker:host_status:%s";
@Autowired @Autowired
private RenderWorkerMapper mapper; private RenderWorkerIntegrationService renderWorkerIntegrationService;
@Autowired
private RenderWorkerConfigIntegrationService renderWorkerConfigIntegrationService;
public RenderWorkerEntity getWorkerByAccessKey(String accessKey) { public RenderWorkerEntity getWorkerByAccessKey(String accessKey) {
String key = String.format(RENDER_WORKER_CACHE_KEY, accessKey); RenderWorkerV2DTO workerDTO = renderWorkerIntegrationService.getWorkerByKey(accessKey);
if (redisTemplate.hasKey(key)) { if (workerDTO == null) {
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(key), RenderWorkerEntity.class); return null;
}
RenderWorkerEntity renderWorker = mapper.findByAccessKey(accessKey);
if (renderWorker != null) {
redisTemplate.opsForValue().set(key, JacksonUtil.toJSONString(renderWorker), 1, TimeUnit.HOURS);
redisTemplate.opsForValue().set(String.format(RENDER_WORKER_CACHE_KEY, renderWorker.getId()), JacksonUtil.toJSONString(renderWorker), 1, TimeUnit.HOURS);
} }
RenderWorkerEntity renderWorker = convertToEntity(workerDTO);
return renderWorker; return renderWorker;
} }
public RenderWorkerEntity getWorker(Long id) { public RenderWorkerEntity getWorker(Long id) {
String key = String.format(RENDER_WORKER_CACHE_KEY, id); RenderWorkerV2DTO workerDTO = renderWorkerIntegrationService.getWorker(id);
if (redisTemplate.hasKey(key)) { if (workerDTO == null) {
return JacksonUtil.parseObject(redisTemplate.opsForValue().get(key), RenderWorkerEntity.class); return null;
}
RenderWorkerEntity renderWorker = mapper.getById(id);
if (renderWorker != null) {
redisTemplate.opsForValue().set(key, JacksonUtil.toJSONString(renderWorker), 1, TimeUnit.HOURS);
redisTemplate.opsForValue().set(String.format(RENDER_WORKER_CACHE_KEY, renderWorker.getAccessKey()), JacksonUtil.toJSONString(renderWorker), 1, TimeUnit.HOURS);
} }
RenderWorkerEntity renderWorker = convertToEntity(workerDTO);
return renderWorker; return renderWorker;
} }
@@ -54,21 +54,6 @@ public class RenderWorkerRepository {
return; return;
} }
status.setUpdateAt(new Date()); status.setUpdateAt(new Date());
RenderWorkerEntity worker = new RenderWorkerEntity();
worker.setCpuCount(status.getCpu_count());
worker.setCpuUsage(status.getCpu_usage());
// 上报的是字节,存储的是兆
worker.setMemoryAvailable(status.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(status.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(status.getPlatform());
worker.setRuntimeVersion(status.getRuntime_version());
worker.setSupportFeature(String.join(",", status.getSupport_feature()));
worker.setVersion(status.getVersion());
worker.setUpdateAt(status.getUpdateAt());
if (!redisTemplate.hasKey(key)) {
mapper.updateHost(id, worker);
}
redisTemplate.opsForValue().set(key, JacksonUtil.toJSONString(status), 1, TimeUnit.HOURS); redisTemplate.opsForValue().set(key, JacksonUtil.toJSONString(status), 1, TimeUnit.HOURS);
} }
@@ -81,12 +66,34 @@ public class RenderWorkerRepository {
return null; return null;
} }
public void clearCache(Long id) { private RenderWorkerEntity convertToEntity(RenderWorkerV2DTO dto) {
RenderWorkerEntity worker = getWorker(id); if (dto == null) {
redisTemplate.delete(String.format(RENDER_WORKER_CACHE_KEY, id)); return null;
if (worker != null) {
redisTemplate.delete(String.format(RENDER_WORKER_CACHE_KEY, worker.getAccessKey()));
} }
redisTemplate.delete(String.format(RENDER_WORKER_STATUS_CACHE_KEY, id)); RenderWorkerEntity entity = new RenderWorkerEntity();
entity.setId(dto.getId());
entity.setName(dto.getName());
entity.setAccessKey(dto.getKey());
entity.setStatus(dto.getIsActive());
entity.setCreateAt(dto.getCreateTime());
entity.setUpdateAt(dto.getUpdateTime());
return entity;
} }
/**
* 获取渲染工作器配置管理器
*
* @param workerId 工作器ID
* @return RenderWorkerConfigManager实例,如果获取失败返回null
*/
public RenderWorkerConfigManager getWorkerConfigManager(Long workerId) {
try {
List<RenderWorkerConfigV2DTO> configList = renderWorkerConfigIntegrationService.getWorkerConfigs(workerId);
return new RenderWorkerConfigManager(configList);
} catch (Exception e) {
log.error("获取渲染工作器配置管理器失败, workerId: {}", workerId, e);
return null;
}
}
} }

View File

@@ -3,7 +3,7 @@ package com.ycwl.basic.repository;
import com.ycwl.basic.facebody.enums.FaceBodyAdapterType; import com.ycwl.basic.facebody.enums.FaceBodyAdapterType;
import com.ycwl.basic.integration.common.util.ConfigValueUtil; import com.ycwl.basic.integration.common.util.ConfigValueUtil;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2ListResponse; import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2WithConfigDTO;
import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService; import com.ycwl.basic.integration.scenic.service.ScenicIntegrationService;
import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService; import com.ycwl.basic.integration.scenic.service.ScenicConfigIntegrationService;
@@ -212,7 +212,7 @@ public class ScenicRepository {
String name = scenicReqQuery.getName(); String name = scenicReqQuery.getName();
// 调用 zt-scenic 服务的 list 方法 // 调用 zt-scenic 服务的 list 方法
ScenicV2ListResponse response = scenicIntegrationService.listScenics(page, pageSize, status, name); PageResponse<ScenicV2DTO> response = scenicIntegrationService.listScenics(page, pageSize, status, name);
// 将 ScenicV2DTO 列表转换为 ScenicEntity 列表 // 将 ScenicV2DTO 列表转换为 ScenicEntity 列表
if (response != null && response.getList() != null) { if (response != null && response.getList() != null) {

View File

@@ -5,7 +5,8 @@ import com.github.pagehelper.PageInfo;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager; import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO; import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.integration.device.service.DeviceIntegrationService; 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 com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.mapper.ExtraDeviceMapper; import com.ycwl.basic.mapper.ExtraDeviceMapper;
import com.ycwl.basic.mapper.ScenicAccountMapper; import com.ycwl.basic.mapper.ScenicAccountMapper;
import com.ycwl.basic.model.jwt.JwtInfo; import com.ycwl.basic.model.jwt.JwtInfo;
@@ -86,7 +87,7 @@ public class AppScenicServiceImpl implements AppScenicService {
public ApiResponse<ScenicDeviceCountVO> deviceCountByScenicId(Long scenicId) { public ApiResponse<ScenicDeviceCountVO> deviceCountByScenicId(Long scenicId) {
JwtInfo worker = JwtTokenUtil.getWorker(); JwtInfo worker = JwtTokenUtil.getWorker();
// 通过zt-device服务获取设备统计 // 通过zt-device服务获取设备统计
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.getScenicActiveDevices(scenicId, 1, 1000); PageResponse<DeviceV2DTO> deviceListResponse = deviceIntegrationService.getScenicActiveDevices(scenicId, 1, 1000);
ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO(); ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO();
if (deviceListResponse != null && deviceListResponse.getList() != null) { if (deviceListResponse != null && deviceListResponse.getList() != null) {
scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size()); scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size());
@@ -120,7 +121,7 @@ public class AppScenicServiceImpl implements AppScenicService {
} }
// 通过zt-device服务获取设备统计 // 通过zt-device服务获取设备统计
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.getScenicActiveDevices(id, 1, 1000); PageResponse<DeviceV2DTO> deviceListResponse = deviceIntegrationService.getScenicActiveDevices(id, 1, 1000);
ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO(); ScenicDeviceCountVO scenicDeviceCountVO = new ScenicDeviceCountVO();
if (deviceListResponse != null && deviceListResponse.getList() != null) { if (deviceListResponse != null && deviceListResponse.getList() != null) {
scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size()); scenicDeviceCountVO.setTotalDeviceCount(deviceListResponse.getList().size());
@@ -276,7 +277,7 @@ public class AppScenicServiceImpl implements AppScenicService {
@Override @Override
public ApiResponse<List<DeviceRespVO>> getDevices(Long scenicId) { public ApiResponse<List<DeviceRespVO>> getDevices(Long scenicId) {
DeviceV2ListResponse deviceV2ListResponse = deviceIntegrationService.listDevices(1, 1000, null, null, null, 1, scenicId); PageResponse<DeviceV2DTO> deviceV2ListResponse = deviceIntegrationService.listDevices(1, 1000, null, null, null, 1, scenicId);
List<DeviceRespVO> deviceRespVOList = deviceV2ListResponse.getList().stream().map(device -> { List<DeviceRespVO> deviceRespVOList = deviceV2ListResponse.getList().stream().map(device -> {
DeviceRespVO deviceRespVO = new DeviceRespVO(); DeviceRespVO deviceRespVO = new DeviceRespVO();
deviceRespVO.setId(device.getId()); deviceRespVO.setId(device.getId());

View File

@@ -1,22 +0,0 @@
package com.ycwl.basic.service.pc;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.renderWorker.req.RenderWorkerReqQuery;
import com.ycwl.basic.utils.ApiResponse;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/3 15:07
*/
public interface RenderWorkerService {
ApiResponse<PageInfo<RenderWorkerEntity>> pageQuery(RenderWorkerReqQuery renderWorkerReqQuery);
ApiResponse<List<RenderWorkerEntity>> list(RenderWorkerReqQuery renderWorkerReqQuery);
ApiResponse<RenderWorkerEntity> detail(Long id);
ApiResponse<Integer> add(RenderWorkerEntity renderWorker);
ApiResponse<Integer> deleteById(Long id);
ApiResponse<Integer> update(RenderWorkerEntity renderWorker);
ApiResponse<Integer> updateStatus(Long id);
}

View File

@@ -1,139 +0,0 @@
package com.ycwl.basic.service.pc.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.mapper.RenderWorkerMapper;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.renderWorker.req.RenderWorkerReqQuery;
import com.ycwl.basic.model.task.req.ClientStatusReqVo;
import com.ycwl.basic.repository.RenderWorkerRepository;
import com.ycwl.basic.service.pc.RenderWorkerService;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
* @Author:longbinbin
* @Date:2024/12/3 15:09
*/
@Service
public class RenderWorkerServiceImpl implements RenderWorkerService {
@Autowired
private RenderWorkerMapper renderWorkerMapper;
@Autowired
private RenderWorkerRepository renderWorkerRepository;
@Override
public ApiResponse<PageInfo<RenderWorkerEntity>> pageQuery(RenderWorkerReqQuery renderWorkerReqQuery) {
PageHelper.startPage(renderWorkerReqQuery.getPageNum(), renderWorkerReqQuery.getPageSize());
List<RenderWorkerEntity> list = renderWorkerMapper.list(renderWorkerReqQuery);
list.forEach(worker -> {
ClientStatusReqVo clientStatus = renderWorkerRepository.getWorkerHostStatus(worker.getId());
if (clientStatus == null) {
return;
}
worker.setCpuCount(clientStatus.getCpu_count());
worker.setCpuUsage(clientStatus.getCpu_usage());
// 上报的是字节,存储的是兆
worker.setMemoryAvailable(clientStatus.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(clientStatus.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(clientStatus.getPlatform());
worker.setRuntimeVersion(clientStatus.getRuntime_version());
worker.setSupportFeature(String.join(",", clientStatus.getSupport_feature()));
worker.setVersion(clientStatus.getVersion());
worker.setUpdateAt(clientStatus.getUpdateAt());
});
PageInfo<RenderWorkerEntity> pageInfo = new PageInfo<>(list);
return ApiResponse.success(pageInfo);
}
@Override
public ApiResponse<List<RenderWorkerEntity>> list(RenderWorkerReqQuery renderWorkerReqQuery) {
List<RenderWorkerEntity> list = renderWorkerMapper.list(renderWorkerReqQuery);
list.forEach(worker -> {
ClientStatusReqVo clientStatus = renderWorkerRepository.getWorkerHostStatus(worker.getId());
if (clientStatus == null) {
return;
}
worker.setCpuCount(clientStatus.getCpu_count());
worker.setCpuUsage(clientStatus.getCpu_usage());
// 上报的是字节,存储的是兆
worker.setMemoryAvailable(clientStatus.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(clientStatus.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(clientStatus.getPlatform());
worker.setRuntimeVersion(clientStatus.getRuntime_version());
worker.setSupportFeature(String.join(",", clientStatus.getSupport_feature()));
worker.setVersion(clientStatus.getVersion());
worker.setUpdateAt(clientStatus.getUpdateAt());
});
return ApiResponse.success(list);
}
@Override
public ApiResponse<RenderWorkerEntity> detail(Long id) {
RenderWorkerEntity worker = renderWorkerMapper.getById(id);
ClientStatusReqVo clientStatus = renderWorkerRepository.getWorkerHostStatus(worker.getId());
if (clientStatus != null) {
worker.setCpuCount(clientStatus.getCpu_count());
worker.setCpuUsage(clientStatus.getCpu_usage());
// 上报的是字节,存储的是兆
worker.setMemoryAvailable(clientStatus.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(clientStatus.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(clientStatus.getPlatform());
worker.setRuntimeVersion(clientStatus.getRuntime_version());
worker.setSupportFeature(String.join(",", clientStatus.getSupport_feature()));
worker.setVersion(clientStatus.getVersion());
worker.setUpdateAt(clientStatus.getUpdateAt());
}
return ApiResponse.success(worker);
}
@Override
public ApiResponse<Integer> add(RenderWorkerEntity renderWorker) {
renderWorker.setId(SnowFlakeUtil.getLongId());
if (StringUtils.isEmpty(renderWorker.getAccessKey())) {
renderWorker.setAccessKey(SnowFlakeUtil.getId());
}
renderWorker.setStatus(0);
int add = renderWorkerMapper.add(renderWorker);
if (add == 0) {
return ApiResponse.fail("渲染机添加失败");
}else {
return ApiResponse.success(add);
}
}
@Override
public ApiResponse<Integer> deleteById(Long id) {
renderWorkerRepository.clearCache(id);
return ApiResponse.success(renderWorkerMapper.deleteById(id));
}
@Override
public ApiResponse<Integer> update(RenderWorkerEntity renderWorker) {
renderWorkerRepository.clearCache(renderWorker.getId());
int update = renderWorkerMapper.update(renderWorker);
if (update == 0) {
return ApiResponse.fail("渲染机修改失败");
}else {
return ApiResponse.success(update);
}
}
@Override
public ApiResponse<Integer> updateStatus(Long id) {
renderWorkerRepository.clearCache(id);
return ApiResponse.success(renderWorkerMapper.updateStatus(id));
}
}

View File

@@ -2,7 +2,8 @@ package com.ycwl.basic.service.task.impl;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.MD5; import cn.hutool.crypto.digest.MD5;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager; import com.ycwl.basic.integration.common.manager.RenderWorkerConfigManager;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.utils.JacksonUtil; import com.ycwl.basic.utils.JacksonUtil;
import com.ycwl.basic.biz.OrderBiz; import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.biz.TaskStatusBiz; import com.ycwl.basic.biz.TaskStatusBiz;
@@ -12,7 +13,6 @@ import com.ycwl.basic.constant.TaskConstant;
import com.ycwl.basic.mapper.FaceMapper; import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.mapper.FaceSampleMapper; import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.MemberMapper; import com.ycwl.basic.mapper.MemberMapper;
import com.ycwl.basic.mapper.RenderWorkerMapper;
import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.mapper.TaskMapper; import com.ycwl.basic.mapper.TaskMapper;
import com.ycwl.basic.mapper.TemplateMapper; import com.ycwl.basic.mapper.TemplateMapper;
@@ -83,6 +83,8 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
public class TaskTaskServiceImpl implements TaskService { public class TaskTaskServiceImpl implements TaskService {
private static final String WORKER_SELF_HOSTED_CACHE_KEY = "worker_self_hosted_scenic:%s";
private static final int CACHE_EXPIRE_MINUTES = 3;
@Autowired @Autowired
private TaskMapper taskMapper; private TaskMapper taskMapper;
@Autowired @Autowired
@@ -121,13 +123,40 @@ public class TaskTaskServiceImpl implements TaskService {
private DeviceRepository deviceRepository; private DeviceRepository deviceRepository;
@Autowired @Autowired
private VideoReUploader videoReUploader; private VideoReUploader videoReUploader;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) { private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) {
String accessKey = req.getAccessKey(); String accessKey = req.getAccessKey();
if (accessKey == null) { if (accessKey == null) {
return null; return null;
} }
return repository.getWorkerByAccessKey(accessKey); RenderWorkerEntity worker = repository.getWorkerByAccessKey(accessKey);
if (worker == null) {
return null;
}
if (worker.getStatus() != 1) {
return null;
}
return worker;
}
private boolean isWorkerSelfHostedScenic(Long scenicId) {
String cacheKey = String.format(WORKER_SELF_HOSTED_CACHE_KEY, scenicId);
String cachedValue = redisTemplate.opsForValue().get(cacheKey);
if (cachedValue != null) {
return Boolean.parseBoolean(cachedValue);
}
// 缓存中没有,查询配置
ScenicConfigManager config = scenicRepository.getScenicConfigManager(scenicId);
boolean workerSelfHostedScenic = Boolean.TRUE.equals(config.getBoolean("worker_self_hosted"));
// 缓存结果,设置30分钟过期
redisTemplate.opsForValue().set(cacheKey, String.valueOf(workerSelfHostedScenic), CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
return workerSelfHostedScenic;
} }
@Override @Override
@@ -136,7 +165,6 @@ public class TaskTaskServiceImpl implements TaskService {
if (worker == null) { if (worker == null) {
return null; return null;
} }
worker.setOnline(1);
worker.setName(null); worker.setName(null);
worker.setStatus(null); worker.setStatus(null);
// get status // get status
@@ -162,21 +190,25 @@ public class TaskTaskServiceImpl implements TaskService {
} else { } else {
updTemplateList = templateRepository.getAllEnabledTemplateList(); updTemplateList = templateRepository.getAllEnabledTemplateList();
} }
RenderWorkerConfigManager configManager = repository.getWorkerConfigManager(worker.getId());
try { try {
if (lock.tryLock(2, TimeUnit.SECONDS)) { if (lock.tryLock(2, TimeUnit.SECONDS)) {
try { try {
List<TaskRespVO> taskList; List<TaskRespVO> taskList;
if (worker.getScenicOnly() != null) { if (Strings.isNotBlank(configManager.getString("scenic_only"))) {
taskList = taskMapper.selectNotRunningByScenicList(worker.getScenicOnly()); taskList = taskMapper.selectNotRunningByScenicList(configManager.getString("scenic_only"));
} else { } else {
taskList = taskMapper.selectNotRunning(); var _taskList = taskMapper.selectNotRunning();
taskList = _taskList.stream().filter(task -> {
boolean workerSelfHostedScenic = isWorkerSelfHostedScenic(task.getScenicId());
return !workerSelfHostedScenic;
}).limit(1).toList();
} }
resp.setTasks(taskList); resp.setTasks(taskList);
resp.setTemplates(updTemplateList); resp.setTemplates(updTemplateList);
taskList.forEach(task -> { taskList.forEach(task -> {
taskMapper.assignToWorker(task.getId(), worker.getId()); taskMapper.assignToWorker(task.getId(), worker.getId());
videoTaskRepository.clearTaskCache(task.getId()); videoTaskRepository.clearTaskCache(task.getId());
repository.clearCache(worker.getId());
}); });
} finally { } finally {
lock.unlock(); lock.unlock();
@@ -551,10 +583,11 @@ public class TaskTaskServiceImpl implements TaskService {
if (worker == null) { if (worker == null) {
return null; return null;
} }
RenderWorkerConfigManager config = repository.getWorkerConfigManager(worker.getId());
IStorageAdapter adapter; IStorageAdapter adapter;
try { try {
adapter = StorageFactory.get(worker.getStoreType()); adapter = StorageFactory.get(config.getString("store_type"));
adapter.loadConfig(JacksonUtil.parseObject(worker.getStoreConfigJson(), Map.class)); adapter.loadConfig(config.getObject("store_config_json", Map.class));
} catch (Exception e) { } catch (Exception e) {
adapter = scenicService.getScenicStorageAdapter(task.getScenicId()); adapter = scenicService.getScenicStorageAdapter(task.getScenicId());
} }

View File

@@ -5,7 +5,7 @@ import com.ycwl.basic.device.DeviceFactory;
import com.ycwl.basic.device.operator.IDeviceStorageOperator; import com.ycwl.basic.device.operator.IDeviceStorageOperator;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager; import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.integration.device.service.DeviceIntegrationService; import com.ycwl.basic.integration.device.service.DeviceIntegrationService;
import com.ycwl.basic.integration.device.dto.device.DeviceV2ListResponse; import com.ycwl.basic.integration.common.response.PageResponse;
import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO; import com.ycwl.basic.integration.device.dto.device.DeviceV2DTO;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
@@ -37,9 +37,8 @@ public class VideoPieceCleaner {
public void clean() { public void clean() {
log.info("开始删除视频文件"); log.info("开始删除视频文件");
// 通过zt-device服务获取所有激活设备 // 通过zt-device服务获取所有激活设备
DeviceV2ListResponse deviceListResponse = deviceIntegrationService.listDevices(1, 10000, null, null, null, 1, null); PageResponse<DeviceV2DTO> deviceListResponse = deviceIntegrationService.listDevices(1, 10000, null, null, null, 1, null);
List<DeviceEntity> deviceList; if (deviceListResponse == null || deviceListResponse.getList() == null) {
if (deviceListResponse == null) {
return; return;
} }
for (DeviceV2DTO device : deviceListResponse.getList()) { for (DeviceV2DTO device : deviceListResponse.getList()) {

View File

@@ -1,142 +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.RenderWorkerMapper">
<insert id="add">
insert into render_worker(id, `name`, platform, runtime_version, version, access_key,
cpu_count, cpu_usage, memory_total, memory_available, support_feature, scenic_only, test_only, `online`, `status`)
VALUES (#{id}, #{name}, #{platform}, #{runtimeVersion}, #{version}, #{accessKey},
#{cpuCount}, #{cpuUsage}, #{memoryTotal}, #{memoryAvailable}, #{supportFeature}, #{scenicOnly}, #{testOnly}, #{online}, #{status})
</insert>
<update id="update">
update render_worker
<set>
<if test="name!= null and name!= ''">
name = #{name},
</if>
<if test="platform!= null and platform!= ''">
platform = #{platform},
</if>
<if test="runtimeVersion!= null and runtimeVersion!= ''">
runtime_version = #{runtimeVersion},
</if>
<if test="version!= null and version!= ''">
version = #{version},
</if>
<if test="accessKey!= null and accessKey!= ''">
access_key = #{accessKey},
</if>
<if test="cpuCount!= null">
cpu_count = #{cpuCount},
</if>
<if test="cpuUsage!= null">
cpu_usage = #{cpuUsage},
</if>
<if test="memoryTotal!= null">
memory_total = #{memoryTotal},
</if>
<if test="memoryAvailable!= null">
memory_available = #{memoryAvailable},
</if>
<if test="supportFeature!= null">
support_feature = #{supportFeature},
</if>
<if test="scenicOnly!= null">
scenic_only = #{scenicOnly},
</if>
<if test="testOnly!= null">
test_only = #{testOnly},
</if>
<if test="online!= null">
`online` = #{online},
</if>
</set>
where id = #{id}
</update>
<update id="updateStatus">
update render_worker
set status = (CASE
status
WHEN 1 THEN
0
WHEN 0 THEN
1
ELSE null
END)
where id = #{id}
</update>
<update id="updateHost">
update render_worker
set platform = #{status.platform},
runtime_version = #{status.runtimeVersion},
version = #{status.version},
cpu_count = #{status.cpuCount},
cpu_usage = #{status.cpuUsage},
memory_total = #{status.memoryTotal},
memory_available = #{status.memoryAvailable},
support_feature = #{status.supportFeature},
update_at = #{status.updateAt}
where id = #{id}
</update>
<delete id="deleteById">
delete from render_worker where id = #{id}
</delete>
<select id="list" resultType="com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity">
select id, `name`, platform, runtime_version, version, access_key,
cpu_count, cpu_usage, memory_total, memory_available, support_feature, scenic_only, test_only, `online`, `status`, create_at, update_at
from render_worker
<where>
<if test="name!= null and name!= ''">
and `name` like concat('%', #{name}, '%')
</if>
<if test="platform!= null and platform!= ''">
and platform like concat('%', #{platform}, '%')
</if>
<if test="runtimeVersion!= null and runtimeVersion!= ''">
and runtime_version like concat('%', #{runtimeVersion}, '%')
</if>
<if test="version!= null and version!= ''">
and version like concat('%', #{version}, '%')
</if>
<if test="cpuCount!= null">
and cpu_count = #{cpuCount}
</if>
<if test="cpuUsage!= null">
and cpu_usage = #{cpuUsage}
</if>
<if test="supportFeature!= null">
and support_feature like concat('%',#{supportFeature},'%')
</if>
<if test="scenicOnly!= null">
and scenic_only = #{scenicOnly}
</if>
<if test="testOnly!= null">
and test_only = #{testOnly}
</if>
<if test="online!= null">
and `online` = #{online}
</if>
<if test="status!= null">
and `status` = #{status}
</if>
<if test="startTime!=null">
and create_at >= #{startTime}
</if>
<if test="endTime!=null">
and create_at &lt;= #{endTime}
</if>
</where>
</select>
<select id="getById" resultType="com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity">
select id, `name`, platform, runtime_version, version, access_key,
cpu_count, cpu_usage, memory_total, memory_available, support_feature, scenic_only, test_only, `online`, `status`, create_at, update_at
from render_worker
where id = #{id}
</select>
<select id="findByAccessKey" resultType="com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity">
select id, `name`, scenic_only, test_only, `online`, `status`, create_at, update_at, store_type, store_config_json
from render_worker
where access_key = #{accessKey} and status = 1
</select>
</mapper>

View File

@@ -84,14 +84,6 @@
from task from task
where status = 0 where status = 0
and worker_id is null and worker_id is null
and NOT EXISTS (
SELECT 1
FROM render_worker rw
WHERE
rw.status = 1
AND FIND_IN_SET(task.scenic_id, rw.scenic_only) > 0 -- 检查scenic_id是否在逗号分隔的字符串中
)
limit 1
</select> </select>
<select id="selectAllNotRunning" resultType="com.ycwl.basic.model.pc.task.entity.TaskEntity"> <select id="selectAllNotRunning" resultType="com.ycwl.basic.model.pc.task.entity.TaskEntity">
select * select *