- 新增SmbFileService服务类,支持SMB/CIFS协议的文件操作 - 修改合同文件管理逻辑,支持SMB路径检查与目录创建 - 优化BankTableCell实现工厂模式并更新相关文档 - 调整Redis配置并添加连接测试 - 修复合同发票视图模型的时间处理问题 - 更新项目版本至0.0.134-SNAPSHOT
223 lines
8.3 KiB
Markdown
223 lines
8.3 KiB
Markdown
# 客户端 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);
|
||
// 窗口显示后的逻辑
|
||
}
|
||
}
|
||
```
|
||
|
||
## 13. TableCell 工厂模式规范
|
||
|
||
### 13.1 工厂方法命名
|
||
|
||
- TableCell类应提供静态工厂方法`forTableColumn`,用于创建单元格工厂
|
||
- 方法命名必须为`forTableColumn`,保持一致性
|
||
|
||
### 13.2 工厂方法参数
|
||
|
||
- 工厂方法应接收服务层参数,如`BankService`
|
||
- 参数类型应与TableCell构造函数所需的服务类型一致
|
||
|
||
### 13.3 工厂方法返回类型
|
||
|
||
- 返回类型应为`Callback<javafx.scene.control.TableColumn<V, T>, javafx.scene.control.TableCell<V, T>>`
|
||
- 其中`V`为ViewModel类型,`T`为单元格值类型
|
||
|
||
### 13.4 工厂方法实现
|
||
|
||
- 在工厂方法内部,创建并返回TableCell实例
|
||
- 使用泛型参数确保类型安全
|
||
|
||
### 13.5 使用方式
|
||
|
||
- 在设置表格列的单元格工厂时,应调用TableCell的静态工厂方法
|
||
- 避免直接使用`new TableCell<>(service)`的方式创建实例
|
||
|
||
### 13.6 示例代码
|
||
|
||
```java
|
||
/**
|
||
* 银行单元格
|
||
*/
|
||
@NoArgsConstructor
|
||
public class BankTableCell<T> extends AsyncUpdateTableCell<T, Integer, BankVo> {
|
||
/**
|
||
* 创建单元格工厂
|
||
*
|
||
* @param bankService 银行服务
|
||
* @return 单元格工厂
|
||
*/
|
||
public static <V> Callback<javafx.scene.control.TableColumn<V, Integer>, javafx.scene.control.TableCell<V, Integer>> forTableColumn(
|
||
BankService bankService) {
|
||
return param -> new BankTableCell<V>(bankService);
|
||
}
|
||
|
||
public BankTableCell(BankService service) {
|
||
setService(service);
|
||
}
|
||
|
||
@Override
|
||
protected BankService getServiceBean() {
|
||
return getBean(BankService.class);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 13.7 使用示例
|
||
|
||
```java
|
||
// 推荐方式:使用工厂方法
|
||
column.setCellFactory(BankTableCell.forTableColumn(getBankService()));
|
||
|
||
// 不推荐方式:直接实例化
|
||
// column.setCellFactory(param -> new BankTableCell<>(getBankService())); |