refactor(model): 重构模型类包结构并优化序列化处理
重构模型类包结构,将模型类按功能模块划分到不同的子包中。优化序列化处理,为VO类添加serialVersionUID并实现Serializable接口。移除部分冗余的serialVersionUID字段,简化模型类代码。同时修复UITools中空值处理的问题,并更新pom版本至0.0.100-SNAPSHOT。 - 将模型类按功能模块划分到ds子包中 - 为VO类添加序列化支持 - 移除冗余的serialVersionUID字段 - 修复UITools空值处理问题 - 更新项目版本号
This commit is contained in:
157
.trae/rules/client_controller_rules.md
Normal file
157
.trae/rules/client_controller_rules.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# 客户端 Controller 类规则
|
||||
|
||||
## 1. 目录结构
|
||||
|
||||
客户端控制器位于`client/src/main/java/com/ecep/contract/controller/`目录下,按业务模块组织:
|
||||
|
||||
- **根目录**:包含基础控制器、抽象控制器和主窗口控制器
|
||||
- **业务子包**:按业务领域组织,如`company/`、`project/`、`contract/`、`vendor/`等
|
||||
- **tab/子包**:包含所有Tab皮肤控制器和相关接口、抽象类
|
||||
- **table/子包**:包含表格相关控制器和单元格实现
|
||||
|
||||
## 2. 命名规范
|
||||
|
||||
- **基础控制器**:`BaseController.java`
|
||||
- **抽象控制器**:以`Abst`开头,如`AbstEntityController.java`、`AbstManagerWindowController.java`
|
||||
- **窗口控制器**:以`WindowController`结尾,如`HomeWindowController.java`
|
||||
- **管理窗口控制器**:以`ManagerWindowController`结尾,如`CompanyManagerWindowController.java`
|
||||
- **Tab皮肤控制器**:以`TabSkin`结尾,如`CompanyTabSkinBase.java`
|
||||
- **管理器皮肤**:以`ManagerSkin`结尾,如`CompanyManagerSkin.java`
|
||||
- **组件控制器**:如`OkHttpLoginController.java`
|
||||
|
||||
## 3. 继承关系
|
||||
|
||||
控制器类遵循以下继承层次结构:
|
||||
|
||||
- `BaseController`:所有控制器的基础类,提供窗口显示、异常处理等通用功能
|
||||
- `AbstEntityBasedController`:基于实体的控制器抽象类
|
||||
- `AbstManagerWindowController`:管理窗口控制器的抽象类,包含搜索、分页等功能
|
||||
- `AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>>`:实体详情窗口控制器的抽象类
|
||||
- 具体业务窗口控制器,如`CompanyWindowController`、`ProjectWindowController`等
|
||||
|
||||
Tab相关控制器继承关系:
|
||||
|
||||
- `Skin`:皮肤接口基础
|
||||
- `TabSkin`:Tab皮肤接口
|
||||
- `AbstGenericTabSkin<C extends BaseController>`:通用Tab皮肤抽象类
|
||||
- `AbstEntityBasedTabSkin<C extends AbstEntityController<T, V>, T extends IdentityEntity, V extends IdentityViewModel<T>>`:基于实体的Tab皮肤抽象类
|
||||
- 具体业务Tab皮肤控制器,如`CompanyTabSkinBase.java`、`ContractTabSkinFiles.java`等
|
||||
|
||||
## 4. 注解使用
|
||||
|
||||
客户端控制器类应使用以下注解:
|
||||
|
||||
- `@Component`:声明为Spring组件,使其可被Spring容器管理
|
||||
- `@Scope("prototype")`:设置为原型作用域,确保每次请求创建新实例
|
||||
- `@Lazy`:延迟加载,提高应用启动性能
|
||||
- `@FxmlPath("/ui/[业务模块]/[文件名].fxml")`:指定对应的FXML文件路径
|
||||
- `@Autowired`:自动注入依赖的服务层组件
|
||||
|
||||
## 5. FXML文件规范
|
||||
|
||||
- **文件位置**:FXML文件通常位于`/client/src/main/resources/ui/`目录下,按业务模块组织子目录
|
||||
- **命名规范**:使用小写字母和下划线,如`home.fxml`、`company/company.fxml`
|
||||
- **关联方式**:通过`@FxmlPath`注解指定控制器对应的FXML文件路径
|
||||
- **组件ID**:FXML文件中组件的id应与控制器中对应的字段名保持一致(驼峰命名法)
|
||||
|
||||
## 6. 控制器方法规范
|
||||
|
||||
### 6.1 初始化方法
|
||||
|
||||
- `initialize()`:控制器初始化方法,用于设置UI组件初始状态、绑定事件监听器等
|
||||
- 此方法会在FXML加载完成后自动调用
|
||||
|
||||
### 6.2 窗口生命周期方法
|
||||
|
||||
- `onShown(WindowEvent windowEvent)`:窗口显示时触发的方法
|
||||
- 用于执行窗口显示后的初始化操作,如数据加载、组件状态更新等
|
||||
- 通常需要调用`super.onShown(windowEvent)`确保父类逻辑执行
|
||||
|
||||
### 6.3 窗口显示方法
|
||||
|
||||
- `public static void show()`:静态方法,用于便捷地显示控制器对应的窗口
|
||||
- 通常有多个重载版本,支持不同参数(如viewModel、owner窗口等)
|
||||
- 内部调用`BaseController.show()`方法实现窗口显示逻辑
|
||||
|
||||
### 6.4 Tab相关方法
|
||||
|
||||
Tab皮肤控制器包含以下核心方法:
|
||||
|
||||
- `install()`:安装Tab皮肤,加载FXML并初始化UI组件
|
||||
- `initializeUIComponents()`:初始化UI组件,设置数据绑定和事件监听
|
||||
- `getTab()`:获取对应的Tab对象
|
||||
- `save()`:保存Tab中的数据变更
|
||||
- `dispose()`:释放资源,清理监听器等
|
||||
|
||||
## 7. 字段规范
|
||||
|
||||
- **UI组件字段**:与FXML文件中的组件id对应,使用`public`访问修饰符
|
||||
- **服务依赖字段**:使用`@Autowired`注解注入,通常使用`private`访问修饰符
|
||||
- **ViewModel字段**:通常作为控制器的核心数据模型,提供`getter/setter`方法
|
||||
- **命名规范**:使用驼峰命名法,如`nameField`、`tabPane`、`saveBtn`等
|
||||
|
||||
## 8. 数据绑定与更新
|
||||
|
||||
- **ViewModel绑定**:控制器通常通过ViewModel与实体数据进行绑定
|
||||
- **异步数据加载**:使用`CompletableFuture`进行异步数据加载,避免阻塞UI线程
|
||||
- **UI更新**:在JavaFX应用线程中更新UI组件,可使用`Platform.runLater()`确保线程安全
|
||||
|
||||
## 9. 事件处理
|
||||
|
||||
- **按钮点击事件**:通过`setOnAction()`方法绑定事件处理器
|
||||
- **表单字段变更**:可通过JavaFX的属性绑定机制监听字段变更
|
||||
- **Tab切换事件**:实现`onTabShown()`方法处理Tab显示事件
|
||||
|
||||
## 10. 异常处理
|
||||
|
||||
- 使用`handleException()`方法统一处理异常
|
||||
- 异常信息通常会显示在状态栏并记录日志
|
||||
- 异步操作中的异常应通过`exceptionally()`或`handle()`方法捕获处理
|
||||
|
||||
## 11. 最佳实践
|
||||
|
||||
- **职责分离**:控制器应专注于UI交互和数据展示,业务逻辑应委托给服务层
|
||||
- **避免重复代码**:利用抽象类和接口提取通用逻辑
|
||||
- **资源管理**:及时释放资源,避免内存泄漏
|
||||
- **线程安全**:确保在JavaFX应用线程中更新UI
|
||||
- **代码可读性**:添加适当的注释,遵循命名规范
|
||||
|
||||
## 12. 示例代码结构
|
||||
|
||||
```java
|
||||
@Lazy
|
||||
@Scope("prototype")
|
||||
@Component
|
||||
@FxmlPath("/ui/[模块名]/[文件名].fxml")
|
||||
public class [业务]WindowController extends AbstEntityController<[Vo类型], [ViewModel类型]> {
|
||||
private static final Logger logger = LoggerFactory.getLogger([业务]WindowController.class);
|
||||
|
||||
@Autowired
|
||||
private [业务]Service [业务]Service;
|
||||
|
||||
// UI组件字段
|
||||
public BorderPane root;
|
||||
public TabPane tabPane;
|
||||
public TextField nameField;
|
||||
// ...其他UI组件
|
||||
|
||||
public static void show([Vo类型] entity, Window window) {
|
||||
show([ViewModel类型].from(entity), window);
|
||||
}
|
||||
|
||||
public static void show([ViewModel类型] viewModel, Window window) {
|
||||
BaseController.show([业务]WindowController.class, viewModel, window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// 初始化UI组件和事件监听
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShown(WindowEvent windowEvent) {
|
||||
super.onShown(windowEvent);
|
||||
// 窗口显示后的逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
239
.trae/rules/client_converter_rules.md
Normal file
239
.trae/rules/client_converter_rules.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# 客户端转换器类规则
|
||||
|
||||
## 1. 目录结构
|
||||
- 所有类型转换器类必须位于 `com.ecep.contract.converter` 包下
|
||||
- 按照实体类型组织,无需进一步分包
|
||||
|
||||
## 2. 命名规范
|
||||
- 类名格式:`[EntityName]StringConverter`
|
||||
- 例如:`ContractStringConverter`、`CompanyStringConverter`、`VendorStringConverter`
|
||||
|
||||
## 3. 继承关系
|
||||
### 3.1 基础转换器类型
|
||||
- 直接继承 `javafx.util.StringConverter<T>` 接口
|
||||
- 适用于简单的转换场景
|
||||
- 例如:
|
||||
```java
|
||||
public class ContractStringConverter extends StringConverter<ContractVo> {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 增强转换器类型
|
||||
- 继承自定义的 `EntityStringConverter<T>` 抽象类
|
||||
- 适用于需要更多功能(如延迟初始化、搜索建议等)的场景
|
||||
- 大多数业务实体转换器应使用此类型
|
||||
- 例如:
|
||||
```java
|
||||
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 枚举转换器类型
|
||||
- 继承 `EnumEntityStringConverter<E extends Enum<?>, T extends BaseEnumEntity<E>>`
|
||||
- 专门用于处理枚举类型实体
|
||||
- 例如:
|
||||
```java
|
||||
public class EnumEntityStringConverter<E extends Enum<?>, T extends BaseEnumEntity<E>> extends StringConverter<T> {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 核心接口实现
|
||||
### 4.1 StringConverter<T> 接口
|
||||
- 所有转换器必须实现 `toString(T object)` 方法
|
||||
- 所有转换器必须实现 `fromString(String string)` 方法
|
||||
|
||||
## 5. Spring框架集成
|
||||
### 5.1 组件注解
|
||||
- 增强转换器类型通常使用 `@Component` 注解标记为Spring组件
|
||||
- 结合 `@Lazy` 注解实现延迟加载
|
||||
- 例如:
|
||||
```java
|
||||
@Lazy
|
||||
@Component
|
||||
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 依赖注入
|
||||
- 使用 `@Autowired` 注解注入对应的Service实例
|
||||
- 通常也需要为注入的Service添加 `@Lazy` 注解
|
||||
- 例如:
|
||||
```java
|
||||
@Lazy
|
||||
@Autowired
|
||||
private CompanyService service;
|
||||
```
|
||||
|
||||
## 6. 初始化机制
|
||||
### 6.1 构造函数
|
||||
- 基础转换器通常使用构造函数接收Service实例
|
||||
- 例如:
|
||||
```java
|
||||
public ContractStringConverter(ContractService service) {
|
||||
this.service = service;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 初始化方法
|
||||
- 增强转换器使用 `@PostConstruct` 注解标记初始化方法
|
||||
- 在初始化方法中配置转换器的各种功能
|
||||
- 例如:
|
||||
```java
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
setInitialized(project -> service.findById(project.getId()));
|
||||
setSuggestion(service::search);
|
||||
setFromString(service::findByName);
|
||||
}
|
||||
```
|
||||
|
||||
## 7. EntityStringConverter 特性配置
|
||||
继承自 EntityStringConverter 的增强转换器可以配置以下特性:
|
||||
|
||||
### 7.1 对象初始化
|
||||
- 使用 `setInitialized(Function<T, T> initialized)` 配置对象延迟初始化逻辑
|
||||
- 通常调用Service的findById方法
|
||||
- 例如:`setInitialized(project -> service.findById(project.getId()))`
|
||||
|
||||
### 7.2 搜索建议
|
||||
- 使用 `setSuggestion(Function<String, List<T>> suggestion)` 配置自动补全数据提供方法
|
||||
- 通常调用Service的search方法
|
||||
- 例如:`setSuggestion(service::search)`
|
||||
|
||||
### 7.3 字符串转换
|
||||
- 使用 `setFromString(Function<String, T> fromString)` 配置从字符串到对象的转换逻辑
|
||||
- 通常调用Service的findByName方法
|
||||
- 例如:`setFromString(service::findByName)`
|
||||
|
||||
### 7.4 格式化器
|
||||
- 使用 `setFormater(Function<T, String> formater)` 配置自定义格式化逻辑
|
||||
- 用于自定义对象的字符串表示
|
||||
- 例如:`setFormater(contract -> contract.getCode() + " " + contract.getName())`
|
||||
|
||||
## 8. 核心方法实现规范
|
||||
### 8.1 toString(T object) 方法
|
||||
- 将对象转换为可读的字符串表示
|
||||
- 必须处理null情况
|
||||
- 通常返回对象的名称、编码或其他关键标识
|
||||
- 对于基础转换器:
|
||||
```java
|
||||
@Override
|
||||
public String toString(ContractVo cc) {
|
||||
return cc.getCode() + " " + cc.getName();
|
||||
}
|
||||
```
|
||||
- 对于枚举转换器:
|
||||
```java
|
||||
@Override
|
||||
public String toString(T object) {
|
||||
return object == null ? "" : object.getValue();
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 fromString(String string) 方法
|
||||
- 从字符串还原对象实例
|
||||
- 通常调用Service的findByName或findByCode方法
|
||||
- 例如:
|
||||
```java
|
||||
@Override
|
||||
public ContractVo fromString(String string) {
|
||||
return service.findByCode(string);
|
||||
}
|
||||
```
|
||||
- 对于某些场景(如枚举转换器),可能返回null
|
||||
|
||||
## 9. 与Service类的关联
|
||||
### 9.1 Service中的转换器创建
|
||||
- 在Service类中通常会创建并持有对应的StringConverter实例
|
||||
- 实现 `getStringConverter()` 方法返回转换器实例
|
||||
- 例如:
|
||||
```java
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "contract-cache")
|
||||
public class ContractService extends QueryService<ContractVo, ContractViewModel> {
|
||||
|
||||
private final ContractStringConverter stringConverter = new ContractStringConverter(this);
|
||||
|
||||
@Override
|
||||
public StringConverter<ContractVo> getStringConverter() {
|
||||
return stringConverter;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Service方法支持
|
||||
- Service需提供 `findByName`、`findById`、`search` 等方法供转换器使用
|
||||
- 这些方法通常需要添加缓存注解以提高性能
|
||||
|
||||
## 10. 使用场景
|
||||
StringConverter主要用于以下场景:
|
||||
- ComboBox、ChoiceBox等选择组件的数据绑定和显示
|
||||
- 表单数据的绑定和转换
|
||||
- 支持用户输入的自动补全功能
|
||||
- 在表格、列表等UI组件中显示对象的可读表示
|
||||
|
||||
## 11. 最佳实践
|
||||
- 对于大多数业务实体,优先使用继承 `EntityStringConverter<T>` 的增强转换器
|
||||
- 使用Spring的依赖注入和组件管理功能
|
||||
- 正确处理null值和空字符串
|
||||
- 为频繁调用的Service方法添加缓存支持
|
||||
- 实现合理的toString逻辑,提供有意义的对象表示
|
||||
|
||||
## 12. 示例代码结构
|
||||
### 12.1 基础转换器示例
|
||||
```java
|
||||
package com.ecep.contract.converter;
|
||||
|
||||
import com.ecep.contract.service.ContractService;
|
||||
import com.ecep.contract.vo.ContractVo;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class ContractStringConverter extends StringConverter<ContractVo> {
|
||||
private final ContractService service;
|
||||
|
||||
public ContractStringConverter(ContractService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(ContractVo cc) {
|
||||
return cc == null ? "" : cc.getCode() + " " + cc.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContractVo fromString(String string) {
|
||||
return string == null || string.isEmpty() ? null : service.findByCode(string);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 12.2 增强转换器示例
|
||||
```java
|
||||
package com.ecep.contract.converter;
|
||||
|
||||
import com.ecep.contract.service.CompanyService;
|
||||
import com.ecep.contract.vo.CompanyVo;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
public class CompanyStringConverter extends EntityStringConverter<CompanyVo> {
|
||||
@Lazy
|
||||
@Autowired
|
||||
private CompanyService service;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
setInitialized(company -> service.findById(company.getId()));
|
||||
setSuggestion(service::search);
|
||||
setFromString(service::findByName);
|
||||
}
|
||||
}
|
||||
```
|
||||
146
.trae/rules/client_service_rules.md
Normal file
146
.trae/rules/client_service_rules.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 客户端 Service 类规则
|
||||
|
||||
## 1. 目录结构
|
||||
- 所有客户端 Service 类位于 `client/src/main/java/com/ecep/contract/service/` 目录下
|
||||
- 按业务领域组织,直接放置在 service 包下,不进行子包划分
|
||||
- 服务类命名与实体类一一对应
|
||||
|
||||
## 2. 命名规范
|
||||
- 服务类命名格式为:`[实体名称]Service.java`
|
||||
- 例如:`CompanyService.java`、`ContractService.java`、`ProjectService.java`
|
||||
- 基础服务接口命名为:`IEntityService.java`、`ViewModelService.java`
|
||||
- 泛型基础服务类命名为:`QueryService.java`
|
||||
|
||||
## 3. 继承关系
|
||||
- 业务服务类通常继承自泛型基础服务类 `QueryService<T, TV>`
|
||||
- `T` 表示 VO 类型(实现了 IdentityEntity 接口)
|
||||
- `TV` 表示 ViewModel 类型(实现了 IdentityViewModel<T> 接口)
|
||||
- `QueryService` 实现了 `ViewModelService<T, TV>` 接口
|
||||
- `ViewModelService` 继承了 `IEntityService<T>` 接口
|
||||
- 特定场景下可以不继承 `QueryService`,直接实现所需接口或创建独立服务类
|
||||
|
||||
```java
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "company")
|
||||
public class CompanyService extends QueryService<CompanyVo, CompanyViewModel> {
|
||||
// 业务方法实现
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 注解使用
|
||||
- **@Service**:标记为 Spring 服务组件,使其可被自动发现和注入
|
||||
- **@CacheConfig**:配置缓存名称,通常与服务类名对应
|
||||
- **@Cacheable**:标记方法结果可缓存,需指定缓存键(key)
|
||||
- **@CacheEvict**:标记方法执行后清除缓存,可指定缓存键或清除所有
|
||||
- **@Caching**:组合多个缓存操作(如同时清除多个缓存条目)
|
||||
- **@Autowired**:用于自动注入依赖的其他服务
|
||||
|
||||
```java
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "contract")
|
||||
public class ContractService extends QueryService<ContractVo, ContractViewModel> {
|
||||
@Autowired
|
||||
private SysConfService confService;
|
||||
|
||||
@Cacheable(key = "#p0")
|
||||
public ContractVo findById(Integer id) {
|
||||
return super.findById(id);
|
||||
}
|
||||
|
||||
@Caching(evict = {
|
||||
@CacheEvict(key = "#p0.id"),
|
||||
@CacheEvict(key = "'code-'+#p0.code")
|
||||
})
|
||||
public ContractVo save(ContractVo contract) {
|
||||
return super.save(contract);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 缓存机制
|
||||
- 每个服务类应有独立的缓存名称空间
|
||||
- 缓存键(key)应具有唯一性,通常使用 ID、代码或名称等唯一标识
|
||||
- 保存和删除操作时应清除相关缓存,保持数据一致性
|
||||
- 可使用 SpEL 表达式动态生成缓存键
|
||||
- 频繁查询的数据应考虑缓存,提高性能
|
||||
|
||||
## 6. 异步调用机制
|
||||
- 使用 `async()` 方法进行异步远程调用
|
||||
- 方法参数通常包括:方法名、参数值、参数类型列表
|
||||
- 使用 `CompletableFuture` 处理异步结果
|
||||
- 使用 `handle()` 方法处理响应和异常
|
||||
- 远程调用异常应包装为 RuntimeException 并提供详细错误信息
|
||||
|
||||
```java
|
||||
@Cacheable(key = "'code-'+#p0")
|
||||
public ContractVo findByCode(String code) {
|
||||
try {
|
||||
return async("findByCode", code, String.class).handle((response, ex) -> {
|
||||
if (ex != null) {
|
||||
throw new RuntimeException("远程方法+findByCode+调用失败", ex);
|
||||
}
|
||||
if (response != null) {
|
||||
return updateValue(createNewEntity(), response);
|
||||
}
|
||||
return null;
|
||||
}).get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("查询失败: " + code, e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 基础方法实现
|
||||
- 应实现 `IEntityService<T>` 接口定义的核心方法:
|
||||
- `findById(Integer id)`:根据 ID 查询实体
|
||||
- `save(T entity)`:保存实体
|
||||
- `delete(T entity)`:删除实体
|
||||
- `findAll()`:查询所有实体
|
||||
- `findAll(Map<String, Object> params, Pageable pageable)`:条件分页查询
|
||||
- `getStringConverter()`:获取类型转换器
|
||||
- 通常通过继承 `QueryService` 来复用这些基础方法的实现
|
||||
- 可根据业务需求重写或扩展基础方法
|
||||
|
||||
## 8. 业务方法规范
|
||||
- 业务方法应与服务端对应,保持方法名和参数一致
|
||||
- 方法命名应清晰表达其功能,如 `findByName`, `findByCode`
|
||||
- 复杂业务逻辑应封装为独立方法
|
||||
- 参数校验应在方法开始处进行
|
||||
- 返回值类型应明确,避免使用过于泛化的类型
|
||||
|
||||
## 9. 类型转换器
|
||||
- 实现 `getStringConverter()` 方法,返回对应的 StringConverter 实例
|
||||
- 通常创建专用的 Converter 类,如 `CustomerCatalogStringConverter`
|
||||
- 转换器实例应作为服务类的成员变量,避免重复创建
|
||||
|
||||
```java
|
||||
public class CustomerCatalogService extends QueryService<CustomerCatalogVo, CustomerCatalogViewModel> {
|
||||
private final CustomerCatalogStringConverter stringConverter = new CustomerCatalogStringConverter(this);
|
||||
|
||||
@Override
|
||||
public StringConverter<CustomerCatalogVo> getStringConverter() {
|
||||
return stringConverter;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 错误处理
|
||||
- 远程调用异常应捕获并包装为更具描述性的 RuntimeException
|
||||
- 提供详细的错误信息,包括调用的方法名和参数
|
||||
- 对于可预期的业务异常,可添加专门的处理逻辑
|
||||
- 不推荐使用 printStackTrace(),应使用日志记录异常
|
||||
|
||||
## 11. 工具方法和辅助功能
|
||||
- 通用功能可封装为工具方法
|
||||
- 配置相关操作可通过 `SysConfService` 实现
|
||||
- 文件路径处理应使用 `File` 类和相关工具方法
|
||||
- 日期时间处理应使用 Java 8+ 的日期时间 API
|
||||
|
||||
## 12. 最佳实践
|
||||
- 遵循单一职责原则,每个服务类专注于一个业务领域
|
||||
- 优先使用继承和接口实现来复用代码
|
||||
- 合理使用缓存提高性能,但注意缓存一致性
|
||||
- 异步调用应正确处理异常和超时情况
|
||||
- 服务类之间的依赖应通过 `@Autowired` 注入,避免硬编码
|
||||
- 方法实现应简洁明了,复杂逻辑应拆分
|
||||
- 为重要方法添加 JavaDoc 注释,说明其功能和参数含义
|
||||
0
.trae/rules/client_task_rules.md
Normal file
0
.trae/rules/client_task_rules.md
Normal file
69
.trae/rules/entity_rules.md
Normal file
69
.trae/rules/entity_rules.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 实体类规则
|
||||
|
||||
## 1. 目录结构
|
||||
- 实体类统一放置在 `common/src/main/java/com/ecep/contract/model/` 目录下
|
||||
- 按业务领域组织,不使用子目录
|
||||
|
||||
## 2. 命名规范
|
||||
- 类名:使用驼峰命名法,首字母大写,如 `Contract.java`、`Company.java`
|
||||
- 表名:通常与类名对应,使用大写字母和下划线,如 `CONTRACT`、`COMPANY`
|
||||
- 字段名:使用小写字母和驼峰命名法,对应数据库中使用大写字母和下划线的列名
|
||||
|
||||
## 3. 继承与接口实现
|
||||
- 实体类通常实现以下接口:
|
||||
- `IdentityEntity`:提供主键ID的getter/setter及equals方法实现
|
||||
- `BasedEntity`:提供toPrettyString()方法
|
||||
- `NamedEntity`:提供name属性的getter/setter(适用于有名称的实体)
|
||||
- `Voable<T>`:提供toVo()方法,用于将实体转换为对应的VO对象
|
||||
- 特定领域接口:如 `CompanyBasedEntity`、`ContractBasedEntity`、`ProjectBasedEntity` 等
|
||||
|
||||
## 4. 注解规范
|
||||
- **JPA注解**:
|
||||
- `@Entity`:标记为实体类
|
||||
- `@Table(name = "表名", schema = "数据库名")`:指定对应的数据库表和数据库
|
||||
- `@Id`:标记主键字段
|
||||
- `@GeneratedValue(strategy = GenerationType.IDENTITY)`:指定主键生成策略
|
||||
- `@Column(name = "列名", nullable = false, length = 长度)`:指定字段对应的数据库列属性
|
||||
- `@ManyToOne(fetch = FetchType.LAZY)`:指定多对一关系,通常使用LAZY加载
|
||||
- `@JoinColumn(name = "外键列名")`:指定外键列
|
||||
- `@Enumerated(EnumType.ORDINAL)`:指定枚举类型的存储方式
|
||||
- `@Lob`:指定大对象类型
|
||||
- `@Version`:指定乐观锁版本字段
|
||||
- **Lombok注解**:
|
||||
- `@Getter`:生成getter方法
|
||||
- `@Setter`:生成setter方法
|
||||
- `@ToString`:生成toString方法,复杂关联对象可使用`@ToString.Exclude`排除
|
||||
|
||||
## 5. 字段规范
|
||||
- 主键字段:
|
||||
- 类型:`Integer`
|
||||
- 命名:`id`
|
||||
- 注解:`@Id`、`@GeneratedValue(strategy = GenerationType.IDENTITY)`
|
||||
- 其他字段:
|
||||
- 基本类型:使用Java包装类型(如`Integer`、`Boolean`)而不是原始类型
|
||||
- 时间类型:使用`java.time.LocalDate`或`java.time.LocalDateTime`
|
||||
- 布尔类型:使用`boolean`或`Boolean`,对应数据库中的`BIT`或`BOOLEAN`类型
|
||||
- 关联关系:使用`@ManyToOne`、`@OneToMany`等注解定义
|
||||
|
||||
## 6. 方法规范
|
||||
- `equals`方法:通常使用`HibernateProxyUtils`工具类处理代理对象的比较
|
||||
- `hashCode`方法:通常使用`HibernateProxyUtils.hashCode(this)`实现
|
||||
- `toVo`方法:实现`Voable`接口,将实体转换为对应的VO对象
|
||||
- `toString`方法:通常使用Lombok的`@ToString`注解生成
|
||||
|
||||
## 7. 注释规范
|
||||
- 类注释:使用JavaDoc格式,描述实体类的用途
|
||||
- 字段注释:使用JavaDoc格式,描述字段的含义、数据来源、取值范围等
|
||||
- 对于特殊字段,可包含详细的说明表格或示例
|
||||
|
||||
## 8. 序列化规范
|
||||
- **重要**:实体类**不包含**`Serializable`接口和`serialVersionUID`字段
|
||||
- 序列化相关功能由对应的VO类实现
|
||||
|
||||
## 9. 最佳实践
|
||||
- 实体类应保持简洁,只包含数据和基本的数据访问方法
|
||||
- 业务逻辑应在服务层实现
|
||||
- 复杂的查询逻辑应在Repository层或通过Specification实现
|
||||
- 避免在实体类中使用复杂的计算逻辑
|
||||
- 对于多对多关系,建议使用中间表和两个一对多关系实现
|
||||
- 关联关系应使用懒加载(LAZY)以提高性能
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contract-Manager 项目规则
|
||||
# 项目规则
|
||||
|
||||
## 技术栈规范
|
||||
|
||||
@@ -39,10 +39,61 @@
|
||||
- SQL文件:表名使用大写和下划线,如 `CONTRACT_TYPE_LOCAL.sql`
|
||||
|
||||
## 目录结构规范
|
||||
- 源代码位于 `src/main/java` 目录
|
||||
- 资源文件位于 `src/main/resources` 目录
|
||||
- 测试代码位于 `src/test` 目录
|
||||
- 数据库脚本位于 `docs/db` 目录
|
||||
### 项目整体结构
|
||||
- `client/`: 客户端模块,基于JavaFX实现的桌面应用
|
||||
- `server/`: 服务端模块,基于Spring Boot实现的后端服务
|
||||
- `common/`: 公共模块,包含客户端和服务端共享的代码
|
||||
- `docs/`: 项目文档和数据库脚本目录
|
||||
- `.trae/`: Trae IDE相关配置和规则
|
||||
- `.mvn/`: Maven包装器配置
|
||||
- 根目录下包含Maven构建文件和配置文件
|
||||
|
||||
### server 模块目录结构
|
||||
- `src/main/java/com/ecep/contract/`: 主包路径
|
||||
- `api/`: API接口定义
|
||||
- `cloud/`: 云服务集成相关代码
|
||||
- `config/`: Spring Boot配置类
|
||||
- `controller/`: Web控制器
|
||||
- `ds/`: 数据访问层,按业务领域组织
|
||||
- `company/`: 公司相关业务
|
||||
- `contract/`: 合同相关业务
|
||||
- `customer/`: 客户相关业务
|
||||
- `project/`: 项目相关业务
|
||||
- `vendor/`: 供应商相关业务
|
||||
- 每个业务领域包含:`model/`(实体类)、`repository/`(数据访问接口)、`service/`(业务逻辑)、`vo/`(视图对象)
|
||||
- `handler/`: WebSocket处理器
|
||||
- `service/`: 服务层,包含一些通用服务和任务处理器
|
||||
- `ui/`: UI相关组件
|
||||
- `util/`: 工具类
|
||||
- 核心服务接口和基类文件直接位于contract包下
|
||||
- `src/main/resources/`: 资源文件目录
|
||||
- `src/test/`: 测试代码目录
|
||||
|
||||
### client 模块目录结构
|
||||
- `src/main/java/com/ecep/contract/`: 主包路径
|
||||
- `controller/`: JavaFX控制器,按业务领域组织, 详细规范见 .trae\rules\client_controller_rules.md
|
||||
- 包含各种业务窗口控制器和Tab皮肤控制器
|
||||
- `converter/`: 类型转换器,详细规范见 .trae\rules\client_converter_rules.md
|
||||
- `serializer/`: 序列化相关类
|
||||
- `service/`: 客户端服务层,与服务端交互, 详细规范见 .trae\rules\client_service_rules.md
|
||||
- `task/`: 客户端任务类, 详细规范见 .trae\rules\client_task_rules.md
|
||||
- `util/`: 工具类
|
||||
- `vm/`: 视图模型
|
||||
- `src/main/resources/ui/`: FXML界面文件目录
|
||||
- `src/test/`: 测试代码目录
|
||||
|
||||
### common 模块目录结构
|
||||
- `src/main/java/ecep/contract/`: 包含客户端和服务端共享的代码
|
||||
- `constant/`: 常量类,按业务领域组织
|
||||
- `model/`: 实体类,按业务领域组织, 不包含Serializable接口和serialVersionUID字段, 详细规范见 .trae\rules\entity_rules.md
|
||||
- `vo/`: 视图对象,按业务领域组织, 包含Serializable接口和serialVersionUID字段, 详细规范见 .trae\rules\vo_rules.md
|
||||
- `util/`: 工具类
|
||||
|
||||
### 文档和资源目录
|
||||
- `docs/db/`: 数据库脚本文件
|
||||
- `docs/task/`: 任务相关文档和规范
|
||||
- `docs/model/`: 数据模型文档
|
||||
- 其他项目文档和资源
|
||||
|
||||
## 数据库规范
|
||||
- 表名使用大写字母和下划线,如 `COMPANY_VENDOR_FILE_TYPE_LOCAL`
|
||||
|
||||
58
.trae/rules/vo_rules.md
Normal file
58
.trae/rules/vo_rules.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# VO类规则
|
||||
|
||||
## 1. 目录结构
|
||||
- VO类统一放置在 `common/src/main/java/com/ecep/contract/vo/` 目录下
|
||||
- 按业务领域组织,不使用子目录
|
||||
|
||||
## 2. 命名规范
|
||||
- 类名:使用驼峰命名法,首字母大写,以Vo结尾,如 `ContractVo.java`、`CompanyVo.java`
|
||||
|
||||
## 3. 继承与接口实现
|
||||
- **必须实现** `java.io.Serializable` 接口,用于对象序列化
|
||||
- **通常实现** `IdentityEntity` 接口,提供主键ID的getter/setter方法
|
||||
- **可能实现** `NamedEntity` 接口,提供name属性的getter/setter方法(适用于有名称的VO)
|
||||
- **可能实现**特定领域接口,如:
|
||||
- `CompanyBasedVo`:提供companyId属性的getter/setter方法
|
||||
- `ProjectBasedVo`:提供project属性的getter/setter方法
|
||||
- `ContractBasedVo`:提供contractId属性的getter/setter方法
|
||||
- 其他类似的基于特定实体的接口
|
||||
|
||||
## 4. 注解规范
|
||||
- 通常使用Lombok的 `@Data` 注解,自动生成getter、setter、equals、hashCode和toString方法
|
||||
|
||||
## 5. 序列化规范
|
||||
- **必须包含** `private static final long serialVersionUID = 1L;` 字段,用于版本控制
|
||||
- 所有字段类型必须是可序列化的
|
||||
|
||||
## 6. 字段规范
|
||||
- 字段类型:
|
||||
- 整数类型:可使用 `Integer` 包装类型或 `int` 原始类型
|
||||
- 布尔类型:可使用 `Boolean` 包装类型或 `boolean` 原始类型
|
||||
- 时间类型:使用 `java.time.LocalDate`(日期)或 `java.time.LocalDateTime`(日期时间)
|
||||
- 字符串类型:使用 `String`
|
||||
- 浮点数类型:使用 `double` 或 `Double`
|
||||
- 关联关系:
|
||||
- 通常只包含关联实体的ID字段,如 `companyId`、`project`、`contractId` 等
|
||||
- 不包含实体对象的直接引用
|
||||
- 默认值:
|
||||
- 布尔类型字段通常有默认值(如 `false`)
|
||||
- 其他类型根据业务需求设置默认值
|
||||
|
||||
## 7. 注释规范
|
||||
- 类注释:使用JavaDoc格式,描述VO类的用途,如 `/** 项目报价视图对象 */`
|
||||
- 字段注释:使用JavaDoc格式,描述字段的含义、用途或业务规则
|
||||
- 关键字段(如外键、状态字段)应提供清晰的注释说明
|
||||
|
||||
## 8. 最佳实践
|
||||
- VO类应保持简洁,只包含数据和基本的数据访问方法
|
||||
- 避免在VO类中包含复杂的业务逻辑
|
||||
- 字段命名应与对应的实体类保持一致或有明确的映射关系
|
||||
- 确保所有字段都是可序列化的
|
||||
- 对于有默认值的字段,应在声明时初始化
|
||||
|
||||
## 9. 与实体类的区别
|
||||
- **序列化**:VO类必须实现 `Serializable` 接口并包含 `serialVersionUID` 字段,而实体类不包含
|
||||
- **用途**:VO类主要用于数据传输和展示,实体类主要用于持久化
|
||||
- **关联关系**:VO类通常只包含关联实体的ID,实体类包含实体对象的引用
|
||||
- **注解**:VO类主要使用Lombok注解,实体类使用JPA注解和Lombok注解
|
||||
- **方法**:VO类通常只有getter/setter方法,实体类可能包含更多业务相关方法
|
||||
Reference in New Issue
Block a user