# 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)` 提取结果