Files
contract-manager/docs/task/client_service_layer_rules.md
songqq bf90117116 docs: 添加客户端模块Service层规则文档
添加客户端模块Service层的架构设计、缓存管理、数据操作模式等详细规则文档,总结项目中的最佳实践和规范要求
2025-09-25 17:56:36 +08:00

150 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Contract-Manager 项目 客户端模块的 Service 层规则与逻辑总结
## 1. 基础架构
### 1.1 继承体系
- 所有业务Service都继承自泛型抽象类 `QueryService<T extends IdentityEntity, TV extends IdentityViewModel<T>>`
- `QueryService` 实现了 `IEntityService<T>``ViewModelService<T, TV>` 接口
- 泛型参数说明:
- `T`: 实体类型通常为Vo类
- `TV`: 视图模型类型
### 1.2 核心接口
`IEntityService<T>` 定义了基础的实体操作方法:
```java
public interface IEntityService<T> {
T findById(Integer id);
T save(T entity);
void delete(T entity);
List<T> findAll();
Page<T> findAll(Map<String, Object> params, Pageable pageable);
StringConverter<T> getStringConverter();
}
```
## 2. 缓存管理
### 2.1 缓存注解使用
- 使用 `@CacheConfig(cacheNames = "缓存名称")` 设置缓存名称
- 使用 `@Cacheable` 注解标记查询方法,如 `findById``findAll`
- 使用 `@CachePut` 注解标记更新方法,如 `save``delete`
- 使用 `@CacheEvict` 注解标记清除缓存的方法
- 使用 `@Caching` 组合多个缓存操作
### 2.2 缓存键规则
- `#p0`: 表示方法的第一个参数
- `'all'`: 表示所有数据的缓存键
- `'code-'+#p0`: 组合键,如 `'code-'+#p0.code` 用于按编码缓存
## 3. 数据操作模式
### 3.1 基础CRUD操作
所有Service通过继承`QueryService`获得以下基础操作:
- `findById(Integer id)`: 根据ID查询单条数据
- `save(T entity)`: 保存实体
- `delete(T entity)`: 删除实体
- `findAll()`: 查询所有数据
- `findAll(Map<String, Object> params, Pageable pageable)`: 带参数和分页的查询
### 3.2 异步操作
- 通过 `async(String method, Object... params)` 方法实现异步调用
- 返回 `CompletableFuture<JsonNode>` 对象
- 有专门的异步查询方法,如 `asyncFindById``asyncFindAll``asyncCount`
### 3.3 业务查询方法
业务Service通常会添加特定的查询方法
- `findByCode(String code)`: 按编码查询
- `findByName(String name)`: 按名称查询
- `findAllByName(String name)`: 按名称查询所有匹配项
### 3.4 参数构建
- 使用 `ParamUtils` 工具类构建查询参数
- 常用方法:`ParamUtils.builder().equals("字段名", 值).build()`
## 4. 国际化支持
### 4.1 多语言查询
- 所有继承自`BaseEnumEntity`的Vo类对应的Service都必须实现国际化支持
- 提供基于Locale的查询方法`findAll(Locale locale)`
- 实现方式:通过 `locale.toLanguageTag()` 转换为语言标签进行查询
- 返回结果通常转换为Map<EnumType, EntityType>,以枚举类型为键,方便按类型查找
- 方法上应添加`@Cacheable`注解进行缓存
### 4.2 辅助方法
- 必须实现`findOneByLang(Locale locale, String key, Object value)`私有辅助方法,根据语言和参数查找单个对象
- 实现方式:使用`ParamUtils.builder().equals("lang", locale.toLanguageTag()).equals(key, value).build()`构建参数
- 使用`Pageable.ofSize(1)`进行单条记录的精确查询
- 使用`stream().findFirst().orElse(null)`提取结果
- 必须实现`findByLocaleAndValue(Locale locale, String string)`方法,按语言和值查询
- 调用`findOneByLang(locale, "value", string)`实现
- 必须实现`findByLocaleAndType(Locale locale, EnumType type)`方法,按语言和枚举类型查询
- 调用`findOneByLang(locale, "type", type)`实现
## 5. 类型转换
### 5.1 StringConverter实现
- 所有Service都实现 `getStringConverter()` 方法,返回 `StringConverter<T>` 实例
- 通常会创建专门的Converter类`CompanyFileTypeStringConverter``CustomerFileTypeStringConverter`
- Converter类通常接收对应的Service作为构造参数
## 6. 业务特性
### 6.1 文件路径管理
- 部分Service管理文件路径`getBasePath()` 方法
- 通常通过 `SysConfService` 获取配置的基础路径
- 路径存储为 `File` 对象
### 6.2 业务验证
- 包含业务特定的验证逻辑,如 `verifyEnterpriseStatus``verify` 方法
- 使用 `MessageHolder` 存储验证结果和错误信息
### 6.3 Spring Bean获取
- 使用 `SpringApp.getBean(ServiceClass.class)` 获取其他Service实例
- 用于Service之间的协作调用
## 7. 代码规范
### 7.1 类命名
- 服务类名以 `Service` 结尾,如 `ContractService``CompanyService`
- 实现类直接使用业务名称+Service无I前缀
### 7.2 注释规范
- 方法和类应有清晰的Javadoc注释
- 私有辅助方法也应有注释说明其用途
### 7.3 异常处理
- 使用 `RuntimeException` 包装所有异常
- 异常消息应清晰描述错误情况
- 部分Service使用日志记录错误信息
## 8. 特定业务Service模式
### 8.1 实体类型Service
`CompanyFileTypeService``CompanyCustomerFileTypeService` 等:
- 管理枚举类型的本地化数据
- 提供缓存管理
- 实现StringConverter
- 提供基于Locale的查询方法
### 8.2 业务对象Service
`ContractService``CompanyService``ProjectService` 等:
- 管理核心业务实体
- 实现特定的业务逻辑
- 处理文件和路径
- 提供业务验证
### 8.3 关联关系管理
- Service之间通过依赖注入或 `SpringApp.getBean()` 进行协作
- 处理实体之间的关联关系,如 `Contract``Company``Vendor`
## 9. 特殊实现模式
### 9.1 自定义BeanName
部分Service重写 `getBeanName()` 方法指定WebSocket通信的Bean名称
### 9.2 未实现方法
使用 `throw new UnsupportedOperationException("Unimplemented method 'methodName'")` 标记未实现的方法
### 9.3 分页查询
- 大量使用 `Pageable.ofSize(1)` 进行单条记录的精确查询
- 使用 `stream().findFirst().orElse(null)` 提取结果