feat: 实现客户端与服务器端Tasker通信机制及文件管理功能

refactor: 重构Tasker基类与服务获取逻辑
fix: 修复文件路径显示问题及任务注册加载机制
docs: 添加客户端与服务器端Tasker通信规则文档
style: 优化代码格式与日志输出
build: 添加tasker_mapper.json配置文件
chore: 清理无用代码与文件
This commit is contained in:
2025-09-25 00:14:34 +08:00
parent dc764e6ed8
commit 2057c3ca67
28 changed files with 1014 additions and 446 deletions

View File

@@ -0,0 +1,215 @@
# 客户端 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<String, String> 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<Object>`基类
## 3. 客户端Tasker实现规则
客户端Tasker是任务的发起方需要遵循以下实现规则
### 3.1 核心属性
- 通常包含一个可设置的业务对象如示例中的`@Setter private CompanyCustomerVo customer;`
- 配置Logger日志记录器
### 3.2 核心方法实现
- **getTaskName()**返回任务名称通常使用类名
- **updateProgress()**继承或重写进度更新方法
- **execute()**调用`callRemoteTask()`方法将任务发送到服务器端传递必要参数
### 3.3 示例实现
```java
public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> 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<Object> 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通信的一致性、可靠性和可维护性。