Files
contract-manager/docs/task/service_layer_rules.md
songqq 09b0da498b feat(service): 实现国际化支持并优化Service层
重构文件类型相关Service以支持国际化查询
添加findOneByLang辅助方法统一查询逻辑
实现StringConverter支持UI控件显示
优化缓存配置和查询性能
新增UnitStringConverter和CustomerCatalogStringConverter
完善文档和测试用例
2025-09-24 16:20:49 +08:00

5.6 KiB
Raw Blame History

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> 定义了基础的实体操作方法:

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 注解标记查询方法,如 findByIdfindAll
  • 使用 @CachePut 注解标记更新方法,如 savedelete
  • 使用 @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> 对象
  • 有专门的异步查询方法,如 asyncFindByIdasyncFindAllasyncCount

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类CompanyFileTypeStringConverterCustomerFileTypeStringConverter
  • Converter类通常接收对应的Service作为构造参数

6. 业务特性

6.1 文件路径管理

  • 部分Service管理文件路径getBasePath() 方法
  • 通常通过 SysConfService 获取配置的基础路径
  • 路径存储为 File 对象

6.2 业务验证

  • 包含业务特定的验证逻辑,如 verifyEnterpriseStatusverify 方法
  • 使用 MessageHolder 存储验证结果和错误信息

6.3 Spring Bean获取

  • 使用 SpringApp.getBean(ServiceClass.class) 获取其他Service实例
  • 用于Service之间的协作调用

7. 代码规范

7.1 类命名

  • 服务类名以 Service 结尾,如 ContractServiceCompanyService
  • 实现类直接使用业务名称+Service无I前缀

7.2 注释规范

  • 方法和类应有清晰的Javadoc注释
  • 私有辅助方法也应有注释说明其用途

7.3 异常处理

  • 使用 RuntimeException 包装所有异常
  • 异常消息应清晰描述错误情况
  • 部分Service使用日志记录错误信息

8. 特定业务Service模式

8.1 实体类型Service

CompanyFileTypeServiceCompanyCustomerFileTypeService 等:

  • 管理枚举类型的本地化数据
  • 提供缓存管理
  • 实现StringConverter
  • 提供基于Locale的查询方法

8.2 业务对象Service

ContractServiceCompanyServiceProjectService 等:

  • 管理核心业务实体
  • 实现特定的业务逻辑
  • 处理文件和路径
  • 提供业务验证

8.3 关联关系管理

  • Service之间通过依赖注入或 SpringApp.getBean() 进行协作
  • 处理实体之间的关联关系,如 ContractCompanyVendor

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