feat(service): 实现国际化支持并优化Service层

重构文件类型相关Service以支持国际化查询
添加findOneByLang辅助方法统一查询逻辑
实现StringConverter支持UI控件显示
优化缓存配置和查询性能
新增UnitStringConverter和CustomerCatalogStringConverter
完善文档和测试用例
This commit is contained in:
2025-09-24 16:20:49 +08:00
parent 45eed8281f
commit 09b0da498b
32 changed files with 1968 additions and 78 deletions

View File

@@ -0,0 +1,220 @@
# TableCell 实现规则与模式指南
## 1. 目的与作用
TableCell 是 JavaFX TableView 组件中的关键元素,负责表格中单个单元格的渲染和交互。在 Contract-Manager 项目中TableCell 主要用于:
- 显示各种类型的数据(数字、日期、实体引用等)
- 提供自定义的显示格式和样式
- 支持异步加载关联数据
- 实现单元格内编辑功能
## 2. 文件结构与命名规范
- **文件位置**`client/src/main/java/com/ecep/contract/controller/table/cell/`
- **命名规范**:使用 PascalCase驼峰命名法首字母大写`TableCell` 结尾
- 示例:`CompanyTableCell.java``LocalDateFieldTableCell.java`
## 3. TableCell 分类与实现模式
### 3.1 基础 TableCell
直接继承 JavaFX 的 `TableCell` 类,适用于简单的数据显示。
**实现规则**
- 继承 `TableCell<S, T>`,其中 S 是表格行数据类型T 是单元格数据类型
- 实现 `updateItem(T item, boolean empty)` 方法
- 提供静态的 `forTableColumn()` 工厂方法
**示例**
```java
public class NumberTableCell<S> extends TableCell<S, Number> {
private final NumberStringConverter numberStringConverter;
public NumberTableCell(NumberStringConverter numberStringConverter) {
this.numberStringConverter = numberStringConverter;
}
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(numberStringConverter.toString(item));
}
}
public static <S> Callback<TableColumn<S, Number>, TableCell<S, Number>> forTableColumn(NumberStringConverter numberStringConverter) {
return param -> new NumberTableCell<>(numberStringConverter);
}
}
```
### 3.2 异步更新 TableCell
继承 `AsyncUpdateTableCell`,用于异步加载并显示关联的实体数据。
**实现规则**
- 继承 `AsyncUpdateTableCell<V, K, T>`,其中 V 是表格行数据类型K 是 ID 类型T 是实体类型
- 提供构造函数接收服务对象,或实现 `getServiceBean()` 方法从 Spring 容器获取服务
- 可选:重写 `initialize()` 方法自定义实体加载逻辑
- 可选:重写 `format()` 方法自定义实体显示格式
**示例**
```java
@NoArgsConstructor
public class CompanyTableCell<V> extends AsyncUpdateTableCell<V, Integer, CompanyVo> {
public CompanyTableCell(CompanyService companyService) {
setService(companyService);
}
@Override
protected CompanyService getServiceBean() {
return SpringApp.getBean(CompanyService.class);
}
}
```
### 3.3 可编辑 TableCell
实现单元格内编辑功能,如日期选择、文本编辑等。
**实现规则**
- 继承 `TableCell` 或其子类
- 实现 `startEdit()``cancelEdit()` 方法
-`updateItem()` 方法中处理编辑状态的显示
- 提供编辑控件和事件处理
**示例**
```java
public class LocalDateFieldTableCell<S> extends TableCell<S, LocalDate> {
private final DatePicker datePicker;
// 构造函数、converter 属性等
@Override
public void startEdit() {
if (isEmpty()) {
return;
}
super.startEdit();
setGraphic(datePicker);
datePicker.setConverter(getConverter());
datePicker.setValue(getItem());
datePicker.requestFocus();
}
@Override
public void cancelEdit() {
super.cancelEdit();
LocalDate item = getItem();
if (item == null) {
setText(null);
} else {
setText(getConverter().toString(item));
}
setGraphic(null);
}
@Override
protected void updateItem(LocalDate item, boolean empty) {
// 实现更新逻辑
}
}
```
## 4. AsyncUpdateTableCell 核心功能
`AsyncUpdateTableCell` 是项目中最常用的基类,提供以下核心功能:
### 4.1 异步加载机制
- 显示占位符(`#id`)直到数据加载完成
- 使用线程池执行异步任务
- 自动取消不再需要的异步任务
- 确保在 JavaFX 应用线程更新 UI
### 4.2 实体显示格式化
- 默认使用 `toString()` 方法格式化实体
-`NamedEntity``BasedEntity` 有特殊处理
- 允许子类重写 `format()` 方法自定义格式
### 4.3 服务获取方式
- 通过构造函数注入服务
- 或通过 `getServiceBean()` 方法从 Spring 容器获取
## 5. 特殊情况处理
### 5.1 非 Integer 类型 ID
当实体 ID 不是 Integer 类型时,需要重写 `initialize()` 方法:
```java
@Override
protected ContractFileTypeLocalVo initialize() {
ContractFileType item = getItem();
ContractFileTypeLocalVo localVo = getServiceBean().findByType(
Desktop.instance.getActiveEmployee().localeProperty().get(), item);
return localVo;
}
```
### 5.2 自定义显示格式
当需要自定义实体的显示格式时,重写 `format()` 方法:
```java
@Override
public String format(ContractFileTypeLocalVo entity) {
if (entity == null) {
return null;
}
return entity.getValue();
}
```
## 6. 工厂方法模式
所有 TableCell 类都应提供静态的 `forTableColumn()` 工厂方法,用于创建单元格工厂回调:
- 无参数版本:`public static <S> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn()`
- 带服务参数版本:`public static <S> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(ServiceType service)`
## 7. 代码规范
- 使用 Lombok 注解(如 `@NoArgsConstructor`)简化代码
- 添加适当的 JavaDoc 注释
- 遵循 JavaFX 最佳实践
- 处理空值和异常情况
- 确保线程安全UI 更新在 JavaFX 应用线程执行
## 8. 实现检查清单
创建新的 TableCell 时,请检查以下项目:
- [ ] 类名符合 `*TableCell` 命名规范
- [ ] 放置在正确的包路径下
- [ ] 选择合适的基类TableCell、AsyncUpdateTableCell
- [ ] 实现必要的构造函数
- [ ] 实现或重写 `updateItem()` 方法
- [ ] 提供静态的 `forTableColumn()` 工厂方法
- [ ] 对于异步更新类型,实现 `getServiceBean()` 或提供服务注入
- [ ] 添加适当的注释
- [ ] 处理空值和异常情况
## 9. 示例实现
### 9.1 简单数据类型 TableCell
参考 `NumberTableCell.java``LocalDateFieldTableCell.java`
### 9.2 实体引用 TableCell
参考 `CompanyTableCell.java``ContractTableCell.java`
### 9.3 特殊处理 TableCell
参考 `ContractFileTypeTableCell.java`