# 客户端 Tasker 至 服务器端 Tasker 通信规则与逻辑 本文档总结了 Contract-Manager 项目中客户端 Tasker 与服务器端 Tasker 之间的通信规则、调用逻辑和实现模式,基于对以下文件的分析: - `d:\idea-workspace\Contract-Manager\server\src\main\java\com\ecep\contract\ds\customer\tasker\CompanyCustomerEvaluationFormUpdateTask.java` - `d:\idea-workspace\Contract-Manager\client\src\main\java\com\ecep\contract\controller\customer\CompanyCustomerEvaluationFormUpdateTask.java` - `d:\idea-workspace\Contract-Manager\client\src\main\java\com\ecep\contract\controller\customer\CustomerTabSkinFile.java` ## 1. 架构设计原则 项目采用了清晰的客户端-服务器分离架构,任务处理遵循以下原则: - **客户端轻量级**:负责任务发起、参数传递和结果展示 - **服务器端重量级**:负责实际业务逻辑处理和数据操作 - **WebSocket通信**:使用WebSocket实现客户端与服务器端的任务通信和进度同步 ## 2. 类命名与结构规范 ### 2.1 命名规则 - 客户端与服务器端的对应Tasker类**必须使用相同的类名**(如示例中的`CompanyCustomerEvaluationFormUpdateTask`) - 客户端Tasker位于`client`模块的控制器包下 - 服务器端Tasker位于`server`模块的tasker包下 ### 2.2 任务名称注册规则 - 所有服务器端Tasker类必须通过配置文件`tasker_mapper.json`进行注册,并由`WebSocketServerTaskManager`类在`afterPropertiesSet`方法中加载 - 注册格式为`"TaskClassName": "fully.qualified.ClassName"` - 注册的Task名称将用于客户端和服务器端之间的任务识别 - 配置文件`tasker_mapper.json`应位于`src/main/resources`目录下,格式如下: ```json { "taskers": { "TaskClassName": "fully.qualified.ClassName", // 更多任务映射... } } ``` #### 任务名称注册实现示例 ```java @Override public void afterPropertiesSet() throws Exception { // 从tasker_mapper.json文件中加载任务注册信息 try { Resource resource = resourceLoader.getResource("classpath:tasker_mapper.json"); try (InputStream inputStream = resource.getInputStream()) { JsonNode rootNode = objectMapper.readTree(inputStream); JsonNode taskersNode = rootNode.get("taskers"); if (taskersNode != null && taskersNode.isObject()) { Map taskMap = new java.util.HashMap<>(); taskersNode.fields().forEachRemaining(entry -> { taskMap.put(entry.getKey(), entry.getValue().asText()); }); taskClzMap = taskMap; } } } catch (Exception e) { logger.error("Failed to load tasker_mapper.json", e); // 使用默认值作为fallback taskClzMap = Map.of(); } }``` ### 2.3 接口实现区分 - 客户端Tasker实现`WebSocketClientTasker`接口 - 服务器端Tasker实现`WebSocketServerTasker`接口 ### 2.4 继承关系 - 客户端和服务器端Tasker均继承自`Tasker`基类 ## 3. 客户端Tasker实现规则 客户端Tasker是任务的发起方,需要遵循以下实现规则: ### 3.1 核心属性 - 通常包含一个可设置的业务对象(如示例中的`@Setter private CompanyCustomerVo customer;`) - 配置Logger日志记录器 ### 3.2 核心方法实现 - **getTaskName()**:返回任务名称,通常使用类名 - **updateProgress()**:继承或重写进度更新方法 - **execute()**:调用`callRemoteTask()`方法将任务发送到服务器端,传递必要参数 ### 3.3 示例实现 ```java public class CompanyCustomerEvaluationFormUpdateTask extends Tasker implements WebSocketClientTasker { private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class); @Setter private CompanyCustomerVo customer; // 业务对象 @Override public String getTaskName() { return getClass().getSimpleName(); } @Override protected Object execute(MessageHolder holder) throws Exception { updateTitle("客户评估表更新任务"); // 设置任务标题 // 调用远程任务,传递locale和业务对象ID return callRemoteTask(holder, getLocale(), customer.getId()); } } ``` ## 4. 服务器端Tasker实现规则 服务器端Tasker是任务的实际执行者,需要遵循以下实现规则: ### 4.1 参数接收 - 实现`init(JsonNode argsNode)`方法接收客户端传递的参数 - 从参数中提取业务对象ID并加载完整业务对象 ### 4.2 服务获取 - 通过`getCachedBean(Service.class)`方法获取所需的服务实例 - 可以提供辅助方法封装服务获取逻辑 ### 4.3 任务执行 - 实现`execute(MessageHolder holder)`方法包含实际业务逻辑 - 使用`holder.info()/error()`等方法记录任务执行状态 - 调用`updateProgress()`方法更新任务进度 ### 4.4 示例实现 ```java public class CompanyCustomerEvaluationFormUpdateTask extends Tasker implements WebSocketServerTasker { private CompanyCustomer customer; // 业务对象 @Override public void init(JsonNode argsNode) { // 从参数中提取业务对象ID并加载 int customerId = argsNode.get(0).asInt(); customer = getCachedBean(CompanyCustomerService.class).findById(customerId); } // 辅助方法获取服务 CompanyCustomerFileService getCompanyCustomerFileService() { return getCachedBean(CompanyCustomerFileService.class); } @Override protected Object execute(MessageHolder holder) throws Exception { // 执行实际业务逻辑 updateEvaluationForm(holder); return null; } // 具体业务逻辑实现 public void updateEvaluationForm(MessageHolder holder) { // 业务逻辑代码... updateProgress(1, 10); // 更新进度 // 更多业务逻辑... } } ``` ## 5. 客户端Tasker调用流程 在UI控制器中调用客户端Tasker的标准流程如下: ### 5.1 实例化与参数设置 1. 创建客户端Tasker实例 2. 设置必要的业务对象参数 ### 5.2 任务执行与监控 1. 使用`UITools.showTaskDialogAndWait()`显示任务对话框 2. 调用`initializeTask()`初始化任务执行环境 3. 通过消费者函数处理任务消息 ### 5.3 任务完成处理 1. 任务完成后执行必要的数据刷新操作 ### 5.4 示例调用 ```java public void onUpdateEvaluationFormAction(ActionEvent event) { // 1. 创建Tasker并设置参数 CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask(); task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get())); // 2. 显示任务对话框并执行任务 UITools.showTaskDialogAndWait("更新评价表", task, consumer -> { initializeTask(task, "更新评价表", msg -> consumer.accept(Message.info(msg))); }); // 3. 任务完成后刷新数据 loadTableDataSet(); } ``` ## 6. 任务进度管理 - 服务器端使用`updateProgress(current, total)`方法更新任务进度 - 进度值通常以0-1000或类似小范围数值表示完成百分比 - 客户端通过WebSocket接收进度更新并显示 ## 7. 异常处理机制 - 服务器端使用`MessageHolder.error()`方法记录错误信息 - 客户端通过任务对话框展示错误信息 - 服务器端在关键操作点进行异常捕获和处理 ## 8. 数据一致性保障 - 任务完成后客户端通常调用数据刷新方法(如`loadTableDataSet()`)确保UI显示最新数据 - 服务器端负责业务数据的持久化操作 ## 9. 最佳实践建议 1. **任务拆分**:复杂任务应拆分为多个小任务,便于进度跟踪和错误定位 2. **状态反馈**:在关键节点提供清晰的状态信息,增强用户体验 3. **资源释放**:确保文件流等资源正确关闭,避免资源泄露 4. **事务控制**:对于涉及多步数据操作的任务,考虑使用事务确保数据一致性 5. **错误重试**:针对网络波动等临时性问题,考虑实现任务重试机制 6. **配置管理**:使用`tasker_mapper.json`文件统一管理任务注册信息,避免在代码中硬编码任务映射 7. **异常处理**:确保实现配置文件加载失败的fallback机制,保证系统稳定性 8. **版本控制**:对任务配置文件进行版本控制,便于追踪变更历史 通过遵循以上规则和模式,可以确保Contract-Manager项目中客户端与服务器端Tasker通信的一致性、可靠性和可维护性。