Compare commits
86 Commits
07c3f39a95
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 643338f4b0 | |||
| 880671a5a9 | |||
| 4e738bea3c | |||
| 3cf3a717be | |||
| e8c8305f40 | |||
| be63ff62a4 | |||
| 18057a657e | |||
| db07befffe | |||
| 94030a5a39 | |||
| 7e59f2c17e | |||
| c8b0d57f22 | |||
| 6eebdb1744 | |||
| 9dc90507cb | |||
| 0c26d329c6 | |||
| e3661630fe | |||
| 5d6fb961b6 | |||
| 72edb07798 | |||
| 330418cfd6 | |||
| c10bd369c0 | |||
| f0e85c5a18 | |||
| a784438e97 | |||
| 02afa189f8 | |||
| 87290f15b0 | |||
| e761990ebf | |||
| 1cb0edbd07 | |||
| dd49c3927a | |||
| 7d4961dae4 | |||
| 235269f86f | |||
| 0b45f6eef2 | |||
| 22ab2c7bdf | |||
| eea4d93ae1 | |||
| 71a358fa77 | |||
| cf0a7e18ea | |||
| d2e0dc4555 | |||
| 1c1ff678a5 | |||
| 5b3ab3ed00 | |||
| 59d78619da | |||
| 86e18632aa | |||
| ddd9dad945 | |||
| 333feb0d5a | |||
| 553feac0a4 | |||
| bda92193d4 | |||
| 0dcc9236a8 | |||
| e49952a63c | |||
| 35e8fba805 | |||
| c4eec0a9dd | |||
| 51b8c16798 | |||
| 49413ad473 | |||
| 64471b46f8 | |||
| 1f354853b0 | |||
| b03b5385a5 | |||
| df6188db40 | |||
| 510952d72e | |||
| a4db8a1644 | |||
| 42a8f9ab30 | |||
| 045a1e9eed | |||
| 97a2586c21 | |||
| 45f7b611c5 | |||
| bf90117116 | |||
| 928d10c681 | |||
| fa6920806d | |||
| c21269975d | |||
| ad42a49858 | |||
| 2057c3ca67 | |||
| dc764e6ed8 | |||
| 09b0da498b | |||
| 45eed8281f | |||
| 7b023fd07b | |||
| 9561ad99e6 | |||
| 9c3306eea3 | |||
| 71d3ecab52 | |||
| 5919636c04 | |||
| 57fbae90c5 | |||
| 4b8c1d4038 | |||
| 543311c676 | |||
| 73cbb4e19e | |||
| 515b255567 | |||
| 386b6d01b4 | |||
| 39dbce013f | |||
| b84e011857 | |||
| 866e08224a | |||
| 8aac509e51 | |||
| 35a15f4702 | |||
| 3c3003fdf3 | |||
| 35b33d401b | |||
| 039d753bab |
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PLAYWRIGHT_MCP_EXTENSION_TOKEN=TB7T39NhEbruyS7L-E7RXIGYk39PVK7eu1h-WP8M1Cg
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -36,3 +36,5 @@ build/
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
/config.properties
|
/config.properties
|
||||||
|
node_modules
|
||||||
|
node_modules
|
||||||
|
|||||||
223
.trae/rules/client_controller_rules.md
Normal file
223
.trae/rules/client_controller_rules.md
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
# 客户端 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()));
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
508
.trae/rules/client_service_rules.md
Normal file
508
.trae/rules/client_service_rules.md
Normal file
@@ -0,0 +1,508 @@
|
|||||||
|
# Client Service 实现编写指南
|
||||||
|
|
||||||
|
## 📋 概述
|
||||||
|
|
||||||
|
本指南总结 Client 模块 Service 层的实现经验,用于指导后续 Service 的编写。本指南基于 Contract-Manager 项目中已实现的 Service 模式整理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ 基础架构
|
||||||
|
|
||||||
|
### 1. 继承层次结构
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Service 接口定义
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础 Service 实现
|
||||||
|
public abstract class QueryService<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
||||||
|
implements ViewModelService<T, TV> {
|
||||||
|
// 核心实现
|
||||||
|
}
|
||||||
|
|
||||||
|
// 具体业务 Service
|
||||||
|
@Service
|
||||||
|
@CacheConfig(cacheNames = "business")
|
||||||
|
public class XxxService extends QueryService<XxxVo, XxxViewModel> {
|
||||||
|
// 业务特定实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 核心特性
|
||||||
|
|
||||||
|
- **泛型支持**:`QueryService` 使用泛型处理不同类型的 Vo 和 ViewModel
|
||||||
|
- **WebSocket 通信**:通过 `WebSocketClientService` 与 Server 端通信
|
||||||
|
- **异步处理**:使用 `CompletableFuture` 实现异步操作
|
||||||
|
- **缓存机制**:集成 Spring Cache 支持多级缓存
|
||||||
|
- **错误处理**:统一的异常处理和日志记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Service 编写规范
|
||||||
|
|
||||||
|
### 1. 类声明和注解
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service // Spring 组件注解
|
||||||
|
@CacheConfig(cacheNames = "xxx") // 缓存配置,xxx为业务域名称
|
||||||
|
public class XxxService extends QueryService<XxxVo, XxxViewModel> {
|
||||||
|
// Service 实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 缓存策略
|
||||||
|
|
||||||
|
#### 缓存注解使用
|
||||||
|
```java
|
||||||
|
@Cacheable(key = "#p0") // 按ID缓存
|
||||||
|
@Cacheable(key = "'code-'+#p0") // 按代码缓存
|
||||||
|
@Cacheable(key = "'name-'+#p0") // 按名称缓存
|
||||||
|
|
||||||
|
@CacheEvict(key = "#p0.id") // 删除时清除ID缓存
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code") // 清除代码缓存
|
||||||
|
|
||||||
|
@Caching(evict = {
|
||||||
|
@CacheEvict(key = "#p0.id"),
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code")
|
||||||
|
}) // 批量清除缓存
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 缓存键设计原则
|
||||||
|
- **ID 缓存**:`#p0`(第一个参数,通常是ID)
|
||||||
|
- **代码缓存**:`'code-'+#p0`(业务代码的缓存)
|
||||||
|
- **名称缓存**:`'name-'+#p0`(业务名称的缓存)
|
||||||
|
- **关联缓存**:`'company-'+#p0.id`(关联实体的缓存)
|
||||||
|
|
||||||
|
### 3. 核心方法实现
|
||||||
|
|
||||||
|
#### findById 方法
|
||||||
|
```java
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
@Override
|
||||||
|
public XxxVo findById(Integer id) {
|
||||||
|
return super.findById(id); // 调用父类方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### save 方法(带缓存清除)
|
||||||
|
```java
|
||||||
|
@Caching(evict = {
|
||||||
|
@CacheEvict(key = "#p0.id"),
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public XxxVo save(XxxVo entity) {
|
||||||
|
return super.save(entity);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### delete 方法(带缓存清除)
|
||||||
|
```java
|
||||||
|
@Caching(evict = {
|
||||||
|
@CacheEvict(key = "#p0.id"),
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public void delete(XxxVo entity) {
|
||||||
|
super.delete(entity);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 异步通信模式
|
||||||
|
|
||||||
|
### 1. 基本异步调用
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 异步调用示例
|
||||||
|
public XxxVo 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 复杂对象处理
|
||||||
|
|
||||||
|
```java
|
||||||
|
public List<XxxDetailVo> findDetailsByXxxId(Integer xxxId) {
|
||||||
|
try {
|
||||||
|
return async("findDetailsByXxxId", List.of(xxxId), List.of(Integer.class))
|
||||||
|
.handle((response, ex) -> {
|
||||||
|
if (ex != null) {
|
||||||
|
throw new RuntimeException("远程方法调用失败", ex);
|
||||||
|
}
|
||||||
|
if (response != null) {
|
||||||
|
try {
|
||||||
|
List<XxxDetailVo> content = new ArrayList<>();
|
||||||
|
for (JsonNode node : response) {
|
||||||
|
XxxDetailVo newEntity = new XxxDetailVo();
|
||||||
|
objectMapper.updateValue(newEntity, node);
|
||||||
|
content.add(newEntity);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(response.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💼 业务逻辑模式
|
||||||
|
|
||||||
|
### 1. 文件系统集成
|
||||||
|
|
||||||
|
#### 路径管理
|
||||||
|
```java
|
||||||
|
@Autowired
|
||||||
|
private SysConfService confService;
|
||||||
|
|
||||||
|
private File basePath;
|
||||||
|
|
||||||
|
public File getBasePath() {
|
||||||
|
if (basePath == null) {
|
||||||
|
basePath = new File(confService.getString(Constant.KEY_BASE_PATH));
|
||||||
|
}
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证路径是否在基础目录内
|
||||||
|
public boolean checkXxxPathInBasePath(XxxVo xxx) {
|
||||||
|
if (!existsXxxPath(xxx)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File basePath = getBasePath();
|
||||||
|
if (basePath == null || !basePath.exists()) {
|
||||||
|
throw new IllegalArgumentException("基础目录不存在");
|
||||||
|
}
|
||||||
|
File path = new File(xxx.getPath());
|
||||||
|
return path.getAbsolutePath().startsWith(basePath.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查路径是否存在
|
||||||
|
public boolean existsXxxPath(XxxVo xxx) {
|
||||||
|
if (!StringUtils.hasText(xxx.getPath())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File path = new File(xxx.getPath());
|
||||||
|
return path.exists();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 目录创建
|
||||||
|
```java
|
||||||
|
public File makePath(XxxVo xxx) {
|
||||||
|
File basePath = getBasePath();
|
||||||
|
if (!basePath.exists()) {
|
||||||
|
holder.error("存储目录不存在:" + basePath.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建目录路径逻辑
|
||||||
|
String fileName = FileUtils.escapeFileName(xxx.getName());
|
||||||
|
File dir = new File(basePath, fileName);
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkdir()) {
|
||||||
|
holder.error("创建目录失败:" + dir.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 业务验证模式
|
||||||
|
|
||||||
|
#### 数据完整性验证
|
||||||
|
```java
|
||||||
|
public void verifyXxx(XxxVo xxx, LocalDate verifyDate, MessageHolder holder) {
|
||||||
|
// 检查关键字段
|
||||||
|
if (!StringUtils.hasText(xxx.getCode())) {
|
||||||
|
holder.error("编号异常:未设置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查状态字段
|
||||||
|
String status = xxx.getStatus();
|
||||||
|
if (StringUtils.hasText(status) && status.contains("无效")) {
|
||||||
|
LocalDate end = xxx.getEndDate();
|
||||||
|
LocalDate begin = xxx.getBeginDate();
|
||||||
|
if (begin == null || end == null) {
|
||||||
|
holder.error("状态异常:" + status);
|
||||||
|
} else {
|
||||||
|
if (!MyDateTimeUtils.dateValidFilter(verifyDate, begin, end, 0)) {
|
||||||
|
holder.error("状态异常:" + status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.error("状态异常:未设置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 关联实体验证
|
||||||
|
```java
|
||||||
|
public boolean verifyAsXxxType(XxxVo xxx, LocalDate verifyDate, MessageHolder holder) {
|
||||||
|
boolean valid = false;
|
||||||
|
|
||||||
|
// 检查关联实体
|
||||||
|
RelatedVo related = relatedService.findById(xxx.getRelatedId());
|
||||||
|
if (related == null) {
|
||||||
|
holder.error("关联实体不存在");
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查关联数据
|
||||||
|
if (!StringUtils.hasText(xxx.getRelatedField())) {
|
||||||
|
holder.error("关联字段未设置");
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查业务规则
|
||||||
|
if (xxx.getStatus() == XxxStatus.INACTIVE) {
|
||||||
|
holder.error("业务状态异常:已停用");
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 复杂业务查询
|
||||||
|
|
||||||
|
#### 分页查询
|
||||||
|
```java
|
||||||
|
public List<XxxVo> findAllByXxxCondition(XxxCondition condition, LocalDate beginDate, LocalDate endDate) {
|
||||||
|
return findAll(ParamUtils.builder()
|
||||||
|
.equals("field1", condition.getField1())
|
||||||
|
.between("createDate", beginDate, endDate)
|
||||||
|
.equals("status", "ACTIVE")
|
||||||
|
.build(), Pageable.unpaged()).getContent();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 组合条件查询
|
||||||
|
```java
|
||||||
|
public Page<XxxVo> findAllWithComplexCondition(XxxQueryParam param) {
|
||||||
|
ParamUtils.ParamBuilder builder = ParamUtils.builder()
|
||||||
|
.equals("category", param.getCategory());
|
||||||
|
|
||||||
|
if (StringUtils.hasText(param.getName())) {
|
||||||
|
builder.like("name", "%" + param.getName() + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.getDateRange() != null) {
|
||||||
|
builder.between("createDate", param.getDateRange().getStart(),
|
||||||
|
param.getDateRange().getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
return findAll(builder.build(), Pageable.ofSize(param.getPageSize()));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 工具类和依赖注入
|
||||||
|
|
||||||
|
### 1. 常用工具类依赖
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 常用注入
|
||||||
|
@Autowired
|
||||||
|
private SysConfService confService; // 系统配置服务
|
||||||
|
@Autowired
|
||||||
|
private RelatedService relatedService; // 关联实体服务
|
||||||
|
|
||||||
|
// 静态工具类使用
|
||||||
|
import com.ecep.contract.util.FileUtils; // 文件工具
|
||||||
|
import com.ecep.contract.util.ParamUtils; // 参数工具
|
||||||
|
import com.ecep.contract.util.MyStringUtils; // 字符串工具
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 工具类使用示例
|
||||||
|
|
||||||
|
#### ParamUtils 构建查询条件
|
||||||
|
```java
|
||||||
|
// 构建复杂查询条件
|
||||||
|
Map<String, Object> params = ParamUtils.builder()
|
||||||
|
.equals("field1", value1)
|
||||||
|
.equals("field2", value2)
|
||||||
|
.like("name", "%" + keyword + "%")
|
||||||
|
.between("date", startDate, endDate)
|
||||||
|
.in("status", List.of("ACTIVE", "PENDING"))
|
||||||
|
.orderBy("createTime", "desc")
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### FileUtils 处理文件路径
|
||||||
|
```java
|
||||||
|
// 文件名转义
|
||||||
|
String safeFileName = FileUtils.escapeFileName(companyName);
|
||||||
|
|
||||||
|
// 获取父级前缀
|
||||||
|
String parentPrefix = FileUtils.getParentPrefixByDistrict(district);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 错误处理和日志
|
||||||
|
|
||||||
|
### 1. 异常处理模式
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 查询异常处理
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务验证异常
|
||||||
|
public boolean businessMethod(XxxVo xxx) {
|
||||||
|
if (xxx == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xxx.isValid()) {
|
||||||
|
throw new IllegalArgumentException("实体数据无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 日志记录
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 在 QueryService 中已集成日志
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(QueryService.class);
|
||||||
|
|
||||||
|
// 在业务方法中使用
|
||||||
|
public void deleteXxx(XxxVo xxx) {
|
||||||
|
try {
|
||||||
|
super.delete(xxx);
|
||||||
|
logger.info("删除实体成功 #{}", xxx.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("删除实体失败 #{}", xxx.getId(), e);
|
||||||
|
throw new RuntimeException("删除实体失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 测试和验证
|
||||||
|
|
||||||
|
### 1. 方法验证检查点
|
||||||
|
|
||||||
|
- **输入参数验证**:检查空值、格式、范围
|
||||||
|
- **业务逻辑验证**:检查状态、关联、权限
|
||||||
|
- **文件系统验证**:检查路径、权限、空间
|
||||||
|
- **数据库验证**:检查连接、事务、一致性
|
||||||
|
|
||||||
|
### 2. 常见测试场景
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 测试用例示例
|
||||||
|
@Test
|
||||||
|
public void testFindById() {
|
||||||
|
// 正常情况
|
||||||
|
XxxVo result = xxxService.findById(1);
|
||||||
|
assertNotNull(result);
|
||||||
|
|
||||||
|
// 异常情况
|
||||||
|
assertThrows(RuntimeException.class, () -> {
|
||||||
|
xxxService.findById(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaveWithCache() {
|
||||||
|
XxxVo xxx = createTestXxx();
|
||||||
|
xxx.setCode("TEST001");
|
||||||
|
|
||||||
|
XxxVo saved = xxxService.save(xxx);
|
||||||
|
assertNotNull(saved.getId());
|
||||||
|
|
||||||
|
// 验证缓存
|
||||||
|
XxxVo cached = xxxService.findById(saved.getId());
|
||||||
|
assertEquals(saved.getId(), cached.getId());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 最佳实践
|
||||||
|
|
||||||
|
### 1. 代码组织原则
|
||||||
|
|
||||||
|
- **单一职责**:每个 Service 专注特定的业务域
|
||||||
|
- **依赖注入**:合理使用 `@Autowired` 注入依赖
|
||||||
|
- **缓存策略**:为高频查询字段配置缓存
|
||||||
|
- **异常处理**:统一处理业务异常和系统异常
|
||||||
|
|
||||||
|
### 2. 性能优化建议
|
||||||
|
|
||||||
|
- **缓存配置**:合理设置缓存过期时间
|
||||||
|
- **分页查询**:避免大数据量一次性查询
|
||||||
|
- **异步处理**:使用异步调用提升响应速度
|
||||||
|
- **批量操作**:考虑批量处理减少网络开销
|
||||||
|
|
||||||
|
### 3. 可维护性提升
|
||||||
|
|
||||||
|
- **命名规范**:遵循项目统一的命名约定
|
||||||
|
- **注释文档**:为复杂业务逻辑添加注释
|
||||||
|
- **代码复用**:提取公共逻辑到工具类
|
||||||
|
- **版本兼容**:考虑前后端版本兼容性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 相关规范
|
||||||
|
|
||||||
|
- **Controller 规范**:[client_controller_rules.md](.trae/rules/client_controller_rules.md)
|
||||||
|
- **转换器规范**:[client_converter_rules.md](.trae/rules/client_converter_rules.md)
|
||||||
|
- **Task 规范**:[client_task_rules.md](.trae/rules/client_task_rules.md)
|
||||||
|
- **Entity 规范**:[entity_rules.md](.trae/rules/entity_rules.md)
|
||||||
|
- **VO 规范**:[vo_rules.md](.trae/rules/vo_rules.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本文档基于 Contract-Manager 项目现有 Service 实现模式总结,遵循项目既定的技术架构和编程规范。*
|
||||||
|
|
||||||
|
**文档版本**: v1.0.0
|
||||||
|
**最后更新**: 2024-12-19
|
||||||
|
**维护团队**: Contract Manager Development Team
|
||||||
419
.trae/rules/client_task_rules.md
Normal file
419
.trae/rules/client_task_rules.md
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
# 客户端Tasker实现WebSocketClientTasker接口规范
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档基于 `ContractRepairAllTasker` 实现 `WebSocketClientTasker` 接口的经验,总结了客户端Tasker类升级为支持WebSocket通信的最佳实践和规范。
|
||||||
|
|
||||||
|
## WebSocketClientTasker接口介绍
|
||||||
|
|
||||||
|
`WebSocketClientTasker` 接口定义了通过WebSocket与服务器通信的任务的通用方法,包括任务名称、消息更新、进度更新等核心功能。
|
||||||
|
|
||||||
|
### 核心方法
|
||||||
|
|
||||||
|
1. **getTaskName()** - 获取任务名称,用于在WebSocket通信中标识任务
|
||||||
|
2. **updateMessage(Level, String)** - 更新任务执行过程中的消息
|
||||||
|
3. **updateTitle(String)** - 更新任务标题
|
||||||
|
4. **updateProgress(long, long)** - 更新任务进度
|
||||||
|
5. **cancelTask()** - 取消任务执行(默认实现为空)
|
||||||
|
6. **callRemoteTask(MessageHolder, Locale, Object...)** - 调用远程WebSocket任务
|
||||||
|
7. **callRemoteTaskAsync(MessageHolder, Locale, Object...)** - 异步调用远程WebSocket任务
|
||||||
|
8. **generateTaskId()** - 生成唯一的任务ID
|
||||||
|
|
||||||
|
### 典型实现模式概览
|
||||||
|
|
||||||
|
通过分析项目中的17个实现类,我们发现了以下典型实现模式:
|
||||||
|
|
||||||
|
1. **标准实现**:继承Tasker<Object>并实现WebSocketClientTasker
|
||||||
|
2. **属性注入**:使用@Setter注解或手动设置属性传递任务参数
|
||||||
|
3. **Spring Bean获取**:通过SpringApp.getBean()获取服务实例
|
||||||
|
4. **消息更新**:简洁的消息更新方式
|
||||||
|
5. **参数传递**:通过callRemoteTask的可变参数传递任务所需数据
|
||||||
|
|
||||||
|
## Tasker实现WebSocketClientTasker最佳实践
|
||||||
|
|
||||||
|
### 1. 类定义和继承
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 任务类描述
|
||||||
|
* 用于通过WebSocket与服务器通信执行具体操作
|
||||||
|
*/
|
||||||
|
public class 任务类名 extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(任务类名.class);
|
||||||
|
|
||||||
|
// 实现方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 参数传递模式
|
||||||
|
|
||||||
|
#### 2.1 使用@Setter注解注入参数
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 更新供应商评价表任务
|
||||||
|
*/
|
||||||
|
public class CompanyVendorEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CompanyVendorEvaluationFormUpdateTask.class);
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private VendorVo vendor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("更新供应商评价表");
|
||||||
|
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2 使用@Getter和@Setter注解
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 客户文件重建任务类
|
||||||
|
*/
|
||||||
|
public class CustomerRebuildFilesTasker extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private CustomerVo companyCustomer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("重建客户文件");
|
||||||
|
return callRemoteTask(holder, getLocale(), companyCustomer.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 必要方法实现
|
||||||
|
|
||||||
|
#### 2.1 getTaskName()
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "Task名称"; // 必须与服务器端对应Tasker类名匹配
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意事项**:
|
||||||
|
- 任务名称必须与服务器端对应的Tasker注册名(tasker_mapper.json中的key)保持一致
|
||||||
|
- 名称应简洁明了,反映任务的核心功能
|
||||||
|
|
||||||
|
#### 2.2 updateProgress()
|
||||||
|
updateProgress 方法重载为public,用于外部调用更新进度
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total); // 调用父类方法更新进度
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 updateTitle()
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void updateTitle(String title) {
|
||||||
|
super.updateTitle(title); // 使用Tasker的updateTitle方法更新标题
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.4 execute() 方法重写
|
||||||
|
|
||||||
|
**标准实现**:
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
logger.info("开始执行任务描述");
|
||||||
|
updateTitle("任务标题");
|
||||||
|
|
||||||
|
// 调用远程任务,可选传入参数
|
||||||
|
Object result = callRemoteTask(holder, getLocale(), 可选参数...);
|
||||||
|
|
||||||
|
logger.info("任务执行完成");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**简洁实现**(适用于简单任务):
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("更新供应商评价表");
|
||||||
|
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**带消息更新的实现**:
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
// 设置任务标题
|
||||||
|
updateTitle("全量库存同步任务");
|
||||||
|
|
||||||
|
// 更新任务消息
|
||||||
|
updateMessage("开始执行全量库存同步...");
|
||||||
|
|
||||||
|
// 调用远程WebSocket任务
|
||||||
|
return callRemoteTask(holder, getLocale());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键步骤**:
|
||||||
|
1. 记录任务开始日志(可选)
|
||||||
|
2. 设置任务标题
|
||||||
|
3. 可选:添加任务开始消息
|
||||||
|
4. 调用远程任务执行核心逻辑,传入必要参数
|
||||||
|
5. 记录任务完成日志(可选)
|
||||||
|
6. 返回执行结果
|
||||||
|
|
||||||
|
### 3. 日志记录
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
// 在类中定义
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(任务类名.class);
|
||||||
|
|
||||||
|
// 在关键位置使用日志
|
||||||
|
logger.info("任务开始");
|
||||||
|
logger.warn("警告信息");
|
||||||
|
logger.error("错误信息", exception);
|
||||||
|
```
|
||||||
|
|
||||||
|
**日志使用建议**:
|
||||||
|
- 复杂任务建议记录详细日志
|
||||||
|
- 简单任务可以简化或省略日志记录
|
||||||
|
- 确保异常情况下有适当的错误日志记录
|
||||||
|
|
||||||
|
### 4. 异常处理
|
||||||
|
|
||||||
|
在`execute`方法中应妥善处理可能的异常,并通过MessageHolder通知用户:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
try {
|
||||||
|
// 任务执行逻辑
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("任务执行失败", e);
|
||||||
|
holder.addMessage(Level.SEVERE, "任务执行失败: " + e.getMessage());
|
||||||
|
throw e; // 向上抛出异常,让框架处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**异常处理策略**:
|
||||||
|
- 对于简单任务,可以依赖框架的异常处理机制
|
||||||
|
- 对于复杂任务,建议添加自定义的异常处理逻辑
|
||||||
|
- 确保异常信息对用户友好且具有足够的调试信息
|
||||||
|
|
||||||
|
## 完整实现示例
|
||||||
|
|
||||||
|
### 示例1:简单任务实现
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.task;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合同修复任务类
|
||||||
|
* 用于通过WebSocket与服务器通信执行合同数据修复操作
|
||||||
|
*/
|
||||||
|
public class ContractRepairAllTasker extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ContractRepairAllTasker.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTitle(String title) {
|
||||||
|
// 使用Tasker的updateTitle方法更新标题
|
||||||
|
super.updateTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "ContractRepairAllTask"; // 与服务器端对应Tasker类名匹配
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
logger.info("开始执行合同修复任务");
|
||||||
|
updateTitle("合同数据修复");
|
||||||
|
Object result = callRemoteTask(holder, getLocale());
|
||||||
|
logger.info("合同修复任务执行完成");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例2:带参数的任务实现
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.task;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
import com.ecep.contract.vo.VendorVo;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新供应商评价表
|
||||||
|
*/
|
||||||
|
public class CompanyVendorEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CompanyVendorEvaluationFormUpdateTask.class);
|
||||||
|
@Setter
|
||||||
|
private VendorVo vendor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "CompanyVendorEvaluationFormUpdateTask"; // 与服务器端对应Tasker类名匹配
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("更新供应商评价表");
|
||||||
|
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例3:使用Spring Bean的任务实现
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.task;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.SpringApp;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
import com.ecep.contract.service.YongYouU8Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合同同步任务
|
||||||
|
*/
|
||||||
|
public class ContractSyncTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ContractSyncTask.class);
|
||||||
|
|
||||||
|
private YongYouU8Service yongYouU8Service;
|
||||||
|
|
||||||
|
private YongYouU8Service getYongYouU8Service() {
|
||||||
|
if (yongYouU8Service == null) {
|
||||||
|
yongYouU8Service = SpringApp.getBean(YongYouU8Service.class);
|
||||||
|
}
|
||||||
|
return yongYouU8Service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskName() {
|
||||||
|
return "ContractSyncTask"; // 与服务器端对应Tasker类名匹配
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("用友U8系统-同步合同");
|
||||||
|
return callRemoteTask(holder, getLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项和最佳实践
|
||||||
|
|
||||||
|
### 1. 命名规范
|
||||||
|
- 任务类名应采用驼峰命名法,以`Tasker`结尾或描述性名称如`Task`
|
||||||
|
- getTaskName()返回的名称应与服务器端对应Tasker类名完全匹配
|
||||||
|
- 类注释应清晰描述任务的功能和用途
|
||||||
|
|
||||||
|
### 2. 继承关系
|
||||||
|
- 必须同时继承Tasker类并实现WebSocketClientTasker接口
|
||||||
|
- Tasker泛型参数通常为Object
|
||||||
|
- 确保正确导入所有必要的包
|
||||||
|
|
||||||
|
### 3. 参数处理
|
||||||
|
- 对于需要参数的任务,使用@Setter注解简化属性设置
|
||||||
|
- 对于需要在多处使用的参数,考虑添加@Getter注解
|
||||||
|
- 确保参数验证(如果必要)
|
||||||
|
|
||||||
|
### 4. Spring Bean获取
|
||||||
|
- 使用SpringApp.getBean()获取所需的服务实例
|
||||||
|
- 考虑使用懒加载模式,避免不必要的Bean初始化
|
||||||
|
|
||||||
|
### 5. 消息和进度更新
|
||||||
|
- 使用updateTitle()设置有意义的任务标题
|
||||||
|
- 通过MessageHolder或updateMessage()记录详细的执行消息
|
||||||
|
- 确保进度更新反映真实的执行进度
|
||||||
|
|
||||||
|
### 6. 异常处理
|
||||||
|
- 在关键操作处添加try-catch块
|
||||||
|
- 记录异常日志并通知用户
|
||||||
|
- 适当向上抛出异常以确保框架能正确处理
|
||||||
|
|
||||||
|
### 7. 日志级别使用
|
||||||
|
- INFO: 记录正常的操作流程
|
||||||
|
- WARNING: 记录可能的问题,但不影响继续执行
|
||||||
|
- ERROR: 记录严重错误,通常会终止执行
|
||||||
|
|
||||||
|
### 8. 远程调用参数
|
||||||
|
- 确保传入的参数类型与服务器端Tasker期望的一致
|
||||||
|
- 对于不需要参数的任务,可以不传入额外参数
|
||||||
|
- 对于需要多个参数的任务,确保参数顺序正确
|
||||||
|
|
||||||
|
### 9. 代码风格
|
||||||
|
- 保持代码简洁明了
|
||||||
|
- 遵循项目的代码格式化规范
|
||||||
|
- 添加必要的注释说明核心逻辑
|
||||||
|
|
||||||
|
### 10. 实现策略选择
|
||||||
|
- 简单任务:使用简洁的实现方式,省略不必要的日志
|
||||||
|
- 复杂任务:添加详细的日志记录和异常处理
|
||||||
|
- 有特定需求的任务:根据需要重写接口中的其他方法
|
||||||
|
|
||||||
|
## 与服务器端交互流程
|
||||||
|
|
||||||
|
1. 客户端Tasker通过callRemoteTask()方法提交任务
|
||||||
|
2. WebSocketClientService负责建立与服务器的连接并发送任务信息
|
||||||
|
3. 服务器接收到任务后,创建对应的Tasker实例并执行
|
||||||
|
4. 执行过程中的消息、进度等通过WebSocket实时返回给客户端
|
||||||
|
5. 客户端Tasker通过updateMessage()、updateProgress()等方法更新UI
|
||||||
|
|
||||||
|
## 扩展和自定义
|
||||||
|
|
||||||
|
如需为特定任务提供自定义功能,可以:
|
||||||
|
|
||||||
|
1. 重写cancelTask()方法实现任务取消逻辑
|
||||||
|
2. 根据需要添加额外的字段和方法
|
||||||
|
3. 扩展execute()方法实现更复杂的任务流程
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
通过分析项目中的17个WebSocketClientTasker实现类,我们总结了客户端Tasker实现的多种模式和最佳实践。这些实现从简单到复杂,涵盖了各种使用场景,为后续Tasker的编写提供了全面的参考。
|
||||||
|
|
||||||
|
客户端Tasker类实现WebSocketClientTasker接口是实现与服务器实时通信的关键步骤。通过遵循本文档中的规范和最佳实践,可以确保任务执行的可靠性、进度的实时更新和良好的用户体验。
|
||||||
|
|
||||||
|
在实际开发中,应根据任务的复杂度和具体需求,选择合适的实现模式和策略,同时保持代码的一致性和可维护性。
|
||||||
74
.trae/rules/entity_rules.md
Normal file
74
.trae/rules/entity_rules.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# 实体类规则
|
||||||
|
|
||||||
|
## 1. 目录结构
|
||||||
|
- **基础实体类和接口**:放置在 `common/src/main/java/com/ecep/contract/model/` 目录下
|
||||||
|
- **业务领域实体类**:放置在 `server/src/main/java/com/ecep/contract/ds/{业务领域}/model/` 目录下,按业务领域组织
|
||||||
|
- 例如:公司相关实体在 `ds/company/model/` 目录下
|
||||||
|
- 合同相关实体在 `ds/contract/model/` 目录下
|
||||||
|
- 客户相关实体在 `ds/customer/model/` 目录下
|
||||||
|
- 项目相关实体在 `ds/project/model/` 目录下
|
||||||
|
- 供应商相关实体在 `ds/vendor/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` 在 `ds/company/model/` 目录下,定义与某一业务领域相关的通用方法(如getter/setter)
|
||||||
|
|
||||||
|
## 4. 注解规范
|
||||||
|
- **JPA注解**:
|
||||||
|
- `@Entity`:标记为实体类
|
||||||
|
- `@Table(name = "表名", schema = "supplier_ms")`:指定对应的数据库表,schema通常为"supplier_ms"
|
||||||
|
- `@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,23 +1,284 @@
|
|||||||
# server 模块
|
# 项目规则
|
||||||
Java 21
|
|
||||||
Spring Boot 3.3.7
|
|
||||||
Spring Data JPA 3.3.7
|
|
||||||
MySQL 8.0.33
|
|
||||||
Lombok 1.18.32
|
|
||||||
POI 5.2.5
|
|
||||||
PDFBox 3.0.1
|
|
||||||
Redis
|
|
||||||
|
|
||||||
# client 模块
|
## 技术栈规范
|
||||||
Java 21
|
|
||||||
JavaFX 21
|
|
||||||
ControlsFX 11.1.2
|
|
||||||
Lombok 1.18.32
|
|
||||||
caffeine 3.1.8
|
|
||||||
.fxml 界面UI, /client/src/main/resources/ui/ 目录下
|
|
||||||
websocket 与 server 模块通信
|
|
||||||
|
|
||||||
|
### server 模块
|
||||||
|
- Java 21
|
||||||
|
- Spring Boot 3.3.7
|
||||||
|
- Spring Data JPA 3.3.7
|
||||||
|
- MySQL 8.0.33
|
||||||
|
- Lombok 1.18.32
|
||||||
|
- POI 5.2.5
|
||||||
|
- PDFBox 3.0.1
|
||||||
|
- Redis
|
||||||
|
|
||||||
|
### client 模块
|
||||||
|
- Java 21
|
||||||
|
- JavaFX 21
|
||||||
|
- ControlsFX 11.1.2
|
||||||
|
- Lombok 1.18.32
|
||||||
|
- caffeine 3.1.8
|
||||||
|
- .fxml 界面UI, 放置于 /client/src/main/resources/ui/ 目录下
|
||||||
|
- websocket 与 server 模块通信
|
||||||
|
- StringConverter 创建规则与实现逻辑 `docs/task/string_converter_implementation_guide.md`
|
||||||
|
- TableCell 规则与逻辑 `docs/task/table_cell_implementation_guide.md`
|
||||||
|
- Service 规则与逻辑 `docs/task/serservice_layer_rules.md`
|
||||||
|
- 客户端 Tasker 至 服务器端 Tasker 通信规则与逻辑 `docs/task/client_server_tasker_communication_rules.md`
|
||||||
|
|
||||||
|
### common 模块
|
||||||
|
- Java 21
|
||||||
|
- Lombok 1.18.32
|
||||||
|
|
||||||
|
## 文件命名规范
|
||||||
|
- Java类名:使用驼峰命名法,首字母大写,如 `ContractService.java`
|
||||||
|
- 接口名:使用驼峰命名法,首字母大写,以I开头,如 `IContractService.java`
|
||||||
|
- 控制器类名:以Controller结尾,如 `ContractController.java`
|
||||||
|
- 服务类名:以Service结尾,如 `ContractService.java`
|
||||||
|
- 实体类名:使用驼峰命名法,首字母大写,如 `Contract.java`
|
||||||
|
- FXML文件:使用小写字母和下划线,如 `contract_view.fxml`
|
||||||
|
- SQL文件:表名使用大写和下划线,如 `CONTRACT_TYPE_LOCAL.sql`
|
||||||
|
|
||||||
|
## 目录结构规范
|
||||||
|
### 项目整体结构
|
||||||
|
- `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/`(业务逻辑, 详细规范见 `.trae\rules\server_service_rules.md`)、`tasker/`(任务处理器, 详细规范见 `.trae\rules\server_tasker_rules.md`)、`controller/`(控制器, 详细规范见 `.trae\rules\server_controller_rules.md`)
|
||||||
|
- `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`
|
||||||
|
- 字段名使用大写字母和下划线,如 `CREATE_DATE`
|
||||||
|
- 主键命名为 `ID`
|
||||||
|
- 外键命名格式为 `[关联表名]_ID`
|
||||||
|
- 唯一约束命名格式为 `UK_[表名缩写]_[字段名]`
|
||||||
|
|
||||||
|
## 代码规范
|
||||||
|
- 使用Lombok注解简化代码,如 `@Data`、`@Slf4j` 等
|
||||||
|
- 类和方法应有适当的JavaDoc注释
|
||||||
|
- 变量命名应清晰表达其含义
|
||||||
|
- 避免魔法数字,使用常量替代
|
||||||
|
- 异常处理使用统一的异常处理机制
|
||||||
|
|
||||||
|
## 忽略文件
|
||||||
ignore:
|
ignore:
|
||||||
- .idea
|
- .idea
|
||||||
- target
|
- target
|
||||||
|
- *.iml
|
||||||
|
- .gitignore
|
||||||
|
- .gitattributes
|
||||||
|
- out/
|
||||||
|
- *.log
|
||||||
|
- build/
|
||||||
|
- .DS_Store
|
||||||
|
- *.class
|
||||||
|
|
||||||
|
|
||||||
|
# 6A工作流执行规则
|
||||||
|
## 阶段1: Align (对齐阶段)
|
||||||
|
### 目标: 模糊需求 → 精确规范
|
||||||
|
### 执行步骤
|
||||||
|
1. **项目上下文分析**
|
||||||
|
- 分析现有项目结构、技术栈、架构模式、依赖关系
|
||||||
|
- 分析现有代码模式、现有文档和约定
|
||||||
|
- 理解业务域和数据模型
|
||||||
|
2. **需求理解确认**
|
||||||
|
- 创建 `docs/任务名/ALIGNMENT_[任务名].md`
|
||||||
|
- 包含项目和任务特性规范
|
||||||
|
- 包含原始需求、边界确认(明确任务范围)、需求理解(对现有项目的理解)、疑问澄清(存在歧义的地方)
|
||||||
|
3. **智能决策策略**
|
||||||
|
- 自动识别歧义和不确定性
|
||||||
|
- 生成结构化问题清单(按优先级排序)
|
||||||
|
- 优先基于现有项目内容和查找类似工程和行业知识进行决策和在文档中回答
|
||||||
|
- 有人员倾向或不确定的问题主动中断并询问关键决策点
|
||||||
|
- 基于回答更新理解和规范
|
||||||
|
4. **中断并询问关键决策点**
|
||||||
|
- 主动中断询问,迭代执行智能决策策略
|
||||||
|
5. **最终共识**
|
||||||
|
- 生成 `docs/任务名/CONSENSUS_[任务名].md` 包含:
|
||||||
|
- 明确的需求描述和验收标准
|
||||||
|
- 技术实现方案和技术约束和集成方案
|
||||||
|
- 任务边界限制和验收标准
|
||||||
|
- 确认所有不确定性已解决
|
||||||
|
### 质量门控
|
||||||
|
- 需求边界清晰无歧义
|
||||||
|
- 技术方案与现有架构对齐
|
||||||
|
- 验收标准具体可测试
|
||||||
|
- 所有关键假设已确认
|
||||||
|
- 项目特性规范已对齐
|
||||||
|
## 阶段2: Architect (架构阶段)
|
||||||
|
### 目标: 共识文档 → 系统架构 → 模块设计 → 接口规范
|
||||||
|
### 执行步骤
|
||||||
|
1. **系统分层设计**
|
||||||
|
- 基于CONSENSUS、ALIGNMENT文档设计架构
|
||||||
|
- 生成 `docs/任务名/DESIGN_[任务名].md` 包含:
|
||||||
|
- 整体架构图(mermaid绘制)
|
||||||
|
- 分层设计和核心组件
|
||||||
|
- 模块依赖关系图
|
||||||
|
- 接口契约定义
|
||||||
|
- 数据流向图
|
||||||
|
- 异常处理策略
|
||||||
|
2. **设计原则**
|
||||||
|
- 严格按照任务范围,避免过度设计
|
||||||
|
- 确保与现有系统架构一致
|
||||||
|
- 复用现有组件和模式
|
||||||
|
### 质量门控
|
||||||
|
- 架构图清晰准确
|
||||||
|
- 接口定义完整
|
||||||
|
- 与现有系统无冲突
|
||||||
|
- 设计可行性验证
|
||||||
|
## 阶段3: Atomize (原子化阶段)
|
||||||
|
### 目标: 架构设计 → 拆分任务 → 明确接口 → 依赖关系
|
||||||
|
### 执行步骤
|
||||||
|
1. **子任务拆分**
|
||||||
|
- 基于DESIGN文档生成 `docs/任务名/TASK_[任务名].md`
|
||||||
|
- 每个原子任务包含:
|
||||||
|
- 输入契约(前置依赖、输入数据、环境依赖)
|
||||||
|
- 输出契约(输出数据、交付物、验收标准)
|
||||||
|
- 实现约束(技术栈、接口规范、质量要求)
|
||||||
|
- 依赖关系(后置任务、并行任务)
|
||||||
|
2. **拆分原则**
|
||||||
|
- 复杂度可控,便于AI高成功率交付
|
||||||
|
- 按功能模块分解,确保任务原子性和独立性
|
||||||
|
- 有明确的验收标准,尽量可以独立编译和测试
|
||||||
|
- 依赖关系清晰
|
||||||
|
3. **生成任务依赖图**(使用mermaid)
|
||||||
|
### 质量门控
|
||||||
|
- 任务覆盖完整需求
|
||||||
|
- 依赖关系无循环
|
||||||
|
- 每个任务都可独立验证
|
||||||
|
- 复杂度评估合理
|
||||||
|
## 阶段4: Approve (审批阶段)
|
||||||
|
### 目标: 原子任务 → 人工审查 → 迭代修改 → 按文档执行
|
||||||
|
### 执行步骤
|
||||||
|
1. **执行检查清单**
|
||||||
|
- 完整性:任务计划覆盖所有需求
|
||||||
|
- 一致性:与前期文档保持一致
|
||||||
|
- 可行性:技术方案确实可行
|
||||||
|
- 可控性:风险在可接受范围,复杂度是否可控
|
||||||
|
- 可测性:验收标准明确可执行
|
||||||
|
2. **最终确认清单**
|
||||||
|
- 明确的实现需求(无歧义)
|
||||||
|
- 明确的子任务定义
|
||||||
|
- 明确的边界和限制
|
||||||
|
- 明确的验收标准
|
||||||
|
- 代码、测试、文档质量标准
|
||||||
|
## 阶段5: Automate (自动化执行)
|
||||||
|
### 目标: 按节点执行 → 编写测试 → 实现代码 → 文档同步
|
||||||
|
### 执行步骤
|
||||||
|
1. **逐步实施子任务**
|
||||||
|
- 创建 `docs/任务名/ACCEPTANCE_[任务名].md` 记录完成情况
|
||||||
|
2. **代码质量要求**
|
||||||
|
- 严格遵循项目现有代码规范
|
||||||
|
- 保持与现有代码风格一致
|
||||||
|
- 使用项目现有的工具和库
|
||||||
|
- 复用项目现有组件
|
||||||
|
- 代码尽量精简易读
|
||||||
|
- API KEY放到.env文件中并且不要提交git
|
||||||
|
3. **异常处理**
|
||||||
|
- 遇到不确定问题立刻中断执行
|
||||||
|
- 在TASK文档中记录问题详细信息和位置
|
||||||
|
- 寻求人工澄清后继续
|
||||||
|
4. **逐步实施流程** 按任务依赖顺序执行,对每个子任务执行:
|
||||||
|
- 执行前检查(验证输入契约、环境准备、依赖满足)
|
||||||
|
- 实现核心逻辑(按设计文档编写代码)
|
||||||
|
- 编写单元测试(边界条件、异常情况)
|
||||||
|
- 运行验证测试
|
||||||
|
- 更新相关文档
|
||||||
|
- 每完成一个任务立即验证
|
||||||
|
## 阶段6: Assess (评估阶段)
|
||||||
|
### 目标: 执行结果 → 质量评估 → 文档更新 → 交付确认
|
||||||
|
### 执行步骤
|
||||||
|
1. **验证执行结果**
|
||||||
|
- 更新 `docs/任务名/ACCEPTANCE_[任务名].md`
|
||||||
|
- 整体验收检查:
|
||||||
|
- 所有需求已实现
|
||||||
|
- 验收标准全部满足
|
||||||
|
- 项目编译通过
|
||||||
|
- 所有测试通过
|
||||||
|
- 功能完整性验证
|
||||||
|
- 实现与设计文档一致
|
||||||
|
2. **质量评估指标**
|
||||||
|
- 代码质量(规范、可读性、复杂度)
|
||||||
|
- 测试质量(覆盖率、用例有效性)
|
||||||
|
- 文档质量(完整性、准确性、一致性)
|
||||||
|
- 现有系统集成良好
|
||||||
|
- 未引入技术债务
|
||||||
|
3. **最终交付物**
|
||||||
|
- 生成 `docs/任务名/FINAL_[任务名].md`(项目总结报告)
|
||||||
|
- 生成 `docs/任务名/TODO_[任务名].md`(精简明确哪些待办的事宜和哪些缺少的配置等,我方便直接寻找支持)
|
||||||
|
4. **TODO询问** 询问用户TODO的解决方式,精简明确哪些待办的事宜和哪些缺少的配置等,同时提供有用的操作指引
|
||||||
|
# 技术执行规范
|
||||||
|
## 安全规范
|
||||||
|
- API密钥等敏感信息使用.env文件管理
|
||||||
|
## 文档同步
|
||||||
|
- 代码变更同时更新相关文档
|
||||||
|
## 测试策略
|
||||||
|
- 测试优先:先写测试,后写实现
|
||||||
|
- 边界覆盖:覆盖正常流程、边界条件、异常情况
|
||||||
|
## 交互体验优化
|
||||||
|
### 进度反馈
|
||||||
|
- 显示当前执行阶段
|
||||||
|
- 提供详细的执行步骤
|
||||||
|
- 标示完成情况
|
||||||
|
- 突出需要关注的问题
|
||||||
|
### 异常处理机制
|
||||||
|
#### 中断条件
|
||||||
|
- 遇到无法自主决策的问题
|
||||||
|
- 觉得需要询问用户的问题
|
||||||
|
- 技术实现出现阻塞
|
||||||
|
- 文档不一致需要确认修正
|
||||||
|
#### 恢复策略
|
||||||
|
- 保存当前执行状态
|
||||||
|
- 记录问题详细信息
|
||||||
|
- 询问并等待人工干预
|
||||||
|
- 从中断点任务继续执行
|
||||||
374
.trae/rules/repository_comprehensive_analysis_report.md
Normal file
374
.trae/rules/repository_comprehensive_analysis_report.md
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
# Repository一致性分析 - 综合报告
|
||||||
|
|
||||||
|
## 📊 执行摘要
|
||||||
|
|
||||||
|
### 分析概览
|
||||||
|
- **总Repository数量**: 22个
|
||||||
|
- **涉及业务域**: contract, company, customer, project, vendor
|
||||||
|
- **分析时间**: 2024年项目全面分析
|
||||||
|
- **分析范围**: 覆盖server模块下所有5个业务域的Repository实现
|
||||||
|
|
||||||
|
### 目录覆盖情况
|
||||||
|
✅ `server/src/main/java/com/ecep/contract/ds/contract/repository/` (13个)
|
||||||
|
✅ `server/src/main/java/com/ecep/contract/ds/company/repository/` (3个)
|
||||||
|
✅ `server/src/main/java/com/ecep/contract/ds/customer/repository/` (2个)
|
||||||
|
✅ `server/src/main/java/com/ecep/contract/ds/project/repository/` (3个)
|
||||||
|
✅ `server/src/main/java/com/ecep/contract/ds/vendor/repository/` (2个)
|
||||||
|
|
||||||
|
## 🔍 关键发现与评估
|
||||||
|
|
||||||
|
### 🔴 严重问题:继承层次不一致(5个)
|
||||||
|
|
||||||
|
#### 1. 直接继承JpaRepository(4个)
|
||||||
|
- `ContractFileRepository` - 直接继承`JpaRepository`和`JpaSpecificationExecutor`
|
||||||
|
- `ContractItemRepository` - 直接继承`JpaRepository`和`JpaSpecificationExecutor`
|
||||||
|
- `VendorFileRepository` - 直接继承`JpaRepository`和`JpaSpecificationExecutor`
|
||||||
|
- `ProjectQuotationRepository` - 直接继承`JpaRepository`和`JpaSpecificationExecutor`
|
||||||
|
|
||||||
|
#### 2. 继承多个冗余接口(1个)
|
||||||
|
- `PurchaseOrderRepository` - 同时继承了4个接口:`CrudRepository`, `PagingAndSortingRepository`, `JpaRepository`, `JpaSpecificationExecutor`
|
||||||
|
|
||||||
|
### 🟡 中等问题(6个)
|
||||||
|
|
||||||
|
#### 注解缺失(2个)
|
||||||
|
- `CompanyFileRepository` - 缺少`@Repository`注解
|
||||||
|
- `ProjectFileRepository` - 缺少`@Repository`注解
|
||||||
|
|
||||||
|
#### 文档注释缺失(4个)
|
||||||
|
- `CompanyFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `ProjectFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `VendorFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `ProjectQuotationRepository` - 缺少JavaDoc注释
|
||||||
|
|
||||||
|
### 📈 整体质量评估
|
||||||
|
|
||||||
|
#### 正确实现统计
|
||||||
|
- ✅ **17个Repository正确继承MyRepository** (77%)
|
||||||
|
- ❌ **5个Repository继承层次错误** (23%)
|
||||||
|
- 🟡 **2个Repository缺少注解** (9%)
|
||||||
|
- 🟡 **4个Repository文档不完整** (18%)
|
||||||
|
|
||||||
|
#### 质量分级分布
|
||||||
|
- **A级(完全正确)**: 13个 (59%)
|
||||||
|
- **B级(轻微问题)**: 6个 (27%)
|
||||||
|
- **C级(严重错误)**: 3个 (14%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 详细技术分析
|
||||||
|
|
||||||
|
## 继承一致性详细分析
|
||||||
|
|
||||||
|
### ✅ 正确继承MyRepository的Repository(17个)
|
||||||
|
|
||||||
|
**contract业务域(7个):**
|
||||||
|
- ContractRepository
|
||||||
|
- ContractBalanceRepository
|
||||||
|
- SalesOrderRepository
|
||||||
|
- ContractTypeRepository
|
||||||
|
- ContractKindRepository
|
||||||
|
- ContractInvoiceRepository
|
||||||
|
|
||||||
|
**company业务域(3个):**
|
||||||
|
- CompanyRepository
|
||||||
|
- CompanyFileRepository (有注解问题)
|
||||||
|
- CompanyCustomerRepository
|
||||||
|
|
||||||
|
**customer业务域(2个):**
|
||||||
|
- CustomerCatalogRepository
|
||||||
|
|
||||||
|
**project业务域(2个):**
|
||||||
|
- ProjectRepository
|
||||||
|
- ProjectFileRepository (有注解问题)
|
||||||
|
|
||||||
|
**vendor业务域(1个):**
|
||||||
|
- VendorRepository
|
||||||
|
|
||||||
|
### ❌ 继承层次错误的Repository(5个)
|
||||||
|
|
||||||
|
#### Contract业务域(3个)
|
||||||
|
1. **ContractFileRepository**
|
||||||
|
2. **ContractItemRepository**
|
||||||
|
3. **PurchaseOrderRepository**
|
||||||
|
|
||||||
|
#### Project业务域(1个)
|
||||||
|
4. **ProjectQuotationRepository**
|
||||||
|
|
||||||
|
#### Vendor业务域(1个)
|
||||||
|
5. **VendorFileRepository**
|
||||||
|
|
||||||
|
## 问题影响分析
|
||||||
|
|
||||||
|
### 技术影响
|
||||||
|
- ❌ 违反了统一的架构设计原则
|
||||||
|
- ❌ 失去了MyRepository提供的统一功能增强
|
||||||
|
- ❌ 代码风格不一致,影响可维护性
|
||||||
|
- ❌ 开发团队需要维护多种不同的实现模式
|
||||||
|
- ❌ 增加了代码维护成本和技术债务
|
||||||
|
|
||||||
|
### 业务影响
|
||||||
|
- ❌ 代码可维护性降低
|
||||||
|
- ❌ 新团队成员学习成本增加
|
||||||
|
- ❌ 代码审查复杂度提高
|
||||||
|
- ❌ 系统稳定性潜在风险增加
|
||||||
|
|
||||||
|
## 详细修复方案
|
||||||
|
|
||||||
|
### 🔴 优先级1:修复继承层次错误
|
||||||
|
|
||||||
|
#### 1. ContractFileRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前错误实现
|
||||||
|
public interface ContractFileRepository
|
||||||
|
extends JpaRepository<ContractFile, Integer>, JpaSpecificationExecutor<ContractFile> {
|
||||||
|
|
||||||
|
// ✅ 正确修复
|
||||||
|
@Repository
|
||||||
|
public interface ContractFileRepository extends MyRepository<ContractFile, Integer> {
|
||||||
|
// 自动获得MyRepository提供的所有功能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. ContractItemRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前错误实现
|
||||||
|
public interface ContractItemRepository
|
||||||
|
extends JpaRepository<ContractItem, Integer>, JpaSpecificationExecutor<ContractItem> {
|
||||||
|
|
||||||
|
// ✅ 正确修复
|
||||||
|
@Repository
|
||||||
|
public interface ContractItemRepository extends MyRepository<ContractItem, Integer> {
|
||||||
|
// 统一继承结构,简化维护
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. VendorFileRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前错误实现
|
||||||
|
@Repository
|
||||||
|
public interface VendorFileRepository
|
||||||
|
extends JpaRepository<VendorFile, Integer>, JpaSpecificationExecutor<VendorFile> {
|
||||||
|
|
||||||
|
// ✅ 正确修复
|
||||||
|
@Repository
|
||||||
|
public interface VendorFileRepository extends MyRepository<VendorFile, Integer> {
|
||||||
|
// 保持注解的同时修正继承结构
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. ProjectQuotationRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前错误实现
|
||||||
|
@Repository
|
||||||
|
public interface ProjectQuotationRepository
|
||||||
|
extends JpaRepository<ProjectQuotation, Integer>, JpaSpecificationExecutor<ProjectQuotation> {
|
||||||
|
|
||||||
|
// ✅ 正确修复
|
||||||
|
@Repository
|
||||||
|
public interface ProjectQuotationRepository extends MyRepository<ProjectQuotation, Integer> {
|
||||||
|
// 获得MyRepository的所有增强功能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. PurchaseOrderRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前错误实现(继承4个冗余接口)
|
||||||
|
public interface PurchaseOrderRepository extends
|
||||||
|
CrudRepository<PurchaseOrder, Integer>,
|
||||||
|
PagingAndSortingRepository<PurchaseOrder, Integer>,
|
||||||
|
JpaRepository<PurchaseOrder, Integer>,
|
||||||
|
JpaSpecificationExecutor<PurchaseOrder> {
|
||||||
|
|
||||||
|
// ✅ 正确修复
|
||||||
|
@Repository
|
||||||
|
public interface PurchaseOrderRepository extends MyRepository<PurchaseOrder, Integer> {
|
||||||
|
// 单一继承,清晰简洁
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🟡 优先级2:修复注解缺失
|
||||||
|
|
||||||
|
#### CompanyFileRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前缺少注解
|
||||||
|
public interface CompanyFileRepository extends MyRepository<CompanyFile, Integer> {
|
||||||
|
|
||||||
|
// ✅ 添加注解
|
||||||
|
@Repository
|
||||||
|
public interface CompanyFileRepository extends MyRepository<CompanyFile, Integer> {
|
||||||
|
// 添加完整的JavaDoc注释
|
||||||
|
/**
|
||||||
|
* 公司文件数据访问接口
|
||||||
|
* 提供公司相关文件的CRUD操作和业务查询功能
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ProjectFileRepository
|
||||||
|
```java
|
||||||
|
// ❌ 当前缺少注解
|
||||||
|
public interface ProjectFileRepository extends MyRepository<ProjectFile, Integer> {
|
||||||
|
|
||||||
|
// ✅ 添加注解
|
||||||
|
@Repository
|
||||||
|
public interface ProjectFileRepository extends MyRepository<ProjectFile, Integer> {
|
||||||
|
/**
|
||||||
|
* 项目文件数据访问接口
|
||||||
|
* 提供项目文件的管理和查询功能
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🟡 优先级3:完善文档注释
|
||||||
|
|
||||||
|
为以下Repository补充完整的JavaDoc注释:
|
||||||
|
- CompanyFileRepository
|
||||||
|
- ProjectFileRepository
|
||||||
|
- VendorFileRepository
|
||||||
|
- ProjectQuotationRepository
|
||||||
|
|
||||||
|
标准JavaDoc格式示例:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* [功能描述]数据访问接口
|
||||||
|
*
|
||||||
|
* 提供[业务描述]的CRUD操作和业务查询功能
|
||||||
|
*
|
||||||
|
* @author [作者]
|
||||||
|
* @since [版本]
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实施计划
|
||||||
|
|
||||||
|
### 阶段1:立即修复(1-2天)
|
||||||
|
**目标**: 解决所有严重的继承层次错误
|
||||||
|
|
||||||
|
**执行步骤**:
|
||||||
|
1. 备份当前的Repository实现
|
||||||
|
2. 逐一修复5个继承层次错误的Repository
|
||||||
|
3. 添加缺失的@Repository注解
|
||||||
|
4. 运行编译测试确保无破坏性变更
|
||||||
|
5. 执行基本功能验证测试
|
||||||
|
|
||||||
|
**验收标准**:
|
||||||
|
- 所有Repository都继承MyRepository
|
||||||
|
- 编译通过,无语法错误
|
||||||
|
- 基本CRUD功能正常
|
||||||
|
|
||||||
|
### 阶段2:文档完善(1天)
|
||||||
|
**目标**: 统一JavaDoc注释规范
|
||||||
|
|
||||||
|
**执行步骤**:
|
||||||
|
1. 为4个Repository补充JavaDoc注释
|
||||||
|
2. 检查并更新相关单元测试
|
||||||
|
3. 验证文档格式规范性
|
||||||
|
4. 代码审查确认
|
||||||
|
|
||||||
|
**验收标准**:
|
||||||
|
- 所有Repository都有完整的JavaDoc注释
|
||||||
|
- 测试用例覆盖主要功能
|
||||||
|
- 代码审查通过
|
||||||
|
|
||||||
|
### 阶段3:质量保证(半天)
|
||||||
|
**目标**: 确保修改不影响系统稳定性
|
||||||
|
|
||||||
|
**执行步骤**:
|
||||||
|
1. 全面编译测试
|
||||||
|
2. 运行相关单元测试套件
|
||||||
|
3. 执行集成测试验证
|
||||||
|
4. 更新相关文档
|
||||||
|
5. 最终验收确认
|
||||||
|
|
||||||
|
**验收标准**:
|
||||||
|
- 所有测试通过
|
||||||
|
- 系统功能完整
|
||||||
|
- 性能无明显影响
|
||||||
|
|
||||||
|
## 对规范文档的影响
|
||||||
|
|
||||||
|
### ✅ 已更新的规范文档
|
||||||
|
- **`server_repository_rules.md`** - 已包含错误示例和正确实现指南
|
||||||
|
- 新增"2.2 重要错误示例"章节
|
||||||
|
- 新增"2.3 当前项目错误统计"章节
|
||||||
|
- 新增"2.5 MyRepository基类能力"章节
|
||||||
|
|
||||||
|
### 📋 需要更新的检查清单
|
||||||
|
1. **继承层次检查** - 确保所有Repository继承MyRepository
|
||||||
|
2. **注解完整性检查** - 验证所有Repository都有@Repository注解
|
||||||
|
3. **文档规范性检查** - 确认JavaDoc注释完整性
|
||||||
|
4. **代码风格一致性检查** - 验证命名规范和代码格式
|
||||||
|
|
||||||
|
## 🎯 预期收益
|
||||||
|
|
||||||
|
### 技术收益
|
||||||
|
- ✅ **统一架构设计** - 所有Repository遵循一致的设计模式
|
||||||
|
- ✅ **代码一致性提升** - 消除实现差异,提高代码质量
|
||||||
|
- ✅ **维护成本降低** - 单一继承结构,简化维护工作
|
||||||
|
- ✅ **团队开发效率提升** - 统一规范,降低学习成本
|
||||||
|
|
||||||
|
### 质量收益
|
||||||
|
- ✅ **减少代码冗余** - 消除重复的接口继承
|
||||||
|
- ✅ **提高代码可读性** - 统一模式,易于理解
|
||||||
|
- ✅ **降低技术债务** - 减少不一致实现的技术负担
|
||||||
|
- ✅ **增强系统稳定性** - 统一模式,减少潜在风险
|
||||||
|
|
||||||
|
## 📅 后续行动
|
||||||
|
|
||||||
|
### 立即行动(本周)
|
||||||
|
1. 🔧 修复5个继承层次错误的Repository
|
||||||
|
2. 🔧 添加缺失的@Repository注解
|
||||||
|
3. 🔧 编译测试验证功能完整性
|
||||||
|
|
||||||
|
### 短期完善(下周)
|
||||||
|
1. 📝 补充JavaDoc文档注释
|
||||||
|
2. 🧪 运行完整的单元测试套件
|
||||||
|
3. 👥 代码审查确认实现质量
|
||||||
|
|
||||||
|
### 长期维护
|
||||||
|
1. 📋 建立Repository开发规范检查清单
|
||||||
|
2. 🔍 定期进行Repository实现一致性检查
|
||||||
|
3. 🎓 团队培训和规范宣贯
|
||||||
|
4. 🤖 建立CI/CD流程中的自动检查机制
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
如需技术支持或有任何疑问,请参考:
|
||||||
|
|
||||||
|
### 相关文档
|
||||||
|
- **详细技术分析**: `repository_analysis_report.md`
|
||||||
|
- **实施规范指导**: `server_repository_rules.md`
|
||||||
|
- **项目整体规范**: `.trae/rules/` 目录
|
||||||
|
|
||||||
|
### 联系方式
|
||||||
|
- 技术问题: 参考详细分析报告中的修复代码示例
|
||||||
|
- 规范疑问: 查看server_repository_rules.md文档
|
||||||
|
- 实施支持: 遵循本报告的分阶段实施计划
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
通过对项目中22个Repository的全面分析,我们发现了明显的实现不一致性问题,特别是23%的Repository存在继承层次错误。这些问题严重影响了代码的可维护性和架构的统一性。
|
||||||
|
|
||||||
|
**关键统计数据**:
|
||||||
|
- 17个Repository正确实现 (77%)
|
||||||
|
- 5个继承层次错误 (23%)
|
||||||
|
- 2个注解缺失 (9%)
|
||||||
|
- 4个文档不完整 (18%)
|
||||||
|
|
||||||
|
**修复优先级**:
|
||||||
|
1. **高优先级**: 修复5个继承层次错误(1-2天)
|
||||||
|
2. **中优先级**: 补充2个缺失注解(半天)
|
||||||
|
3. **低优先级**: 完善4个文档注释(1天)
|
||||||
|
|
||||||
|
建议按照本报告提出的分阶段实施计划,逐步改进所有Repository实现,确保项目达到统一的架构设计标准。通过这些改进,将显著提升代码质量、降低维护成本,并为团队的长期发展奠定坚实基础。
|
||||||
|
|
||||||
|
**分析状态**: ✅ 完成
|
||||||
|
**下一步**: 执行分阶段修复计划
|
||||||
|
**预计完成时间**: 3-4个工作日
|
||||||
|
|
||||||
|
---
|
||||||
|
**报告生成时间**: 2024年
|
||||||
|
**分析深度**: 全面技术分析
|
||||||
|
**实施可行性**: 高(详细修复方案已提供)
|
||||||
460
.trae/rules/server_repository_rules.md
Normal file
460
.trae/rules/server_repository_rules.md
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
# Server模块 Repository 实现经验总结
|
||||||
|
|
||||||
|
## 1. Repository 基本架构
|
||||||
|
|
||||||
|
### 1.1 接口设计原则
|
||||||
|
- Repository接口必须继承`MyRepository<T, ID>`基类
|
||||||
|
- 使用`@Repository`注解标记为Spring组件
|
||||||
|
- 遵循接口隔离原则,按业务功能组织方法
|
||||||
|
|
||||||
|
### 1.2 包结构规范
|
||||||
|
```
|
||||||
|
com.ecep.contract.ds.{business}.repository.{EntityName}Repository.java
|
||||||
|
```
|
||||||
|
示例:`ContractBalanceRepository`位于`com.ecep.contract.ds.contract.repository`
|
||||||
|
|
||||||
|
## 2. 接口继承层次
|
||||||
|
|
||||||
|
### 2.1 基础接口结构
|
||||||
|
```java
|
||||||
|
@Repository
|
||||||
|
public interface ContractBalanceRepository extends MyRepository<ContractBalance, Integer> {
|
||||||
|
// 自定义查询方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 ⚠️ 重要错误示例 - 避免这些实现方式
|
||||||
|
|
||||||
|
**❌ 错误示例1:直接继承JpaRepository**
|
||||||
|
```java
|
||||||
|
// ContractFileRepository, ContractItemRepository, VendorFileRepository, ProjectQuotationRepository
|
||||||
|
@Repository
|
||||||
|
public interface ContractFileRepository
|
||||||
|
extends JpaRepository<ContractFile, Integer>, JpaSpecificationExecutor<ContractFile> {
|
||||||
|
// 错误:不应该直接继承JpaRepository
|
||||||
|
List<ContractFile> findByContractId(Integer contractId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ 错误示例2:继承多个冗余接口**
|
||||||
|
```java
|
||||||
|
// PurchaseOrderRepository
|
||||||
|
public interface PurchaseOrderRepository extends
|
||||||
|
CrudRepository<PurchaseOrder, Integer>,
|
||||||
|
PagingAndSortingRepository<PurchaseOrder, Integer>,
|
||||||
|
JpaRepository<PurchaseOrder, Integer>,
|
||||||
|
JpaSpecificationExecutor<PurchaseOrder> {
|
||||||
|
// 错误:冗余接口继承,应该只继承MyRepository
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ 错误示例3:缺少@Repository注解**
|
||||||
|
```java
|
||||||
|
// CompanyFileRepository, ProjectFileRepository
|
||||||
|
public interface CompanyFileRepository extends MyRepository<CompanyFile, Integer> {
|
||||||
|
// 错误:缺少@Repository注解
|
||||||
|
List<CompanyFile> findByCompany(Company company);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ 错误示例4:缺少JavaDoc注释**
|
||||||
|
```java
|
||||||
|
// 多个Repository存在此问题
|
||||||
|
public interface VendorFileRepository extends MyRepository<VendorFile, Integer> {
|
||||||
|
// 错误:缺少JavaDoc注释说明用途和方法
|
||||||
|
List<VendorFile> findAllByVendorId(int vendorId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ 正确实现示例**
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 合同Repository - 提供合同相关的数据库访问操作
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface ContractRepository extends MyRepository<Contract, Integer> {
|
||||||
|
// 正确:统一继承MyRepository,有完整的JavaDoc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据合同代码查询合同
|
||||||
|
* @param code 合同代码
|
||||||
|
* @return 合同 Optional
|
||||||
|
*/
|
||||||
|
Optional<Contract> findByCode(String code);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态查询合同列表
|
||||||
|
* @param status 合同状态
|
||||||
|
* @return 合同列表
|
||||||
|
*/
|
||||||
|
List<Contract> findByStatus(ContractStatus status);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 当前项目错误统计
|
||||||
|
|
||||||
|
基于对全项目22个Repository的全面分析,发现以下问题分布:
|
||||||
|
|
||||||
|
**🔴 继承层次错误(5个):**
|
||||||
|
- `ContractFileRepository` - 直接继承JpaRepository
|
||||||
|
- `ContractItemRepository` - 直接继承JpaRepository
|
||||||
|
- `VendorFileRepository` - 直接继承JpaRepository
|
||||||
|
- `ProjectQuotationRepository` - 直接继承JpaRepository
|
||||||
|
- `PurchaseOrderRepository` - 继承4个冗余接口
|
||||||
|
|
||||||
|
**🟡 注解缺失(2个):**
|
||||||
|
- `CompanyFileRepository` - 缺少@Repository注解
|
||||||
|
- `ProjectFileRepository` - 缺少@Repository注解
|
||||||
|
|
||||||
|
**🟡 文档注释不完整(4个):**
|
||||||
|
- `CompanyFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `ProjectFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `VendorFileRepository` - 缺少JavaDoc注释
|
||||||
|
- `ProjectQuotationRepository` - 缺少JavaDoc注释
|
||||||
|
|
||||||
|
**✅ 正确实现(17个):**
|
||||||
|
- contract: ContractRepository, ContractBalanceRepository, SalesOrderRepository, ContractTypeRepository, ContractKindRepository, ContractInvoiceRepository
|
||||||
|
- company: CompanyRepository
|
||||||
|
- customer: CompanyCustomerRepository, CustomerCatalogRepository
|
||||||
|
- project: ProjectRepository
|
||||||
|
- vendor: VendorRepository
|
||||||
|
|
||||||
|
### 2.5 MyRepository基类能力
|
||||||
|
|
||||||
|
`MyRepository`接口继承结构:
|
||||||
|
- `JpaRepository`提供CRUD操作
|
||||||
|
- 继承`JpaSpecificationExecutor`提供条件查询能力
|
||||||
|
- 继承`QueryByExampleExecutor`提供示例查询能力
|
||||||
|
|
||||||
|
## 3. 查询方法设计
|
||||||
|
|
||||||
|
### 3.1 方法命名规范
|
||||||
|
使用Spring Data JPA的查询方法命名约定:
|
||||||
|
|
||||||
|
#### 基本查询方法
|
||||||
|
- `findBy{属性名}` - 根据属性查找
|
||||||
|
- `findBy{属性名}And{属性名}` - 多条件AND查询
|
||||||
|
- `findBy{属性名}Or{属性名}` - 多条件OR查询
|
||||||
|
- `findBy{属性名}OrderBy{排序属性}` - 带排序查询
|
||||||
|
|
||||||
|
#### 关联对象查询
|
||||||
|
- `findBy{关联对象属性名}.{关联对象属性}` - 嵌套属性查询
|
||||||
|
- 支持多级嵌套,如:`findByContractCompanyName`
|
||||||
|
|
||||||
|
#### 特殊查询类型
|
||||||
|
```java
|
||||||
|
// 根据业务ID查找
|
||||||
|
ContractBalance findByGuid(UUID guid);
|
||||||
|
|
||||||
|
// 根据合同ID查找(带分页支持)
|
||||||
|
Page<ContractBalance> findByContractId(Integer contractId, Pageable pageable);
|
||||||
|
|
||||||
|
// 根据多个条件组合查询
|
||||||
|
List<ContractBalance> findByContractIdAndRefId(Integer contractId, String refId);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 自定义查询实现
|
||||||
|
当方法命名无法满足需求时,使用`@Query`注解:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Query("SELECT cb FROM ContractBalance cb WHERE cb.contract.id = :contractId " +
|
||||||
|
"AND cb.guid = :guid AND cb.setupDate >= :startDate")
|
||||||
|
Page<ContractBalance> findByContractAndGuidAndDateRange(
|
||||||
|
@Param("contractId") Integer contractId,
|
||||||
|
@Param("guid") UUID guid,
|
||||||
|
@Param("startDate") LocalDateTime startDate,
|
||||||
|
Pageable pageable
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 分页和排序支持
|
||||||
|
|
||||||
|
### 4.1 分页查询
|
||||||
|
```java
|
||||||
|
// 在Repository接口中定义
|
||||||
|
Page<ContractBalance> findByContractId(Integer contractId, Pageable pageable);
|
||||||
|
|
||||||
|
// 在Service中调用
|
||||||
|
Page<ContractBalance> result = repository.findByContractId(contractId,
|
||||||
|
PageRequest.of(page, size, Sort.by("setupDate").descending()));
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 排序规范
|
||||||
|
- 默认按主键ID降序排列
|
||||||
|
- 业务相关字段按创建时间降序
|
||||||
|
- 支持多字段排序:`Sort.by("field1").ascending().and(Sort.by("field2").descending())`
|
||||||
|
|
||||||
|
## 5. 统计和聚合查询
|
||||||
|
|
||||||
|
### 5.1 基础统计
|
||||||
|
```java
|
||||||
|
@Query("SELECT COUNT(cb) FROM ContractBalance cb WHERE cb.contract.id = :contractId")
|
||||||
|
Long countByContractId(@Param("contractId") Integer contractId);
|
||||||
|
|
||||||
|
@Query("SELECT SUM(cb.balanceAmount) FROM ContractBalance cb WHERE cb.contract.id = :contractId")
|
||||||
|
BigDecimal sumBalanceByContractId(@Param("contractId") Integer contractId);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 复杂聚合
|
||||||
|
```java
|
||||||
|
@Query("SELECT new com.ecep.contract.vo.ContractBalanceStatsVO(" +
|
||||||
|
"cb.contract.id, COUNT(cb), SUM(cb.balanceAmount)) " +
|
||||||
|
"FROM ContractBalance cb GROUP BY cb.contract.id")
|
||||||
|
List<ContractBalanceStatsVO> getBalanceStatsByContract();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 缓存策略
|
||||||
|
|
||||||
|
### 6.1 缓存注解使用
|
||||||
|
虽然Repository本身不直接使用缓存,但需要考虑Service层的缓存需求:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 在Service层配合使用
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
ContractBalance findById(Integer id);
|
||||||
|
|
||||||
|
@CacheEvict(key = "#p0.id")
|
||||||
|
ContractBalance save(ContractBalance contractBalance);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 缓存键设计原则
|
||||||
|
- 单一记录:使用主键作为键,如`"balance-" + id`
|
||||||
|
- 按业务维度缓存:使用业务标识,如`"contract-" + contractId`
|
||||||
|
- 避免缓存键冲突
|
||||||
|
|
||||||
|
## 7. 事务管理
|
||||||
|
|
||||||
|
### 7.1 事务传播级别
|
||||||
|
- 查询方法:默认`REQUIRED`
|
||||||
|
- 写操作:明确指定`@Transactional(propagation = Propagation.REQUIRED)`
|
||||||
|
|
||||||
|
### 7.2 异常处理
|
||||||
|
```java
|
||||||
|
@Transactional
|
||||||
|
public ContractBalance saveWithRetry(ContractBalance balance) {
|
||||||
|
try {
|
||||||
|
return repository.save(balance);
|
||||||
|
} catch (DataIntegrityViolationException e) {
|
||||||
|
// 处理数据完整性异常
|
||||||
|
throw new BusinessException("数据重复或违反约束", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 性能优化
|
||||||
|
|
||||||
|
### 8.1 懒加载优化
|
||||||
|
```java
|
||||||
|
// 在查询时指定抓取策略
|
||||||
|
@Query("SELECT cb FROM ContractBalance cb LEFT JOIN FETCH cb.contract " +
|
||||||
|
"LEFT JOIN FETCH cb.employee WHERE cb.id = :id")
|
||||||
|
Optional<ContractBalance> findByIdWithRelations(@Param("id") Integer id);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 批量操作优化
|
||||||
|
```java
|
||||||
|
// 批量插入
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM ContractBalance cb WHERE cb.contract.id IN :contractIds")
|
||||||
|
void deleteByContractIds(@Param("contractIds") List<Integer> contractIds);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 错误处理和调试
|
||||||
|
|
||||||
|
### 9.1 常见异常类型
|
||||||
|
- `DataAccessException` - 数据访问异常
|
||||||
|
- `DataIntegrityViolationException` - 数据完整性异常
|
||||||
|
- `EmptyResultDataAccessException` - 空结果异常
|
||||||
|
|
||||||
|
### 9.2 日志记录
|
||||||
|
```java
|
||||||
|
@Repository
|
||||||
|
@Slf4j
|
||||||
|
public class ContractBalanceRepository {
|
||||||
|
|
||||||
|
@Query("...")
|
||||||
|
public List<ContractBalance> findComplexQuery(...) {
|
||||||
|
log.debug("执行复杂查询: {}", jpql);
|
||||||
|
// 执行查询
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 测试策略
|
||||||
|
|
||||||
|
### 10.1 Repository测试
|
||||||
|
```java
|
||||||
|
@DataJpaTest
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class ContractBalanceRepositoryTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestEntityManager em;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ContractBalanceRepository repository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFindByContractId() {
|
||||||
|
// 测试数据准备
|
||||||
|
ContractBalance balance = createTestBalance();
|
||||||
|
|
||||||
|
// 执行测试
|
||||||
|
Page<ContractBalance> result = repository.findByContractId(
|
||||||
|
balance.getContract().getId(),
|
||||||
|
PageRequest.of(0, 10)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 断言验证
|
||||||
|
assertThat(result.getContent()).hasSize(1);
|
||||||
|
assertThat(result.getContent().get(0)).isEqualTo(balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 性能测试
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
@RepeatedTest(100)
|
||||||
|
@Timeout(value = 5, unit = TimeUnit.SECONDS)
|
||||||
|
void performanceTest() {
|
||||||
|
// 性能测试实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. 最佳实践总结
|
||||||
|
|
||||||
|
### 11.1 设计原则
|
||||||
|
1. **单一职责**:每个Repository只负责一个实体类型
|
||||||
|
2. **接口隔离**:根据客户端需求设计专用查询方法
|
||||||
|
3. **命名清晰**:方法名应能直观表达查询意图
|
||||||
|
4. **性能优先**:合理使用索引和查询优化
|
||||||
|
|
||||||
|
### 11.2 代码规范
|
||||||
|
1. **注解完整**:正确使用`@Repository`、`@Query`、`@Param`
|
||||||
|
2. **参数校验**:对传入参数进行null和有效性检查
|
||||||
|
3. **异常处理**:捕获并处理数据库访问异常
|
||||||
|
4. **日志记录**:记录关键查询操作和性能指标
|
||||||
|
|
||||||
|
### 11.3 维护性
|
||||||
|
1. **版本兼容**:考虑数据库模式变更的影响
|
||||||
|
2. **向后兼容**:新增方法不影响现有功能
|
||||||
|
3. **文档完整**:复杂查询添加JavaDoc说明
|
||||||
|
4. **测试覆盖**:确保关键查询逻辑有测试覆盖
|
||||||
|
|
||||||
|
## 12. 常见问题和解决方案
|
||||||
|
|
||||||
|
### 12.1 N+1查询问题
|
||||||
|
**问题**:查询列表时触发N+1次关联查询
|
||||||
|
**解决**:使用`JOIN FETCH`或`@EntityGraph`
|
||||||
|
|
||||||
|
### 12.2 性能瓶颈
|
||||||
|
**问题**:复杂查询导致响应慢
|
||||||
|
**解决**:
|
||||||
|
1. 添加数据库索引
|
||||||
|
2. 优化查询语句
|
||||||
|
3. 使用分页限制结果集
|
||||||
|
4. 考虑使用缓存
|
||||||
|
|
||||||
|
### 12.3 事务死锁
|
||||||
|
**问题**:并发操作导致事务死锁
|
||||||
|
**解决**:
|
||||||
|
1. 合理设计事务边界
|
||||||
|
2. 调整事务隔离级别
|
||||||
|
3. 实现重试机制
|
||||||
|
|
||||||
|
## 13. 扩展建议
|
||||||
|
|
||||||
|
### 13.1 动态查询支持
|
||||||
|
考虑使用`Querydsl`或`Specification`支持动态查询构建。
|
||||||
|
|
||||||
|
### 13.2 读写分离
|
||||||
|
实现主从数据库分离,提升查询性能。
|
||||||
|
|
||||||
|
### 13.3 分布式缓存
|
||||||
|
结合Redis等分布式缓存,提升查询性能。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 实施检查清单
|
||||||
|
|
||||||
|
### 14.1 Repository实现后验证步骤
|
||||||
|
在每个Repository实现完成后,请按以下清单进行验证:
|
||||||
|
|
||||||
|
#### ✅ 基础架构检查
|
||||||
|
- [ ] Repository接口继承了 `MyRepository<T, ID>`
|
||||||
|
- [ ] 使用了 `@Repository` 注解
|
||||||
|
- [ ] 包路径符合规范:`com.ecep.contract.ds.{business}.repository`
|
||||||
|
- [ ] 类名符合命名规范:`{EntityName}Repository`
|
||||||
|
|
||||||
|
#### ✅ 方法设计检查
|
||||||
|
- [ ] 遵循Spring Data JPA命名约定
|
||||||
|
- [ ] 包含必要的业务查询方法
|
||||||
|
- [ ] 复杂查询使用 `@Query` 注解
|
||||||
|
- [ ] 分页方法支持 `Pageable` 参数
|
||||||
|
|
||||||
|
#### ✅ 文档和注释检查
|
||||||
|
- [ ] 类级别JavaDoc注释完整
|
||||||
|
- [ ] 关键方法有JavaDoc说明
|
||||||
|
- [ ] 参数和返回值有明确说明
|
||||||
|
|
||||||
|
#### ✅ 性能和质量检查
|
||||||
|
- [ ] 避免N+1查询问题
|
||||||
|
- [ ] 适当使用索引提示
|
||||||
|
- [ ] 包含必要的单元测试
|
||||||
|
- [ ] 异常处理考虑周全
|
||||||
|
|
||||||
|
### 14.2 常见错误检查
|
||||||
|
在提交代码前,特别检查是否犯了以下常见错误:
|
||||||
|
|
||||||
|
#### ❌ 继承层次错误
|
||||||
|
```java
|
||||||
|
// 错误示例 - 不要这样做
|
||||||
|
public interface SomeRepository extends JpaRepository<Entity, Integer>
|
||||||
|
public interface AnotherRepository extends CrudRepository<Entity, Integer>
|
||||||
|
|
||||||
|
// 正确做法 - 统一使用MyRepository
|
||||||
|
public interface SomeRepository extends MyRepository<Entity, Integer>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ❌ 方法命名不规范
|
||||||
|
```java
|
||||||
|
// 错误示例
|
||||||
|
List<Entity> getDataByCondition(String condition) // 使用get前缀而非find
|
||||||
|
|
||||||
|
// 正确做法
|
||||||
|
List<Entity> findByCondition(String condition) // 使用find前缀
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ❌ 缺少必要文档
|
||||||
|
```java
|
||||||
|
// 错误示例 - 无注释
|
||||||
|
List<Entity> findByStatus(String status);
|
||||||
|
|
||||||
|
// 正确做法 - 完整注释
|
||||||
|
/**
|
||||||
|
* 根据状态查询实体列表
|
||||||
|
*
|
||||||
|
* @param status 状态值
|
||||||
|
* @return 符合状态的实体列表
|
||||||
|
*/
|
||||||
|
List<Entity> findByStatus(String status);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 14.3 架构一致性验证
|
||||||
|
确保新实现的Repository与项目中其他Repository保持一致:
|
||||||
|
|
||||||
|
1. **继承模式统一**:所有Repository都必须继承MyRepository
|
||||||
|
2. **注解使用统一**:统一使用@Repository注解
|
||||||
|
3. **命名约定统一**:遵循Spring Data JPA命名规范
|
||||||
|
4. **文档风格统一**:保持JavaDoc注释风格一致
|
||||||
|
|
||||||
|
### 14.4 后续维护注意事项
|
||||||
|
- 新增查询方法时遵循现有命名规范
|
||||||
|
- 修改现有方法时保持向后兼容性
|
||||||
|
- 定期审查Repository方法的性能和合理性
|
||||||
|
- 及时更新相关文档和测试用例
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本文档基于ContractBalanceRepository实现经验总结,结合项目实际情况分析,其他Repository实现应参考此文档规范。特别注意避免文档中提到的常见错误。*
|
||||||
435
.trae/rules/server_service_rules.md
Normal file
435
.trae/rules/server_service_rules.md
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
# 服务器端Service设计规范
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
每个业务域下的service目录结构示例:
|
||||||
|
```
|
||||||
|
ds/
|
||||||
|
├── company/service/
|
||||||
|
│ ├── CompanyService.java # 主业务服务
|
||||||
|
│ ├── CompanyContactService.java # 联系人服务
|
||||||
|
│ ├── CompanyFileService.java # 文件管理服务
|
||||||
|
│ ├── CompanyOldNameService.java # 曾用名服务
|
||||||
|
│ └── ...
|
||||||
|
├── contract/service/
|
||||||
|
│ ├── ContractService.java # 主业务服务
|
||||||
|
│ ├── ContractCatalogService.java # 分类目录服务
|
||||||
|
│ └── ...
|
||||||
|
├── customer/service/
|
||||||
|
│ ├── CustomerService.java # 主业务服务(继承CompanyBasicService)
|
||||||
|
│ └── ...
|
||||||
|
├── project/service/
|
||||||
|
│ ├── ProjectService.java # 主业务服务
|
||||||
|
│ ├── ProjectFileService.java # 文件管理服务
|
||||||
|
│ └── ...
|
||||||
|
└── vendor/service/
|
||||||
|
├── VendorService.java # 主业务服务(继承CompanyBasicService)
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心基类和接口体系
|
||||||
|
|
||||||
|
### 主要基类
|
||||||
|
- **EntityService<M, Vo, ID>**: 通用实体服务基类,提供CRUD操作的标准实现
|
||||||
|
- **CompanyBasicService**: 专门处理公司相关业务的基础服务类,支持公司关联查询
|
||||||
|
|
||||||
|
### 核心接口
|
||||||
|
- **IEntityService<T>**: 实体基本操作接口
|
||||||
|
- **QueryService<Vo>**: 查询服务接口
|
||||||
|
- **VoableService<M, Vo>**: 实体与视图对象转换服务接口
|
||||||
|
|
||||||
|
## 注解使用规范
|
||||||
|
|
||||||
|
### 类级别注解
|
||||||
|
```java
|
||||||
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
|
@Service // Spring服务组件
|
||||||
|
@CacheConfig(cacheNames = "业务缓存名称") // 缓存配置
|
||||||
|
public class CompanyService extends EntityService<Company, CompanyVo, Integer>
|
||||||
|
implements IEntityService<Company>, QueryService<CompanyVo>, VoableService<Company, CompanyVo> {
|
||||||
|
// 实现代码
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法级别注解
|
||||||
|
```java
|
||||||
|
// 查询方法缓存 - 使用参数作为缓存键
|
||||||
|
@Cacheable(key = "#p0") // ID查询
|
||||||
|
public CompanyVo findById(Integer id) {
|
||||||
|
return repository.findById(id).map(Company::toVo).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "'name-'+#p0") // 名称查询,带前缀
|
||||||
|
public CompanyVo findByName(String name) {
|
||||||
|
return repository.findFirstByName(name).map(Company::toVo).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改方法缓存清理 - 清理所有相关缓存
|
||||||
|
@Caching(evict = {
|
||||||
|
@CacheEvict(key = "#p0.id"),
|
||||||
|
@CacheEvict(key = "'name-'+#p0.name"),
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code")
|
||||||
|
})
|
||||||
|
public Contract save(Contract contract) {
|
||||||
|
return contractRepository.save(contract);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖注入规范
|
||||||
|
|
||||||
|
### Repository注入
|
||||||
|
```java
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private CompanyRepository repository;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service间依赖注入
|
||||||
|
```java
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private ContractService contractService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private VendorService vendorService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private CompanyContactService companyContactService;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 外部服务依赖注入
|
||||||
|
```java
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private CloudRkService cloudRkService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private CloudTycService cloudTycService;
|
||||||
|
|
||||||
|
@Autowired(required = false) // 可选依赖
|
||||||
|
private YongYouU8Service yongYouU8Service;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询实现模式
|
||||||
|
|
||||||
|
### 标准查询实现
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public Page<CompanyVo> findAll(JsonNode paramsNode, Pageable pageable) {
|
||||||
|
Specification<Company> spec = null;
|
||||||
|
|
||||||
|
// 搜索文本查询
|
||||||
|
if (paramsNode.has("searchText")) {
|
||||||
|
spec = getSpecification(paramsNode.get("searchText").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字段等值查询
|
||||||
|
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name", "uniscid", "abbName");
|
||||||
|
|
||||||
|
// 分页查询并转换为VO
|
||||||
|
return findAll(spec, pageable).map(Company::toVo);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 复杂查询条件构建
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public Specification<Company> getSpecification(String searchText) {
|
||||||
|
if (!StringUtils.hasText(searchText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (root, query, builder) -> {
|
||||||
|
return builder.or(
|
||||||
|
builder.like(root.get("name"), "%" + searchText + "%"),
|
||||||
|
builder.like(root.get("code"), "%" + searchText + "%"),
|
||||||
|
builder.like(root.get("description"), "%" + searchText + "%"));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CompanyBasicService继承模式
|
||||||
|
继承CompanyBasicService的Service(如CustomerService、VendorService)提供公司关联查询:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public Specification<Vendor> getSpecification(String searchText) {
|
||||||
|
if (!StringUtils.hasText(searchText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用公司关联查询
|
||||||
|
Specification<Vendor> nameSpec = (root, query, builder) -> {
|
||||||
|
Path<Company> company = root.get("company");
|
||||||
|
return companyService.buildSearchPredicate(searchText, company, query, builder);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 数字ID查询
|
||||||
|
if (MyStringUtils.isAllDigit(searchText)) {
|
||||||
|
try {
|
||||||
|
int id = Integer.parseInt(searchText);
|
||||||
|
nameSpec = SpecificationUtils.or(nameSpec, (root, query, builder) -> {
|
||||||
|
return builder.equal(root.get("id"), id);
|
||||||
|
});
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实体搜索
|
||||||
|
List<VendorEntity> searched = vendorEntityService.search(searchText);
|
||||||
|
if (!searched.isEmpty()) {
|
||||||
|
nameSpec = SpecificationUtils.or(nameSpec, (root, query, builder) -> {
|
||||||
|
return builder.in(root.get("id")).value(searched.stream()
|
||||||
|
.map(VendorEntity::getVendor)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(Vendor::getId)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameSpec;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 业务逻辑实现模式
|
||||||
|
|
||||||
|
### 1. 主业务Service(继承EntityService)
|
||||||
|
```java
|
||||||
|
@Lazy
|
||||||
|
@Service
|
||||||
|
@CacheConfig(cacheNames = "contract")
|
||||||
|
public class ContractService extends EntityService<Contract, ContractVo, Integer>
|
||||||
|
implements IEntityService<Contract>, QueryService<ContractVo>, VoableService<Contract, ContractVo> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContractRepository getRepository() {
|
||||||
|
return contractRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
public ContractVo findById(Integer id) {
|
||||||
|
return getRepository().findById(id).map(Contract::toVo).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务特定方法
|
||||||
|
public List<Contract> findAllByCompany(Company company) {
|
||||||
|
return contractRepository.findAllByCompanyId(company.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getContractCatalogPath(ContractCatalogVo catalog, ContractVo contract) {
|
||||||
|
// 文件路径处理逻辑
|
||||||
|
String parent = catalog.getParent();
|
||||||
|
File dir = getBasePath();
|
||||||
|
// ... 路径构建逻辑
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 继承CompanyBasicService的Service
|
||||||
|
```java
|
||||||
|
@Lazy
|
||||||
|
@Service
|
||||||
|
@CacheConfig(cacheNames = "company-customer")
|
||||||
|
public class CustomerService extends CompanyBasicService
|
||||||
|
implements IEntityService<CompanyCustomer>, QueryService<CustomerVo>,
|
||||||
|
VoableService<CompanyCustomer, CustomerVo> {
|
||||||
|
|
||||||
|
// 提供公司关联查询
|
||||||
|
public CompanyCustomer findByCompany(Company company) {
|
||||||
|
return repository.findByCompany(company).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomerVo findByCompany(CompanyVo company) {
|
||||||
|
return repository.findByCompanyId(company.getId()).map(CompanyCustomer::toVo).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件重建业务逻辑
|
||||||
|
public boolean reBuildingFiles(CompanyCustomer companyCustomer, MessageHolder holder) {
|
||||||
|
List<CompanyCustomerFile> dbFiles = companyCustomerFileService.findAllByCustomer(companyCustomer);
|
||||||
|
Map<String, CompanyCustomerFile> map = new HashMap<>();
|
||||||
|
|
||||||
|
boolean modified = fetchDbFiles(dbFiles, map, holder::info);
|
||||||
|
|
||||||
|
List<File> needMoveToCompanyPath = new ArrayList<>();
|
||||||
|
fetchFiles(companyCustomer.getPath(), needMoveToCompanyPath, retrieveFiles, map, holder::info);
|
||||||
|
|
||||||
|
moveFileToCompany(companyCustomer.getCompany(), needMoveToCompanyPath);
|
||||||
|
|
||||||
|
holder.info("导入 " + retrieveFiles.size() + " 个文件");
|
||||||
|
|
||||||
|
if (!retrieveFiles.isEmpty()) {
|
||||||
|
retrieveFiles.forEach(v -> v.setCustomer(companyCustomer));
|
||||||
|
companyCustomerFileService.saveAll(retrieveFiles);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文件管理Service实现模式
|
||||||
|
|
||||||
|
### 文件路径管理
|
||||||
|
```java
|
||||||
|
public File getBasePath() {
|
||||||
|
return new File(confService.getString(ContractConstant.KEY_BASE_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getContractCatalogPath(ContractCatalogVo catalog, ContractVo contract) {
|
||||||
|
String parent = catalog.getParent();
|
||||||
|
File dir = getBasePath();
|
||||||
|
|
||||||
|
if (StringUtils.hasText(parent)) {
|
||||||
|
dir = new File(dir, parent);
|
||||||
|
if (!dir.exists() && !dir.mkdir()) {
|
||||||
|
System.out.println("unable make directory = " + dir.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = new File(dir, catalog.getPath());
|
||||||
|
if (!dir.exists() && !dir.mkdir()) {
|
||||||
|
System.out.println("unable make directory = " + dir.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catalog.isUseYear()) {
|
||||||
|
String code = contract.getCode();
|
||||||
|
String catalogCode = catalog.getCode();
|
||||||
|
int length = catalogCode.length();
|
||||||
|
String yearCode = code.substring(length, length + 2);
|
||||||
|
dir = new File(dir, "20" + yearCode);
|
||||||
|
if (!dir.exists() && !dir.mkdir()) {
|
||||||
|
System.out.println("unable make directory = " + dir.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 缓存策略最佳实践
|
||||||
|
|
||||||
|
### 缓存键设计原则
|
||||||
|
- **ID查询**: `@Cacheable(key = "#p0")`
|
||||||
|
- **字符串查询**: `@Cacheable(key = "'name-'+#p0")`
|
||||||
|
- **复合键查询**: `@Cacheable(key = "'code-year-'+#p0+'-'+#p1")`
|
||||||
|
|
||||||
|
### 缓存清理策略
|
||||||
|
```java
|
||||||
|
@Caching(evict = {
|
||||||
|
@CacheEvict(key = "#p0.id"), // 主键缓存
|
||||||
|
@CacheEvict(key = "'name-'+#p0.name"), // 名称缓存
|
||||||
|
@CacheEvict(key = "'code-'+#p0.code"), // 编码缓存
|
||||||
|
@CacheEvict(key = "'guid-'+#p0.guid") // GUID缓存
|
||||||
|
})
|
||||||
|
public Contract save(Contract contract) {
|
||||||
|
return contractRepository.save(contract);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能优化建议
|
||||||
|
|
||||||
|
### 1. 延迟加载
|
||||||
|
所有Service依赖都应使用@Lazy注解,避免循环依赖和启动时的性能问题。
|
||||||
|
|
||||||
|
### 2. 缓存粒度
|
||||||
|
- 单条查询使用细粒度缓存键
|
||||||
|
- 避免缓存大量数据的列表查询
|
||||||
|
- 合理设置缓存过期策略
|
||||||
|
|
||||||
|
### 3. 分页查询
|
||||||
|
使用Page<T>进行分页查询,避免一次性加载大量数据:
|
||||||
|
```java
|
||||||
|
public Page<Project> findAll(Specification<Project> spec, Pageable pageable) {
|
||||||
|
return projectRepository.findAll(spec, pageable);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 批量操作
|
||||||
|
对于大量数据操作,考虑使用批量处理:
|
||||||
|
```java
|
||||||
|
public void saveAll(List<CompanyCustomerFile> files) {
|
||||||
|
companyCustomerFileService.saveAll(retrieveFiles);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 异常处理规范
|
||||||
|
|
||||||
|
### 参数校验
|
||||||
|
```java
|
||||||
|
public void updateByVo(CustomerCatalog model, CustomerCatalogVo vo) {
|
||||||
|
if (model == null) {
|
||||||
|
throw new ServiceException("实体对象不能为空");
|
||||||
|
}
|
||||||
|
if (vo == null) {
|
||||||
|
throw new ServiceException("VO对象不能为空");
|
||||||
|
}
|
||||||
|
// ... 业务逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 业务异常处理
|
||||||
|
```java
|
||||||
|
public Company findAndRemoveDuplicateCompanyByUniscid(String uniscid) {
|
||||||
|
List<Company> companies = repository.findAllByUniscid(uniscid);
|
||||||
|
if (companies.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (companies.size() == 1) {
|
||||||
|
return companies.getFirst();
|
||||||
|
} else {
|
||||||
|
List<Company> result = removeDuplicatesByUniscid(companies);
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
return result.getFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 工具类使用规范
|
||||||
|
|
||||||
|
### SpecificationUtils使用
|
||||||
|
```java
|
||||||
|
// 字段等值查询
|
||||||
|
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name", "code", "status");
|
||||||
|
|
||||||
|
// 参数关联查询
|
||||||
|
spec = SpecificationUtils.andParam(spec, paramsNode, "company", "catalog", "type");
|
||||||
|
|
||||||
|
// 搜索文本组合
|
||||||
|
spec = SpecificationUtils.andWith(searchText, this::buildSearchSpecification);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字符串工具使用CompanyBasicService
|
||||||
|
```java
|
||||||
|
// 全数字判断
|
||||||
|
if (MyStringUtils.isAllDigit(searchText)) {
|
||||||
|
// 数字处理逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空值检查
|
||||||
|
if (!StringUtils.hasText(searchText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践总结
|
||||||
|
|
||||||
|
1. **接口实现完整性**: 所有Service应实现三个核心接口,确保功能一致性
|
||||||
|
2. **缓存策略一致性**: 遵循统一的缓存键设计和清理策略
|
||||||
|
3. **依赖注入规范**: 使用@Lazy避免循环依赖,清晰管理Service间依赖关系
|
||||||
|
4. **查询性能优化**: 合理使用SpecificationUtils构建查询条件,支持分页查询
|
||||||
|
5. **异常处理统一**: 统一的异常处理方式,提高代码健壮性
|
||||||
|
6. **代码复用**: 继承合适的基类,复用通用逻辑
|
||||||
|
7. **文档注释**: 关键方法和复杂业务逻辑应有清晰的JavaDoc注释
|
||||||
|
8. **性能监控**: 关注缓存命中率,适时调整缓存策略
|
||||||
|
|
||||||
|
这套规范确保了Service层的代码质量、性能和可维护性,为整个系统的稳定运行提供了坚实基础。
|
||||||
460
.trae/rules/server_task_rules.md
Normal file
460
.trae/rules/server_task_rules.md
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
# WebSocketServerTasker 接口实现规范
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
本文档总结了实现 `WebSocketServerTasker` 接口的标准做法和最佳实践,基于对项目中已有实现类的分析。该接口用于服务器端实现支持WebSocket通信的任务处理器,实现异步任务的执行和状态监控。
|
||||||
|
|
||||||
|
## 2. WebSocketServerTasker 接口介绍
|
||||||
|
|
||||||
|
`WebSocketServerTasker` 接口位于 `com.ecep.contract.service.tasker` 包下,继承自 `java.util.concurrent.Callable<Object>`,定义了以下核心方法:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface WebSocketServerTasker extends Callable<Object> {
|
||||||
|
// 初始化任务,处理传入的参数
|
||||||
|
void init(JsonNode argsNode);
|
||||||
|
|
||||||
|
// 设置会话信息(默认实现为空)
|
||||||
|
default void setSession(SessionInfo session) {}
|
||||||
|
|
||||||
|
// 设置消息处理函数
|
||||||
|
void setMessageHandler(Predicate<Message> messageHandler);
|
||||||
|
|
||||||
|
// 设置标题处理函数
|
||||||
|
void setTitleHandler(Predicate<String> titleHandler);
|
||||||
|
|
||||||
|
// 设置属性处理函数
|
||||||
|
void setPropertyHandler(BiConsumer<String, Object> propertyHandler);
|
||||||
|
|
||||||
|
// 设置进度处理函数
|
||||||
|
void setProgressHandler(BiConsumer<Long, Long> progressHandler);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 基础实现模式
|
||||||
|
|
||||||
|
### 3.1 推荐实现方式
|
||||||
|
|
||||||
|
通过分析项目中的实现类,推荐以下标准实现模式:
|
||||||
|
|
||||||
|
1. **继承 Tasker<Object> 并实现 WebSocketServerTasker 接口**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class YourTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
// 实现代码
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **在 Tasker 基类中已实现的方法**
|
||||||
|
|
||||||
|
`Tasker` 类已经提供了 `WebSocketServerTasker` 接口中大部分方法的实现,包括:
|
||||||
|
- `setMessageHandler`
|
||||||
|
- `setTitleHandler`
|
||||||
|
- `setPropertyHandler`
|
||||||
|
- `setProgressHandler`
|
||||||
|
- `call()` 方法(实现了 `Callable` 接口)
|
||||||
|
|
||||||
|
这使得实现类只需要关注特定业务逻辑的实现。
|
||||||
|
|
||||||
|
## 3.2 WebSocketServerTasker 注册配置
|
||||||
|
|
||||||
|
要使WebSocketServerTasker能够被客户端通过WebSocket调用,必须在`tasker_mapper.json`配置文件中进行注册。
|
||||||
|
|
||||||
|
### 3.2.1 注册配置文件
|
||||||
|
|
||||||
|
配置文件位置:`server/src/main/resources/tasker_mapper.json`
|
||||||
|
|
||||||
|
### 3.2.2 注册格式
|
||||||
|
|
||||||
|
注册格式为JSON对象,包含一个`tasks`字段,该字段是一个键值对映射,其中:
|
||||||
|
- **键**:任务名称(客户端通过此名称调用任务)
|
||||||
|
- **值**:任务类的完全限定名
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"任务名称": "任务类的完全限定名",
|
||||||
|
"ContractVerifyTasker": "com.ecep.contract.service.tasker.ContractVerifyTasker"
|
||||||
|
},
|
||||||
|
"descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2.3 注册示例
|
||||||
|
|
||||||
|
假设我们创建了一个名为`CustomTasker`的新任务类,其完全限定名为`com.ecep.contract.service.tasker.CustomTasker`,则注册方式如下:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"CustomTasker": "com.ecep.contract.service.tasker.CustomTasker",
|
||||||
|
"OtherTasker": "..."
|
||||||
|
},
|
||||||
|
"descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2.4 注册机制说明
|
||||||
|
|
||||||
|
WebSocketServerTaskManager类在启动时会读取`tasker_mapper.json`文件,初始化任务类映射表。当客户端请求执行任务时,系统会:
|
||||||
|
|
||||||
|
1. 根据客户端提供的任务名称从映射表中查找对应的任务类
|
||||||
|
2. 使用反射机制实例化任务对象
|
||||||
|
3. 设置WebSocket会话和各类处理器
|
||||||
|
4. 执行任务并通过WebSocket返回结果
|
||||||
|
|
||||||
|
## 4. ContractRepairAllTasker 升级经验
|
||||||
|
|
||||||
|
通过分析 `ContractRepairAllTasker` 的实现,我们总结了以下升级经验:
|
||||||
|
|
||||||
|
### 4.1 升级步骤
|
||||||
|
|
||||||
|
1. **继承适当的基础类**:根据业务需求选择继承 `Tasker<Object>` 或其他特定的抽象类(如 `AbstContractRepairTasker`)
|
||||||
|
|
||||||
|
2. **实现 WebSocketServerTasker 接口**:声明实现该接口,自动继承接口定义的方法
|
||||||
|
|
||||||
|
3. **实现 init 方法**:处理任务初始化和参数解析
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
// 解析参数或初始化任务状态
|
||||||
|
// 如果 Client 没有传递参数,就不做处理
|
||||||
|
// do nothing
|
||||||
|
|
||||||
|
// 如果有参数,正确做法:检查参数有效性并安全解析
|
||||||
|
if (argsNode != null && argsNode.size() > 0) {
|
||||||
|
ContractService contractService = getCachedBean(ContractService.class);
|
||||||
|
int contractId = argsNode.get(0).asInt();
|
||||||
|
this.contract = contractService.findById(contractId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **实现或重写 execute 方法**:提供具体的业务逻辑实现
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
// 调用父类初始化
|
||||||
|
super.execute(holder);
|
||||||
|
updateTitle("同步修复所有合同");
|
||||||
|
// 执行具体业务逻辑
|
||||||
|
repair(holder);
|
||||||
|
return null; // 通常返回 null 或处理结果
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **使用更新方法提供状态反馈**:在执行过程中使用 `updateTitle`、`updateProgress` 等方法提供实时反馈
|
||||||
|
|
||||||
|
```java
|
||||||
|
updateProgress(counter.incrementAndGet(), total);
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **支持任务取消**:定期检查 `isCancelled()` 方法,支持任务的取消操作
|
||||||
|
|
||||||
|
```java
|
||||||
|
if (isCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 关键经验
|
||||||
|
|
||||||
|
1. **消息处理**:通过 `MessageHolder` 处理和记录执行过程中的消息,方便调试和错误追踪
|
||||||
|
|
||||||
|
2. **进度更新**:对于批量操作,使用计数器和总数定期更新进度,提供良好的用户体验
|
||||||
|
|
||||||
|
3. **异常处理**:在循环处理中捕获异常并记录,确保单个项目的失败不会导致整个任务失败
|
||||||
|
|
||||||
|
4. **分页处理**:对于大量数据的处理,使用分页查询避免内存溢出
|
||||||
|
|
||||||
|
## 5. 共同模式和最佳实践
|
||||||
|
|
||||||
|
通过分析项目中的17个实现类,我们总结了以下共同模式和最佳实践:
|
||||||
|
|
||||||
|
### 5.1 共同模式
|
||||||
|
|
||||||
|
1. **继承结构**:所有实现类都继承 `Tasker<Object>` 并实现 `WebSocketServerTasker` 接口
|
||||||
|
|
||||||
|
2. **核心方法实现**:
|
||||||
|
- 实现 `init(JsonNode argsNode)` 方法处理参数
|
||||||
|
- 实现或重写 `execute(MessageHolder holder)` 方法实现业务逻辑
|
||||||
|
|
||||||
|
3. **状态更新**:使用 `updateTitle`、`updateMessage`、`updateProgress` 等方法更新任务状态
|
||||||
|
|
||||||
|
4. **Bean获取**:使用 `getCachedBean` 方法获取Spring管理的Bean
|
||||||
|
|
||||||
|
```java
|
||||||
|
ContractService contractService = getCachedBean(ContractService.class);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 最佳实践
|
||||||
|
|
||||||
|
1. **参数验证**:在 `init` 方法中验证和转换输入参数
|
||||||
|
|
||||||
|
2. **进度反馈**:对于长时间运行的任务,提供合理的进度更新
|
||||||
|
|
||||||
|
3. **异常处理**:捕获并处理特定异常,提供有意义的错误信息
|
||||||
|
|
||||||
|
4. **取消支持**:实现任务取消机制,定期检查 `isCancelled()` 状态
|
||||||
|
|
||||||
|
5. **资源清理**:在任务结束或取消时清理资源
|
||||||
|
|
||||||
|
6. **日志记录**:使用 `slf4j` 记录关键操作和异常信息
|
||||||
|
|
||||||
|
7. **属性传递**:使用 `updateProperty` 方法传递任务执行结果或状态
|
||||||
|
|
||||||
|
```java
|
||||||
|
updateProperty("passed", passed);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 完整实现示例
|
||||||
|
|
||||||
|
### 6.1 简单任务实现示例
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.service.tasker;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.ds.contract.service.ContractService;
|
||||||
|
import com.ecep.contract.ui.Tasker;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class SimpleTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private int entityId;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean success = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
// 解析参数
|
||||||
|
this.entityId = argsNode.get(0).asInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
// 更新标题
|
||||||
|
updateTitle("执行简单任务:" + entityId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 更新进度
|
||||||
|
updateProgress(1, 3);
|
||||||
|
|
||||||
|
// 执行业务逻辑
|
||||||
|
ContractService service = getCachedBean(ContractService.class);
|
||||||
|
updateProgress(2, 3);
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
success = true;
|
||||||
|
holder.info("任务执行成功");
|
||||||
|
|
||||||
|
// 更新最终进度
|
||||||
|
updateProgress(3, 3);
|
||||||
|
|
||||||
|
// 更新结果属性
|
||||||
|
updateProperty("success", success);
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.error("任务执行失败: " + e.getMessage());
|
||||||
|
success = false;
|
||||||
|
updateProperty("success", success);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 批量处理任务实现示例
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.service.tasker;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.ds.contract.service.ContractService;
|
||||||
|
import com.ecep.contract.ds.contract.model.Contract;
|
||||||
|
import com.ecep.contract.ui.Tasker;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
public class BatchProcessTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
private ContractService contractService;
|
||||||
|
private List<Integer> entityIds;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
// 初始化服务引用
|
||||||
|
this.contractService = getCachedBean(ContractService.class);
|
||||||
|
|
||||||
|
// 解析实体ID列表
|
||||||
|
entityIds = new ArrayList<>();
|
||||||
|
for (JsonNode node : argsNode) {
|
||||||
|
entityIds.add(node.asInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("批量处理实体任务");
|
||||||
|
|
||||||
|
AtomicInteger counter = new AtomicInteger(0);
|
||||||
|
long total = entityIds.size();
|
||||||
|
|
||||||
|
for (Integer id : entityIds) {
|
||||||
|
// 检查是否取消
|
||||||
|
if (isCancelled()) {
|
||||||
|
holder.info("任务已取消");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageHolder subHolder = holder.sub("处理 ID: " + id + " > ");
|
||||||
|
|
||||||
|
// 执行业务逻辑
|
||||||
|
Contract entity = contractService.getById(id);
|
||||||
|
// 处理实体...
|
||||||
|
|
||||||
|
subHolder.info("处理成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.error("处理 ID: " + id + " 失败: " + e.getMessage());
|
||||||
|
// 记录异常但继续处理下一个
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
updateProgress(counter.incrementAndGet(), total);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTitle("批量处理完成");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 带有依赖的任务实现示例
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.ecep.contract.service.tasker;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.ds.contract.service.ContractService;
|
||||||
|
import com.ecep.contract.ds.customer.service.CustomerService;
|
||||||
|
import com.ecep.contract.ui.Tasker;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class DependentTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private int contractId;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean processed = false;
|
||||||
|
|
||||||
|
private ContractService contractService;
|
||||||
|
private CustomerService customerService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
// 解析参数
|
||||||
|
this.contractId = argsNode.get(0).asInt();
|
||||||
|
|
||||||
|
// 获取服务依赖
|
||||||
|
this.contractService = getCachedBean(ContractService.class);
|
||||||
|
this.customerService = getCachedBean(CustomerService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("执行带有服务依赖的任务");
|
||||||
|
|
||||||
|
updateProgress(1, 5);
|
||||||
|
|
||||||
|
// 使用多个服务协同完成任务
|
||||||
|
try {
|
||||||
|
// 步骤1: 获取合同信息
|
||||||
|
var contract = contractService.getById(contractId);
|
||||||
|
updateProgress(2, 5);
|
||||||
|
|
||||||
|
// 步骤2: 获取相关客户信息
|
||||||
|
var customer = customerService.getById(contract.getCustomerId());
|
||||||
|
updateProgress(3, 5);
|
||||||
|
|
||||||
|
// 步骤3: 执行业务逻辑
|
||||||
|
// ...
|
||||||
|
updateProgress(4, 5);
|
||||||
|
|
||||||
|
processed = true;
|
||||||
|
holder.info("任务处理成功");
|
||||||
|
|
||||||
|
// 更新结果属性
|
||||||
|
updateProperty("processed", processed);
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.error("任务处理失败: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(5, 5);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 最佳实践总结
|
||||||
|
|
||||||
|
1. **继承 Tasker<Object> 并实现 WebSocketServerTasker 接口**:利用已有的基础实现
|
||||||
|
|
||||||
|
2. **合理实现 init 方法**:处理参数初始化,避免在 execute 中重复解析
|
||||||
|
|
||||||
|
3. **实现清晰的 execute 逻辑**:
|
||||||
|
- 开始时设置任务标题
|
||||||
|
- 过程中定期更新进度
|
||||||
|
- 提供详细的消息反馈
|
||||||
|
- 结束时更新最终状态
|
||||||
|
|
||||||
|
4. **使用 Spring Bean**:通过 `getCachedBean` 获取需要的服务依赖
|
||||||
|
|
||||||
|
5. **支持任务取消**:在关键循环中检查 `isCancelled()` 状态
|
||||||
|
|
||||||
|
6. **异常处理**:捕获异常并记录,提供有意义的错误信息
|
||||||
|
|
||||||
|
7. **进度反馈**:对于长时间运行的任务,提供合理的进度更新频率
|
||||||
|
|
||||||
|
8. **属性传递**:使用 `updateProperty` 方法传递任务结果给调用方
|
||||||
|
|
||||||
|
9. **日志记录**:使用 slf4j 记录关键操作和异常
|
||||||
|
|
||||||
|
10. **资源管理**:确保在任务完成或取消时释放相关资源
|
||||||
|
|
||||||
|
## 8. 注意事项
|
||||||
|
|
||||||
|
1. **不要在构造函数中获取 Spring Bean**:使用 `init` 方法或懒加载方式获取
|
||||||
|
|
||||||
|
2. **避免阻塞操作**:长时间阻塞的操作应考虑异步处理
|
||||||
|
|
||||||
|
3. **线程安全**:注意多线程环境下的并发访问问题
|
||||||
|
|
||||||
|
4. **内存管理**:对于处理大量数据的任务,注意内存使用,避免OOM
|
||||||
|
|
||||||
|
5. **超时处理**:考虑设置合理的超时机制
|
||||||
|
|
||||||
|
6. **事务管理**:根据需要考虑事务的范围和传播行为
|
||||||
|
|
||||||
|
7. **任务注册**:创建新的WebSocketServerTasker后,必须在`tasker_mapper.json`文件中注册,否则客户端将无法调用
|
||||||
|
|
||||||
|
8. **任务名称唯一性**:确保任务名称在`tasker_mapper.json`中唯一,避免名称冲突
|
||||||
|
|
||||||
|
9. **类路径正确**:注册时确保使用正确的类完全限定名,否则会导致实例化失败
|
||||||
|
|
||||||
|
10. **重启服务**:修改`tasker_mapper.json`后,需要重启服务才能使新的注册生效
|
||||||
|
|
||||||
|
通过遵循这些规范和最佳实践,可以确保实现的 `WebSocketServerTasker` 类具有良好的可维护性、可扩展性和用户体验。
|
||||||
182
.trae/rules/tasker_implementation_guide.md
Normal file
182
.trae/rules/tasker_implementation_guide.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Tasker实现规范指南
|
||||||
|
|
||||||
|
## 1. Tasker架构概述
|
||||||
|
|
||||||
|
Tasker是系统中用于执行异步任务的核心框架,主要用于处理耗时操作并提供实时进度反馈。Tasker架构包括:
|
||||||
|
|
||||||
|
- **Tasker基类**:提供任务执行的基础框架和通信功能
|
||||||
|
- **WebSocketServerTasker接口**:定义WebSocket通信任务的规范
|
||||||
|
- **MessageHolder**:用于在任务执行过程中传递消息和更新进度
|
||||||
|
|
||||||
|
## 2. 服务器端Tasker实现规范
|
||||||
|
|
||||||
|
### 2.1 基本结构
|
||||||
|
|
||||||
|
服务器端Tasker实现应遵循以下结构:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class YourTaskNameTask extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
// 服务依赖
|
||||||
|
@Setter
|
||||||
|
private YourService service;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
// 初始化任务标题
|
||||||
|
updateTitle("任务标题");
|
||||||
|
// 初始化参数
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
// 实现业务逻辑
|
||||||
|
// 使用holder.info()、holder.error()等方法反馈消息
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 关键注意事项
|
||||||
|
|
||||||
|
1. **继承与实现**:
|
||||||
|
- 必须继承`Tasker<Object>`基类
|
||||||
|
- 必须实现`WebSocketServerTasker`接口
|
||||||
|
|
||||||
|
2. **方法实现**:
|
||||||
|
- **不要重复实现**Tasker基类已提供的handler设置方法:`setMessageHandler`、`setTitleHandler`、`setPropertyHandler`、`setProgressHandler`
|
||||||
|
- 必须实现`init`方法,设置任务标题和初始化参数
|
||||||
|
- 必须实现`execute`方法,实现具体业务逻辑
|
||||||
|
|
||||||
|
3. **进度和消息反馈**:
|
||||||
|
- 使用`updateProgress`方法提供进度更新
|
||||||
|
- 使用`MessageHolder`的`info`、`error`、`debug`等方法提供消息反馈
|
||||||
|
|
||||||
|
4. **任务取消**:
|
||||||
|
- 在适当的地方检查`isCancelled()`状态,支持任务取消
|
||||||
|
|
||||||
|
5. **注册要求**:
|
||||||
|
- 任务类必须在`tasker_mapper.json`中注册
|
||||||
|
|
||||||
|
## 3. 客户端Tasker实现规范
|
||||||
|
|
||||||
|
客户端Tasker实现应遵循:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class YourTaskNameTask extends Tasker<Object> {
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
// 客户端任务逻辑
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 通信规范
|
||||||
|
|
||||||
|
### 4.1 命名规范
|
||||||
|
|
||||||
|
- 客户端和服务器端对应的Tasker类应使用**相同的类名**
|
||||||
|
- 示例:客户端的`ContractSyncAllTask`对应服务器端的`ContractSyncAllTask`
|
||||||
|
|
||||||
|
### 4.2 消息传递
|
||||||
|
|
||||||
|
- 使用`MessageHolder`进行消息传递
|
||||||
|
- 支持不同级别的消息:INFO、ERROR、DEBUG
|
||||||
|
|
||||||
|
## 5. 最佳实践
|
||||||
|
|
||||||
|
1. **任务拆分**:
|
||||||
|
- 将大型任务拆分为多个小任务,提高可维护性
|
||||||
|
|
||||||
|
2. **异常处理**:
|
||||||
|
- 捕获并正确处理异常,使用`holder.error()`提供详细错误信息
|
||||||
|
|
||||||
|
3. **资源管理**:
|
||||||
|
- 确保及时释放资源,特别是在任务取消的情况下
|
||||||
|
|
||||||
|
4. **日志记录**:
|
||||||
|
- 使用`holder.debug()`记录调试信息
|
||||||
|
- 使用`holder.info()`记录进度信息
|
||||||
|
|
||||||
|
5. **状态检查**:
|
||||||
|
- 定期检查`isCancelled()`状态,确保任务可以被及时取消
|
||||||
|
|
||||||
|
## 6. 代码示例
|
||||||
|
|
||||||
|
### 6.1 服务器端Tasker示例
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ExampleSyncTask extends Tasker<Object> implements WebSocketServerTasker {
|
||||||
|
@Setter
|
||||||
|
private ExampleService exampleService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JsonNode argsNode) {
|
||||||
|
updateTitle("示例同步任务");
|
||||||
|
// 从argsNode初始化参数
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
try {
|
||||||
|
// 获取总记录数
|
||||||
|
long total = exampleService.getTotalCount();
|
||||||
|
holder.info("开始同步数据,共" + total + "条记录");
|
||||||
|
|
||||||
|
// 分批处理
|
||||||
|
long processed = 0;
|
||||||
|
List<ExampleEntity> entities = exampleService.getAllEntities();
|
||||||
|
|
||||||
|
for (ExampleEntity entity : entities) {
|
||||||
|
// 检查是否取消
|
||||||
|
if (isCancelled()) {
|
||||||
|
holder.info("任务已取消");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理单个实体
|
||||||
|
exampleService.processEntity(entity);
|
||||||
|
processed++;
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
updateProgress(processed, total);
|
||||||
|
if (processed % 100 == 0) {
|
||||||
|
holder.info("已处理" + processed + "/" + total + "条记录");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.info("同步完成,共处理" + processed + "条记录");
|
||||||
|
return processed;
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.error("同步失败: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 常见错误和避免方法
|
||||||
|
|
||||||
|
1. **重复实现handler方法**:
|
||||||
|
- 错误:在子类中重复实现setMessageHandler等方法
|
||||||
|
- 解决:删除子类中的这些方法,直接使用Tasker基类提供的实现
|
||||||
|
|
||||||
|
2. **缺少任务注册**:
|
||||||
|
- 错误:任务类没有在tasker_mapper.json中注册
|
||||||
|
- 解决:确保所有服务器端Tasker类都在配置文件中正确注册
|
||||||
|
|
||||||
|
3. **类名不匹配**:
|
||||||
|
- 错误:客户端和服务器端Tasker类名不一致
|
||||||
|
- 解决:确保两端使用相同的类名
|
||||||
|
|
||||||
|
4. **进度更新不正确**:
|
||||||
|
- 错误:进度计算错误或没有更新
|
||||||
|
- 解决:正确计算和更新进度,提高用户体验
|
||||||
|
|
||||||
|
5. **资源泄漏**:
|
||||||
|
- 错误:未及时关闭数据库连接等资源
|
||||||
|
- 解决:使用try-with-resources或在finally块中关闭资源
|
||||||
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方法,实体类可能包含更多业务相关方法
|
||||||
203
README.md
203
README.md
@@ -1,13 +1,198 @@
|
|||||||
# 项目介绍
|
# Contract-Manager 项目报告
|
||||||
|
|
||||||
分模块:
|
## 项目概述
|
||||||
- common:
|
Contract-Manager是一个企业级合同管理系统,提供完整的合同生命周期管理,包括合同的创建、审批、执行、归档等功能,同时集成了供应商管理、客户管理、项目管理等相关业务模块。
|
||||||
公共模块,包含实体类、公共的类、枚举、异常、工具类等
|
|
||||||
- server:
|
|
||||||
服务端模块,包含服务端的代码,依赖common模块
|
|
||||||
- client:
|
|
||||||
客户端模块,包含客户端的代码,依赖common模块
|
|
||||||
UI 的fxml文件在 /client/src/main/resources/ui/
|
|
||||||
|
|
||||||
|
## 项目架构
|
||||||
|
|
||||||
|
### 模块划分
|
||||||
|
项目采用模块化设计,分为三个主要模块:
|
||||||
|
|
||||||
|
- **common**:公共模块,包含实体类、枚举、异常、工具类等共享组件
|
||||||
|
- **server**:服务端模块,基于Spring Boot开发,提供HTTP服务和WebSocket通信
|
||||||
|
- **client**:客户端模块,基于JavaFX开发,提供图形用户界面
|
||||||
|
|
||||||
|
### 技术栈
|
||||||
|
|
||||||
|
#### 服务端技术栈
|
||||||
|
- Java 21
|
||||||
|
- Spring Boot 3.3.7
|
||||||
|
- Spring Data JPA 3.3.7
|
||||||
|
- MySQL 8.0.33
|
||||||
|
- Lombok 1.18.32
|
||||||
|
- POI 5.2.5 (Office文档处理)
|
||||||
|
- PDFBox 3.0.1 (PDF文档处理)
|
||||||
|
- Redis (缓存)
|
||||||
|
- WebSocket (实时通信)
|
||||||
|
|
||||||
|
#### 客户端技术栈
|
||||||
|
- Java 21
|
||||||
|
- JavaFX 21
|
||||||
|
- ControlsFX 11.1.2 (UI控件增强)
|
||||||
|
- Lombok 1.18.32
|
||||||
|
- Caffeine 3.1.8 (本地缓存)
|
||||||
|
- WebSocket (与服务端通信)
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
Contract-Manager/
|
||||||
|
├── common/ # 公共模块
|
||||||
|
│ ├── src/main/java/com/ecep/contract/
|
||||||
|
│ │ ├── model/ # 实体类
|
||||||
|
│ │ ├── vo/ # 值对象
|
||||||
|
│ │ ├── util/ # 工具类
|
||||||
|
│ │ ├── constant/ # 常量定义
|
||||||
|
│ │ └── msg/ # 消息相关
|
||||||
|
├── server/ # 服务端模块
|
||||||
|
│ ├── src/main/java/com/ecep/contract/
|
||||||
|
│ │ ├── api/ # API接口
|
||||||
|
│ │ ├── controller/ # 控制器
|
||||||
|
│ │ ├── service/ # 服务层
|
||||||
|
│ │ ├── ds/ # 数据访问层
|
||||||
|
│ │ ├── config/ # 配置类
|
||||||
|
│ │ ├── util/ # 工具类
|
||||||
|
│ │ ├── handler/ # WebSocket处理器
|
||||||
|
│ │ ├── cloud/ # 云端服务集成
|
||||||
|
│ │ └── ui/ # 服务端UI组件
|
||||||
|
├── client/ # 客户端模块
|
||||||
|
│ ├── src/main/java/com/ecep/contract/
|
||||||
|
│ │ ├── controller/ # 控制器
|
||||||
|
│ │ ├── service/ # 服务层
|
||||||
|
│ │ ├── vm/ # 视图模型
|
||||||
|
│ │ ├── util/ # 工具类
|
||||||
|
│ │ ├── task/ # 任务处理
|
||||||
|
│ │ ├── converter/ # 类型转换器
|
||||||
|
│ │ └── serializer/ # 序列化器
|
||||||
|
│ └── src/main/resources/ui/ # FXML界面文件
|
||||||
|
└── docs/ # 文档目录
|
||||||
|
└── db/ # 数据库相关文件
|
||||||
|
└── task/ # 任务
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心功能模块
|
||||||
|
|
||||||
|
### 1. 公司管理
|
||||||
|
- 公司基本信息管理
|
||||||
|
- 公司联系方式管理
|
||||||
|
- 公司银行账户管理
|
||||||
|
- 公司文件管理
|
||||||
|
- 公司黑名单管理
|
||||||
|
|
||||||
|
### 2. 供应商管理
|
||||||
|
- 供应商信息管理
|
||||||
|
- 供应商资质审核
|
||||||
|
- 供应商分类管理
|
||||||
|
- 供应商文件管理
|
||||||
|
- 供应商评估
|
||||||
|
|
||||||
|
### 3. 客户管理
|
||||||
|
- 客户信息管理
|
||||||
|
- 客户分类管理
|
||||||
|
- 客户满意度调查
|
||||||
|
- 客户文件管理
|
||||||
|
|
||||||
|
### 4. 合同管理
|
||||||
|
- 合同创建与编辑
|
||||||
|
- 合同审批流程
|
||||||
|
- 合同执行跟踪
|
||||||
|
- 合同付款计划
|
||||||
|
- 合同文件管理
|
||||||
|
- 合同分类与归档
|
||||||
|
|
||||||
|
### 5. 项目管理
|
||||||
|
- 项目信息管理
|
||||||
|
- 项目成本管理
|
||||||
|
- 项目报价管理
|
||||||
|
- 项目文件管理
|
||||||
|
- 项目资金计划
|
||||||
|
|
||||||
|
### 6. 库存管理
|
||||||
|
- 库存物品管理
|
||||||
|
- 库存价格历史
|
||||||
|
- 库存分类管理
|
||||||
|
|
||||||
|
### 7. 员工与权限管理
|
||||||
|
- 员工信息管理
|
||||||
|
- 角色权限管理
|
||||||
|
- 登录历史记录
|
||||||
|
|
||||||
|
### 8. 云端服务集成
|
||||||
|
- 天眼查企业信息集成
|
||||||
|
- 用友U8系统集成
|
||||||
|
- 其他云端服务集成
|
||||||
|
|
||||||
|
## 数据模型设计
|
||||||
|
|
||||||
|
系统采用JPA注解式实体类设计,主要实体类包括:
|
||||||
|
|
||||||
|
- **基础实体类**:BasedEntity, IdentityEntity, NamedEntity等
|
||||||
|
- **公司相关**:Company, CompanyContact, CompanyBankAccount等
|
||||||
|
- **供应商相关**:Vendor, VendorApproved, VendorFile等
|
||||||
|
- **客户相关**:CompanyCustomer, CustomerSatisfactionSurvey等
|
||||||
|
- **合同相关**:Contract, ContractItem, ContractPayPlan等
|
||||||
|
- **项目相关**:Project, ProjectCost, ProjectFundPlan等
|
||||||
|
- **枚举实体类**:各种类型的本地枚举实体,如VendorTypeLocal, ContractFileTypeLocal等
|
||||||
|
|
||||||
|
## 通信机制
|
||||||
|
|
||||||
|
系统采用WebSocket进行客户端与服务端的实时通信,主要组件包括:
|
||||||
|
|
||||||
|
- **服务端**:WebSocketController, WebSocketServerHandler
|
||||||
|
- **客户端**:WebSocketClientService, WebSocketClientSession
|
||||||
|
|
||||||
|
## 任务处理机制
|
||||||
|
|
||||||
|
系统实现了异步任务处理机制,支持长时间运行的任务监控和管理:
|
||||||
|
|
||||||
|
- **TaskMonitorCenter**:任务监控中心
|
||||||
|
- **MonitoredTask**:可监控的任务基类
|
||||||
|
- **TaskHistory**:任务历史记录
|
||||||
|
|
||||||
|
## 扩展性设计
|
||||||
|
|
||||||
|
系统采用良好的分层架构和接口设计,具有较强的扩展性:
|
||||||
|
|
||||||
|
1. **服务接口标准化**:通过IEntityService等接口统一服务访问方式
|
||||||
|
2. **模块化设计**:各功能模块相对独立,便于扩展和维护
|
||||||
|
3. **配置管理**:支持动态配置和属性绑定
|
||||||
|
4. **国际化支持**:通过messages.properties实现多语言支持
|
||||||
|
|
||||||
|
## 安全机制
|
||||||
|
|
||||||
|
系统实现了基本的安全机制:
|
||||||
|
|
||||||
|
- **用户认证与授权**:基于角色的权限控制
|
||||||
|
- **登录历史记录**:记录用户登录信息
|
||||||
|
- **异常处理**:全局异常处理机制
|
||||||
|
|
||||||
|
## 部署说明
|
||||||
|
|
||||||
|
### 服务端部署
|
||||||
|
1. 配置application.properties中的数据库连接和Redis连接信息
|
||||||
|
2. 打包为jar文件:`mvn clean package`
|
||||||
|
3. 运行jar文件:`java -jar server.jar`
|
||||||
|
|
||||||
|
### 客户端部署
|
||||||
|
1. 确保安装Java 21运行环境
|
||||||
|
2. 配置连接到服务端的WebSocket地址
|
||||||
|
3. 打包为可执行jar或使用jpackage创建安装包
|
||||||
|
|
||||||
|
## Tasker实现规范指南
|
||||||
|
|
||||||
|
有关Tasker框架的详细实现规范,请参阅 [Tasker实现规范指南](docs/task/tasker_implementation_guide.md)。
|
||||||
|
|
||||||
|
## 开发环境要求
|
||||||
|
- JDK 21
|
||||||
|
- Maven 3.6+
|
||||||
|
- MySQL 8.0+
|
||||||
|
- Redis 6.0+
|
||||||
|
- IntelliJ IDEA或Eclipse(推荐使用IDEA)
|
||||||
|
|
||||||
|
## 后续优化方向
|
||||||
|
|
||||||
|
1. 完善单元测试和集成测试
|
||||||
|
2. 优化数据库查询性能
|
||||||
|
3. 增强系统安全机制
|
||||||
|
4. 改进用户界面体验
|
||||||
|
5. 扩展更多云端服务集成
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>Contract-Manager</artifactId>
|
<artifactId>Contract-Manager</artifactId>
|
||||||
<version>0.0.84-SNAPSHOT</version>
|
<version>0.0.135-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>client</artifactId>
|
<artifactId>client</artifactId>
|
||||||
<version>0.0.84-SNAPSHOT</version>
|
<version>0.0.135-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>0.0.84-SNAPSHOT</version>
|
<version>0.0.135-SNAPSHOT</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -120,6 +120,23 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.1.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/relativePath/client/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ package com.ecep.contract;
|
|||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Administrator on 2017/4/16.
|
* Created by Administrator on 2017/4/16.
|
||||||
*/
|
*/
|
||||||
public class ClientV2 {
|
public class ClientV2 {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
System.out.println("当前目录 = " + new File(".").getAbsolutePath());
|
||||||
Application.launch(Desktop.class, args);
|
Application.launch(Desktop.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,9 @@ public class Desktop extends Application {
|
|||||||
controller.setHttpClient(this.httpClient);
|
controller.setHttpClient(this.httpClient);
|
||||||
controller.setHolder(holder);
|
controller.setHolder(holder);
|
||||||
controller.setPrimaryStage(primaryStage);
|
controller.setPrimaryStage(primaryStage);
|
||||||
controller.setProperties(properties);
|
MyProperties myProperties = new MyProperties();
|
||||||
|
myProperties.loadFromProperties(properties);
|
||||||
|
controller.setProperties(myProperties);
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
controller.tryLogin().get();
|
controller.tryLogin().get();
|
||||||
|
|||||||
@@ -1,22 +1,192 @@
|
|||||||
package com.ecep.contract;
|
package com.ecep.contract;
|
||||||
|
|
||||||
import lombok.Getter;
|
import java.io.File;
|
||||||
import lombok.Setter;
|
import java.io.FileInputStream;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import java.io.FileOutputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import lombok.Getter;
|
||||||
import java.nio.file.Path;
|
import lombok.Setter;
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用程序配置类,用于管理系统配置信息
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "my")
|
public class MyProperties implements InitializingBean {
|
||||||
public class MyProperties {
|
private static final Logger logger = LoggerFactory.getLogger(MyProperties.class);
|
||||||
|
private static final String FILE_NAME = "config.properties";
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String downloadsPath;
|
private String downloadsPath;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String serverHost;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String serverPort;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean rememberPassword;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
// 初始化时加载配置文件
|
||||||
|
try {
|
||||||
|
loadFromFile();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("初始化配置文件失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件加载配置
|
||||||
|
*/
|
||||||
|
public void loadFromFile() {
|
||||||
|
File configFile = new File(FILE_NAME);
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
logger.debug("配置文件不存在: {}", configFile.getPath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (FileInputStream input = new FileInputStream(configFile)) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.load(input);
|
||||||
|
loadFromProperties(properties);
|
||||||
|
logger.debug("成功从配置文件加载配置: {}", configFile.getPath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("加载配置文件失败: {}", configFile.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Properties对象并加载配置
|
||||||
|
* 用于从外部设置配置项
|
||||||
|
*
|
||||||
|
* @param properties 配置对象
|
||||||
|
*/
|
||||||
|
public void setProperties(Properties properties) {
|
||||||
|
this.loadFromProperties(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Properties对象加载配置
|
||||||
|
* 用于从config.properties文件中读取配置项
|
||||||
|
*
|
||||||
|
* @param properties 配置对象
|
||||||
|
*/
|
||||||
|
public void loadFromProperties(Properties properties) {
|
||||||
|
if (properties == null) {
|
||||||
|
logger.warn("Properties对象为空,无法加载配置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载下载路径配置
|
||||||
|
String downloadsPath = properties.getProperty("my.downloadsPath");
|
||||||
|
if (StringUtils.hasText(downloadsPath)) {
|
||||||
|
this.setDownloadsPath(downloadsPath);
|
||||||
|
logger.debug("从配置文件加载下载路径: {}", downloadsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载服务器配置
|
||||||
|
String serverHost = properties.getProperty("server.host", "127.0.0.1");
|
||||||
|
if (StringUtils.hasText(serverHost)) {
|
||||||
|
this.setServerHost(serverHost);
|
||||||
|
logger.debug("从配置文件加载服务器地址: {}", serverHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
String serverPort = properties.getProperty("server.port", "8080");
|
||||||
|
if (StringUtils.hasText(serverPort)) {
|
||||||
|
this.setServerPort(serverPort);
|
||||||
|
logger.debug("从配置文件加载服务器端口: {}", serverPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载用户凭证配置
|
||||||
|
String userName = properties.getProperty("user.name");
|
||||||
|
if (StringUtils.hasText(userName)) {
|
||||||
|
this.setUserName(userName);
|
||||||
|
logger.debug("从配置文件加载用户名");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有在记住密码的情况下才加载密码
|
||||||
|
String rememberPasswordStr = properties.getProperty("user.rememberPassword");
|
||||||
|
boolean rememberPassword = "true".equals(rememberPasswordStr);
|
||||||
|
this.setRememberPassword(rememberPassword);
|
||||||
|
|
||||||
|
if (rememberPassword) {
|
||||||
|
String password = properties.getProperty("user.password");
|
||||||
|
if (StringUtils.hasText(password)) {
|
||||||
|
this.setPassword(password);
|
||||||
|
logger.debug("从配置文件加载密码");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将配置保存到Properties对象
|
||||||
|
* 用于将配置项保存到config.properties文件
|
||||||
|
*
|
||||||
|
* @param properties 配置对象
|
||||||
|
*/
|
||||||
|
public void saveToProperties(Properties properties) {
|
||||||
|
if (properties == null) {
|
||||||
|
logger.warn("Properties对象为空,无法保存配置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存下载路径配置
|
||||||
|
if (StringUtils.hasText(getDownloadsPath())) {
|
||||||
|
properties.setProperty("my.downloadsPath", getDownloadsPath());
|
||||||
|
logger.debug("保存下载路径到配置文件: {}", getDownloadsPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存服务器配置
|
||||||
|
if (StringUtils.hasText(getServerHost())) {
|
||||||
|
properties.setProperty("server.host", getServerHost());
|
||||||
|
logger.debug("保存服务器地址到配置文件: {}", getServerHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(getServerPort())) {
|
||||||
|
properties.setProperty("server.port", getServerPort());
|
||||||
|
logger.debug("保存服务器端口到配置文件: {}", getServerPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存用户凭证配置
|
||||||
|
if (StringUtils.hasText(getUserName())) {
|
||||||
|
properties.setProperty("user.name", getUserName());
|
||||||
|
logger.debug("保存用户名为配置文件");
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.setProperty("user.rememberPassword", String.valueOf(isRememberPassword()));
|
||||||
|
|
||||||
|
// 只有在记住密码的情况下才保存密码
|
||||||
|
if (isRememberPassword() && StringUtils.hasText(getPassword())) {
|
||||||
|
properties.setProperty("user.password", getPassword());
|
||||||
|
logger.debug("保存密码到配置文件");
|
||||||
|
} else if (properties.containsKey("user.password")) {
|
||||||
|
// 如果不记住密码,删除已存在的密码配置
|
||||||
|
properties.remove("user.password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 尝试返回当前用户的下载文件夹
|
* 尝试返回当前用户的下载文件夹
|
||||||
@@ -24,7 +194,13 @@ public class MyProperties {
|
|||||||
public File getDownloadDirectory() {
|
public File getDownloadDirectory() {
|
||||||
String downloadsPath = getDownloadsPath();
|
String downloadsPath = getDownloadsPath();
|
||||||
if (StringUtils.hasText(downloadsPath)) {
|
if (StringUtils.hasText(downloadsPath)) {
|
||||||
return new File(downloadsPath);
|
// 确保目录存在
|
||||||
|
File dir = new File(downloadsPath);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
boolean created = dir.mkdirs();
|
||||||
|
logger.debug("创建下载目录: {}, 结果: {}", downloadsPath, created);
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 没有配置下载目录时,尝试使用默认设置
|
// 没有配置下载目录时,尝试使用默认设置
|
||||||
@@ -32,4 +208,29 @@ public class MyProperties {
|
|||||||
Path path = Paths.get(home, "Downloads");
|
Path path = Paths.get(home, "Downloads");
|
||||||
return path.toFile();
|
return path.toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存配置到文件
|
||||||
|
*/
|
||||||
|
public void save() {
|
||||||
|
File configFile = new File(FILE_NAME);
|
||||||
|
try (FileOutputStream output = new FileOutputStream(configFile)) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
|
||||||
|
// 如果文件已存在,先读取现有配置,避免覆盖其他配置
|
||||||
|
if (configFile.exists()) {
|
||||||
|
try (FileInputStream input = new FileInputStream(configFile)) {
|
||||||
|
properties.load(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存当前配置
|
||||||
|
saveToProperties(properties);
|
||||||
|
properties.store(output, "Contract Manager 应用程序配置");
|
||||||
|
logger.debug("成功保存配置到文件: {}", configFile.getPath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("保存配置到文件失败: {}", configFile.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,17 @@ public class SpringApp {
|
|||||||
context = application.run();
|
context = application.run();
|
||||||
logger.debug("SpringApp.launch application.run().");
|
logger.debug("SpringApp.launch application.run().");
|
||||||
Duration between = Duration.between(startup.getBufferedTimeline().getStartTime(), Instant.now());
|
Duration between = Duration.between(startup.getBufferedTimeline().getStartTime(), Instant.now());
|
||||||
|
|
||||||
|
// 初始化MyProperties,从properties加载配置
|
||||||
|
try {
|
||||||
|
MyProperties myProperties = context.getBean(MyProperties.class);
|
||||||
|
myProperties.loadFromProperties(properties);
|
||||||
|
holder.info("MyProperties配置加载完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("加载MyProperties配置失败", e);
|
||||||
|
holder.error("加载MyProperties配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
holder.info("应用程序环境加载完成... " + between);
|
holder.info("应用程序环境加载完成... " + between);
|
||||||
});
|
});
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
|
|||||||
@@ -1,24 +1,5 @@
|
|||||||
package com.ecep.contract;
|
package com.ecep.contract;
|
||||||
|
|
||||||
import com.ecep.contract.constant.WebSocketConstant;
|
|
||||||
import com.ecep.contract.msg.SimpleMessage;
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import okhttp3.*;
|
|
||||||
import okio.ByteString;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,6 +9,32 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.ecep.contract.constant.WebSocketConstant;
|
||||||
|
import com.ecep.contract.controller.OkHttpLoginController;
|
||||||
|
import com.ecep.contract.msg.SimpleMessage;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.WebSocket;
|
||||||
|
import okhttp3.WebSocketListener;
|
||||||
|
import okio.ByteString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket消息服务
|
* WebSocket消息服务
|
||||||
* 提供向服务器端发送WebSocket消息的功能
|
* 提供向服务器端发送WebSocket消息的功能
|
||||||
@@ -46,7 +53,6 @@ public class WebSocketClientService {
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private long readTimeout = 30000;
|
private long readTimeout = 30000;
|
||||||
private String webSocketUrl = "ws://localhost:8080/ws";
|
|
||||||
private boolean isActive = false; // 标记连接是否活跃
|
private boolean isActive = false; // 标记连接是否活跃
|
||||||
private ScheduledFuture<?> heartbeatTask; // 心跳任务
|
private ScheduledFuture<?> heartbeatTask; // 心跳任务
|
||||||
private ScheduledFuture<?> reconnectFuture; // 修改类型为CompletableFuture<Void>
|
private ScheduledFuture<?> reconnectFuture; // 修改类型为CompletableFuture<Void>
|
||||||
@@ -90,7 +96,10 @@ public class WebSocketClientService {
|
|||||||
} else {
|
} else {
|
||||||
logger.error("未找到对应的回调future: {}", messageId);
|
logger.error("未找到对应的回调future: {}", messageId);
|
||||||
}
|
}
|
||||||
} else if (node.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
|
||||||
String sessionId = node.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
|
String sessionId = node.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
|
||||||
WebSocketClientSession session = sessions.get(sessionId);
|
WebSocketClientSession session = sessions.get(sessionId);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
@@ -100,18 +109,41 @@ public class WebSocketClientService {
|
|||||||
session.updateMessage(java.util.logging.Level.SEVERE, "会话异常: " + e.getMessage());
|
session.updateMessage(java.util.logging.Level.SEVERE, "会话异常: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (node.has(WebSocketConstant.ERROR_CODE_FIELD_NAME)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has(WebSocketConstant.ERROR_CODE_FIELD_NAME)) {
|
||||||
int errorCode = node.get(WebSocketConstant.ERROR_CODE_FIELD_NAME).asInt();
|
int errorCode = node.get(WebSocketConstant.ERROR_CODE_FIELD_NAME).asInt();
|
||||||
String errorMsg = node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText();
|
String errorMsg = node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText();
|
||||||
// TODO 需要重新登录
|
|
||||||
logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg);
|
logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg);
|
||||||
|
if (errorCode == WebSocketConstant.ERROR_CODE_UNAUTHORIZED) {
|
||||||
|
|
||||||
|
// 调用所有的 callbacks 和 session 失败并且移除
|
||||||
|
callbacks.keySet().stream().toList()
|
||||||
|
.forEach(key -> callbacks.remove(key).completeExceptionally(new Exception("未授权")));
|
||||||
|
sessions.values().stream().toList().forEach(session -> {
|
||||||
|
session.updateMessage(java.util.logging.Level.SEVERE, "未授权");
|
||||||
|
session.close();
|
||||||
|
});
|
||||||
|
isActive = false;
|
||||||
|
webSocket.close(1000, "");
|
||||||
|
WebSocketClientService.this.webSocket = null;
|
||||||
|
|
||||||
|
// 处理未授权错误,重新登录
|
||||||
|
OkHttpLoginController controller = new OkHttpLoginController();
|
||||||
|
controller.setHttpClient(Desktop.instance.getHttpClient());
|
||||||
|
controller.setProperties(SpringApp.getBean(MyProperties.class));
|
||||||
|
controller.tryLogin().get();
|
||||||
|
// 需要把窗口顶置
|
||||||
|
initWebSocket();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("处理WebSocket消息失败: {}", e.getMessage(), e);
|
logger.error("处理WebSocket消息失败: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||||
// 处理收到的二进制消息
|
// 处理收到的二进制消息
|
||||||
@@ -146,10 +178,11 @@ public class WebSocketClientService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void onCallbackMessage(CompletableFuture<JsonNode> future, JsonNode node) {
|
private void onCallbackMessage(CompletableFuture<JsonNode> future, JsonNode node) {
|
||||||
if (node.has(WebSocketConstant.SUCCESS_FIELD_VALUE)) {
|
if (node.has(WebSocketConstant.SUCCESS_FIELD_NAME)) {
|
||||||
if (!node.get(WebSocketConstant.SUCCESS_FIELD_VALUE).asBoolean()) {
|
if (!node.get(WebSocketConstant.SUCCESS_FIELD_NAME).asBoolean()) {
|
||||||
future.completeExceptionally(
|
future.completeExceptionally(
|
||||||
new RuntimeException("请求失败:来自服务器的消息=" + node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText()));
|
new RuntimeException(
|
||||||
|
"请求失败:来自服务器的消息=" + node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,6 +206,12 @@ public class WebSocketClientService {
|
|||||||
send(objectMapper.writeValueAsString(message));
|
send(objectMapper.writeValueAsString(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocketServerCallbackManage#onMessage 负责接收处理
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public CompletableFuture<JsonNode> send(SimpleMessage msg) {
|
public CompletableFuture<JsonNode> send(SimpleMessage msg) {
|
||||||
CompletableFuture<JsonNode> future = new CompletableFuture<>();
|
CompletableFuture<JsonNode> future = new CompletableFuture<>();
|
||||||
try {
|
try {
|
||||||
@@ -185,7 +224,7 @@ public class WebSocketClientService {
|
|||||||
String json = objectMapper.writeValueAsString(msg);
|
String json = objectMapper.writeValueAsString(msg);
|
||||||
callbacks.put(msg.getMessageId(), future);
|
callbacks.put(msg.getMessageId(), future);
|
||||||
if (webSocket.send(json)) {
|
if (webSocket.send(json)) {
|
||||||
logger.debug("send message success:{}", json);
|
logger.debug("send json success:{}", json);
|
||||||
} else {
|
} else {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
future.completeExceptionally(new RuntimeException("Failed to send WebSocket message"));
|
future.completeExceptionally(new RuntimeException("Failed to send WebSocket message"));
|
||||||
@@ -214,6 +253,8 @@ public class WebSocketClientService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 构建WebSocket请求,包含认证信息
|
// 构建WebSocket请求,包含认证信息
|
||||||
|
var myProperties = SpringApp.getBean(MyProperties.class);
|
||||||
|
String webSocketUrl = "ws://" + myProperties.getServerHost() + ":" + myProperties.getServerPort() + "/ws";
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(webSocketUrl)
|
.url(webSocketUrl)
|
||||||
.build();
|
.build();
|
||||||
@@ -318,19 +359,11 @@ public class WebSocketClientService {
|
|||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void withSession(Consumer<WebSocketClientSession> sessionConsumer) {
|
|
||||||
WebSocketClientSession session = createSession();
|
|
||||||
try {
|
|
||||||
sessionConsumer.accept(session);
|
|
||||||
} finally {
|
|
||||||
// closeSession(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeSession(WebSocketClientSession session) {
|
public void closeSession(WebSocketClientSession session) {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
sessions.remove(session.getSessionId());
|
sessions.remove(session.getSessionId());
|
||||||
session.close();
|
// session.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.ecep.contract.constant.WebSocketConstant;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
@@ -12,9 +14,18 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
public class WebSocketClientSession {
|
public class WebSocketClientSession {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(WebSocketClientSession.class);
|
||||||
|
/**
|
||||||
|
* 会话ID由客户端创建,服务器不保存会话只回传会话ID
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private String sessionId = UUID.randomUUID().toString();
|
private final String sessionId = UUID.randomUUID().toString();
|
||||||
|
@Getter
|
||||||
|
private boolean done = false;
|
||||||
|
|
||||||
private WebSocketClientTasker tasker;
|
private WebSocketClientTasker tasker;
|
||||||
|
|
||||||
@@ -41,7 +52,6 @@ public class WebSocketClientSession {
|
|||||||
webSocketService.send(arguments);
|
webSocketService.send(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void onMessage(JsonNode node) {
|
public void onMessage(JsonNode node) {
|
||||||
if (node.has("type")) {
|
if (node.has("type")) {
|
||||||
String type = node.get("type").asText();
|
String type = node.get("type").asText();
|
||||||
@@ -58,6 +68,8 @@ public class WebSocketClientSession {
|
|||||||
handleAsStart(args);
|
handleAsStart(args);
|
||||||
} else if (type.equals("done")) {
|
} else if (type.equals("done")) {
|
||||||
handleAsDone(args);
|
handleAsDone(args);
|
||||||
|
done = true;
|
||||||
|
close();
|
||||||
} else {
|
} else {
|
||||||
tasker.updateMessage(java.util.logging.Level.INFO, "未知的消息类型: " + node.toString());
|
tasker.updateMessage(java.util.logging.Level.INFO, "未知的消息类型: " + node.toString());
|
||||||
}
|
}
|
||||||
@@ -66,15 +78,14 @@ public class WebSocketClientSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAsDone(JsonNode args) {
|
|
||||||
tasker.updateMessage(java.util.logging.Level.INFO, "任务完成");
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleAsStart(JsonNode args) {
|
private void handleAsStart(JsonNode args) {
|
||||||
tasker.updateMessage(java.util.logging.Level.INFO, "任务开始");
|
tasker.updateMessage(java.util.logging.Level.INFO, "任务开始");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleAsDone(JsonNode args) {
|
||||||
|
tasker.updateMessage(java.util.logging.Level.INFO, "任务完成");
|
||||||
|
}
|
||||||
|
|
||||||
private void handleAsProgress(JsonNode args) {
|
private void handleAsProgress(JsonNode args) {
|
||||||
long current = args.get(0).asLong();
|
long current = args.get(0).asLong();
|
||||||
long total = args.get(1).asLong();
|
long total = args.get(1).asLong();
|
||||||
@@ -86,15 +97,19 @@ public class WebSocketClientSession {
|
|||||||
Object value = args.get(1);
|
Object value = args.get(1);
|
||||||
try {
|
try {
|
||||||
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(tasker.getClass(), name);
|
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(tasker.getClass(), name);
|
||||||
System.out.println("descriptor = " + descriptor);
|
if (descriptor == null) {
|
||||||
System.out.println("descriptor.getPropertyType() = " + descriptor.getPropertyType());
|
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性 " + name + " 不存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Object object = webSocketService.getObjectMapper().convertValue(value, descriptor.getPropertyType());
|
Object object = webSocketService.getObjectMapper().convertValue(value, descriptor.getPropertyType());
|
||||||
System.out.println("object = " + object);
|
if (descriptor.getWriteMethod() == null) {
|
||||||
System.out.println("descriptor.getWriteMethod() = " + descriptor.getWriteMethod());
|
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性 " + name + " 不可写");
|
||||||
System.out.println("descriptor.getWriteMethod().getParameterTypes() = " + descriptor.getWriteMethod().getParameterTypes());
|
} else {
|
||||||
descriptor.getWriteMethod().invoke(tasker, object);
|
descriptor.getWriteMethod().invoke(tasker, object);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性设置失败: " + name + " = " + value);
|
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性设置失败: " + name + " = " + value);
|
||||||
|
logger.error("set {} = {}", name, value, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +121,7 @@ public class WebSocketClientSession {
|
|||||||
private void handleAsMessage(JsonNode args) {
|
private void handleAsMessage(JsonNode args) {
|
||||||
String level = args.get(0).asText();
|
String level = args.get(0).asText();
|
||||||
String message = args.get(1).asText();
|
String message = args.get(1).asText();
|
||||||
updateMessage(java.util.logging.Level.parse(level), message);
|
updateMessage(java.util.logging.Level.parse(level), "[R] " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMessage(Level level, String message) {
|
public void updateMessage(Level level, String message) {
|
||||||
|
|||||||
@@ -3,27 +3,134 @@ package com.ecep.contract;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket客户端任务接口
|
||||||
|
* 定义了所有通过WebSocket与服务器通信的任务的通用方法
|
||||||
|
* 包括任务名称、更新消息、更新标题、更新进度等操作
|
||||||
|
* <p>
|
||||||
|
* 所有通过WebSocket与服务器通信的任务类都应实现此接口, 文档参考 .trace/rules/client_task_rules.md
|
||||||
|
*/
|
||||||
public interface WebSocketClientTasker {
|
public interface WebSocketClientTasker {
|
||||||
|
/**
|
||||||
|
* s
|
||||||
|
* 获取任务名称
|
||||||
|
* 任务名称用于唯一标识任务, 服务器端会根据任务名称来调用对应的任务处理函数
|
||||||
|
*
|
||||||
|
* @return 任务名称
|
||||||
|
*/
|
||||||
String getTaskName();
|
String getTaskName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务执行过程中的消息
|
||||||
|
* 客户端可以通过此方法向用户展示任务执行过程中的重要信息或错误提示
|
||||||
|
*
|
||||||
|
* @param level 消息级别, 用于区分不同类型的消息, 如INFO, WARNING, SEVERE等
|
||||||
|
* @param message 消息内容, 可以是任意字符串, 用于展示给用户
|
||||||
|
*/
|
||||||
void updateMessage(Level level, String message);
|
void updateMessage(Level level, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务标题
|
||||||
|
* 客户端可以通过此方法向用户展示任务的当前执行状态或重要信息
|
||||||
|
*
|
||||||
|
* @param title 任务标题
|
||||||
|
*/
|
||||||
void updateTitle(String title);
|
void updateTitle(String title);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务进度
|
||||||
|
* 客户端可以通过此方法向用户展示任务的执行进度, 如文件上传进度、数据库操作进度等
|
||||||
|
*
|
||||||
|
* @param current 当前进度
|
||||||
|
* @param total 总进度
|
||||||
|
*/
|
||||||
void updateProgress(long current, long total);
|
void updateProgress(long current, long total);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消任务执行
|
||||||
|
* 默认实现为空,可由具体任务重写以提供取消逻辑
|
||||||
|
*/
|
||||||
|
default void cancelTask() {
|
||||||
|
// 默认实现为空,由具体任务重写
|
||||||
|
// 需要获取到 session
|
||||||
|
// 发送 cancel 指令
|
||||||
|
// 关闭 session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用远程WebSocket任务
|
||||||
|
* 客户端可以通过此方法向服务器提交任务, 并等待服务器返回任务执行结果
|
||||||
|
*
|
||||||
|
* @param holder 消息持有者,用于记录任务执行过程中的消息
|
||||||
|
* @param locale 语言环境
|
||||||
|
* @param args 任务参数
|
||||||
|
* @return 任务执行结果
|
||||||
|
*/
|
||||||
default Object callRemoteTask(MessageHolder holder, Locale locale, Object... args) {
|
default Object callRemoteTask(MessageHolder holder, Locale locale, Object... args) {
|
||||||
WebSocketClientService webSocketService = SpringApp.getBean(WebSocketClientService.class);
|
WebSocketClientService webSocketService = SpringApp.getBean(WebSocketClientService.class);
|
||||||
webSocketService.withSession(session -> {
|
|
||||||
try {
|
// 检查WebSocket连接是否可用
|
||||||
session.submitTask(this, locale, args);
|
if (!webSocketService.getOnlineProperty().get()) {
|
||||||
} catch (JsonProcessingException e) {
|
String errorMsg = "WebSocket连接不可用,请检查网络连接或服务器状态";
|
||||||
throw new RuntimeException(e);
|
holder.addMessage(Level.SEVERE, errorMsg);
|
||||||
}
|
return null;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
WebSocketClientSession session = webSocketService.createSession();
|
||||||
|
try {
|
||||||
|
session.submitTask(this, locale, args);
|
||||||
|
holder.info("已提交任务到服务器: " + getTaskName());
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
String errorMsg = "任务提交失败: " + e.getMessage();
|
||||||
|
holder.warn(errorMsg);
|
||||||
|
throw new RuntimeException("任务提交失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// while (!session.isDone()) {
|
||||||
|
// // 使用TimeUnit
|
||||||
|
// try {
|
||||||
|
// TimeUnit.SECONDS.sleep(1);
|
||||||
|
// } catch (InterruptedException e) {
|
||||||
|
// Thread.currentThread().interrupt();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步调用远程WebSocket任务
|
||||||
|
*
|
||||||
|
* @param holder 消息持有者,用于记录任务执行过程中的消息
|
||||||
|
* @param locale 语言环境
|
||||||
|
* @param args 任务参数
|
||||||
|
* @return 包含任务执行结果的CompletableFuture
|
||||||
|
*/
|
||||||
|
default CompletableFuture<Object> callRemoteTaskAsync(MessageHolder holder, Locale locale, Object... args) {
|
||||||
|
CompletableFuture<Object> future = new CompletableFuture<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 立即执行callRemoteTask并返回结果
|
||||||
|
Object result = callRemoteTask(holder, locale, args);
|
||||||
|
future.complete(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成唯一的任务ID
|
||||||
|
*
|
||||||
|
* @return 唯一的任务ID
|
||||||
|
*/
|
||||||
|
default String generateTaskId() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,48 +45,55 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
protected CompletableFuture<T> loadedFuture;
|
protected CompletableFuture<T> loadedFuture;
|
||||||
private final ObservableList<TabSkin> tabSkins = FXCollections.observableArrayList();
|
private final ObservableList<TabSkin> tabSkins = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShown(WindowEvent windowEvent) {
|
public void onShown(WindowEvent windowEvent) {
|
||||||
super.onShown(windowEvent);
|
super.onShown(windowEvent);
|
||||||
|
// 载数据
|
||||||
|
initializeData();
|
||||||
|
// 注册 skin
|
||||||
|
registerTabSkins();
|
||||||
|
// 初始化保存按钮
|
||||||
|
initializeSaveBtn();
|
||||||
|
// 安装 skin
|
||||||
|
installTabSkins();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeSaveBtn() {
|
||||||
|
if (saveBtn != null) {
|
||||||
|
saveBtn.disableProperty().bind(createTabSkinChangedBindings().not());
|
||||||
|
saveBtn.setOnAction(event -> saveTabSkins());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeData() {
|
||||||
ViewModelService<T, TV> service = getViewModelService();
|
ViewModelService<T, TV> service = getViewModelService();
|
||||||
|
|
||||||
if (service instanceof QueryService<T, TV> queryService) {
|
if (service instanceof QueryService<T, TV> queryService) {
|
||||||
setStatus("读取...");
|
setStatus("读取...");
|
||||||
loadedFuture = queryService.asyncFindById(viewModel.getId().get());
|
loadedFuture = queryService.asyncFindById(viewModel.getId().get());
|
||||||
loadedFuture.thenAccept(entity -> {
|
|
||||||
// fixed, bind change if new view model create
|
|
||||||
if (viewModel != null) {
|
|
||||||
viewModel.bindListener();
|
|
||||||
}
|
|
||||||
setStatus();
|
|
||||||
// BaseViewModel.updateInFxApplicationThread(entity, viewModel);
|
|
||||||
});
|
|
||||||
loadedFuture.exceptionally(ex -> {
|
|
||||||
handleException("载入失败,#" + viewModel.getId().get(), ex);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
||||||
T entity = getViewModelService().findById(viewModel.getId().get());
|
return getViewModelService().findById(viewModel.getId().get());
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
setStatus();
|
|
||||||
viewModel.update(entity);
|
|
||||||
});
|
|
||||||
viewModel.bindListener();
|
|
||||||
return entity;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerTabSkins();
|
loadedFuture.thenAccept(entity -> {
|
||||||
if (saveBtn != null) {
|
// fixed, bind change if new view model create
|
||||||
saveBtn.disableProperty().bind(createTabSkinChangedBindings().not());
|
if (viewModel != null) {
|
||||||
saveBtn.setOnAction(event -> saveTabSkins());
|
updateViewModel(entity);
|
||||||
}
|
viewModel.bindListener();
|
||||||
|
}
|
||||||
|
setStatus();
|
||||||
|
});
|
||||||
|
loadedFuture.exceptionally(ex -> {
|
||||||
|
handleException("载入失败,#" + viewModel.getId().get(), ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
installTabSkins();
|
protected void updateViewModel(T entity) {
|
||||||
|
BaseViewModel.updateInFxApplicationThread(entity, viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getEntity() {
|
public T getEntity() {
|
||||||
@@ -108,6 +115,19 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
T entity = getEntity();
|
||||||
|
if (entity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (viewModel.copyTo(entity)) {
|
||||||
|
save(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册 skin
|
||||||
|
*/
|
||||||
protected void registerTabSkins() {
|
protected void registerTabSkins() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +139,9 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 skin
|
||||||
|
*/
|
||||||
protected void installTabSkins() {
|
protected void installTabSkins() {
|
||||||
for (TabSkin tabSkin : tabSkins) {
|
for (TabSkin tabSkin : tabSkins) {
|
||||||
try {
|
try {
|
||||||
@@ -229,10 +252,10 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
return CompletableFuture.allOf(list
|
return CompletableFuture.allOf(list
|
||||||
.stream()
|
.stream()
|
||||||
.map(RefreshableSkin::refresh)
|
.map(RefreshableSkin::refresh)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.toArray(CompletableFuture<?>[]::new))
|
.toArray(CompletableFuture<?>[]::new))
|
||||||
.whenComplete((v, ex) -> {
|
.whenComplete((v, ex) -> {
|
||||||
if (ex != null) {
|
if (ex != null) {
|
||||||
future.completeExceptionally(ex);
|
future.completeExceptionally(ex);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
@@ -290,7 +291,7 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
|
|||||||
* @param propGetter
|
* @param propGetter
|
||||||
*/
|
*/
|
||||||
protected <K> void acceptCellEditEvent(TableColumn.CellEditEvent<TV, K> event,
|
protected <K> void acceptCellEditEvent(TableColumn.CellEditEvent<TV, K> event,
|
||||||
Function<TV, Property<K>> propGetter) {
|
Function<TV, Property<K>> propGetter) {
|
||||||
TV row = event.getRowValue();
|
TV row = event.getRowValue();
|
||||||
Property<K> property = propGetter.apply(row);
|
Property<K> property = propGetter.apply(row);
|
||||||
property.setValue(event.getNewValue());
|
property.setValue(event.getNewValue());
|
||||||
@@ -486,10 +487,10 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected List<TV> loadTableData() {
|
protected List<TV> loadTableData() {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
ViewModelService<T, TV> service = getViewModelService();
|
ViewModelService<T, TV> service = getViewModelService();
|
||||||
long timeMillis = System.currentTimeMillis();
|
long timeMillis = System.currentTimeMillis();
|
||||||
Page<T> page = service.findAll(params, getPageable());
|
Page<T> page = service.findAll(params == null ? null : params.build(), getPageable());
|
||||||
long timeCost = System.currentTimeMillis() - timeMillis;
|
long timeCost = System.currentTimeMillis() - timeMillis;
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("load table data cost: {} ms", timeCost);
|
logger.debug("load table data cost: {} ms", timeCost);
|
||||||
@@ -509,7 +510,8 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
|
|||||||
* @param future
|
* @param future
|
||||||
*/
|
*/
|
||||||
private void asyncLoadTableData(QueryService<T, TV> queryService, CompletableFuture<Void> future) {
|
private void asyncLoadTableData(QueryService<T, TV> queryService, CompletableFuture<Void> future) {
|
||||||
queryService.asyncFindAll(getSpecification(), getPageable()).whenComplete((result, ex) -> {
|
ParamUtils.Builder params = getSpecification();
|
||||||
|
queryService.asyncFindAll(params == null ? null : params.build(), getPageable()).whenComplete((result, ex) -> {
|
||||||
if (ex != null) {
|
if (ex != null) {
|
||||||
future.completeExceptionally(ex);
|
future.completeExceptionally(ex);
|
||||||
return;
|
return;
|
||||||
@@ -538,7 +540,7 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> getSpecification() {
|
public ParamUtils.Builder getSpecification() {
|
||||||
TextField field = controller.searchKeyField;
|
TextField field = controller.searchKeyField;
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
return getViewModelService().getSpecification(field.getText());
|
return getViewModelService().getSpecification(field.getText());
|
||||||
@@ -552,7 +554,7 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
|
|||||||
* @param searchText
|
* @param searchText
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected Map<String, Object> getSpecification(String searchText) {
|
protected ParamUtils.Builder getSpecification(String searchText) {
|
||||||
return getViewModelService().getSpecification(searchText);
|
return getViewModelService().getSpecification(searchText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ import java.util.Locale;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.ecep.contract.util.BeanContext;
|
||||||
|
import com.ecep.contract.util.DefaultBeanContext;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.FxmlUtils;
|
import com.ecep.contract.util.FxmlUtils;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
import org.apache.logging.log4j.util.Strings;
|
import org.apache.logging.log4j.util.Strings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -40,7 +43,7 @@ import javafx.stage.Window;
|
|||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class BaseController {
|
public class BaseController implements BeanContext {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
|
private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
|
||||||
public static HashMap<String, Stage> stages = new HashMap<>();
|
public static HashMap<String, Stage> stages = new HashMap<>();
|
||||||
|
|
||||||
@@ -184,22 +187,15 @@ public class BaseController {
|
|||||||
private String stageKey;
|
private String stageKey;
|
||||||
public Label leftStatusLabel;
|
public Label leftStatusLabel;
|
||||||
public Label rightStatusLabel;
|
public Label rightStatusLabel;
|
||||||
private Employee currentUser;
|
private EmployeeVo currentUser;
|
||||||
|
|
||||||
private HashMap<Class<?>, Object> cachedBeans = new HashMap<>();
|
|
||||||
|
|
||||||
|
private BeanContext beanContext = new DefaultBeanContext();
|
||||||
public <T> T getBean(Class<T> requiredType) throws BeansException {
|
public <T> T getBean(Class<T> requiredType) throws BeansException {
|
||||||
return SpringApp.getBean(requiredType);
|
return beanContext.getBean(requiredType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
|
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
|
||||||
Object object = cachedBeans.get(requiredType);
|
return beanContext.getCachedBean(requiredType);
|
||||||
if (object == null) {
|
|
||||||
object = getBean(requiredType);
|
|
||||||
cachedBeans.put(requiredType, object);
|
|
||||||
}
|
|
||||||
return (T) object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SysConfService getConfService() {
|
public SysConfService getConfService() {
|
||||||
@@ -210,7 +206,7 @@ public class BaseController {
|
|||||||
return getCachedBean(EmployeeService.class);
|
return getCachedBean(EmployeeService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Employee getCurrentUser() {
|
public EmployeeVo getCurrentUser() {
|
||||||
if (currentUser == null) {
|
if (currentUser == null) {
|
||||||
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
|
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class ContractGroupSyncTask extends Tasker<Object>{
|
|
||||||
@Override
|
|
||||||
public Object execute(MessageHolder holder) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class ContractKindSyncTask extends Tasker<Object> {
|
|
||||||
@Override
|
|
||||||
public Object execute(MessageHolder holder) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class ContractSyncAllTask extends Tasker<Object> {
|
|
||||||
@Override
|
|
||||||
public Object execute(MessageHolder holder) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class ContractTypeSyncTask extends Tasker<Object> {
|
|
||||||
@Override
|
|
||||||
public Object execute(MessageHolder holder) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class CustomerClassSyncTask extends Tasker<Object> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object execute(MessageHolder holder) throws Exception {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'execute'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,9 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.cloud.rk.CloudRkManagerWindowController;
|
||||||
|
import com.ecep.contract.controller.cloud.tyc.CloudTycManagerWindowController;
|
||||||
|
import com.ecep.contract.controller.cloud.u8.YongYouU8ManagerWindowController;
|
||||||
import org.controlsfx.control.TaskProgressView;
|
import org.controlsfx.control.TaskProgressView;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -29,7 +32,7 @@ import com.ecep.contract.controller.inventory.InventoryManagerWindowController;
|
|||||||
import com.ecep.contract.controller.permission.EmployeeFunctionsManagerWindowController;
|
import com.ecep.contract.controller.permission.EmployeeFunctionsManagerWindowController;
|
||||||
import com.ecep.contract.controller.permission.EmployeeRoleManagerWindowController;
|
import com.ecep.contract.controller.permission.EmployeeRoleManagerWindowController;
|
||||||
import com.ecep.contract.controller.project.ProjectManagerWindowController;
|
import com.ecep.contract.controller.project.ProjectManagerWindowController;
|
||||||
import com.ecep.contract.controller.vendor.CompanyVendorManagerWindowController;
|
import com.ecep.contract.controller.vendor.VendorManagerWindowController;
|
||||||
import com.ecep.contract.service.CloudRkService;
|
import com.ecep.contract.service.CloudRkService;
|
||||||
import com.ecep.contract.service.YongYouU8Service;
|
import com.ecep.contract.service.YongYouU8Service;
|
||||||
import com.ecep.contract.task.ContractSyncTask;
|
import com.ecep.contract.task.ContractSyncTask;
|
||||||
@@ -79,7 +82,7 @@ public class HomeWindowController extends BaseController {
|
|||||||
openCompanyManagerWindow.setOnAction(event -> showInOwner(CompanyManagerWindowController.class));
|
openCompanyManagerWindow.setOnAction(event -> showInOwner(CompanyManagerWindowController.class));
|
||||||
openProjectManagerWindow.setOnAction(event -> showInOwner(ProjectManagerWindowController.class));
|
openProjectManagerWindow.setOnAction(event -> showInOwner(ProjectManagerWindowController.class));
|
||||||
openContractManagerWindow.setOnAction(event -> showInOwner(ContractManagerWindowController.class));
|
openContractManagerWindow.setOnAction(event -> showInOwner(ContractManagerWindowController.class));
|
||||||
openVendorManagerWindow.setOnAction(event -> showInOwner(CompanyVendorManagerWindowController.class));
|
openVendorManagerWindow.setOnAction(event -> showInOwner(VendorManagerWindowController.class));
|
||||||
openCustomManagerWindow.setOnAction(event -> showInOwner(CompanyCustomerManagerWindowController.class));
|
openCustomManagerWindow.setOnAction(event -> showInOwner(CompanyCustomerManagerWindowController.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +116,7 @@ public class HomeWindowController extends BaseController {
|
|||||||
|
|
||||||
@EventListener
|
@EventListener
|
||||||
public void onCurrentEmployeeInitialed(CurrentEmployeeInitialedEvent event) {
|
public void onCurrentEmployeeInitialed(CurrentEmployeeInitialedEvent event) {
|
||||||
|
System.out.println("event = " + event);
|
||||||
CurrentEmployee currentEmployee = event.getEmployee();
|
CurrentEmployee currentEmployee = event.getEmployee();
|
||||||
if (currentEmployee.isSystemAdministrator()) {
|
if (currentEmployee.isSystemAdministrator()) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
|
|||||||
@@ -1,544 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.net.Inet4Address;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.ecep.contract.Desktop;
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ListView;
|
|
||||||
import javafx.scene.control.PasswordField;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
public class LoginWidowController implements MessageHolder {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LoginWidowController.class);
|
|
||||||
@Setter
|
|
||||||
MessageHolder holder;
|
|
||||||
@Setter
|
|
||||||
Stage primaryStage;
|
|
||||||
@Setter
|
|
||||||
Properties properties;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addMessage(Level level, String message) {
|
|
||||||
holder.addMessage(level, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeProperties() {
|
|
||||||
try (FileOutputStream fos = new FileOutputStream("config.properties")) {
|
|
||||||
// 保存到文件
|
|
||||||
properties.store(fos, "Updated config.properties");
|
|
||||||
info("配置文件已更新!");
|
|
||||||
} catch (java.io.IOException e) {
|
|
||||||
error("保存配置文件失败:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHost() {
|
|
||||||
return properties.getProperty("server.host");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryLogin() {
|
|
||||||
// CompletableFuture<ButtonType> future = new CompletableFuture<>();
|
|
||||||
// 检查配置文件中是否保存用户名和密码
|
|
||||||
String userName = getUserName();
|
|
||||||
if (StringUtils.hasText(userName)) {
|
|
||||||
try {
|
|
||||||
EmployeeInfo employeeInfo = tryToConnect(userName, getPassword());
|
|
||||||
if (employeeInfo.errorCode < 0) {
|
|
||||||
error("登录失败:错误代码=" + employeeInfo.errorCode);
|
|
||||||
} else {
|
|
||||||
logined(employeeInfo);
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showUserNameLoginDialog(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPassword() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getPassword'");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserName() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getUserName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<List<LoginWidowController.MacIP>> getMacAndIP() {
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
// mac ip
|
|
||||||
List<LoginWidowController.MacIP> list = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
|
||||||
while (interfaces.hasMoreElements()) {
|
|
||||||
NetworkInterface anInterface = interfaces.nextElement();
|
|
||||||
if (anInterface.isLoopback()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
byte[] hardwareAddress = anInterface.getHardwareAddress();
|
|
||||||
if (hardwareAddress == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses();
|
|
||||||
if (!inetAddresses.hasMoreElements()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -分割16进制表示法
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (int i = 0; i < hardwareAddress.length; i++) {
|
|
||||||
sb.append(
|
|
||||||
String.format("%02X%s", hardwareAddress[i], i < hardwareAddress.length - 1 ? "-" : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
while (inetAddresses.hasMoreElements()) {
|
|
||||||
InetAddress inetAddress = inetAddresses.nextElement();
|
|
||||||
if (inetAddress instanceof Inet4Address) {
|
|
||||||
list.add(new LoginWidowController.MacIP(sb.toString(), inetAddress.getHostAddress()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SocketException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private EmployeeInfo tryToConnect(String userName, String password) throws SQLException {
|
|
||||||
String host = getHost();
|
|
||||||
String port = getPort();
|
|
||||||
String database = getDatabase();
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("try to connect db server host:{},port:{},database:{},user:{},pwd:{}", host, port, database,
|
|
||||||
userName, "*");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDatabase() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getDatabase'");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPort() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getPort'");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createSession(Connection connection, EmployeeInfo employeeInfo) {
|
|
||||||
employeeInfo.sessionId = addHistory(connection, employeeInfo.employeeId, employeeInfo.binds.getFirst());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int addHistory(Connection connection, int employeeId, MacIP macIP) {
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO EMPLOYEE_LOGIN_HISTORY (IP, MAC, DT, EMPLOYEE_ID) VALUES (?, ?, ?, ?)";
|
|
||||||
try (PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
|
||||||
ps.setString(1, macIP.ip);
|
|
||||||
ps.setString(2, macIP.mac);
|
|
||||||
ps.setObject(3, LocalDateTime.now()); // 根据数据库字段类型调整
|
|
||||||
ps.setInt(4, employeeId);
|
|
||||||
ps.executeUpdate();
|
|
||||||
// 返回 新的主键值
|
|
||||||
ResultSet generatedKeys = ps.getGeneratedKeys();
|
|
||||||
if (generatedKeys.next()) {
|
|
||||||
return generatedKeys.getInt(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
holder.error("申请新会话编号失败");
|
|
||||||
logger.error("unable insert EMPLOYEE_LOGIN_HISTORY, ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MacIP {
|
|
||||||
String ip;
|
|
||||||
String mac;
|
|
||||||
|
|
||||||
public MacIP(String mac, String ip) {
|
|
||||||
this.mac = mac;
|
|
||||||
this.ip = ip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class EmployeeInfo {
|
|
||||||
Integer employeeId;
|
|
||||||
List<MacIP> binds = new ArrayList<>();
|
|
||||||
SimpleStringProperty name = new SimpleStringProperty();
|
|
||||||
SimpleBooleanProperty active = new SimpleBooleanProperty();
|
|
||||||
int sessionId;
|
|
||||||
int errorCode = 0;
|
|
||||||
|
|
||||||
public EmployeeInfo(Integer employeeId) {
|
|
||||||
this.employeeId = employeeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EmployeeInfo error(int code) {
|
|
||||||
EmployeeInfo employeeInfo = new EmployeeInfo(null);
|
|
||||||
employeeInfo.errorCode = code;
|
|
||||||
return employeeInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addBind(MacIP macIP) {
|
|
||||||
binds.add(macIP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<EmployeeInfo> tryLoginWithEmployeeBind(Connection connection,
|
|
||||||
CompletableFuture<List<MacIP>> macAndIP) {
|
|
||||||
CompletableFuture<EmployeeInfo> future = new CompletableFuture<>();
|
|
||||||
macAndIP.thenAccept(macIPS -> {
|
|
||||||
if (macIPS.isEmpty()) {
|
|
||||||
future.complete(EmployeeInfo.error(-1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap<Integer, EmployeeInfo> employeeMap = new HashMap<>();
|
|
||||||
for (MacIP macIP : macIPS) {
|
|
||||||
for (Integer employeeId : findAllBindEmployee(connection, macIP)) {
|
|
||||||
employeeMap.computeIfAbsent(employeeId, k -> new EmployeeInfo(employeeId)).addBind(macIP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (employeeMap.isEmpty()) {
|
|
||||||
error("本机未绑定登录信息,请联系管理员更新.");
|
|
||||||
// 当前计算机的信息,如用户名,计算机名等
|
|
||||||
String username = System.getProperty("user.name");
|
|
||||||
String computerName = System.getenv("COMPUTERNAME");
|
|
||||||
for (MacIP macIP : macIPS) {
|
|
||||||
if (macIP.ip.equals("127.0.0.1")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
registerComputer(username, computerName, connection, macIP);
|
|
||||||
}
|
|
||||||
future.complete(EmployeeInfo.error(-2));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (employeeMap.size() == 1) {
|
|
||||||
// 直接登录
|
|
||||||
EmployeeInfo employeeInfo = employeeMap.values().stream().findFirst().get();
|
|
||||||
// issue #1 登录成功后没有更新员工信息
|
|
||||||
fill(connection, employeeInfo);
|
|
||||||
future.complete(employeeInfo);
|
|
||||||
} else {
|
|
||||||
List<EmployeeInfo> list = employeeMap.values().stream().toList();
|
|
||||||
// 选择登录
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
EmployeeInfo info = showEmployeeSelectDialog(list);
|
|
||||||
future.complete(Objects.requireNonNullElseGet(info, () -> EmployeeInfo.error(-3)));
|
|
||||||
});
|
|
||||||
for (EmployeeInfo info : list) {
|
|
||||||
fill(connection, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EmployeeInfo showEmployeeSelectDialog(List<EmployeeInfo> list) {
|
|
||||||
Stage stage = new Stage();
|
|
||||||
stage.initOwner(primaryStage);
|
|
||||||
stage.setTitle("请选择账户登录系统");
|
|
||||||
stage.setWidth(360);
|
|
||||||
stage.setHeight(280);
|
|
||||||
|
|
||||||
Label label = new Label("您的主机关联了以下账户,请选择一个登录");
|
|
||||||
label.setPadding(new Insets(10, 0, 10, 10));
|
|
||||||
ListView<Label> listView = new ListView<>();
|
|
||||||
|
|
||||||
EventHandler<MouseEvent> eventHandler = event -> {
|
|
||||||
if (event.getClickCount() == 2) {
|
|
||||||
// listView.getSelectionModel().select(cb);
|
|
||||||
stage.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (EmployeeInfo employeeInfo : list) {
|
|
||||||
Label cb = new Label();
|
|
||||||
cb.setUserData(employeeInfo);
|
|
||||||
cb.textProperty().bind(employeeInfo.name);
|
|
||||||
cb.setPadding(new Insets(5));
|
|
||||||
cb.setOnMouseClicked(eventHandler);
|
|
||||||
listView.getItems().add(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 BorderPane 并设置边距
|
|
||||||
BorderPane borderPane = new BorderPane();
|
|
||||||
borderPane.setPadding(new Insets(10));
|
|
||||||
borderPane.setTop(label);
|
|
||||||
borderPane.setCenter(listView);
|
|
||||||
|
|
||||||
Button bottom = new Button("确定");
|
|
||||||
bottom.setDefaultButton(true);
|
|
||||||
bottom.setOnAction(event -> {
|
|
||||||
Label selectedItem = listView.getSelectionModel().getSelectedItem();
|
|
||||||
if (selectedItem == null) {
|
|
||||||
// 没选中,退出继续选择
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stage.close();
|
|
||||||
});
|
|
||||||
BorderPane.setAlignment(bottom, javafx.geometry.Pos.CENTER);
|
|
||||||
|
|
||||||
borderPane.setBottom(bottom);
|
|
||||||
stage.setScene(new Scene(borderPane));
|
|
||||||
stage.setOnCloseRequest(event -> {
|
|
||||||
Label selectedItem = listView.getSelectionModel().getSelectedItem();
|
|
||||||
if (selectedItem == null) {
|
|
||||||
// 关闭时,如何没有做选择,不关闭窗口
|
|
||||||
event.consume();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.showAndWait();
|
|
||||||
|
|
||||||
Label selectedItem = listView.getSelectionModel().getSelectedItem();
|
|
||||||
if (selectedItem == null) {
|
|
||||||
throw new NoSuchElementException("请选择工号登录系统");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (EmployeeInfo) selectedItem.getUserData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fill(Connection connection, EmployeeInfo info) {
|
|
||||||
try {
|
|
||||||
ResultSet rs = connection.createStatement()
|
|
||||||
.executeQuery("SELECT * FROM EMPLOYEE where ID = " + info.employeeId);
|
|
||||||
if (rs.next()) {
|
|
||||||
String name = rs.getString("NAME");
|
|
||||||
boolean isActive = rs.getBoolean("IS_ACTIVE");
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
info.name.set(name);
|
|
||||||
info.active.set(isActive);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.error("查询{}失败", info.employeeId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerComputer(String username, String computerName, Connection connection, MacIP macIP) {
|
|
||||||
info("正在注册本机信息(MAC:" + macIP.mac + ", IP:" + macIP.ip + ")...");
|
|
||||||
String sql = "INSERT INTO EMPLOYEE_AUTH_BIND (IP,MAC,DESCRIPTION)VALUES(?,?,?)";
|
|
||||||
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
|
|
||||||
stmt.setString(1, macIP.ip);
|
|
||||||
stmt.setString(2, macIP.mac);
|
|
||||||
// 当前计算机的信息,如用户名,计算机名等
|
|
||||||
stmt.setString(3, username + "," + computerName);
|
|
||||||
if (stmt.execute()) {
|
|
||||||
info("注册成功");
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
error(String.format("注册失败,请联系管理员或重启应用!MAC: %s, IP: %s", macIP.mac, macIP.ip));
|
|
||||||
logger.error("注册失败 mac:{}, ip:{}", macIP.mac, macIP.ip, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> findAllBindEmployee(Connection connection, MacIP macIP) {
|
|
||||||
List<Integer> list = new ArrayList<>();
|
|
||||||
// 优化后代码
|
|
||||||
String sql = "SELECT * FROM EMPLOYEE_AUTH_BIND WHERE IP = ? AND MAC = ?";
|
|
||||||
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
|
|
||||||
stmt.setString(1, macIP.ip);
|
|
||||||
stmt.setString(2, macIP.mac);
|
|
||||||
try (ResultSet rs = stmt.executeQuery()) {
|
|
||||||
while (rs.next()) {
|
|
||||||
int id = rs.getInt("EMPLOYEE_ID");
|
|
||||||
if (id > 0) {
|
|
||||||
list.add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
String.format("查询本机绑定信息异常,请联系管理员或重启应用!MAC: %s, IP: %s", macIP.mac, macIP.ip),
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showUserNameLoginDialog(Exception exception) {
|
|
||||||
Stage stage = new Stage();
|
|
||||||
stage.initOwner(primaryStage);
|
|
||||||
stage.setTitle("登录");
|
|
||||||
|
|
||||||
// 创建 BorderPane 并设置边距
|
|
||||||
BorderPane borderPane = new BorderPane();
|
|
||||||
borderPane.setPadding(new Insets(10));
|
|
||||||
|
|
||||||
// 创建布局
|
|
||||||
GridPane grid = new GridPane();
|
|
||||||
grid.setHgap(10);
|
|
||||||
grid.setVgap(10);
|
|
||||||
// 为整个 GridPane 设置外边距
|
|
||||||
// GridPane.setMargin(grid, new Insets(10));
|
|
||||||
|
|
||||||
// 账户输入框
|
|
||||||
Label userLabel = new Label("账户:");
|
|
||||||
TextField userField = new TextField();
|
|
||||||
{
|
|
||||||
String username = getUserName();
|
|
||||||
if (StringUtils.hasText(username)) {
|
|
||||||
userField.setText(username);
|
|
||||||
}
|
|
||||||
grid.add(userLabel, 0, 0);
|
|
||||||
grid.add(userField, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码输入框
|
|
||||||
Label passwordLabel = new Label("密码:");
|
|
||||||
PasswordField passwordField = new PasswordField();
|
|
||||||
{
|
|
||||||
String password = getPassword();
|
|
||||||
if (StringUtils.hasText(password)) {
|
|
||||||
passwordField.setText(password);
|
|
||||||
}
|
|
||||||
grid.add(passwordLabel, 0, 1);
|
|
||||||
grid.add(passwordField, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记住密码复选框
|
|
||||||
CheckBox rememberCheckBox = new CheckBox("记住账户密码");
|
|
||||||
{
|
|
||||||
String property = properties.getProperty("username_password.remember", "false");
|
|
||||||
if (Boolean.parseBoolean(property)) {
|
|
||||||
rememberCheckBox.setSelected(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
grid.add(rememberCheckBox, 1, 2);
|
|
||||||
|
|
||||||
// 错误消息提示
|
|
||||||
Label exceptionLabel = new Label();
|
|
||||||
grid.add(exceptionLabel, 0, 3);
|
|
||||||
exceptionLabel.setWrapText(true);
|
|
||||||
GridPane.setColumnSpan(exceptionLabel, 2);
|
|
||||||
if (exception == null) {
|
|
||||||
exceptionLabel.setVisible(false);
|
|
||||||
} else {
|
|
||||||
exceptionLabel.setText(exception.getMessage());
|
|
||||||
exceptionLabel.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
borderPane.setCenter(grid);
|
|
||||||
|
|
||||||
// 登录按钮
|
|
||||||
Button loginButton = new Button("登录");
|
|
||||||
loginButton.setDefaultButton(true);
|
|
||||||
borderPane.setBottom(loginButton);
|
|
||||||
BorderPane.setAlignment(loginButton, javafx.geometry.Pos.CENTER);
|
|
||||||
|
|
||||||
// 登录按钮点击事件
|
|
||||||
loginButton.setOnAction(event -> {
|
|
||||||
String username = userField.getText();
|
|
||||||
String password = passwordField.getText();
|
|
||||||
boolean remember = rememberCheckBox.isSelected();
|
|
||||||
|
|
||||||
// 尝试连接数据库
|
|
||||||
exceptionLabel.setText("");
|
|
||||||
exceptionLabel.setVisible(false);
|
|
||||||
try {
|
|
||||||
EmployeeInfo employeeInfo = tryToConnect(username, password);
|
|
||||||
if (employeeInfo.errorCode < 0) {
|
|
||||||
exceptionLabel.setText("登录失败:错误代码=" + employeeInfo.errorCode);
|
|
||||||
exceptionLabel.setVisible(true);
|
|
||||||
} else {
|
|
||||||
logined(employeeInfo);
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
//
|
|
||||||
exceptionLabel.setText("数据库错误:" + ex.getMessage());
|
|
||||||
exceptionLabel.setVisible(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
properties.setProperty("db.server.username", username);
|
|
||||||
properties.setProperty("db.server.password", password);
|
|
||||||
properties.setProperty("username_password.remember", Boolean.toString(remember));
|
|
||||||
|
|
||||||
// 如果勾选了“记住密码”,则更新配置文件
|
|
||||||
if (remember) {
|
|
||||||
CompletableFuture.runAsync(() -> {
|
|
||||||
storeProperties();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭登录窗口
|
|
||||||
stage.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建场景并设置到窗口
|
|
||||||
Scene scene = new Scene(borderPane, 400, 260);
|
|
||||||
stage.setScene(scene);
|
|
||||||
// stage.setAlwaysOnTop(true);
|
|
||||||
stage.setResizable(false);
|
|
||||||
stage.showAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logined(EmployeeInfo employeeInfo) {
|
|
||||||
info("欢迎 " + employeeInfo.name.get());
|
|
||||||
if (!SpringApp.isRunning()) {
|
|
||||||
info("请稍后...");
|
|
||||||
}
|
|
||||||
Desktop.instance.setActiveEmployeeId(employeeInfo.employeeId);
|
|
||||||
// Desktop.instance.setSessionId(employeeInfo.sessionId);
|
|
||||||
tryShowHomeWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
void tryShowHomeWindow() {
|
|
||||||
try {
|
|
||||||
while (!SpringApp.isRunning()) {
|
|
||||||
System.out.println("等待启动");
|
|
||||||
Thread.sleep(1000);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 必须要等待启动成功后才能关闭主场景,否则进程结束程序退出
|
|
||||||
HomeWindowController.show().thenRun(() -> Platform.runLater(primaryStage::close));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,12 +11,14 @@ import java.util.Properties;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.Desktop;
|
import com.ecep.contract.Desktop;
|
||||||
import com.ecep.contract.MessageHolder;
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.MyProperties;
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@@ -56,31 +58,13 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
@Setter
|
@Setter
|
||||||
private Stage primaryStage;
|
private Stage primaryStage;
|
||||||
@Setter
|
@Setter
|
||||||
private Properties properties;
|
private MyProperties properties;
|
||||||
@Setter
|
@Setter
|
||||||
private OkHttpClient httpClient;
|
private OkHttpClient httpClient;
|
||||||
private WebSocket webSocket;
|
private SimpleStringProperty serverUrl = new SimpleStringProperty();
|
||||||
private String serverUrl;
|
|
||||||
private String webSocketUrl;
|
private String webSocketUrl;
|
||||||
|
|
||||||
public OkHttpLoginController() {
|
public OkHttpLoginController() {
|
||||||
this.httpClient = new OkHttpClient().newBuilder().cookieJar(new CookieJar() {
|
|
||||||
private final List<Cookie> cookies = new java.util.ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
|
||||||
// 保存服务器返回的Cookie(如JSESSIONID)
|
|
||||||
this.cookies.addAll(cookies);
|
|
||||||
System.out.println("保存Cookie: " + cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
|
||||||
// 请求时自动携带Cookie
|
|
||||||
return cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
}).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -91,9 +75,9 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initServerUrls() {
|
private void initServerUrls() {
|
||||||
String host = properties.getProperty("server.host", "localhost");
|
String host = properties.getServerHost();
|
||||||
String port = properties.getProperty("server.port", "8080");
|
String port = properties.getServerPort();
|
||||||
this.serverUrl = "http://" + host + ":" + port;
|
serverUrl.set("http://" + host + ":" + port);
|
||||||
this.webSocketUrl = "ws://" + host + ":" + port + "/ws";
|
this.webSocketUrl = "ws://" + host + ":" + port + "/ws";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,11 +101,11 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getUserName() {
|
private String getUserName() {
|
||||||
return properties.getProperty("user.name", "");
|
return properties.getUserName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPassword() {
|
private String getPassword() {
|
||||||
return properties.getProperty("user.password", "");
|
return properties.getPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showLoginDialog() {
|
private void showLoginDialog() {
|
||||||
@@ -138,6 +122,14 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
grid.setHgap(10);
|
grid.setHgap(10);
|
||||||
grid.setVgap(15);
|
grid.setVgap(15);
|
||||||
|
|
||||||
|
Label hostLabel = new Label("服务器:");
|
||||||
|
TextField hostField = new TextField();
|
||||||
|
{
|
||||||
|
hostField.textProperty().bindBidirectional(serverUrl);
|
||||||
|
grid.add(hostLabel, 0, 0);
|
||||||
|
grid.add(hostField, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// 账户输入框
|
// 账户输入框
|
||||||
Label userLabel = new Label("用户名:");
|
Label userLabel = new Label("用户名:");
|
||||||
TextField userField = new TextField();
|
TextField userField = new TextField();
|
||||||
@@ -146,8 +138,8 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
if (StringUtils.hasText(username)) {
|
if (StringUtils.hasText(username)) {
|
||||||
userField.setText(username);
|
userField.setText(username);
|
||||||
}
|
}
|
||||||
grid.add(userLabel, 0, 0);
|
grid.add(userLabel, 0, 1);
|
||||||
grid.add(userField, 1, 0);
|
grid.add(userField, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 密码输入框
|
// 密码输入框
|
||||||
@@ -158,25 +150,25 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
if (StringUtils.hasText(password)) {
|
if (StringUtils.hasText(password)) {
|
||||||
passwordField.setText(password);
|
passwordField.setText(password);
|
||||||
}
|
}
|
||||||
grid.add(passwordLabel, 0, 1);
|
grid.add(passwordLabel, 0, 2);
|
||||||
grid.add(passwordField, 1, 1);
|
grid.add(passwordField, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记住密码复选框
|
// 记住密码复选框
|
||||||
CheckBox rememberCheckBox = new CheckBox("记住密码");
|
CheckBox rememberCheckBox = new CheckBox("记住密码");
|
||||||
{
|
{
|
||||||
String property = properties.getProperty("remember.password", "false");
|
boolean remember = properties.isRememberPassword();
|
||||||
if (Boolean.parseBoolean(property)) {
|
if (remember) {
|
||||||
rememberCheckBox.setSelected(true);
|
rememberCheckBox.setSelected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
grid.add(rememberCheckBox, 1, 2);
|
grid.add(rememberCheckBox, 1, 3);
|
||||||
|
|
||||||
// 错误消息提示
|
// 错误消息提示
|
||||||
Label errorLabel = new Label();
|
Label errorLabel = new Label();
|
||||||
errorLabel.setStyle("-fx-text-fill: red;");
|
errorLabel.setStyle("-fx-text-fill: red;");
|
||||||
errorLabel.setVisible(false);
|
errorLabel.setVisible(false);
|
||||||
grid.add(errorLabel, 0, 3);
|
grid.add(errorLabel, 0, 4);
|
||||||
GridPane.setColumnSpan(errorLabel, 2);
|
GridPane.setColumnSpan(errorLabel, 2);
|
||||||
|
|
||||||
borderPane.setCenter(grid);
|
borderPane.setCenter(grid);
|
||||||
@@ -204,14 +196,16 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
errorLabel.setVisible(false);
|
errorLabel.setVisible(false);
|
||||||
|
|
||||||
// 保存配置
|
// 保存配置
|
||||||
properties.setProperty("user.name", username);
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
properties.setProperty("user.password", password);
|
properties.setUserName(username);
|
||||||
properties.setProperty("remember.password", "true");
|
properties.setPassword(password);
|
||||||
|
properties.setRememberPassword(true);
|
||||||
} else {
|
} else {
|
||||||
properties.setProperty("user.password", "");
|
properties.setUserName(username);
|
||||||
properties.setProperty("remember.password", "false");
|
properties.setPassword("");
|
||||||
|
properties.setRememberPassword(false);
|
||||||
}
|
}
|
||||||
|
properties.save();
|
||||||
|
|
||||||
// 执行登录
|
// 执行登录
|
||||||
login(username, password).whenComplete((v, e) -> {
|
login(username, password).whenComplete((v, e) -> {
|
||||||
@@ -240,8 +234,8 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
|
|
||||||
private CompletableFuture<Void> login(String username, String password) {
|
private CompletableFuture<Void> login(String username, String password) {
|
||||||
// 添加详细日志,记录服务器URL和请求准备情况
|
// 添加详细日志,记录服务器URL和请求准备情况
|
||||||
info("正在连接服务器: " + serverUrl);
|
info("正在连接服务器: " + serverUrl.get());
|
||||||
logger.debug("login方法被调用,用户名: " + username);
|
logger.debug("用户名: {}", username);
|
||||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -268,7 +262,7 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
RequestBody body = RequestBody.create(objectNode.toString(), JSON);
|
RequestBody body = RequestBody.create(objectNode.toString(), JSON);
|
||||||
|
|
||||||
// 构建并记录完整的请求URL
|
// 构建并记录完整的请求URL
|
||||||
String loginUrl = serverUrl + "/api/login";
|
String loginUrl = serverUrl.get() + "/api/login";
|
||||||
logger.debug("构建登录请求URL: " + loginUrl);
|
logger.debug("构建登录请求URL: " + loginUrl);
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(loginUrl)
|
.url(loginUrl)
|
||||||
@@ -363,21 +357,6 @@ public class OkHttpLoginController implements MessageHolder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket消息发送方法
|
|
||||||
public void sendMessage(String message) {
|
|
||||||
if (webSocket != null) {
|
|
||||||
webSocket.send(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭WebSocket连接
|
|
||||||
public void closeWebSocket() {
|
|
||||||
if (webSocket != null) {
|
|
||||||
webSocket.close(1000, "正常关闭");
|
|
||||||
webSocket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MacIP {
|
static class MacIP {
|
||||||
String mac;
|
String mac;
|
||||||
String ip;
|
String ip;
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.ecep.contract.controller;
|
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
|
|
||||||
public class VendorClassSyncTask extends Tasker<Object> {
|
|
||||||
@Override
|
|
||||||
public Object execute(MessageHolder holder) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ecep.contract.controller.bank;
|
package com.ecep.contract.controller.bank;
|
||||||
|
|
||||||
|
import org.controlsfx.control.SearchableComboBox;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
@@ -34,7 +35,6 @@ public class BankManagerWindowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onCreateNewAction(ActionEvent event) {
|
public void onCreateNewAction(ActionEvent event) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReBuildFilesAction(ActionEvent event) {
|
public void onReBuildFilesAction(ActionEvent event) {
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.rk;
|
||||||
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.model.CloudRk;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CloudRkService;
|
import com.ecep.contract.service.CloudRkService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.vm.CloudRkViewModel;
|
import com.ecep.contract.vm.CloudRkViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudRkVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -21,7 +20,7 @@ import javafx.scene.control.cell.CheckBoxTableCell;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class CloudRkManagerSkin
|
public class CloudRkManagerSkin
|
||||||
extends AbstEntityManagerSkin<CloudRk, CloudRkViewModel, CloudRkManagerSkin, CloudRkManagerWindowController> {
|
extends AbstEntityManagerSkin<CloudRkVo, CloudRkViewModel, CloudRkManagerSkin, CloudRkManagerWindowController> {
|
||||||
@Setter
|
@Setter
|
||||||
private CompanyService companyService;
|
private CompanyService companyService;
|
||||||
|
|
||||||
@@ -104,11 +103,8 @@ public class CloudRkManagerSkin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableRowDoubleClickedAction(CloudRkViewModel item) {
|
protected void onTableRowDoubleClickedAction(CloudRkViewModel item) {
|
||||||
Company company = item.getCompany().get();
|
Integer companyId = item.getCompany().get();
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
item.getCompany().set(company);
|
|
||||||
}
|
|
||||||
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.rk;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -13,13 +14,12 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import com.ecep.contract.Message;
|
import com.ecep.contract.Message;
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
import com.ecep.contract.model.CloudRk;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CloudRkService;
|
import com.ecep.contract.service.CloudRkService;
|
||||||
import com.ecep.contract.task.CloudRkSyncTask;
|
import com.ecep.contract.task.CloudRkSyncTask;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CloudRkViewModel;
|
import com.ecep.contract.vm.CloudRkViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudRkVo;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -31,7 +31,7 @@ import javafx.stage.WindowEvent;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/cloud/rk_manager.fxml")
|
@FxmlPath("/ui/cloud/rk_manager.fxml")
|
||||||
public class CloudRkManagerWindowController
|
public class CloudRkManagerWindowController
|
||||||
extends AbstManagerWindowController<CloudRk, CloudRkViewModel, CloudRkManagerSkin> {
|
extends AbstManagerWindowController<CloudRkVo, CloudRkViewModel, CloudRkManagerSkin> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CloudRkManagerWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(CloudRkManagerWindowController.class);
|
||||||
|
|
||||||
public static void show() {
|
public static void show() {
|
||||||
@@ -43,8 +43,10 @@ public class CloudRkManagerWindowController
|
|||||||
|
|
||||||
public TableColumn<CloudRkViewModel, Number> idColumn;
|
public TableColumn<CloudRkViewModel, Number> idColumn;
|
||||||
public TableColumn<CloudRkViewModel, LocalDateTime> latestUpdateColumn;
|
public TableColumn<CloudRkViewModel, LocalDateTime> latestUpdateColumn;
|
||||||
|
/**
|
||||||
public TableColumn<CloudRkViewModel, Company> companyColumn;
|
* 集团相关方, Company
|
||||||
|
*/
|
||||||
|
public TableColumn<CloudRkViewModel, Integer> companyColumn;
|
||||||
public TableColumn<CloudRkViewModel, String> cloudIdColumn;
|
public TableColumn<CloudRkViewModel, String> cloudIdColumn;
|
||||||
public TableColumn<CloudRkViewModel, LocalDateTime> cloudLatestColumn;
|
public TableColumn<CloudRkViewModel, LocalDateTime> cloudLatestColumn;
|
||||||
public TableColumn<CloudRkViewModel, LocalDateTime> cloudBlackListUpdatedColumn;
|
public TableColumn<CloudRkViewModel, LocalDateTime> cloudBlackListUpdatedColumn;
|
||||||
@@ -63,32 +65,12 @@ public class CloudRkManagerWindowController
|
|||||||
getTitle().set("数据源:集团相关方");
|
getTitle().set("数据源:集团相关方");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeTask(Task<Object> task, String prefix, Consumer<String> consumer) {
|
|
||||||
task.setOnScheduled(e -> {
|
|
||||||
consumer.accept("正在从相关方平台同步" + prefix + ",请稍后...");
|
|
||||||
});
|
|
||||||
task.setOnRunning(e -> {
|
|
||||||
consumer.accept("开始" + prefix + "...");
|
|
||||||
});
|
|
||||||
task.setOnSucceeded(e -> {
|
|
||||||
consumer.accept(prefix + "完成...");
|
|
||||||
});
|
|
||||||
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
consumer.accept(newValue.getMessage());
|
|
||||||
logger.error("{} 发生异常", prefix, newValue);
|
|
||||||
});
|
|
||||||
SpringApp.getBean(ScheduledExecutorService.class).submit(task);
|
|
||||||
consumer.accept("任务已创建...");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDataRepairAction(ActionEvent event) {
|
public void onDataRepairAction(ActionEvent event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSyncAction(ActionEvent event) {
|
public void onSyncAction(ActionEvent event) {
|
||||||
CloudRkSyncTask task = new CloudRkSyncTask();
|
CloudRkSyncTask task = new CloudRkSyncTask();
|
||||||
UITools.showTaskDialogAndWait("同步数据", task, consumer -> {
|
UITools.showTaskDialogAndWait("同步数据", task, null);
|
||||||
initializeTask(task, "同步数据", msg -> consumer.accept(Message.info(msg)));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1,22 +1,15 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.tyc;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.model.CloudTyc;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CloudTycService;
|
import com.ecep.contract.service.CloudTycService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.vm.CloudTycInfoViewModel;
|
import com.ecep.contract.vm.CloudTycInfoViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudTycVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -26,7 +19,7 @@ import lombok.Setter;
|
|||||||
|
|
||||||
public class CloudTycManagerSkin
|
public class CloudTycManagerSkin
|
||||||
extends
|
extends
|
||||||
AbstEntityManagerSkin<CloudTyc, CloudTycInfoViewModel, CloudTycManagerSkin, CloudTycManagerWindowController> {
|
AbstEntityManagerSkin<CloudTycVo, CloudTycInfoViewModel, CloudTycManagerSkin, CloudTycManagerWindowController> {
|
||||||
@Setter
|
@Setter
|
||||||
private CloudTycService cloudTycService;
|
private CloudTycService cloudTycService;
|
||||||
|
|
||||||
@@ -59,7 +52,7 @@ public class CloudTycManagerSkin
|
|||||||
|
|
||||||
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
|
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
|
||||||
|
|
||||||
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatest());
|
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatestUpdate());
|
||||||
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
|
|
||||||
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
|
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
|
||||||
@@ -91,21 +84,20 @@ public class CloudTycManagerSkin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (CloudTycInfoViewModel selectedItem : selectedItems) {
|
for (CloudTycInfoViewModel selectedItem : selectedItems) {
|
||||||
CloudTyc cloudTyc = getCloudTycService().findById(selectedItem.getId().get());
|
CloudTycVo cloudTyc = getCloudTycService().findById(selectedItem.getId().get());
|
||||||
// selectedItem.getDescription().set("");
|
// selectedItem.getDescription().set("");
|
||||||
if (selectedItem.copyTo(cloudTyc)) {
|
if (selectedItem.copyTo(cloudTyc)) {
|
||||||
CloudTyc saved = getCloudTycService().save(cloudTyc);
|
CloudTycVo saved = getCloudTycService().save(cloudTyc);
|
||||||
selectedItem.update(saved);
|
selectedItem.update(saved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
@Override
|
@Override
|
||||||
protected void onTableRowDoubleClickedAction(CloudTycInfoViewModel item) {
|
protected void onTableRowDoubleClickedAction(CloudTycInfoViewModel item) {
|
||||||
Company company = item.getCompany().get();
|
Integer companyId = item.getCompany().get();
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
}
|
|
||||||
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.tyc;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.model.CloudTyc;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CloudTycService;
|
import com.ecep.contract.service.CloudTycService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.CloudTycInfoViewModel;
|
import com.ecep.contract.vm.CloudTycInfoViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudTycVo;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
@@ -26,7 +26,7 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/cloud/tyc_manager.fxml")
|
@FxmlPath("/ui/cloud/tyc_manager.fxml")
|
||||||
public class CloudTycManagerWindowController
|
public class CloudTycManagerWindowController
|
||||||
extends AbstManagerWindowController<CloudTyc, CloudTycInfoViewModel, CloudTycManagerSkin> {
|
extends AbstManagerWindowController<CloudTycVo, CloudTycInfoViewModel, CloudTycManagerSkin> {
|
||||||
|
|
||||||
public static void show() {
|
public static void show() {
|
||||||
show(CloudTycManagerWindowController.class, null);
|
show(CloudTycManagerWindowController.class, null);
|
||||||
@@ -43,8 +43,11 @@ public class CloudTycManagerWindowController
|
|||||||
@FXML
|
@FXML
|
||||||
public TableColumn<CloudTycInfoViewModel, LocalDateTime> latestUpdateColumn;
|
public TableColumn<CloudTycInfoViewModel, LocalDateTime> latestUpdateColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公司, Company
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<CloudTycInfoViewModel, Company> companyColumn;
|
public TableColumn<CloudTycInfoViewModel, Integer> companyColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<CloudTycInfoViewModel, String> cloudIdColumn;
|
public TableColumn<CloudTycInfoViewModel, String> cloudIdColumn;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.u8;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.BaseController;
|
||||||
import org.controlsfx.control.ToggleSwitch;
|
import org.controlsfx.control.ToggleSwitch;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
@@ -1,32 +1,26 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.u8;
|
||||||
|
|
||||||
import java.util.List;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import java.util.Map;
|
import com.ecep.contract.controller.ManagerSkin;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
|
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.model.CloudYu;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.YongYouU8Service;
|
import com.ecep.contract.service.YongYouU8Service;
|
||||||
import com.ecep.contract.util.ParamUtils;
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.vm.CloudYuInfoViewModel;
|
import com.ecep.contract.vm.CloudYuInfoViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudYuVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import lombok.Setter;
|
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||||
|
|
||||||
public class YongYouU8ManagerSkin
|
public class YongYouU8ManagerSkin
|
||||||
extends
|
extends
|
||||||
AbstEntityManagerSkin<CloudYu, CloudYuInfoViewModel, YongYouU8ManagerSkin, YongYouU8ManagerWindowController>
|
AbstEntityManagerSkin<CloudYuVo, CloudYuInfoViewModel, YongYouU8ManagerSkin, YongYouU8ManagerWindowController>
|
||||||
implements ManagerSkin {
|
implements ManagerSkin {
|
||||||
|
|
||||||
public YongYouU8ManagerSkin(YongYouU8ManagerWindowController controller) {
|
public YongYouU8ManagerSkin(YongYouU8ManagerWindowController controller) {
|
||||||
@@ -34,7 +28,7 @@ public class YongYouU8ManagerSkin
|
|||||||
}
|
}
|
||||||
|
|
||||||
YongYouU8Service getU8Service() {
|
YongYouU8Service getU8Service() {
|
||||||
return getBean(YongYouU8Service.class);
|
return getBean(YongYouU8Service.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompanyService getCompanyService() {
|
CompanyService getCompanyService() {
|
||||||
@@ -44,18 +38,24 @@ public class YongYouU8ManagerSkin
|
|||||||
@Override
|
@Override
|
||||||
public void initializeTable() {
|
public void initializeTable() {
|
||||||
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
|
|
||||||
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
|
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
|
||||||
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
|
controller.companyColumn.setCellFactory( CompanyTableCell.forTableColumn(getCompanyService()));
|
||||||
|
|
||||||
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
|
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatestUpdate());
|
||||||
|
|
||||||
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatest());
|
|
||||||
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
|
|
||||||
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
|
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
|
||||||
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
|
|
||||||
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getVendorCode());
|
controller.cloudVendorUpdateDateColumn.setCellValueFactory(param -> param.getValue().getVendorUpdateDate());
|
||||||
|
|
||||||
|
controller.cloudCustomerUpdateDateColumn.setCellValueFactory(param -> param.getValue().getCustomerUpdateDate());
|
||||||
|
|
||||||
|
controller.activeColumn.setCellValueFactory(param -> param.getValue().getActive());
|
||||||
|
controller.activeColumn.setCellFactory(CheckBoxTableCell.forTableColumn(controller.activeColumn));
|
||||||
|
|
||||||
|
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getExceptionMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,10 +80,10 @@ public class YongYouU8ManagerSkin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (CloudYuInfoViewModel selectedItem : selectedItems) {
|
for (CloudYuInfoViewModel selectedItem : selectedItems) {
|
||||||
CloudYu cloudYu = getU8Service().findById(selectedItem.getId().get());
|
CloudYuVo yongYouU8Vo = getU8Service().findById(selectedItem.getId().get());
|
||||||
selectedItem.getCustomerCode().set("");
|
selectedItem.getExceptionMessage().set("");
|
||||||
if (selectedItem.copyTo(cloudYu)) {
|
if (selectedItem.copyTo(yongYouU8Vo)) {
|
||||||
CloudYu saved = getU8Service().save(cloudYu);
|
CloudYuVo saved = getU8Service().save(yongYouU8Vo);
|
||||||
selectedItem.update(saved);
|
selectedItem.update(saved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,10 +91,8 @@ public class YongYouU8ManagerSkin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableRowDoubleClickedAction(CloudYuInfoViewModel item) {
|
protected void onTableRowDoubleClickedAction(CloudYuInfoViewModel item) {
|
||||||
Company company = item.getCompany().get();
|
Integer companyId = item.getCompany().get();
|
||||||
if (!ProxyUtils.isInitialized(item)) {
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
}
|
|
||||||
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
CompanyWindowController.show(company, getTableView().getScene().getWindow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.ecep.contract.controller;
|
package com.ecep.contract.controller.cloud.u8;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
|
import com.ecep.contract.controller.BaseController;
|
||||||
|
import com.ecep.contract.task.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
@@ -9,16 +12,11 @@ import org.springframework.context.annotation.Scope;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.Desktop;
|
import com.ecep.contract.Desktop;
|
||||||
import com.ecep.contract.model.CloudYu;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.YongYouU8Service;
|
import com.ecep.contract.service.YongYouU8Service;
|
||||||
import com.ecep.contract.task.ContractSyncTask;
|
|
||||||
import com.ecep.contract.task.CustomerSyncTask;
|
|
||||||
import com.ecep.contract.task.EmployeesSyncTask;
|
|
||||||
import com.ecep.contract.task.VendorSyncTask;
|
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CloudYuInfoViewModel;
|
import com.ecep.contract.vm.CloudYuInfoViewModel;
|
||||||
|
import com.ecep.contract.vo.CloudYuVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
@@ -29,7 +27,7 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/cloud/u8_manager.fxml")
|
@FxmlPath("/ui/cloud/u8_manager.fxml")
|
||||||
public class YongYouU8ManagerWindowController
|
public class YongYouU8ManagerWindowController
|
||||||
extends AbstManagerWindowController<CloudYu, CloudYuInfoViewModel, YongYouU8ManagerSkin> {
|
extends AbstManagerWindowController<CloudYuVo, CloudYuInfoViewModel, YongYouU8ManagerSkin> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(YongYouU8ManagerWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(YongYouU8ManagerWindowController.class);
|
||||||
|
|
||||||
public static void show() {
|
public static void show() {
|
||||||
@@ -38,9 +36,11 @@ public class YongYouU8ManagerWindowController
|
|||||||
|
|
||||||
public TableColumn<CloudYuInfoViewModel, Number> idColumn;
|
public TableColumn<CloudYuInfoViewModel, Number> idColumn;
|
||||||
public TableColumn<CloudYuInfoViewModel, LocalDateTime> latestUpdateColumn;
|
public TableColumn<CloudYuInfoViewModel, LocalDateTime> latestUpdateColumn;
|
||||||
public TableColumn<CloudYuInfoViewModel, Company> companyColumn;
|
public TableColumn<CloudYuInfoViewModel, Integer> companyColumn;
|
||||||
public TableColumn<CloudYuInfoViewModel, String> cloudIdColumn;
|
|
||||||
public TableColumn<CloudYuInfoViewModel, LocalDateTime> cloudLatestColumn;
|
public TableColumn<CloudYuInfoViewModel, LocalDateTime> cloudLatestColumn;
|
||||||
|
public TableColumn<CloudYuInfoViewModel, java.time.LocalDate> cloudVendorUpdateDateColumn;
|
||||||
|
public TableColumn<CloudYuInfoViewModel, java.time.LocalDate> cloudCustomerUpdateDateColumn;
|
||||||
|
public TableColumn<CloudYuInfoViewModel, Boolean> activeColumn;
|
||||||
public TableColumn<CloudYuInfoViewModel, String> descriptionColumn;
|
public TableColumn<CloudYuInfoViewModel, String> descriptionColumn;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,27 +93,32 @@ public class YongYouU8ManagerWindowController
|
|||||||
|
|
||||||
public void onContractGroupSyncAction(ActionEvent event) {
|
public void onContractGroupSyncAction(ActionEvent event) {
|
||||||
ContractGroupSyncTask task = new ContractGroupSyncTask();
|
ContractGroupSyncTask task = new ContractGroupSyncTask();
|
||||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
UITools.showTaskDialogAndWait("合同组数据同步", task, null);
|
||||||
|
// Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onContractTypeSyncAction(ActionEvent event) {
|
public void onContractTypeSyncAction(ActionEvent event) {
|
||||||
ContractTypeSyncTask task = new ContractTypeSyncTask();
|
ContractTypeSyncTask task = new ContractTypeSyncTask();
|
||||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
UITools.showTaskDialogAndWait("合同类型数据同步", task, null);
|
||||||
|
// Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onContractKindSyncAction(ActionEvent event) {
|
public void onContractKindSyncAction(ActionEvent event) {
|
||||||
ContractKindSyncTask task = new ContractKindSyncTask();
|
ContractKindSyncTask task = new ContractKindSyncTask();
|
||||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
UITools.showTaskDialogAndWait("合同类型数据同步", task, null);
|
||||||
|
// Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onVendorClassSyncAction(ActionEvent event) {
|
public void onVendorClassSyncAction(ActionEvent event) {
|
||||||
VendorClassSyncTask task = new VendorClassSyncTask();
|
VendorClassSyncTask task = new VendorClassSyncTask();
|
||||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
UITools.showTaskDialogAndWait("客户分类数据同步", task, null);
|
||||||
|
// Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCustomerClassSyncAction(ActionEvent event) {
|
public void onCustomerClassSyncAction(ActionEvent event) {
|
||||||
CustomerClassSyncTask task = new CustomerClassSyncTask();
|
CustomerClassSyncTask task = new CustomerClassSyncTask();
|
||||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
UITools.showTaskDialogAndWait("客户分类数据同步", task, null);
|
||||||
|
// Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,14 @@ package com.ecep.contract.controller.company;
|
|||||||
|
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.vm.CompanyViewModel;
|
import com.ecep.contract.vm.CompanyViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
|
||||||
public abstract class AbstCompanyBasedTabSkin
|
public abstract class AbstCompanyBasedTabSkin
|
||||||
extends AbstEntityBasedTabSkin<CompanyWindowController, Company, CompanyViewModel>
|
extends AbstEntityBasedTabSkin<CompanyWindowController, CompanyVo, CompanyViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public AbstCompanyBasedTabSkin(CompanyWindowController controller) {
|
public AbstCompanyBasedTabSkin(CompanyWindowController controller) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.ecep.contract.controller.company;
|
package com.ecep.contract.controller.company;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
||||||
import com.ecep.contract.controller.table.TableOfTabSkin;
|
import com.ecep.contract.controller.table.TableOfTabSkin;
|
||||||
@@ -40,8 +38,8 @@ public abstract class AbstCompanyTableTabSkin<T extends IdentityEntity, TV exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(CompanyVo parent) {
|
public ParamUtils.Builder getSpecification(CompanyVo parent) {
|
||||||
return ParamUtils.equal("company", parent.getId());
|
return ParamUtils.builder().equals("company", parent.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,13 @@
|
|||||||
package com.ecep.contract.controller.company;
|
package com.ecep.contract.controller.company;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.context.annotation.Scope;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.BaseController;
|
import com.ecep.contract.controller.BaseController;
|
||||||
import com.ecep.contract.model.CompanyContact;
|
|
||||||
import com.ecep.contract.service.CompanyContactService;
|
import com.ecep.contract.service.CompanyContactService;
|
||||||
import com.ecep.contract.util.FxmlUtils;
|
import com.ecep.contract.util.FxmlUtils;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CompanyContactViewModel;
|
import com.ecep.contract.vm.CompanyContactViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyContactVo;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.DatePicker;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Tab;
|
|
||||||
import javafx.scene.control.TabPane;
|
|
||||||
import javafx.scene.control.TextArea;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@@ -35,6 +16,16 @@ import javafx.stage.WindowEvent;
|
|||||||
import javafx.util.converter.LocalDateStringConverter;
|
import javafx.util.converter.LocalDateStringConverter;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@@ -84,7 +75,7 @@ public class CompanyContactWindowController extends BaseController {
|
|||||||
public Label versionLabel;
|
public Label versionLabel;
|
||||||
public Button saveBtn;
|
public Button saveBtn;
|
||||||
|
|
||||||
private CompletableFuture<CompanyContact> companyContactLoadedFuture;
|
private CompletableFuture<CompanyContactVo> companyContactLoadedFuture;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show(Stage stage) {
|
public void show(Stage stage) {
|
||||||
@@ -103,7 +94,7 @@ public class CompanyContactWindowController extends BaseController {
|
|||||||
initializeBaseTab();
|
initializeBaseTab();
|
||||||
|
|
||||||
companyContactLoadedFuture = CompletableFuture.supplyAsync(() -> {
|
companyContactLoadedFuture = CompletableFuture.supplyAsync(() -> {
|
||||||
CompanyContact oldName = companyContactService.findById(viewModel.getId().get());
|
CompanyContactVo oldName = companyContactService.findById(viewModel.getId().get());
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
viewModel.update(oldName);
|
viewModel.update(oldName);
|
||||||
viewModel.bindListener();
|
viewModel.bindListener();
|
||||||
@@ -128,9 +119,9 @@ public class CompanyContactWindowController extends BaseController {
|
|||||||
saveBtn.disableProperty().bind(viewModel.getChanged().not());
|
saveBtn.disableProperty().bind(viewModel.getChanged().not());
|
||||||
saveBtn.setOnAction(event -> {
|
saveBtn.setOnAction(event -> {
|
||||||
try {
|
try {
|
||||||
CompanyContact contact = companyContactLoadedFuture.join();
|
CompanyContactVo contact = companyContactLoadedFuture.join();
|
||||||
viewModel.copyTo(contact);
|
viewModel.copyTo(contact);
|
||||||
CompanyContact saved = companyContactService.save(contact);
|
CompanyContactVo saved = companyContactService.save(contact);
|
||||||
viewModel.update(saved);
|
viewModel.update(saved);
|
||||||
companyContactLoadedFuture = CompletableFuture.completedFuture(saved);
|
companyContactLoadedFuture = CompletableFuture.completedFuture(saved);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CompanyOldNameService;
|
import com.ecep.contract.service.CompanyOldNameService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.vm.CompanyViewModel;
|
import com.ecep.contract.vm.CompanyViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -16,7 +16,7 @@ import javafx.scene.control.TextInputDialog;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class CompanyManagerSkin
|
public class CompanyManagerSkin
|
||||||
extends AbstEntityManagerSkin<Company, CompanyViewModel, CompanyManagerSkin, CompanyManagerWindowController> {
|
extends AbstEntityManagerSkin<CompanyVo, CompanyViewModel, CompanyManagerSkin, CompanyManagerWindowController> {
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private CompanyOldNameService companyOldNameService;
|
private CompanyOldNameService companyOldNameService;
|
||||||
@@ -65,11 +65,11 @@ public class CompanyManagerSkin
|
|||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
CompanyService companyService = getCompanyService();
|
CompanyService companyService = getCompanyService();
|
||||||
String newCompanyName = optional.get();
|
String newCompanyName = optional.get();
|
||||||
List<Company> list = companyService.findAllByName(newCompanyName);
|
List<CompanyVo> list = companyService.findAllByName(newCompanyName);
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
// 未登记过
|
// 未登记过
|
||||||
Company company = companyService.createNewCompany(newCompanyName);
|
CompanyVo company = companyService.createNewCompany(newCompanyName);
|
||||||
Company saved = companyService.save(company);
|
CompanyVo saved = companyService.save(company);
|
||||||
CompanyWindowController.show(saved, getTableView().getScene().getWindow());
|
CompanyWindowController.show(saved, getTableView().getScene().getWindow());
|
||||||
} else {
|
} else {
|
||||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import org.springframework.context.annotation.Scope;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.task.CompanyFilesRebuildTasker;
|
import com.ecep.contract.task.CompanyFilesRebuildTasker;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
import com.ecep.contract.vm.CompanyViewModel;
|
import com.ecep.contract.vm.CompanyViewModel;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -25,7 +25,7 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/company/company-manager.fxml")
|
@FxmlPath("/ui/company/company-manager.fxml")
|
||||||
public class CompanyManagerWindowController
|
public class CompanyManagerWindowController
|
||||||
extends AbstManagerWindowController<Company, CompanyViewModel, CompanyManagerSkin> {
|
extends AbstManagerWindowController<CompanyVo, CompanyViewModel, CompanyManagerSkin> {
|
||||||
|
|
||||||
// columns
|
// columns
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.controller.customer.CompanyCustomerWindowController;
|
import com.ecep.contract.controller.customer.CompanyCustomerWindowController;
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
@@ -26,19 +25,17 @@ import com.ecep.contract.controller.tab.CompanyTabSkinInvoice;
|
|||||||
import com.ecep.contract.controller.tab.CompanyTabSkinOldName;
|
import com.ecep.contract.controller.tab.CompanyTabSkinOldName;
|
||||||
import com.ecep.contract.controller.tab.CompanyTabSkinOther;
|
import com.ecep.contract.controller.tab.CompanyTabSkinOther;
|
||||||
import com.ecep.contract.controller.tab.CompanyTabSkinPurchaseBillVoucher;
|
import com.ecep.contract.controller.tab.CompanyTabSkinPurchaseBillVoucher;
|
||||||
import com.ecep.contract.controller.vendor.CompanyVendorWindowController;
|
import com.ecep.contract.controller.vendor.VendorWindowController;
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.model.CompanyVendor;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.CompanyVendorService;
|
import com.ecep.contract.service.VendorService;
|
||||||
import com.ecep.contract.task.CompanyCompositeUpdateTasker;
|
import com.ecep.contract.task.CompanyCompositeUpdateTasker;
|
||||||
import com.ecep.contract.task.CompanyVerifyTasker;
|
import com.ecep.contract.task.CompanyVerifyTasker;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CompanyViewModel;
|
import com.ecep.contract.vm.CompanyViewModel;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.VendorVo;
|
||||||
import com.ecep.contract.vo.CompanyVo;
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
@@ -78,10 +75,6 @@ public class CompanyWindowController
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CompanyService companyService;
|
private CompanyService companyService;
|
||||||
@Autowired
|
|
||||||
private CompanyCustomerService companyCustomerService;
|
|
||||||
@Autowired
|
|
||||||
private CompanyVendorService companyVendorService;
|
|
||||||
|
|
||||||
public BorderPane root;
|
public BorderPane root;
|
||||||
public TabPane tabPane;
|
public TabPane tabPane;
|
||||||
@@ -131,8 +124,8 @@ public class CompanyWindowController
|
|||||||
// private final CompanyVendorViewModel companyVendorViewModel = new
|
// private final CompanyVendorViewModel companyVendorViewModel = new
|
||||||
// CompanyVendorViewModel();
|
// CompanyVendorViewModel();
|
||||||
|
|
||||||
private final SimpleObjectProperty<CompanyCustomer> companyCustomerProperty = new SimpleObjectProperty<>();
|
private final SimpleObjectProperty<CustomerVo> companyCustomerProperty = new SimpleObjectProperty<>();
|
||||||
private final SimpleObjectProperty<CompanyVendor> companyVendorProperty = new SimpleObjectProperty<>();
|
private final SimpleObjectProperty<VendorVo> companyVendorProperty = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
public Pane customerTab_pane1;
|
public Pane customerTab_pane1;
|
||||||
public Button customerTab_openBtn;
|
public Button customerTab_openBtn;
|
||||||
@@ -209,7 +202,8 @@ public class CompanyWindowController
|
|||||||
logger.debug("onCustomerTabShown");
|
logger.debug("onCustomerTabShown");
|
||||||
}
|
}
|
||||||
getLoadedFuture().thenAcceptAsync(company -> {
|
getLoadedFuture().thenAcceptAsync(company -> {
|
||||||
companyCustomerProperty.set(companyCustomerService.findByCompany(company));
|
CustomerVo customerVo = getCachedBean(CustomerService.class).findByCompany(company);
|
||||||
|
companyCustomerProperty.set(customerVo);
|
||||||
}).exceptionally(ex -> {
|
}).exceptionally(ex -> {
|
||||||
UITools.showExceptionAndWait(ex.getMessage(), ex);
|
UITools.showExceptionAndWait(ex.getMessage(), ex);
|
||||||
return null;
|
return null;
|
||||||
@@ -233,7 +227,7 @@ public class CompanyWindowController
|
|||||||
});
|
});
|
||||||
|
|
||||||
vendorTab_openBtn.setOnAction(event -> {
|
vendorTab_openBtn.setOnAction(event -> {
|
||||||
CompanyVendorWindowController.show(companyVendorProperty.get(), root.getScene().getWindow());
|
VendorWindowController.show(companyVendorProperty.get(), root.getScene().getWindow());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +239,8 @@ public class CompanyWindowController
|
|||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("onVendorTabShown company {}", company.getName());
|
logger.debug("onVendorTabShown company {}", company.getName());
|
||||||
}
|
}
|
||||||
companyVendorProperty.set(companyVendorService.findByCompany(company));
|
VendorService vendorService = getBean(VendorService.class);
|
||||||
|
companyVendorProperty.set(vendorService.findByCompany(company));
|
||||||
}).exceptionally(ex -> {
|
}).exceptionally(ex -> {
|
||||||
UITools.showExceptionAndWait(ex.getMessage(), ex);
|
UITools.showExceptionAndWait(ex.getMessage(), ex);
|
||||||
return null;
|
return null;
|
||||||
@@ -259,6 +254,7 @@ public class CompanyWindowController
|
|||||||
CompanyCompositeUpdateTasker task = new CompanyCompositeUpdateTasker();
|
CompanyCompositeUpdateTasker task = new CompanyCompositeUpdateTasker();
|
||||||
task.setCompany(getEntity());
|
task.setCompany(getEntity());
|
||||||
UITools.showTaskDialogAndWait("更新企业信息", task, null);
|
UITools.showTaskDialogAndWait("更新企业信息", task, null);
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,9 +263,9 @@ public class CompanyWindowController
|
|||||||
public void onCompanyVerifyAction(ActionEvent event) {
|
public void onCompanyVerifyAction(ActionEvent event) {
|
||||||
CompanyVo company = getEntity();
|
CompanyVo company = getEntity();
|
||||||
CompanyVerifyTasker task = new CompanyVerifyTasker();
|
CompanyVerifyTasker task = new CompanyVerifyTasker();
|
||||||
task.setCompanyService(companyService);
|
|
||||||
task.setCompany(company);
|
task.setCompany(company);
|
||||||
UITools.showTaskDialogAndWait("企业合规性验证", task, null);
|
UITools.showTaskDialogAndWait("企业合规性验证", task, null);
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCompanyOpenInExplorerAction(ActionEvent event) {
|
public void onCompanyOpenInExplorerAction(ActionEvent event) {
|
||||||
@@ -280,7 +276,7 @@ public class CompanyWindowController
|
|||||||
if (!StringUtils.hasText(path)) {
|
if (!StringUtils.hasText(path)) {
|
||||||
ButtonType buttonType = UITools.showConfirmation("目录未设置", "是否创建目录").join();
|
ButtonType buttonType = UITools.showConfirmation("目录未设置", "是否创建目录").join();
|
||||||
if (buttonType == ButtonType.OK) {
|
if (buttonType == ButtonType.OK) {
|
||||||
if (companyService.makePathAbsent(company)) {
|
if (companyService.makePathAbsent(company, (level, message) -> setStatus(message))) {
|
||||||
save(company);
|
save(company);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.ecep.contract.controller.bank.account;
|
package com.ecep.contract.controller.company.bank_account;
|
||||||
|
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.ecep.contract.controller.bank.account;
|
package com.ecep.contract.controller.company.bank_account;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
@@ -6,7 +6,6 @@ import org.springframework.context.annotation.Scope;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.model.CompanyBankAccount;
|
|
||||||
import com.ecep.contract.service.CompanyBankAccountService;
|
import com.ecep.contract.service.CompanyBankAccountService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.CompanyBankAccountViewModel;
|
import com.ecep.contract.vm.CompanyBankAccountViewModel;
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.ecep.contract.controller.company_old_name;
|
package com.ecep.contract.controller.company.old_name;
|
||||||
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.model.CompanyOldName;
|
|
||||||
import com.ecep.contract.service.CompanyOldNameService;
|
import com.ecep.contract.service.CompanyOldNameService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
|
import com.ecep.contract.vo.CompanyOldNameVo;
|
||||||
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
||||||
|
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
@@ -15,7 +15,7 @@ import lombok.Setter;
|
|||||||
|
|
||||||
|
|
||||||
public class CompanyOldNameTabSkinBase
|
public class CompanyOldNameTabSkinBase
|
||||||
extends AbstEntityBasedTabSkin<CompanyOldNameWindowController, CompanyOldName, CompanyOldNameViewModel>
|
extends AbstEntityBasedTabSkin<CompanyOldNameWindowController, CompanyOldNameVo, CompanyOldNameViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
@Setter
|
@Setter
|
||||||
private CompanyService companyService;
|
private CompanyService companyService;
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.ecep.contract.controller.company_old_name;
|
package com.ecep.contract.controller.company.old_name;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@@ -12,13 +10,15 @@ import com.ecep.contract.CompanyFileType;
|
|||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
||||||
import com.ecep.contract.model.CompanyFile;
|
|
||||||
import com.ecep.contract.model.CompanyOldName;
|
|
||||||
import com.ecep.contract.service.CompanyFileService;
|
import com.ecep.contract.service.CompanyFileService;
|
||||||
import com.ecep.contract.service.CompanyOldNameService;
|
import com.ecep.contract.service.CompanyOldNameService;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
|
import com.ecep.contract.util.ParamUtils.Builder;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CompanyFileViewModel;
|
import com.ecep.contract.vm.CompanyFileViewModel;
|
||||||
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyFileVo;
|
||||||
|
import com.ecep.contract.vo.CompanyOldNameVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -33,7 +33,7 @@ import lombok.Setter;
|
|||||||
*/
|
*/
|
||||||
public class CompanyOldNameTabSkinFile
|
public class CompanyOldNameTabSkinFile
|
||||||
extends
|
extends
|
||||||
AbstEntityTableTabSkin<CompanyOldNameWindowController, CompanyOldName, CompanyOldNameViewModel, CompanyFile, CompanyFileViewModel>
|
AbstEntityTableTabSkin<CompanyOldNameWindowController, CompanyOldNameVo, CompanyOldNameViewModel, CompanyFileVo, CompanyFileViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
@Setter
|
@Setter
|
||||||
private CompanyOldNameService companyOldNameService;
|
private CompanyOldNameService companyOldNameService;
|
||||||
@@ -62,10 +62,8 @@ public class CompanyOldNameTabSkinFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(CompanyOldName parent) {
|
public Builder getSpecification(CompanyOldNameVo parent) {
|
||||||
Map<String, Object> params = new HashMap<>();
|
return ParamUtils.builder().equals("company", parent.getCompanyId());
|
||||||
params.put("company", parent.getCompanyId());
|
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,7 +99,7 @@ public class CompanyOldNameTabSkinFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onTableResetAction(ActionEvent event) {
|
private void onTableResetAction(ActionEvent event) {
|
||||||
CompanyOldName oldName = getParent();
|
CompanyOldNameVo oldName = getParent();
|
||||||
// CompletableFuture.runAsync(() -> {
|
// CompletableFuture.runAsync(() -> {
|
||||||
// if (getCompanyFileService().reBuildingFiles(oldName, this::setStatus)) {
|
// if (getCompanyFileService().reBuildingFiles(oldName, this::setStatus)) {
|
||||||
// loadTableDataSet();
|
// loadTableDataSet();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.ecep.contract.controller.company_old_name;
|
package com.ecep.contract.controller.company.old_name;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -12,10 +12,10 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.model.CompanyOldName;
|
|
||||||
import com.ecep.contract.service.CompanyOldNameService;
|
import com.ecep.contract.service.CompanyOldNameService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vo.CompanyOldNameVo;
|
||||||
import com.ecep.contract.vm.CompanyFileViewModel;
|
import com.ecep.contract.vm.CompanyFileViewModel;
|
||||||
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
import com.ecep.contract.vm.CompanyOldNameViewModel;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ import javafx.stage.WindowEvent;
|
|||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/company/company_old_name.fxml")
|
@FxmlPath("/ui/company/company_old_name.fxml")
|
||||||
public class CompanyOldNameWindowController extends AbstEntityController<CompanyOldName, CompanyOldNameViewModel> {
|
public class CompanyOldNameWindowController extends AbstEntityController<CompanyOldNameVo, CompanyOldNameViewModel> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyOldNameWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(CompanyOldNameWindowController.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +97,7 @@ public class CompanyOldNameWindowController extends AbstEntityController<Company
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onOldCompanyOpenInExplorerAction(ActionEvent event) {
|
public void onOldCompanyOpenInExplorerAction(ActionEvent event) {
|
||||||
CompanyOldName companyOldName = getEntity();
|
CompanyOldNameVo companyOldName = getEntity();
|
||||||
String path = companyOldName.getPath();
|
String path = companyOldName.getPath();
|
||||||
|
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
@@ -2,12 +2,13 @@ package com.ecep.contract.controller.contract;
|
|||||||
|
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
public abstract class AbstContractBasedTabSkin
|
public abstract class AbstContractBasedTabSkin
|
||||||
extends AbstEntityBasedTabSkin<ContractWindowController, Contract, ContractViewModel>
|
extends AbstEntityBasedTabSkin<ContractWindowController, ContractVo, ContractViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public AbstContractBasedTabSkin(ContractWindowController controller) {
|
public AbstContractBasedTabSkin(ContractWindowController controller) {
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
package com.ecep.contract.controller.contract;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
||||||
import com.ecep.contract.controller.table.TableOfTabSkin;
|
import com.ecep.contract.controller.table.TableOfTabSkin;
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.IdentityEntity;
|
import com.ecep.contract.model.IdentityEntity;
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
import com.ecep.contract.vm.IdentityViewModel;
|
import com.ecep.contract.vm.IdentityViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
public abstract class AbstContractTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
public abstract class AbstContractTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
||||||
extends AbstEntityTableTabSkin<ContractWindowController, Contract, ContractViewModel, T, TV>
|
extends AbstEntityTableTabSkin<ContractWindowController, ContractVo, ContractViewModel, T, TV>
|
||||||
implements TabSkin, TableOfTabSkin<Contract, T, TV> {
|
implements TabSkin, TableOfTabSkin<ContractVo, T, TV> {
|
||||||
|
|
||||||
public AbstContractTableTabSkin(ContractWindowController controller) {
|
public AbstContractTableTabSkin(ContractWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -25,9 +24,9 @@ public abstract class AbstContractTableTabSkin<T extends IdentityEntity, TV exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(Contract parent) {
|
public ParamUtils.Builder getSpecification(ContractVo parent) {
|
||||||
Map<String, Object> params = new HashMap<>();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("contract", parent.getId());
|
params.equals("contract", parent.getId());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
|
import com.ecep.contract.vm.ContractInvoiceViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractInvoiceVo;
|
||||||
|
|
||||||
|
public class ContractInvoiceManagerSkin extends
|
||||||
|
AbstEntityManagerSkin<ContractInvoiceVo, ContractInvoiceViewModel, ContractInvoiceManagerSkin, ContractInvoiceManagerWindowController> {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ContractInvoiceManagerSkin.class);
|
||||||
|
|
||||||
|
public ContractInvoiceManagerSkin(ContractInvoiceManagerWindowController controller) {
|
||||||
|
super(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTable() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'initializeTable'");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
|
import com.ecep.contract.service.ContractInvoiceService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.vm.ContractInvoiceViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractInvoiceVo;
|
||||||
|
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Scope("prototype")
|
||||||
|
@Component
|
||||||
|
@FxmlPath("/ui/contract/contract-invoice-manager.fxml")
|
||||||
|
public class ContractInvoiceManagerWindowController extends AbstManagerWindowController<ContractInvoiceVo, ContractInvoiceViewModel, ContractInvoiceManagerSkin> {
|
||||||
|
|
||||||
|
public TableColumn<ContractInvoiceViewModel, Number> idColumn;
|
||||||
|
public TableColumn<ContractInvoiceViewModel, String> codeColumn;
|
||||||
|
public TableColumn<ContractInvoiceViewModel, String> nameColumn;
|
||||||
|
public TableColumn<ContractInvoiceViewModel, Integer> contractColumn;
|
||||||
|
public TableColumn<ContractInvoiceViewModel, Integer> contractItemColumn;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ContractInvoiceService contractInvoiceService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContractInvoiceService getViewModelService() {
|
||||||
|
return contractInvoiceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContractInvoiceManagerSkin createDefaultSkin() {
|
||||||
|
ContractInvoiceManagerSkin skin = new ContractInvoiceManagerSkin(this);
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Stage stage) {
|
||||||
|
super.show(stage);
|
||||||
|
getTitle().set("合同发票管理");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
|
import com.ecep.contract.controller.tab.ContractInvoiceTabSkinBase;
|
||||||
|
import com.ecep.contract.service.ContractInvoiceService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.vm.ContractInvoiceViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractInvoiceVo;
|
||||||
|
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.control.TabPane;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
|
||||||
|
@FxmlPath("/ui/contract/contract-invoice.fxml")
|
||||||
|
@Component
|
||||||
|
public class ContractInvoiceWindowController
|
||||||
|
extends AbstEntityController<ContractInvoiceVo, ContractInvoiceViewModel> {
|
||||||
|
|
||||||
|
public static void show(ContractInvoiceViewModel viewModel, Window owner) {
|
||||||
|
show(ContractInvoiceWindowController.class, viewModel, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tab baseInfoTab;
|
||||||
|
public TextField codeField;
|
||||||
|
public TextField nameField;
|
||||||
|
public TextField invoiceField;
|
||||||
|
public TextField amountField;
|
||||||
|
public TextArea remarkField;
|
||||||
|
public Button saveBtn;
|
||||||
|
public Button cancelBtn;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContractInvoiceService getViewModelService() {
|
||||||
|
return getBean(ContractInvoiceService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerTabSkins() {
|
||||||
|
TabPane tabPane = baseInfoTab.getTabPane();
|
||||||
|
ObservableList<Tab> tabs = tabPane.getTabs();
|
||||||
|
registerTabSkin(baseInfoTab, tab -> new ContractInvoiceTabSkinBase(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
|
import com.ecep.contract.controller.inventory.InventoryWindowController;
|
||||||
|
import com.ecep.contract.controller.tab.ContractItemTabSkinBase;
|
||||||
|
import com.ecep.contract.service.ContractItemService;
|
||||||
|
import com.ecep.contract.service.InventoryService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vm.ContractItemViewModel;
|
||||||
|
import com.ecep.contract.vm.InventoryViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractItemVo;
|
||||||
|
import com.ecep.contract.vo.InventoryVo;
|
||||||
|
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.DatePicker;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.control.TabPane;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Scope("prototype")
|
||||||
|
@Component
|
||||||
|
@FxmlPath("/ui/contract/contract-item.fxml")
|
||||||
|
public class ContractItemWindowController
|
||||||
|
extends AbstEntityController<ContractItemVo, ContractItemViewModel> {
|
||||||
|
|
||||||
|
public static void show(ContractItemVo contractItem, Window owner) {
|
||||||
|
show(ContractItemViewModel.from(contractItem), owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示界面
|
||||||
|
*/
|
||||||
|
public static void show(ContractItemViewModel viewModel, Window window) {
|
||||||
|
show(ContractItemWindowController.class, viewModel, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BorderPane root;
|
||||||
|
public Tab baseInfoTab;
|
||||||
|
|
||||||
|
// 基本信息字段
|
||||||
|
public Label idLabel;
|
||||||
|
public TextField codeField;
|
||||||
|
public TextField titleField;
|
||||||
|
public TextField specificationField;
|
||||||
|
public TextField unitField;
|
||||||
|
public TextField inventoryField;
|
||||||
|
public Button openInventoryBtn;
|
||||||
|
|
||||||
|
// 财务信息字段
|
||||||
|
public TextField exclusiveTaxPriceField;
|
||||||
|
public TextField taxRateField;
|
||||||
|
public TextField taxPriceField;
|
||||||
|
public TextField quantityField;
|
||||||
|
public TextField taxAmountField;
|
||||||
|
public TextField exclusiveTaxAmountField;
|
||||||
|
|
||||||
|
// 日期信息字段
|
||||||
|
public DatePicker startDateField;
|
||||||
|
public DatePicker endDateField;
|
||||||
|
public TextField createDateLabel;
|
||||||
|
public TextField updateDateLabel;
|
||||||
|
public TextField creatorLabel;
|
||||||
|
public TextField updaterLabel;
|
||||||
|
|
||||||
|
// 其他信息
|
||||||
|
public TextField refIdField;
|
||||||
|
public TextArea remarkField;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContractItemService getViewModelService() {
|
||||||
|
return getCachedBean(ContractItemService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InventoryService getInventoryService() {
|
||||||
|
return getCachedBean(InventoryService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onShown(WindowEvent windowEvent) {
|
||||||
|
super.onShown(windowEvent);
|
||||||
|
getTitle().bind(viewModel.getTitle()
|
||||||
|
.map(title -> "[" + viewModel.getId() + "] " + title + " 合同内容详情"));
|
||||||
|
root.getScene().getStylesheets().add("/ui/contract/contract.css");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerTabSkins() {
|
||||||
|
super.registerTabSkins();
|
||||||
|
TabPane tabPane = baseInfoTab.getTabPane();
|
||||||
|
ObservableList<Tab> tabs = tabPane.getTabs();
|
||||||
|
registerTabSkin(baseInfoTab, tab -> new ContractItemTabSkinBase(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开关联的存货信息
|
||||||
|
*/
|
||||||
|
public void onOpenInventoryAction(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
if (viewModel.getInventory() == null) {
|
||||||
|
UITools.showAlertAndWait("没有关联的存货信息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer inventoryId = viewModel.getInventory().get();
|
||||||
|
InventoryVo inventory = getInventoryService().findById(inventoryId);
|
||||||
|
if (inventory != null) {
|
||||||
|
InventoryWindowController.show(InventoryViewModel.from(inventory), root.getScene().getWindow());
|
||||||
|
} else {
|
||||||
|
UITools.showAlertAndWait("未找到关联的存货信息");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
UITools.showAlertAndWait("打开存货信息失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算税额和金额
|
||||||
|
*/
|
||||||
|
public void onCalculateAmountsAction(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
viewModel.calculateAmounts();
|
||||||
|
UITools.showAlertAndWait("金额计算完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
UITools.showAlertAndWait("计算失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,18 +10,14 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.controller.tab.ContractManagerSkin;
|
import com.ecep.contract.controller.tab.ContractManagerSkin;
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.ContractGroup;
|
|
||||||
import com.ecep.contract.model.ContractKind;
|
|
||||||
import com.ecep.contract.model.ContractType;
|
|
||||||
import com.ecep.contract.model.Employee;
|
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.task.ContractFilesRebuildAllTasker;
|
import com.ecep.contract.task.ContractFilesRebuildAllTasker;
|
||||||
import com.ecep.contract.task.ContractRepairAllTasker;
|
import com.ecep.contract.task.ContractRepairAllTasker;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractGroupVo;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
@@ -34,27 +30,27 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/contract/contract-manager.fxml")
|
@FxmlPath("/ui/contract/contract-manager.fxml")
|
||||||
public class ContractManagerWindowController
|
public class ContractManagerWindowController
|
||||||
extends AbstManagerWindowController<Contract, ContractViewModel, ContractManagerSkin> {
|
extends AbstManagerWindowController<ContractVo, ContractViewModel, ContractManagerSkin> {
|
||||||
|
|
||||||
public ComboBox<ContractGroup> groupSelector;
|
public ComboBox<ContractGroupVo> groupSelector;
|
||||||
public CheckBox composeViewBtn;
|
public CheckBox composeViewBtn;
|
||||||
|
|
||||||
// columns
|
// columns
|
||||||
public TableColumn<ContractViewModel, Number> idColumn;
|
public TableColumn<ContractViewModel, Number> idColumn;
|
||||||
public TableColumn<ContractViewModel, String> nameColumn;
|
public TableColumn<ContractViewModel, String> nameColumn;
|
||||||
public TableColumn<ContractViewModel, String> codeColumn;
|
public TableColumn<ContractViewModel, String> codeColumn;
|
||||||
public TableColumn<ContractViewModel, ContractGroup> groupColumn;
|
public TableColumn<ContractViewModel, Integer> groupColumn;
|
||||||
public TableColumn<ContractViewModel, ContractType> typeColumn;
|
public TableColumn<ContractViewModel, Integer> typeColumn;
|
||||||
public TableColumn<ContractViewModel, ContractKind> kindColumn;
|
public TableColumn<ContractViewModel, Integer> kindColumn;
|
||||||
|
|
||||||
public TableColumn<ContractViewModel, String> parentCodeColumn;
|
public TableColumn<ContractViewModel, String> parentCodeColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDate> setupDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> setupDateColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDate> orderDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> orderDateColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDate> startDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> startDateColumn;
|
||||||
public TableColumn<ContractViewModel, Employee> employeeColumn;
|
public TableColumn<ContractViewModel, Integer> employeeColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDateTime> createdColumn;
|
public TableColumn<ContractViewModel, LocalDateTime> createdColumn;
|
||||||
public TableColumn<ContractViewModel, Number> amountColumn;
|
public TableColumn<ContractViewModel, Number> amountColumn;
|
||||||
public TableColumn<ContractViewModel, Company> companyColumn;
|
public TableColumn<ContractViewModel, Integer> companyColumn;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ContractService contractService;
|
private ContractService contractService;
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
package com.ecep.contract.controller.contract;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.util.ComboBoxUtils;
|
||||||
import com.ecep.contract.controller.ComboBoxUtils;
|
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.ExtendVendorInfo;
|
|
||||||
import com.ecep.contract.model.VendorGroup;
|
|
||||||
import com.ecep.contract.service.ExtendVendorInfoService;
|
import com.ecep.contract.service.ExtendVendorInfoService;
|
||||||
import com.ecep.contract.service.VendorGroupService;
|
import com.ecep.contract.service.VendorGroupService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.ExtendVendorInfoViewModel;
|
import com.ecep.contract.vm.ExtendVendorInfoViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
import com.ecep.contract.vo.ExtendVendorInfoVo;
|
||||||
|
import com.ecep.contract.vo.VendorGroupVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
@@ -25,19 +22,15 @@ import javafx.scene.control.Label;
|
|||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.util.converter.NumberStringConverter;
|
import javafx.util.converter.NumberStringConverter;
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@FxmlPath("/ui/contract/contract-tab-ext-vendor-info.fxml")
|
@FxmlPath("/ui/contract/contract-tab-ext-vendor-info.fxml")
|
||||||
public class ContractTabSkinExtendVendorInfo
|
public class ContractTabSkinExtendVendorInfo extends AbstContractBasedTabSkin {
|
||||||
extends AbstContractBasedTabSkin {
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
private ExtendVendorInfoService extendVendorInfoService;
|
|
||||||
@Setter
|
|
||||||
private VendorGroupService vendorGroupService;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VendorGroup
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public ComboBox<VendorGroup> vendorGroupField;
|
public ComboBox<VendorGroupVo> vendorGroupField;
|
||||||
@FXML
|
@FXML
|
||||||
public Label vendorGroupLabel;
|
public Label vendorGroupLabel;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -47,7 +40,7 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
@FXML
|
@FXML
|
||||||
public CheckBox prePurchaseField;
|
public CheckBox prePurchaseField;
|
||||||
|
|
||||||
CompletableFuture<ExtendVendorInfo> loadedFuture;
|
CompletableFuture<ExtendVendorInfoVo> loadedFuture;
|
||||||
private ExtendVendorInfoViewModel viewModel = new ExtendVendorInfoViewModel();
|
private ExtendVendorInfoViewModel viewModel = new ExtendVendorInfoViewModel();
|
||||||
|
|
||||||
public ContractTabSkinExtendVendorInfo(ContractWindowController controller) {
|
public ContractTabSkinExtendVendorInfo(ContractWindowController controller) {
|
||||||
@@ -75,13 +68,13 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
if (loadedFuture == null) {
|
if (loadedFuture == null) {
|
||||||
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
||||||
initializeTab();
|
initializeTab();
|
||||||
Contract contract = getEntity();
|
ContractVo contract = getEntity();
|
||||||
return loadExtendVendorInfo(contract);
|
return loadExtendVendorInfo(contract);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateViewModel(ExtendVendorInfo info) {
|
void updateViewModel(ExtendVendorInfoVo info) {
|
||||||
if (Platform.isFxApplicationThread()) {
|
if (Platform.isFxApplicationThread()) {
|
||||||
viewModel.update(info);
|
viewModel.update(info);
|
||||||
} else {
|
} else {
|
||||||
@@ -89,13 +82,14 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtendVendorInfo loadExtendVendorInfo(Contract contract) {
|
private ExtendVendorInfoVo loadExtendVendorInfo(ContractVo contract) {
|
||||||
ExtendVendorInfoService service = getExtendVendorInfoService();
|
ExtendVendorInfoService service = getExtendVendorInfoService();
|
||||||
try {
|
try {
|
||||||
ExtendVendorInfo info = service.findByContract(contract);
|
ExtendVendorInfoVo info = service.findByContract(contract);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
info = service.newInstanceByContract(contract);
|
info = new ExtendVendorInfoVo();
|
||||||
info = service.save(info);
|
info.setContractId(contract.getId());
|
||||||
|
// 注意:这里可能需要调整,取决于service接口的实现
|
||||||
}
|
}
|
||||||
updateViewModel(info);
|
updateViewModel(info);
|
||||||
viewModel.bindListener();
|
viewModel.bindListener();
|
||||||
@@ -108,17 +102,11 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTab() {
|
public void initializeTab() {
|
||||||
List<VendorGroup> groups = getVendorGroupService().findAll();
|
ComboBoxUtils.initialComboBox(vendorGroupField, viewModel.getGroup(), getVendorGroupService(), true);
|
||||||
ComboBoxUtils.initialComboBox(vendorGroupField, groups, true);
|
|
||||||
vendorGroupField.valueProperty().bindBidirectional(viewModel.getGroup());
|
|
||||||
vendorGroupLabel.textProperty().bind(vendorGroupField.valueProperty().map(v -> {
|
vendorGroupLabel.textProperty().bind(vendorGroupField.valueProperty().map(v -> {
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
return "-";
|
return "-";
|
||||||
}
|
}
|
||||||
if (!ProxyUtils.isInitialized(v)) {
|
|
||||||
v = getVendorGroupService().findById(v.getId());
|
|
||||||
viewModel.getGroup().set(v);
|
|
||||||
}
|
|
||||||
return v.getDescription();
|
return v.getDescription();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -126,7 +114,11 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
new NumberStringConverter());
|
new NumberStringConverter());
|
||||||
assignedProviderField.selectedProperty().bindBidirectional(viewModel.getAssignedProvider());
|
assignedProviderField.selectedProperty().bindBidirectional(viewModel.getAssignedProvider());
|
||||||
assignedProviderField.disableProperty().bind(Bindings.createBooleanBinding(() -> {
|
assignedProviderField.disableProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||||
VendorGroup group = viewModel.getGroup().get();
|
Integer groupId = viewModel.getGroup().get();
|
||||||
|
if (groupId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
VendorGroupVo group = getVendorGroupService().findById(groupId);
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -139,9 +131,10 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
@Override
|
@Override
|
||||||
public void save() {
|
public void save() {
|
||||||
if (loadedFuture != null) {
|
if (loadedFuture != null) {
|
||||||
ExtendVendorInfo vendorInfo = loadedFuture.join();
|
ExtendVendorInfoVo vendorInfo = loadedFuture.join();
|
||||||
if (viewModel.copyTo(vendorInfo)) {
|
if (viewModel.copyTo(vendorInfo)) {
|
||||||
ExtendVendorInfo saved = getExtendVendorInfoService().save(vendorInfo);
|
// 注意:这里需要根据实际service接口实现调整,可能需要调用不同的方法
|
||||||
|
ExtendVendorInfoVo saved = getExtendVendorInfoService().save(vendorInfo);
|
||||||
updateViewModel(saved);
|
updateViewModel(saved);
|
||||||
loadedFuture = CompletableFuture.completedFuture(saved);
|
loadedFuture = CompletableFuture.completedFuture(saved);
|
||||||
}
|
}
|
||||||
@@ -149,16 +142,10 @@ public class ContractTabSkinExtendVendorInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExtendVendorInfoService getExtendVendorInfoService() {
|
public ExtendVendorInfoService getExtendVendorInfoService() {
|
||||||
if (extendVendorInfoService == null) {
|
return getCachedBean(ExtendVendorInfoService.class);
|
||||||
extendVendorInfoService = SpringApp.getBean(ExtendVendorInfoService.class);
|
|
||||||
}
|
|
||||||
return extendVendorInfoService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VendorGroupService getVendorGroupService() {
|
public VendorGroupService getVendorGroupService() {
|
||||||
if (vendorGroupService == null) {
|
return getCachedBean(VendorGroupService.class);
|
||||||
vendorGroupService = SpringApp.getBean(VendorGroupService.class);
|
|
||||||
}
|
|
||||||
return vendorGroupService;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
|
import com.ecep.contract.*;
|
||||||
|
import com.ecep.contract.constant.ContractConstant;
|
||||||
|
import com.ecep.contract.controller.tab.ContractFilesRebuildTasker;
|
||||||
|
import com.ecep.contract.controller.tab.CustomerContractCostFormUpdateTask;
|
||||||
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
|
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
||||||
|
import com.ecep.contract.controller.table.cell.ContractFileTypeTableCell;
|
||||||
|
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
||||||
|
import com.ecep.contract.service.ContractFileService;
|
||||||
|
import com.ecep.contract.service.ContractFileTypeService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vm.ContractFileViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractFileTypeLocalVo;
|
||||||
|
import com.ecep.contract.vo.ContractFileVo;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
import lombok.Setter;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.io.IOUtils;
|
import org.apache.pdfbox.io.IOUtils;
|
||||||
import org.apache.pdfbox.io.MemoryUsageSetting;
|
import org.apache.pdfbox.io.MemoryUsageSetting;
|
||||||
@@ -25,53 +36,23 @@ import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
|||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.ContractFileType;
|
import javax.imageio.ImageIO;
|
||||||
import com.ecep.contract.ContractPayWay;
|
import java.awt.image.BufferedImage;
|
||||||
import com.ecep.contract.DesktopUtils;
|
import java.io.File;
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import java.io.FileNotFoundException;
|
||||||
import com.ecep.contract.SpringApp;
|
import java.io.IOException;
|
||||||
import com.ecep.contract.constant.ContractConstant;
|
import java.time.LocalDate;
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
import java.util.ArrayList;
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
import java.util.List;
|
||||||
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
import java.util.Objects;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.ContractFile;
|
|
||||||
import com.ecep.contract.model.ContractFileTypeLocal;
|
|
||||||
import com.ecep.contract.model.ContractType;
|
|
||||||
import com.ecep.contract.service.ContractFileService;
|
|
||||||
import com.ecep.contract.service.ContractFileTypeService;
|
|
||||||
import com.ecep.contract.util.FxmlPath;
|
|
||||||
import com.ecep.contract.util.UITools;
|
|
||||||
import com.ecep.contract.vm.ContractFileViewModel;
|
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.MapChangeListener;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.collections.ObservableMap;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ButtonType;
|
|
||||||
import javafx.scene.control.ContextMenu;
|
|
||||||
import javafx.scene.control.Menu;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.SelectionMode;
|
|
||||||
import javafx.scene.control.Tab;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.control.cell.TextFieldTableCell;
|
|
||||||
import javafx.stage.WindowEvent;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合同文件
|
* 合同文件
|
||||||
*/
|
*/
|
||||||
@FxmlPath("/ui/contract/contract-tab-file.fxml")
|
@FxmlPath("/ui/contract/contract-tab-file.fxml")
|
||||||
public class ContractTabSkinFiles
|
public class ContractTabSkinFiles
|
||||||
extends AbstContractTableTabSkin<ContractFile, ContractFileViewModel>
|
extends AbstContractTableTabSkin<ContractFileVo, ContractFileViewModel>
|
||||||
implements TabSkin, EditableEntityTableTabSkin<ContractFile, ContractFileViewModel> {
|
implements TabSkin, EditableEntityTableTabSkin<ContractFileVo, ContractFileViewModel> {
|
||||||
|
|
||||||
public Button fileTableReBuildBtn;
|
public Button fileTableReBuildBtn;
|
||||||
public MenuItem fileTable_menu_refresh;
|
public MenuItem fileTable_menu_refresh;
|
||||||
@@ -81,15 +62,13 @@ public class ContractTabSkinFiles
|
|||||||
public Menu fileTable_menu_change_type;
|
public Menu fileTable_menu_change_type;
|
||||||
public Menu fileTable_menu_change_type_and_name;
|
public Menu fileTable_menu_change_type_and_name;
|
||||||
public TableColumn<ContractFileViewModel, Number> fileTable_idColumn;
|
public TableColumn<ContractFileViewModel, Number> fileTable_idColumn;
|
||||||
public TableColumn<ContractFileViewModel, ContractFileTypeLocal> fileTable_typeColumn;
|
public TableColumn<ContractFileViewModel, ContractFileType> fileTable_typeColumn;
|
||||||
public TableColumn<ContractFileViewModel, String> fileTable_filePathColumn;
|
public TableColumn<ContractFileViewModel, String> fileTable_filePathColumn;
|
||||||
public TableColumn<ContractFileViewModel, LocalDate> fileTable_applyDateColumn;
|
public TableColumn<ContractFileViewModel, LocalDate> fileTable_applyDateColumn;
|
||||||
public TableColumn<ContractFileViewModel, String> fileTable_descriptionColumn;
|
public TableColumn<ContractFileViewModel, String> fileTable_descriptionColumn;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private ContractFileService contractFileService;
|
private ContractFileService contractFileService;
|
||||||
private final ObservableMap<ContractFileType, ContractFileTypeLocal> fileTypeLocalMap = FXCollections
|
|
||||||
.observableHashMap();
|
|
||||||
|
|
||||||
public ContractTabSkinFiles(ContractWindowController controller) {
|
public ContractTabSkinFiles(ContractWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -114,26 +93,10 @@ public class ContractTabSkinFiles
|
|||||||
return controller.fileTab;
|
return controller.fileTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ContractFileTypeLocalStringConverter extends StringConverter<ContractFileTypeLocal> {
|
|
||||||
@Override
|
|
||||||
public String toString(ContractFileTypeLocal local) {
|
|
||||||
if (local == null) {
|
|
||||||
return "-";
|
|
||||||
}
|
|
||||||
return local.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContractFileTypeLocal fromString(String string) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTab() {
|
public void initializeTab() {
|
||||||
TableView<ContractFileViewModel> table = getTableView();
|
TableView<ContractFileViewModel> table = getTableView();
|
||||||
ContractPayWay payWay = viewModel.getPayWay().get();
|
ContractPayWay payWay = viewModel.getPayWay().get();
|
||||||
ContractType contractType = viewModel.getType().get();
|
|
||||||
boolean isCustomer = payWay == ContractPayWay.RECEIVE;
|
boolean isCustomer = payWay == ContractPayWay.RECEIVE;
|
||||||
boolean isVendor = payWay == ContractPayWay.PAY;
|
boolean isVendor = payWay == ContractPayWay.PAY;
|
||||||
|
|
||||||
@@ -150,46 +113,12 @@ public class ContractTabSkinFiles
|
|||||||
fileTable_idColumn.setReorderable(false);
|
fileTable_idColumn.setReorderable(false);
|
||||||
|
|
||||||
fileTable_typeColumn
|
fileTable_typeColumn
|
||||||
.setCellValueFactory(param -> Bindings.valueAt(fileTypeLocalMap, param.getValue().getType()));
|
.setCellValueFactory(param -> param.getValue().getType());
|
||||||
fileTable_typeColumn
|
fileTable_typeColumn
|
||||||
.setCellFactory(param -> new TextFieldTableCell<>(new ContractFileTypeLocalStringConverter()));
|
.setCellFactory(ContractFileTypeTableCell.forTableColumn(getCachedBean(ContractFileTypeService.class)));
|
||||||
// 监听 type map 变化
|
|
||||||
fileTypeLocalMap
|
|
||||||
.addListener((MapChangeListener<? super ContractFileType, ? super ContractFileTypeLocal>) change -> {
|
|
||||||
List<ContractFileTypeLocal> types = fileTypeLocalMap.values().stream().filter(typeLocal -> {
|
|
||||||
ContractFileType type = typeLocal.getType();
|
|
||||||
if (type == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isCustomer && !type.isSupportCustomer()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isVendor || type.isSupportVendor();
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
fileTable_menu_change_type.getItems().setAll(types.stream()
|
|
||||||
.map(typeLocal -> {
|
|
||||||
MenuItem item = new MenuItem();
|
|
||||||
item.setText(typeLocal.getValue());
|
|
||||||
item.getProperties().put("typeLocal", typeLocal);
|
|
||||||
item.setOnAction(this::onFileTableContextMenuChangeTypeAndNameAction);
|
|
||||||
return item;
|
|
||||||
}).toList());
|
|
||||||
|
|
||||||
fileTable_menu_change_type_and_name.getItems().setAll(types.stream()
|
|
||||||
.filter(typeLocal -> StringUtils.hasText(typeLocal.getSuggestFileName()))
|
|
||||||
.map(typeLocal -> {
|
|
||||||
MenuItem item = new MenuItem();
|
|
||||||
item.setText(typeLocal.getValue());
|
|
||||||
item.getProperties().put("typeLocal", typeLocal);
|
|
||||||
item.getProperties().put("rename", true);
|
|
||||||
item.setOnAction(this::onFileTableContextMenuChangeTypeAndNameAction);
|
|
||||||
return item;
|
|
||||||
}).toList());
|
|
||||||
});
|
|
||||||
|
|
||||||
fileTable_typeColumn.setEditable(false);
|
fileTable_typeColumn.setEditable(false);
|
||||||
|
|
||||||
|
|
||||||
/* 文件名编辑器 */
|
/* 文件名编辑器 */
|
||||||
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFileName());
|
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFileName());
|
||||||
fileTable_filePathColumn.setCellFactory(TextFieldTableCell.forTableColumn());
|
fileTable_filePathColumn.setCellFactory(TextFieldTableCell.forTableColumn());
|
||||||
@@ -249,7 +178,34 @@ public class ContractTabSkinFiles
|
|||||||
createVendorContractRequestByTemplateUpdateMenuItem(),
|
createVendorContractRequestByTemplateUpdateMenuItem(),
|
||||||
createVendorContractApplyByTemplateUpdateMenuItem());
|
createVendorContractApplyByTemplateUpdateMenuItem());
|
||||||
|
|
||||||
fileTypeLocalMap.putAll(getCachedBean(ContractFileTypeService.class).findAll(getLocale()));
|
|
||||||
|
runAsync(() -> {
|
||||||
|
getCachedBean(ContractFileTypeService.class).findAll(getLocale()).forEach((k, v) -> {
|
||||||
|
if (isCustomer && !k.isSupportCustomer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isVendor && !k.isSupportVendor()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MenuItem item = new MenuItem();
|
||||||
|
item.setText(v.getValue());
|
||||||
|
item.getProperties().put("typeLocal", v);
|
||||||
|
item.setOnAction(this::onFileTableContextMenuChangeTypeAndNameAction);
|
||||||
|
fileTable_menu_change_type.getItems().add(item);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(v.getSuggestFileName())) {
|
||||||
|
MenuItem item = new MenuItem();
|
||||||
|
item.setText(v.getValue());
|
||||||
|
item.getProperties().put("typeLocal", v);
|
||||||
|
item.getProperties().put("rename", true);
|
||||||
|
item.setOnAction(this::onFileTableContextMenuChangeTypeAndNameAction);
|
||||||
|
fileTable_menu_change_type_and_name.getItems().add(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
super.initializeTab();
|
super.initializeTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +229,7 @@ public class ContractTabSkinFiles
|
|||||||
* @param event 菜单事件
|
* @param event 菜单事件
|
||||||
*/
|
*/
|
||||||
private void onFileTableComposePDFAction(ActionEvent event) {
|
private void onFileTableComposePDFAction(ActionEvent event) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
if (!StringUtils.hasText(contract.getPath())) {
|
if (!StringUtils.hasText(contract.getPath())) {
|
||||||
setStatus("合同未设置路径");
|
setStatus("合同未设置路径");
|
||||||
return;
|
return;
|
||||||
@@ -329,7 +285,7 @@ public class ContractTabSkinFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContractFileViewModel append = new ContractFileViewModel();
|
ContractFileViewModel append = new ContractFileViewModel();
|
||||||
append.getContract().set(contract);
|
append.getContract().set(contract.getId());
|
||||||
append.getFileName().set(dest.getName());
|
append.getFileName().set(dest.getName());
|
||||||
append.getType().set(ContractFileType.General);
|
append.getType().set(ContractFileType.General);
|
||||||
saveRow(append);
|
saveRow(append);
|
||||||
@@ -343,7 +299,7 @@ public class ContractTabSkinFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onFileTableComposePDFAction_V2(ActionEvent event) {
|
private void onFileTableComposePDFAction_V2(ActionEvent event) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
if (!StringUtils.hasText(contract.getPath())) {
|
if (!StringUtils.hasText(contract.getPath())) {
|
||||||
setStatus("合同未设置路径");
|
setStatus("合同未设置路径");
|
||||||
return;
|
return;
|
||||||
@@ -426,7 +382,7 @@ public class ContractTabSkinFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContractFileViewModel append = new ContractFileViewModel();
|
ContractFileViewModel append = new ContractFileViewModel();
|
||||||
append.getContract().set(contract);
|
append.getContract().set(contract.getId());
|
||||||
append.getFileName().set(dest.getName());
|
append.getFileName().set(dest.getName());
|
||||||
append.getType().set(ContractFileType.General);
|
append.getType().set(ContractFileType.General);
|
||||||
saveRow(append);
|
saveRow(append);
|
||||||
@@ -482,7 +438,7 @@ public class ContractTabSkinFiles
|
|||||||
item.setText("成本核算审批表");
|
item.setText("成本核算审批表");
|
||||||
item.visibleProperty().bind(viewModel.getPayWay().isEqualTo(ContractPayWay.RECEIVE));
|
item.visibleProperty().bind(viewModel.getPayWay().isEqualTo(ContractPayWay.RECEIVE));
|
||||||
item.setOnAction(event -> {
|
item.setOnAction(event -> {
|
||||||
Contract contract = getContractService().findById(viewModel.getId().get());
|
ContractVo contract = getContractService().findById(viewModel.getId().get());
|
||||||
if (contract == null) {
|
if (contract == null) {
|
||||||
setStatus("异常,对应的合同不存在#" + viewModel.getId().get());
|
setStatus("异常,对应的合同不存在#" + viewModel.getId().get());
|
||||||
return;
|
return;
|
||||||
@@ -502,13 +458,13 @@ public class ContractTabSkinFiles
|
|||||||
.filter(v -> v.getType().get().equals(ContractFileType.Cost))
|
.filter(v -> v.getType().get().equals(ContractFileType.Cost))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
ContractFile file = new ContractFile();
|
ContractFileVo file = new ContractFileVo();
|
||||||
file.setContract(contract);
|
file.setContractId(contract.getId());
|
||||||
file.setType(ContractFileType.Cost);
|
file.setType(ContractFileType.Cost);
|
||||||
file.setApplyDate(LocalDate.now());
|
file.setApplyDate(LocalDate.now());
|
||||||
|
|
||||||
String fileName = item.getText();
|
String fileName = item.getText();
|
||||||
ContractFileTypeLocal local = fileTypeLocalMap.get(ContractFileType.CostForm);
|
ContractFileTypeLocalVo local = getCachedBean(ContractFileTypeService.class).findByType(getLocale(), ContractFileType.CostForm);
|
||||||
if (local != null) {
|
if (local != null) {
|
||||||
if (StringUtils.hasText(local.getSuggestFileName())) {
|
if (StringUtils.hasText(local.getSuggestFileName())) {
|
||||||
fileName = local.getSuggestFileName();
|
fileName = local.getSuggestFileName();
|
||||||
@@ -597,13 +553,13 @@ public class ContractTabSkinFiles
|
|||||||
//
|
//
|
||||||
fileTable_menu_change_type.setVisible(true);
|
fileTable_menu_change_type.setVisible(true);
|
||||||
for (MenuItem item : fileTable_menu_change_type.getItems()) {
|
for (MenuItem item : fileTable_menu_change_type.getItems()) {
|
||||||
ContractFileTypeLocal typeLocal = (ContractFileTypeLocal) item.getProperties().get("typeLocal");
|
ContractFileTypeLocalVo typeLocal = (ContractFileTypeLocalVo) item.getProperties().get("typeLocal");
|
||||||
item.setVisible(typeLocal.getType() != selectedItem.getType().get());
|
item.setVisible(typeLocal.getType() != selectedItem.getType().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
fileTable_menu_change_type_and_name.setVisible(true);
|
fileTable_menu_change_type_and_name.setVisible(true);
|
||||||
for (MenuItem item : fileTable_menu_change_type.getItems()) {
|
for (MenuItem item : fileTable_menu_change_type.getItems()) {
|
||||||
ContractFileTypeLocal typeLocal = (ContractFileTypeLocal) item.getProperties().get("typeLocal");
|
ContractFileTypeLocalVo typeLocal = (ContractFileTypeLocalVo) item.getProperties().get("typeLocal");
|
||||||
item.setVisible(typeLocal.getType() != selectedItem.getType().get());
|
item.setVisible(typeLocal.getType() != selectedItem.getType().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,7 +572,7 @@ public class ContractTabSkinFiles
|
|||||||
if (selectedItems == null || selectedItems.isEmpty()) {
|
if (selectedItems == null || selectedItems.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContractFileTypeLocal typeLocal = (ContractFileTypeLocal) item.getProperties().get("typeLocal");
|
ContractFileTypeLocalVo typeLocal = (ContractFileTypeLocalVo) item.getProperties().get("typeLocal");
|
||||||
if (typeLocal == null) {
|
if (typeLocal == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -657,27 +613,17 @@ public class ContractTabSkinFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContractFile loadRowData(ContractFileViewModel row) {
|
public ContractFileVo loadRowData(ContractFileViewModel row) {
|
||||||
return getContractFileService().findById(row.getId().get());
|
return getContractFileService().findById(row.getId().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContractFile saveRowData(ContractFile entity) {
|
|
||||||
return getContractFileService().save(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteRowData(ContractFile entity) {
|
|
||||||
getContractFileService().delete(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置文件
|
* 重置文件
|
||||||
* <br>
|
* <br>
|
||||||
* 依据已存在的文件重建
|
* 依据已存在的文件重建
|
||||||
*/
|
*/
|
||||||
public void onFileReBuildingAction(ActionEvent event) {
|
public void onFileReBuildingAction(ActionEvent event) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
ContractFilesRebuildTasker task = new ContractFilesRebuildTasker();
|
ContractFilesRebuildTasker task = new ContractFilesRebuildTasker();
|
||||||
task.setContract(contract);
|
task.setContract(contract);
|
||||||
UITools.showTaskDialogAndWait("文件重置", task, null);
|
UITools.showTaskDialogAndWait("文件重置", task, null);
|
||||||
@@ -698,13 +644,13 @@ public class ContractTabSkinFiles
|
|||||||
setStatus("目录错误,不存在");
|
setStatus("目录错误,不存在");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
|
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
File dest = new File(dir, file.getName());
|
File dest = new File(dir, file.getName());
|
||||||
if (file.renameTo(dest)) {
|
if (file.renameTo(dest)) {
|
||||||
ContractFile ccf = new ContractFile();
|
ContractFileVo ccf = new ContractFileVo();
|
||||||
ccf.setContract(contract);
|
ccf.setContractId(contract.getId());
|
||||||
ccf.setType(ContractFileType.General);
|
ccf.setType(ContractFileType.General);
|
||||||
ccf.setFileName(dest.getName());
|
ccf.setFileName(dest.getName());
|
||||||
getContractFileService().save(ccf);
|
getContractFileService().save(ccf);
|
||||||
@@ -721,7 +667,7 @@ public class ContractTabSkinFiles
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
if (!StringUtils.hasText(contract.getPath())) {
|
if (!StringUtils.hasText(contract.getPath())) {
|
||||||
setStatus("合同未设置路径");
|
setStatus("合同未设置路径");
|
||||||
return;
|
return;
|
||||||
@@ -757,12 +703,12 @@ public class ContractTabSkinFiles
|
|||||||
File outputFile = new File(contractPath, name + "-" + (i + 1) + ".pdf");
|
File outputFile = new File(contractPath, name + "-" + (i + 1) + ".pdf");
|
||||||
page.save(outputFile);
|
page.save(outputFile);
|
||||||
page.close();
|
page.close();
|
||||||
ContractFile contractFile = new ContractFile();
|
ContractFileVo contractFile = new ContractFileVo();
|
||||||
contractFile.setContract(contract);
|
contractFile.setContractId(contract.getId());
|
||||||
getContractService().syncContractFile(contractFile, outputFile, (lv, message) -> {
|
getContractService().syncContractFile(contractFile, outputFile, (lv, message) -> {
|
||||||
setStatus(message);
|
setStatus(message);
|
||||||
});
|
});
|
||||||
ContractFile saved = getContractFileService().save(contractFile);
|
ContractFileVo saved = getContractFileService().save(contractFile);
|
||||||
dataSet.add(ContractFileViewModel.from(saved));
|
dataSet.add(ContractFileViewModel.from(saved));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -775,7 +721,7 @@ public class ContractTabSkinFiles
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableDeleteAction(ActionEvent event) {
|
protected void onTableDeleteAction(ActionEvent event) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
String path = contract.getPath();
|
String path = contract.getPath();
|
||||||
if (!StringUtils.hasText(path)) {
|
if (!StringUtils.hasText(path)) {
|
||||||
setStatus("未设置目录");
|
setStatus("未设置目录");
|
||||||
@@ -792,7 +738,7 @@ public class ContractTabSkinFiles
|
|||||||
protected boolean deleteRow(ContractFileViewModel row, boolean confirm) {
|
protected boolean deleteRow(ContractFileViewModel row, boolean confirm) {
|
||||||
boolean deleted = super.deleteRow(row);
|
boolean deleted = super.deleteRow(row);
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
File file = new File(contract.getPath(), row.getFileName().get());
|
File file = new File(contract.getPath(), row.getFileName().get());
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
setStatus(file.getAbsolutePath() + " 文件不存在, 无法删除");
|
setStatus(file.getAbsolutePath() + " 文件不存在, 无法删除");
|
||||||
@@ -1,18 +1,10 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.model.ContractPayPlan;
|
|
||||||
import com.ecep.contract.service.ContractPayPlanService;
|
import com.ecep.contract.service.ContractPayPlanService;
|
||||||
import com.ecep.contract.service.ViewModelService;
|
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.ContractPayPlanViewModel;
|
import com.ecep.contract.vm.ContractPayPlanViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractPayPlanVo;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
@@ -20,8 +12,12 @@ import javafx.scene.control.cell.TextFieldTableCell;
|
|||||||
import javafx.util.converter.CurrencyStringConverter;
|
import javafx.util.converter.CurrencyStringConverter;
|
||||||
import javafx.util.converter.NumberStringConverter;
|
import javafx.util.converter.NumberStringConverter;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@FxmlPath("/ui/contract/contract-tab-pay-plan.fxml")
|
@FxmlPath("/ui/contract/contract-tab-pay-plan.fxml")
|
||||||
public class ContractTabSkinPayPlan extends AbstContractTableTabSkin<ContractPayPlan, ContractPayPlanViewModel> {
|
public class ContractTabSkinPayPlan extends AbstContractTableTabSkin<ContractPayPlanVo, ContractPayPlanViewModel> {
|
||||||
|
|
||||||
public TableColumn<ContractPayPlanViewModel, Number> idColumn;
|
public TableColumn<ContractPayPlanViewModel, Number> idColumn;
|
||||||
public TableColumn<ContractPayPlanViewModel, LocalDate> payDateColumn;
|
public TableColumn<ContractPayPlanViewModel, LocalDate> payDateColumn;
|
||||||
@@ -50,7 +46,7 @@ public class ContractTabSkinPayPlan extends AbstContractTableTabSkin<ContractPay
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ViewModelService<ContractPayPlan, ContractPayPlanViewModel> getViewModelService() {
|
protected ContractPayPlanService getViewModelService() {
|
||||||
return getPayPlanService();
|
return getPayPlanService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import com.ecep.contract.ContractPayWay;
|
import com.ecep.contract.ContractPayWay;
|
||||||
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
import com.ecep.contract.controller.vendor.purchase.order.PurchaseOrderWindowController;
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
|
||||||
import com.ecep.contract.controller.vendor.PurchaseOrderWindowController;
|
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
import com.ecep.contract.converter.EmployeeStringConverter;
|
||||||
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.PurchaseOrdersService;
|
import com.ecep.contract.service.PurchaseOrdersService;
|
||||||
import com.ecep.contract.vm.PurchaseOrderViewModel;
|
import com.ecep.contract.vm.PurchaseOrderViewModel;
|
||||||
import com.ecep.contract.model.PurchaseOrder;
|
import com.ecep.contract.vo.PurchaseOrderVo;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
|
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
@@ -22,19 +23,16 @@ import javafx.scene.control.TableColumn;
|
|||||||
*/
|
*/
|
||||||
@FxmlPath("/ui/contract/contract-tab-purchase-orders.fxml")
|
@FxmlPath("/ui/contract/contract-tab-purchase-orders.fxml")
|
||||||
public class ContractTabSkinPurchaseOrders
|
public class ContractTabSkinPurchaseOrders
|
||||||
extends AbstContractTableTabSkin<PurchaseOrder, PurchaseOrderViewModel>
|
extends AbstContractTableTabSkin<PurchaseOrderVo, PurchaseOrderViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
private PurchaseOrdersService purchaseOrdersService;
|
|
||||||
private EmployeeStringConverter employeeStringConverter;
|
|
||||||
|
|
||||||
public TableColumn<PurchaseOrderViewModel, Number> idColumn;
|
public TableColumn<PurchaseOrderViewModel, Number> idColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, String> codeColumn;
|
public TableColumn<PurchaseOrderViewModel, String> codeColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, String> table_makerColumn;
|
public TableColumn<PurchaseOrderViewModel, Integer> table_makerColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_makerDateColumn;
|
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_makerDateColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, String> table_verifierColumn;
|
public TableColumn<PurchaseOrderViewModel, Integer> table_verifierColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_verifierDateColumn;
|
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_verifierDateColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, String> table_closerColumn;
|
public TableColumn<PurchaseOrderViewModel, Integer> table_closerColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_closerDateColumn;
|
public TableColumn<PurchaseOrderViewModel, LocalDateTime> table_closerDateColumn;
|
||||||
public TableColumn<PurchaseOrderViewModel, String> table_descriptionColumn;
|
public TableColumn<PurchaseOrderViewModel, String> table_descriptionColumn;
|
||||||
|
|
||||||
@@ -71,14 +69,16 @@ public class ContractTabSkinPurchaseOrders
|
|||||||
idColumn.setCellValueFactory(param -> param.getValue().getId());
|
idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
||||||
|
|
||||||
EmployeeStringConverter converter = getEmployeeStringConverter();
|
table_makerColumn.setCellValueFactory(param -> param.getValue().getMaker());
|
||||||
table_makerColumn.setCellValueFactory(param -> param.getValue().getMaker().map(converter::toString));
|
table_makerColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
table_makerDateColumn.setCellValueFactory(param -> param.getValue().getMakerDate());
|
table_makerDateColumn.setCellValueFactory(param -> param.getValue().getMakerDate());
|
||||||
table_makerDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
table_makerDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
table_verifierColumn.setCellValueFactory(param -> param.getValue().getMaker().map(converter::toString));
|
table_verifierColumn.setCellValueFactory(param -> param.getValue().getMaker());
|
||||||
|
table_verifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
table_verifierDateColumn.setCellValueFactory(param -> param.getValue().getVerifierDate());
|
table_verifierDateColumn.setCellValueFactory(param -> param.getValue().getVerifierDate());
|
||||||
table_verifierDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
table_verifierDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
table_closerColumn.setCellValueFactory(param -> param.getValue().getMaker().map(converter::toString));
|
table_closerColumn.setCellValueFactory(param -> param.getValue().getMaker());
|
||||||
|
table_closerColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
table_closerDateColumn.setCellValueFactory(param -> param.getValue().getCloserDate());
|
table_closerDateColumn.setCellValueFactory(param -> param.getValue().getCloserDate());
|
||||||
table_closerDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
table_closerDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
|
|
||||||
@@ -92,16 +92,14 @@ public class ContractTabSkinPurchaseOrders
|
|||||||
}
|
}
|
||||||
|
|
||||||
PurchaseOrdersService getPurchaseOrdersService() {
|
PurchaseOrdersService getPurchaseOrdersService() {
|
||||||
if (purchaseOrdersService == null) {
|
return getBean(PurchaseOrdersService.class);
|
||||||
purchaseOrdersService = getBean(PurchaseOrdersService.class);
|
|
||||||
}
|
|
||||||
return purchaseOrdersService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmployeeStringConverter getEmployeeStringConverter() {
|
EmployeeStringConverter getEmployeeStringConverter() {
|
||||||
if (employeeStringConverter == null) {
|
return getBean(EmployeeStringConverter.class);
|
||||||
employeeStringConverter = getBean(EmployeeStringConverter.class);
|
}
|
||||||
}
|
|
||||||
return employeeStringConverter;
|
CompanyService getCompanyService() {
|
||||||
|
return controller.getCompanyService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
import com.ecep.contract.ContractPayWay;
|
import com.ecep.contract.ContractPayWay;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.controller.customer.SalesOrderWindowController;
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
import com.ecep.contract.converter.EmployeeStringConverter;
|
||||||
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.SaleOrdersService;
|
import com.ecep.contract.service.SaleOrdersService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.SalesOrderViewModel;
|
import com.ecep.contract.vm.SalesOrderViewModel;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.vo.SalesOrderVo;
|
||||||
import com.ecep.contract.model.SalesOrder;
|
|
||||||
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.control.cell.TextFieldTableCell;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,25 +25,47 @@ import lombok.Setter;
|
|||||||
*/
|
*/
|
||||||
@FxmlPath("/ui/contract/contract-tab-sale-orders.fxml")
|
@FxmlPath("/ui/contract/contract-tab-sale-orders.fxml")
|
||||||
public class ContractTabSkinSaleOrders
|
public class ContractTabSkinSaleOrders
|
||||||
extends AbstContractTableTabSkin<SalesOrder, SalesOrderViewModel>
|
extends AbstContractTableTabSkin<SalesOrderVo, SalesOrderViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private SaleOrdersService saleOrdersService;
|
private SaleOrdersService saleOrdersService;
|
||||||
|
private CompanyService companyService;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private EmployeeStringConverter employeeStringConverter;
|
private EmployeeStringConverter employeeStringConverter;
|
||||||
|
|
||||||
public TableColumn<SalesOrderViewModel, Number> idColumn;
|
public TableColumn<SalesOrderViewModel, Number> idColumn;
|
||||||
public TableColumn<SalesOrderViewModel, String> codeColumn;
|
public TableColumn<SalesOrderViewModel, String> codeColumn;
|
||||||
public TableColumn<SalesOrderViewModel, Employee> employeeColumn;
|
/**
|
||||||
public TableColumn<SalesOrderViewModel, Employee> makerColumn;
|
* 业务员, Employee
|
||||||
|
*/
|
||||||
|
public TableColumn<SalesOrderViewModel, Integer> employeeColumn;
|
||||||
|
/**
|
||||||
|
* 创建人, Employee
|
||||||
|
*/
|
||||||
|
public TableColumn<SalesOrderViewModel, Integer> makerColumn;
|
||||||
public TableColumn<SalesOrderViewModel, LocalDate> makerDateColumn;
|
public TableColumn<SalesOrderViewModel, LocalDate> makerDateColumn;
|
||||||
public TableColumn<SalesOrderViewModel, Employee> verifierColumn;
|
/**
|
||||||
|
* 审核人, Employee
|
||||||
|
*/
|
||||||
|
public TableColumn<SalesOrderViewModel, Integer> verifierColumn;
|
||||||
public TableColumn<SalesOrderViewModel, LocalDate> verifierDateColumn;
|
public TableColumn<SalesOrderViewModel, LocalDate> verifierDateColumn;
|
||||||
|
public TableColumn<SalesOrderViewModel, Number> refIdColumn;
|
||||||
|
public TableColumn<SalesOrderViewModel, Number> taxRateColumn;
|
||||||
|
public TableColumn<SalesOrderViewModel, String> customerAddressColumn;
|
||||||
|
/**
|
||||||
|
* 修改人, Employee
|
||||||
|
*/
|
||||||
|
public TableColumn<SalesOrderViewModel, Integer> modifierColumn;
|
||||||
|
public TableColumn<SalesOrderViewModel, LocalDate> modifierDateColumn;
|
||||||
|
/**
|
||||||
|
* 关闭人, Employee
|
||||||
|
*/
|
||||||
|
public TableColumn<SalesOrderViewModel, Integer> closerColumn;
|
||||||
|
public TableColumn<SalesOrderViewModel, LocalDate> closerDateColumn;
|
||||||
public TableColumn<SalesOrderViewModel, String> descriptionColumn;
|
public TableColumn<SalesOrderViewModel, String> descriptionColumn;
|
||||||
public MenuItem subContractTable_menu_refresh;
|
public MenuItem subContractTable_menu_refresh;
|
||||||
public TextField contractSearchKeyField;
|
|
||||||
public Button searchBtn;
|
|
||||||
private Tab tab;
|
private Tab tab;
|
||||||
|
|
||||||
public ContractTabSkinSaleOrders(ContractWindowController controller, Tab tab) {
|
public ContractTabSkinSaleOrders(ContractWindowController controller, Tab tab) {
|
||||||
@@ -73,31 +92,37 @@ public class ContractTabSkinSaleOrders
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTab() {
|
public void initializeTab() {
|
||||||
contractSearchKeyField.setOnKeyReleased(event -> {
|
super.initializeTab();
|
||||||
if (event.getCode() == KeyCode.ENTER) {
|
|
||||||
searchBtn.fire();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
searchBtn.setOnAction(this::onTableRefreshAction);
|
|
||||||
subContractTable_menu_refresh.setOnAction(this::onTableRefreshAction);
|
subContractTable_menu_refresh.setOnAction(this::onTableRefreshAction);
|
||||||
|
|
||||||
idColumn.setCellValueFactory(param -> param.getValue().getId());
|
idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
||||||
employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
|
employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
|
||||||
employeeColumn.setCellFactory(TextFieldTableCell.forTableColumn(getEmployeeStringConverter()));
|
employeeColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
makerColumn.setCellValueFactory(param -> param.getValue().getMaker());
|
makerColumn.setCellValueFactory(param -> param.getValue().getMaker());
|
||||||
makerColumn.setCellFactory(TextFieldTableCell.forTableColumn(getEmployeeStringConverter()));
|
makerColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
makerDateColumn.setCellValueFactory(param -> param.getValue().getMakerDate());
|
makerDateColumn.setCellValueFactory(param -> param.getValue().getMakerDate());
|
||||||
makerDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
makerDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
||||||
|
|
||||||
verifierColumn.setCellValueFactory(param -> param.getValue().getVerifier());
|
verifierColumn.setCellValueFactory(param -> param.getValue().getVerifier());
|
||||||
verifierColumn.setCellFactory(TextFieldTableCell.forTableColumn(getEmployeeStringConverter()));
|
verifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
verifierDateColumn.setCellValueFactory(param -> param.getValue().getVerifierDate());
|
verifierDateColumn.setCellValueFactory(param -> param.getValue().getVerifierDate());
|
||||||
verifierDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
verifierDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
||||||
|
|
||||||
|
// 设置新增字段的单元格值工厂和工厂类
|
||||||
|
refIdColumn.setCellValueFactory(param -> param.getValue().getRefId());
|
||||||
|
taxRateColumn.setCellValueFactory(param -> param.getValue().getTaxRate());
|
||||||
|
customerAddressColumn.setCellValueFactory(param -> param.getValue().getCustomerAddress());
|
||||||
|
modifierColumn.setCellValueFactory(param -> param.getValue().getModifier());
|
||||||
|
modifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
modifierDateColumn.setCellValueFactory(param -> param.getValue().getModifierDate());
|
||||||
|
modifierDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
||||||
|
closerColumn.setCellValueFactory(param -> param.getValue().getCloser());
|
||||||
|
closerColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
closerDateColumn.setCellValueFactory(param -> param.getValue().getCloserDate());
|
||||||
|
closerDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
|
||||||
|
|
||||||
descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
|
descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
|
||||||
super.initializeTab();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,17 +130,17 @@ public class ContractTabSkinSaleOrders
|
|||||||
SalesOrderWindowController.show(item, getTableView().getScene().getWindow());
|
SalesOrderWindowController.show(item, getTableView().getScene().getWindow());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmployeeStringConverter getEmployeeStringConverter() {
|
|
||||||
if (employeeStringConverter == null) {
|
|
||||||
employeeStringConverter = getBean(EmployeeStringConverter.class);
|
|
||||||
}
|
|
||||||
return employeeStringConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaleOrdersService getSaleOrdersService() {
|
SaleOrdersService getSaleOrdersService() {
|
||||||
if (saleOrdersService == null) {
|
if (saleOrdersService == null) {
|
||||||
saleOrdersService = getBean(SaleOrdersService.class);
|
saleOrdersService = getBean(SaleOrdersService.class);
|
||||||
}
|
}
|
||||||
return saleOrdersService;
|
return saleOrdersService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompanyService getCompanyService() {
|
||||||
|
if (companyService == null) {
|
||||||
|
companyService = getBean(CompanyService.class);
|
||||||
|
}
|
||||||
|
return companyService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.ContractPayWay;
|
import com.ecep.contract.ContractPayWay;
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.model.Contract;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
@@ -25,7 +25,7 @@ import javafx.scene.input.KeyCode;
|
|||||||
*/
|
*/
|
||||||
@FxmlPath("/ui/contract/contract-tab-sub-contract.fxml")
|
@FxmlPath("/ui/contract/contract-tab-sub-contract.fxml")
|
||||||
public class ContractTabSkinSubContract
|
public class ContractTabSkinSubContract
|
||||||
extends AbstContractTableTabSkin<Contract, ContractViewModel>
|
extends AbstContractTableTabSkin<ContractVo, ContractViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public TableColumn<ContractViewModel, Number> subContractTable_idColumn;
|
public TableColumn<ContractViewModel, Number> subContractTable_idColumn;
|
||||||
@@ -35,6 +35,7 @@ public class ContractTabSkinSubContract
|
|||||||
public TableColumn<ContractViewModel, LocalDate> subContractTable_inureDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> subContractTable_inureDateColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDate> subContractTable_orderDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> subContractTable_orderDateColumn;
|
||||||
public TableColumn<ContractViewModel, LocalDate> subContractTable_varyDateColumn;
|
public TableColumn<ContractViewModel, LocalDate> subContractTable_varyDateColumn;
|
||||||
|
public TableColumn<ContractViewModel, Integer> subContractTable_companyColumn;
|
||||||
public MenuItem subContractTable_menu_refresh;
|
public MenuItem subContractTable_menu_refresh;
|
||||||
public TextField contractSearchKeyField;
|
public TextField contractSearchKeyField;
|
||||||
public Button contractSearchBtn;
|
public Button contractSearchBtn;
|
||||||
@@ -61,12 +62,9 @@ public class ContractTabSkinSubContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(Contract parent) {
|
public ParamUtils.Builder getSpecification(ContractVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
if (params == null) {
|
params.equals("parentCode", parent.getCode());
|
||||||
params = new HashMap<>();
|
|
||||||
}
|
|
||||||
params.put("parentCode", parent.getCode());
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +88,8 @@ public class ContractTabSkinSubContract
|
|||||||
subContractTable_inureDateColumn.setCellValueFactory(param -> param.getValue().getInureDate());
|
subContractTable_inureDateColumn.setCellValueFactory(param -> param.getValue().getInureDate());
|
||||||
subContractTable_orderDateColumn.setCellValueFactory(param -> param.getValue().getOrderDate());
|
subContractTable_orderDateColumn.setCellValueFactory(param -> param.getValue().getOrderDate());
|
||||||
subContractTable_varyDateColumn.setCellValueFactory(param -> param.getValue().getVaryDate());
|
subContractTable_varyDateColumn.setCellValueFactory(param -> param.getValue().getVaryDate());
|
||||||
|
subContractTable_companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
|
||||||
|
subContractTable_companyColumn.setCellFactory(CompanyTableCell.forTableColumn(getCachedBean(CompanyService.class)));
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
getTableView().getSortOrder().add(subContractTable_codeColumn);
|
getTableView().getSortOrder().add(subContractTable_codeColumn);
|
||||||
@@ -1,26 +1,25 @@
|
|||||||
package com.ecep.contract.controller.tab;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.ecep.contract.service.ContractFileService;
|
||||||
import org.controlsfx.control.textfield.TextFields;
|
import org.controlsfx.control.textfield.TextFields;
|
||||||
|
|
||||||
import com.ecep.contract.ContractPayWay;
|
import com.ecep.contract.ContractPayWay;
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
|
||||||
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
||||||
import com.ecep.contract.controller.vendor.VendorBidWindowController;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.controller.table.cell.ContractFileTableCell;
|
||||||
import com.ecep.contract.model.Contract;
|
import com.ecep.contract.controller.vendor.bid.VendorBidWindowController;
|
||||||
import com.ecep.contract.model.ContractBidVendor;
|
|
||||||
import com.ecep.contract.model.ContractFile;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.ContractBidVendorService;
|
import com.ecep.contract.service.ContractBidVendorService;
|
||||||
import com.ecep.contract.service.ContractFileService;
|
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.vm.CompanyViewModel;
|
import com.ecep.contract.vm.CompanyViewModel;
|
||||||
import com.ecep.contract.vm.ContractBidVendorViewModel;
|
import com.ecep.contract.vm.ContractBidVendorViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
import com.ecep.contract.vo.ContractBidVendorVo;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
@@ -29,7 +28,6 @@ import javafx.scene.control.ChoiceDialog;
|
|||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TableCell;
|
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -39,13 +37,16 @@ import lombok.Setter;
|
|||||||
*/
|
*/
|
||||||
@FxmlPath("/ui/contract/contract-tab-bid.fxml")
|
@FxmlPath("/ui/contract/contract-tab-bid.fxml")
|
||||||
public class ContractTabSkinVendorBid
|
public class ContractTabSkinVendorBid
|
||||||
extends AbstContractTableTabSkin<ContractBidVendor, ContractBidVendorViewModel>
|
extends AbstContractTableTabSkin<ContractBidVendorVo, ContractBidVendorViewModel>
|
||||||
implements TabSkin, EditableEntityTableTabSkin<ContractBidVendor, ContractBidVendorViewModel> {
|
implements TabSkin, EditableEntityTableTabSkin<ContractBidVendorVo, ContractBidVendorViewModel> {
|
||||||
@Setter
|
@Setter
|
||||||
private ContractBidVendorService service;
|
private ContractBidVendorService service;
|
||||||
public TableColumn<ContractBidVendorViewModel, Number> bidVendorTable_idColumn;
|
public TableColumn<ContractBidVendorViewModel, Number> bidVendorTable_idColumn;
|
||||||
public TableColumn<ContractBidVendorViewModel, String> bidVendorTable_companyColumn;
|
public TableColumn<ContractBidVendorViewModel, Integer> bidVendorTable_companyColumn;
|
||||||
public TableColumn<ContractBidVendorViewModel, ContractFile> bidVendorTable_quotationSheetColumn;
|
/**
|
||||||
|
* 报价单, 合同文件, ContractFile
|
||||||
|
*/
|
||||||
|
public TableColumn<ContractBidVendorViewModel, Integer> bidVendorTable_quotationSheetColumn;
|
||||||
|
|
||||||
public Button bidVendorCreateBtn;
|
public Button bidVendorCreateBtn;
|
||||||
public MenuItem bidVendorTable_menu_refresh;
|
public MenuItem bidVendorTable_menu_refresh;
|
||||||
@@ -73,32 +74,6 @@ public class ContractTabSkinVendorBid
|
|||||||
return controller.bidVendorTab;
|
return controller.bidVendorTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class QuotationSheetColumnTableCell extends TableCell<ContractBidVendorViewModel, ContractFile> {
|
|
||||||
private ContractFileService contractFileService;
|
|
||||||
|
|
||||||
public ContractFileService getContractFileService() {
|
|
||||||
if (contractFileService == null) {
|
|
||||||
contractFileService = SpringApp.getBean(ContractFileService.class);
|
|
||||||
}
|
|
||||||
return contractFileService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateItem(ContractFile item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
if (empty || item == null) {
|
|
||||||
setText(null);
|
|
||||||
} else {
|
|
||||||
if (!ProxyUtils.isInitialized(item)) {
|
|
||||||
item = getContractFileService().findById(item.getId());
|
|
||||||
ContractBidVendorViewModel viewModel = getTableRow().getItem();
|
|
||||||
viewModel.getQuotationSheet().set(item);
|
|
||||||
}
|
|
||||||
setText(item.getFileName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTab() {
|
public void initializeTab() {
|
||||||
bidVendorCreateBtn.setOnAction(this::onBidVendorTableCreateAction);
|
bidVendorCreateBtn.setOnAction(this::onBidVendorTableCreateAction);
|
||||||
@@ -107,15 +82,10 @@ public class ContractTabSkinVendorBid
|
|||||||
bidVendorTable_menu_chose_sheet.setOnAction(this::onBidVendorTableChoseQuotationSheetAction);
|
bidVendorTable_menu_chose_sheet.setOnAction(this::onBidVendorTableChoseQuotationSheetAction);
|
||||||
|
|
||||||
bidVendorTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
bidVendorTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
bidVendorTable_companyColumn.setCellValueFactory(param -> param.getValue().getCompany().map(c -> {
|
bidVendorTable_companyColumn.setCellValueFactory(param -> param.getValue().getCompanyId());
|
||||||
if (c == null) {
|
bidVendorTable_companyColumn.setCellFactory(CompanyTableCell.forTableColumn(getCompanyService()));
|
||||||
return null;
|
bidVendorTable_quotationSheetColumn.setCellValueFactory(param -> param.getValue().getQuotationSheetFileId());
|
||||||
} else {
|
bidVendorTable_quotationSheetColumn.setCellFactory(ContractFileTableCell.forTableColumn(SpringApp.getBean(ContractFileService.class)));
|
||||||
return c.getName();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
bidVendorTable_quotationSheetColumn.setCellValueFactory(param -> param.getValue().getQuotationSheet());
|
|
||||||
bidVendorTable_quotationSheetColumn.setCellFactory(param -> new QuotationSheetColumnTableCell());
|
|
||||||
|
|
||||||
super.initializeTab();
|
super.initializeTab();
|
||||||
}
|
}
|
||||||
@@ -125,9 +95,9 @@ public class ContractTabSkinVendorBid
|
|||||||
showInOwner(VendorBidWindowController.class, item);
|
showInOwner(VendorBidWindowController.class, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void onBidVendorTableCreateAction(ActionEvent event) {
|
public void onBidVendorTableCreateAction(ActionEvent event) {
|
||||||
Contract contract = getParent();
|
ContractVo contract = getParent();
|
||||||
|
|
||||||
|
|
||||||
ChoiceDialog<String> dialog = new ChoiceDialog<>();
|
ChoiceDialog<String> dialog = new ChoiceDialog<>();
|
||||||
dialog.setTitle("添加比价供应商");
|
dialog.setTitle("添加比价供应商");
|
||||||
@@ -154,13 +124,13 @@ public class ContractTabSkinVendorBid
|
|||||||
comboBox.setEditable(true);
|
comboBox.setEditable(true);
|
||||||
dialog.showAndWait().ifPresent(selectedItem -> {
|
dialog.showAndWait().ifPresent(selectedItem -> {
|
||||||
|
|
||||||
Company company = getCompanyService().findAllByName(selectedItem).getFirst();
|
CompanyVo company = getCompanyService().findAllByName(selectedItem).getFirst();
|
||||||
|
|
||||||
List<ContractBidVendor> list = getService().findByContractAndCompany(contract, company);
|
List<ContractBidVendorVo> list = getService().findByContractAndCompany(contract, company);
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
ContractBidVendor bidVendor = new ContractBidVendor();
|
ContractBidVendorVo bidVendor = new ContractBidVendorVo();
|
||||||
bidVendor.setContract(contract);
|
bidVendor.setContractId(contract.getId());
|
||||||
bidVendor.setCompany(company);
|
bidVendor.setCompanyId(company.getId());
|
||||||
getService().save(bidVendor);
|
getService().save(bidVendor);
|
||||||
loadTableDataSet();
|
loadTableDataSet();
|
||||||
} else {
|
} else {
|
||||||
@@ -172,7 +142,6 @@ public class ContractTabSkinVendorBid
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 匹配报价表
|
* 匹配报价表
|
||||||
*
|
*
|
||||||
@@ -198,5 +167,4 @@ public class ContractTabSkinVendorBid
|
|||||||
return controller.getCompanyService();
|
return controller.getCompanyService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,15 @@ import static com.ecep.contract.util.TableViewUtils.bindEnterPressed;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import org.controlsfx.control.PopOver;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -29,7 +30,6 @@ import com.ecep.contract.Message;
|
|||||||
import com.ecep.contract.MessageHolder;
|
import com.ecep.contract.MessageHolder;
|
||||||
import com.ecep.contract.controller.BaseController;
|
import com.ecep.contract.controller.BaseController;
|
||||||
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.model.Employee;
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.service.EmployeeService;
|
import com.ecep.contract.service.EmployeeService;
|
||||||
@@ -40,6 +40,8 @@ import com.ecep.contract.task.ContractVerifyResultExportAsExcelFileTasker;
|
|||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ParamUtils;
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleListProperty;
|
import javafx.beans.property.SimpleListProperty;
|
||||||
@@ -51,13 +53,6 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.CheckMenuItem;
|
|
||||||
import javafx.scene.control.DatePicker;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TableCell;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
@@ -80,6 +75,7 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
return super.show(loader, owner, modality);
|
return super.show(loader, owner, modality);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
public static class MessageExt extends Message {
|
public static class MessageExt extends Message {
|
||||||
@@ -91,12 +87,16 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class Model implements MessageHolder {
|
public static class Model {
|
||||||
private SimpleStringProperty code = new SimpleStringProperty();
|
private SimpleStringProperty code = new SimpleStringProperty();
|
||||||
private SimpleStringProperty name = new SimpleStringProperty();
|
private SimpleStringProperty name = new SimpleStringProperty();
|
||||||
private SimpleObjectProperty<Employee> employee = new SimpleObjectProperty<>();
|
private SimpleObjectProperty<Integer> employee = new SimpleObjectProperty<>();
|
||||||
private SimpleObjectProperty<LocalDate> setupDate = new SimpleObjectProperty<>();
|
private SimpleObjectProperty<LocalDate> setupDate = new SimpleObjectProperty<>();
|
||||||
private SimpleListProperty<MessageExt> messages = new SimpleListProperty<>(FXCollections.observableArrayList());
|
private SimpleListProperty<MessageExt> messages = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MessageHolderImpl implements MessageHolder {
|
||||||
|
List<MessageExt> messages = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMessage(Level level, String message) {
|
public void addMessage(Level level, String message) {
|
||||||
@@ -159,16 +159,10 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContractVerifyComm comm = new ContractVerifyComm();
|
ContractVerifyComm comm = new ContractVerifyComm(this);
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ProjectSaleTypeService saleTypeService;
|
|
||||||
@Autowired
|
|
||||||
private VendorGroupService vendorGroupService;
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ContractService contractService;
|
private ContractService contractService;
|
||||||
@Autowired
|
|
||||||
private EmployeeService employeeService;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public DatePicker setupDateBeginSelector;
|
public DatePicker setupDateBeginSelector;
|
||||||
@@ -205,7 +199,7 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
@FXML
|
@FXML
|
||||||
public TableColumn<Model, String> viewTable_nameColumn;
|
public TableColumn<Model, String> viewTable_nameColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<Model, Employee> viewTable_employeeColumn;
|
public TableColumn<Model, Integer> viewTable_employeeColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<Model, LocalDate> viewTable_setupDateColumn;
|
public TableColumn<Model, LocalDate> viewTable_setupDateColumn;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -219,7 +213,6 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
|
|
||||||
public void onShown(WindowEvent windowEvent) {
|
public void onShown(WindowEvent windowEvent) {
|
||||||
super.onShown(windowEvent);
|
super.onShown(windowEvent);
|
||||||
comm.setContractService(contractService);
|
|
||||||
viewTable.getScene().getStylesheets().add("/ui/contract/contract-verify.css");
|
viewTable.getScene().getStylesheets().add("/ui/contract/contract-verify.css");
|
||||||
|
|
||||||
LocalDate now = LocalDate.now();
|
LocalDate now = LocalDate.now();
|
||||||
@@ -231,11 +224,7 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
viewTable_employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
|
viewTable_employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
|
||||||
viewTable_employeeColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
|
viewTable_employeeColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
|
||||||
viewTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
|
viewTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
|
||||||
// viewTable_stateColumn.setCellValueFactory(param ->
|
|
||||||
// param.getValue().getMessages().map(messages -> {
|
|
||||||
// return
|
|
||||||
// messages.stream().map(Message::getMessage).collect(Collectors.joining(", "));
|
|
||||||
// }));
|
|
||||||
viewTable_stateColumn.setCellValueFactory(param -> param.getValue().getMessages());
|
viewTable_stateColumn.setCellValueFactory(param -> param.getValue().getMessages());
|
||||||
viewTable_stateColumn.setCellFactory(param -> new StateTableCell());
|
viewTable_stateColumn.setCellFactory(param -> new StateTableCell());
|
||||||
|
|
||||||
@@ -273,22 +262,25 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
long total = contractService.count(params);
|
long total = contractService.count(params);
|
||||||
setStatus("合同:" + total + " 条");
|
setStatus("合同:" + total + " 条");
|
||||||
|
|
||||||
|
MessageHolderImpl messageHolder = new MessageHolderImpl();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isCloseRequested()) {
|
if (isCloseRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Page<Contract> page = contractService.findAll(params, pageRequest);
|
Page<ContractVo> page = contractService.findAll(params, pageRequest);
|
||||||
for (Contract contract : page) {
|
for (ContractVo contract : page) {
|
||||||
|
messageHolder.messages.clear();
|
||||||
if (isCloseRequested()) {
|
if (isCloseRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
counter.incrementAndGet();
|
counter.incrementAndGet();
|
||||||
Model model = new Model();
|
Model model = new Model();
|
||||||
viewTableDataSet.add(model);
|
viewTableDataSet.add(model);
|
||||||
Employee handler = contract.getHandler();
|
Integer handler = contract.getHandlerId();
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
model.getEmployee().set(contract.getEmployee());
|
model.getEmployee().set(contract.getEmployeeId());
|
||||||
} else {
|
} else {
|
||||||
model.getEmployee().set(handler);
|
model.getEmployee().set(handler);
|
||||||
}
|
}
|
||||||
@@ -297,11 +289,11 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
model.getName().set(contract.getName());
|
model.getName().set(contract.getName());
|
||||||
model.getSetupDate().set(contract.getSetupDate());
|
model.getSetupDate().set(contract.getSetupDate());
|
||||||
|
|
||||||
comm.verify(contract, model);
|
comm.verify(contract, messageHolder);
|
||||||
setStatus("合同验证进度:" + counter.get() + " / " + total);
|
setStatus("合同验证进度:" + counter.get() + " / " + total);
|
||||||
// 移除中间消息
|
// 移除中间消息
|
||||||
if (!model.getMessages().isEmpty()) {
|
if (!messageHolder.messages.isEmpty()) {
|
||||||
model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
|
model.getMessages().setAll(messageHolder.messages.stream().filter(msg -> msg.getLevel().intValue() > Level.INFO.intValue()).limit(50).toList());
|
||||||
}
|
}
|
||||||
if (model.getMessages().isEmpty()) {
|
if (model.getMessages().isEmpty()) {
|
||||||
if (onlyShowVerifiedChecker.isSelected()) {
|
if (onlyShowVerifiedChecker.isSelected()) {
|
||||||
@@ -336,7 +328,8 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runAsync(() -> {
|
runAsync(() -> {
|
||||||
Contract contract = null;
|
ContractVo contract = null;
|
||||||
|
MessageHolderImpl messageHolder = new MessageHolderImpl();
|
||||||
try {
|
try {
|
||||||
contract = contractService.findByCode(contractCode);
|
contract = contractService.findByCode(contractCode);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -348,17 +341,18 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
}
|
}
|
||||||
model.getMessages().clear();
|
model.getMessages().clear();
|
||||||
try {
|
try {
|
||||||
comm.verify(contract, model);
|
comm.verify(contract, messageHolder);
|
||||||
// 移除中间消息
|
|
||||||
if (!model.getMessages().isEmpty()) {
|
|
||||||
model.getMessages().removeIf(msg -> msg.getLevel().intValue() <= Level.INFO.intValue());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(model.getCode().get(), e);
|
logger.error(model.getCode().get(), e);
|
||||||
model.error(e.getMessage());
|
messageHolder.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.getMessages().isEmpty()) {
|
// 移除中间消息
|
||||||
|
if (!messageHolder.messages.isEmpty()) {
|
||||||
|
model.getMessages().setAll(messageHolder.messages.stream().filter(msg -> msg.getLevel().intValue() > Level.INFO.intValue()).limit(50).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageHolder.messages.isEmpty()) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
viewTableDataSet.remove(model);
|
viewTableDataSet.remove(model);
|
||||||
});
|
});
|
||||||
@@ -379,7 +373,7 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
if (!StringUtils.hasText(contractCode)) {
|
if (!StringUtils.hasText(contractCode)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Contract contract = null;
|
ContractVo contract = null;
|
||||||
try {
|
try {
|
||||||
contract = contractService.findByCode(contractCode);
|
contract = contractService.findByCode(contractCode);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -392,6 +386,38 @@ public class ContractVerifyWindowController extends BaseController {
|
|||||||
ContractWindowController.show(contract, viewTable.getScene().getWindow());
|
ContractWindowController.show(contract, viewTable.getScene().getWindow());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void onShowVerifyStatusAction(ActionEvent event) {
|
||||||
|
|
||||||
|
// 在新新窗口中显示 状态消息 Model# messages
|
||||||
|
|
||||||
|
Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
|
||||||
|
if (selectedItem == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView<MessageExt> listView = new ListView<>();
|
||||||
|
listView.setItems(selectedItem.messages);
|
||||||
|
listView.setCellFactory(v -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(MessageExt item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (item == null || empty) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText(item.getMessage());
|
||||||
|
setGraphic(new Label(item.getPrefix()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
PopOver popOver = new PopOver(listView);
|
||||||
|
popOver.setArrowLocation(PopOver.ArrowLocation.TOP_LEFT);
|
||||||
|
MenuItem menuItem = (MenuItem) event.getSource();
|
||||||
|
Node node = viewTable.lookup(".table-row-cell:selected");
|
||||||
|
popOver.show(node);
|
||||||
|
}
|
||||||
|
|
||||||
public void onExportVerifyResultAsFileAction(ActionEvent e) {
|
public void onExportVerifyResultAsFileAction(ActionEvent e) {
|
||||||
FileChooser chooser = new FileChooser();
|
FileChooser chooser = new FileChooser();
|
||||||
chooser.setTitle("导出核验结果");
|
chooser.setTitle("导出核验结果");
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
package com.ecep.contract.controller.contract;
|
package com.ecep.contract.controller.contract;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.ContractPayWay;
|
import com.ecep.contract.ContractPayWay;
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.tab.*;
|
import com.ecep.contract.controller.tab.ContractTabSkinBase;
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.controller.tab.ContractTabSkinInvoices;
|
||||||
import com.ecep.contract.model.Contract;
|
import com.ecep.contract.controller.tab.ContractTabSkinItemsV2;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.task.ContractRepairTask;
|
import com.ecep.contract.task.ContractRepairTask;
|
||||||
@@ -14,30 +22,26 @@ import com.ecep.contract.task.ContractVerifyTasker;
|
|||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.*;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.context.annotation.Scope;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/contract/contract.fxml")
|
@FxmlPath("/ui/contract/contract.fxml")
|
||||||
public class ContractWindowController
|
public class ContractWindowController
|
||||||
extends AbstEntityController<Contract, ContractViewModel> {
|
extends AbstEntityController<ContractVo, ContractViewModel> {
|
||||||
|
|
||||||
public static void show(Contract contract, Window owner) {
|
|
||||||
ContractViewModel model = new ContractViewModel();
|
|
||||||
model.update(contract);
|
public static void show(ContractVo contract, Window owner) {
|
||||||
show(model, owner);
|
show(ContractViewModel.from(contract), owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +65,7 @@ public class ContractWindowController
|
|||||||
public Button openRelativeCompanyVendorBtn;
|
public Button openRelativeCompanyVendorBtn;
|
||||||
|
|
||||||
public TextField nameField;
|
public TextField nameField;
|
||||||
|
public CheckBox contractNameLockedCk;
|
||||||
public TextField guidField;
|
public TextField guidField;
|
||||||
public TextField codeField;
|
public TextField codeField;
|
||||||
public TextField parentCodeField;
|
public TextField parentCodeField;
|
||||||
@@ -137,9 +142,16 @@ public class ContractWindowController
|
|||||||
registerTabSkin(contractTab, t -> new ContractTabSkinSubContract(this));
|
registerTabSkin(contractTab, t -> new ContractTabSkinSubContract(this));
|
||||||
tabs.remove(bidVendorTab);
|
tabs.remove(bidVendorTab);
|
||||||
Tab saleOrderTab = new Tab("销售订单");
|
Tab saleOrderTab = new Tab("销售订单");
|
||||||
|
payPlanTab.setText("收款计划");
|
||||||
tabs.add(saleOrderTab);
|
tabs.add(saleOrderTab);
|
||||||
registerTabSkin(saleOrderTab, tab -> new ContractTabSkinSaleOrders(this, tab));
|
registerTabSkin(saleOrderTab, tab -> new ContractTabSkinSaleOrders(this, tab));
|
||||||
tabs.add(new Tab("票据"));
|
Tab invoiceTab = new Tab("发票");
|
||||||
|
tabs.add(invoiceTab);
|
||||||
|
registerTabSkin(invoiceTab, tab -> new ContractTabSkinInvoices(this, tab));
|
||||||
|
tabs.add(new Tab("出库单"));
|
||||||
|
tabs.add(new Tab("发货单"));
|
||||||
|
tabs.add(new Tab("签收单"));
|
||||||
|
|
||||||
} else if (payWay == ContractPayWay.PAY) {
|
} else if (payWay == ContractPayWay.PAY) {
|
||||||
registerTabSkin(extendVendorInfo, t -> new ContractTabSkinExtendVendorInfo(this));
|
registerTabSkin(extendVendorInfo, t -> new ContractTabSkinExtendVendorInfo(this));
|
||||||
tabs.remove(contractTab);
|
tabs.remove(contractTab);
|
||||||
@@ -149,9 +161,10 @@ public class ContractWindowController
|
|||||||
tabs.add(purchaseOrderTab);
|
tabs.add(purchaseOrderTab);
|
||||||
registerTabSkin(purchaseOrderTab, tab -> new ContractTabSkinPurchaseOrders(this, tab));
|
registerTabSkin(purchaseOrderTab, tab -> new ContractTabSkinPurchaseOrders(this, tab));
|
||||||
|
|
||||||
tabs.add(new Tab("发货单"));
|
tabs.add(new Tab("入库单"));
|
||||||
tabs.add(new Tab("签收单"));
|
|
||||||
tabs.add(new Tab("付款单"));
|
tabs.add(new Tab("付款单"));
|
||||||
|
|
||||||
|
payPlanTab.setText("付款计划");
|
||||||
}
|
}
|
||||||
|
|
||||||
registerTabSkin(itemTab, tab -> new ContractTabSkinItemsV2(this));
|
registerTabSkin(itemTab, tab -> new ContractTabSkinItemsV2(this));
|
||||||
@@ -160,7 +173,7 @@ public class ContractWindowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onContractOpenInExplorerAction(ActionEvent event) {
|
public void onContractOpenInExplorerAction(ActionEvent event) {
|
||||||
Contract contract = getEntity();
|
ContractVo contract = getEntity();
|
||||||
String path = contract.getPath();
|
String path = contract.getPath();
|
||||||
if (!StringUtils.hasText(path)) {
|
if (!StringUtils.hasText(path)) {
|
||||||
setStatus("未设置目录");
|
setStatus("未设置目录");
|
||||||
@@ -175,13 +188,13 @@ public class ContractWindowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onContractOpenRelativeCompanyAction(ActionEvent event) {
|
public void onContractOpenRelativeCompanyAction(ActionEvent event) {
|
||||||
Contract contract = getEntity();
|
ContractVo contract = getEntity();
|
||||||
if (contract.getCompany() == null) {
|
if (contract.getCompanyId() == null) {
|
||||||
UITools.showAlertAndWait("没有关联的公司,你可以尝试同步修复异常。");
|
UITools.showAlertAndWait("没有关联的公司,你可以尝试同步修复异常。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Integer companyId = contract.getCompany().getId();
|
Integer companyId = contract.getCompanyId();
|
||||||
Company company = getCompanyService().findById(companyId);
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
if (company != null) {
|
if (company != null) {
|
||||||
CompanyWindowController.show(company, root.getScene().getWindow());
|
CompanyWindowController.show(company, root.getScene().getWindow());
|
||||||
}
|
}
|
||||||
@@ -224,7 +237,7 @@ public class ContractWindowController
|
|||||||
* 验证合同合规性
|
* 验证合同合规性
|
||||||
*/
|
*/
|
||||||
public void onContractVerifyAction(ActionEvent event) {
|
public void onContractVerifyAction(ActionEvent event) {
|
||||||
Contract contract = getEntity();
|
ContractVo contract = getEntity();
|
||||||
ContractVerifyTasker task = new ContractVerifyTasker();
|
ContractVerifyTasker task = new ContractVerifyTasker();
|
||||||
task.setContract(contract);
|
task.setContract(contract);
|
||||||
UITools.showTaskDialogAndWait("同步合规性验证", task, null);
|
UITools.showTaskDialogAndWait("同步合规性验证", task, null);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
package com.ecep.contract.controller.contract.sale_order;
|
||||||
|
|
||||||
|
import com.ecep.contract.vo.SalesOrderVo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
@@ -11,7 +12,6 @@ import com.ecep.contract.util.FxmlPath;
|
|||||||
import com.ecep.contract.controller.tab.SalesOrderTabSkinBase;
|
import com.ecep.contract.controller.tab.SalesOrderTabSkinBase;
|
||||||
import com.ecep.contract.controller.tab.SalesOrderTabSkinBillVoucher;
|
import com.ecep.contract.controller.tab.SalesOrderTabSkinBillVoucher;
|
||||||
import com.ecep.contract.controller.tab.SalesOrderTabSkinItems;
|
import com.ecep.contract.controller.tab.SalesOrderTabSkinItems;
|
||||||
import com.ecep.contract.model.SalesOrder;
|
|
||||||
import com.ecep.contract.service.SaleOrdersService;
|
import com.ecep.contract.service.SaleOrdersService;
|
||||||
import com.ecep.contract.vm.SalesOrderViewModel;
|
import com.ecep.contract.vm.SalesOrderViewModel;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ import javafx.stage.Window;
|
|||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/contract/sale-orders.fxml")
|
@FxmlPath("/ui/contract/sale-orders.fxml")
|
||||||
public class SalesOrderWindowController extends AbstEntityController<SalesOrder, SalesOrderViewModel> {
|
public class SalesOrderWindowController extends AbstEntityController<SalesOrderVo, SalesOrderViewModel> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SalesOrderWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(SalesOrderWindowController.class);
|
||||||
public TabPane tabPane;
|
public TabPane tabPane;
|
||||||
public Button saveBtn;
|
public Button saveBtn;
|
||||||
@@ -43,6 +43,15 @@ public class SalesOrderWindowController extends AbstEntityController<SalesOrder,
|
|||||||
public TextField makeDateField;
|
public TextField makeDateField;
|
||||||
public TextField makerField;
|
public TextField makerField;
|
||||||
public TextArea descriptionField;
|
public TextArea descriptionField;
|
||||||
|
public TextField refIdField;
|
||||||
|
public TextField taxRateField;
|
||||||
|
public TextField customerField;
|
||||||
|
public TextField customerAddressField;
|
||||||
|
public TextField modifierField;
|
||||||
|
public TextField modifierDateField;
|
||||||
|
public TextField closerField;
|
||||||
|
public TextField closerDateField;
|
||||||
|
public TextField contractField;
|
||||||
|
|
||||||
|
|
||||||
public static void show(SalesOrderViewModel viewModel, Window window) {
|
public static void show(SalesOrderViewModel viewModel, Window window) {
|
||||||
@@ -2,16 +2,16 @@ package com.ecep.contract.controller.customer;
|
|||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.model.IdentityEntity;
|
import com.ecep.contract.model.IdentityEntity;
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
||||||
import com.ecep.contract.vm.IdentityViewModel;
|
import com.ecep.contract.vm.IdentityViewModel;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
public abstract class AbstCompanyCustomerTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
public abstract class AbstCompanyCustomerTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
||||||
extends
|
extends
|
||||||
AbstEntityTableTabSkin<CompanyCustomerWindowController, CompanyCustomer, CompanyCustomerViewModel, T, TV>
|
AbstEntityTableTabSkin<CompanyCustomerWindowController, CustomerVo, CompanyCustomerViewModel, T, TV>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public AbstCompanyCustomerTableTabSkin(CompanyCustomerWindowController controller) {
|
public AbstCompanyCustomerTableTabSkin(CompanyCustomerWindowController controller) {
|
||||||
@@ -22,8 +22,8 @@ public abstract class AbstCompanyCustomerTableTabSkin<T extends IdentityEntity,
|
|||||||
return controller.getCompanyService();
|
return controller.getCompanyService();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CompanyCustomerService getCompanyCustomerService() {
|
protected CustomerService getCompanyCustomerService() {
|
||||||
return getCachedBean(CompanyCustomerService.class);
|
return getCachedBean(CustomerService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
package com.ecep.contract.controller.customer;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerFileTypeService;
|
||||||
|
import com.ecep.contract.service.ViewModelService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.vo.CustomerFileTypeLocalVo;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.text.FontWeight;
|
||||||
|
import javafx.util.converter.LocalDateStringConverter;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
@@ -15,23 +30,24 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.FileSystemUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.controller.BaseController;
|
import com.ecep.contract.controller.BaseController;
|
||||||
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
|
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
||||||
import com.ecep.contract.model.CompanyCustomerFile;
|
import com.ecep.contract.vo.CustomerFileVo;
|
||||||
import com.ecep.contract.service.CompanyCustomerFileService;
|
import com.ecep.contract.service.CompanyCustomerFileService;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
|
||||||
import com.ecep.contract.util.FileUtils;
|
import com.ecep.contract.util.FileUtils;
|
||||||
import com.ecep.contract.util.FxmlUtils;
|
import com.ecep.contract.util.FxmlUtils;
|
||||||
import com.ecep.contract.vm.CompanyCustomerFileViewModel;
|
import com.ecep.contract.vm.CompanyCustomerEvaluationFormFileViewModel;
|
||||||
|
import com.ecep.contract.vm.CustomerFileViewModel;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.geometry.Bounds;
|
import javafx.geometry.Bounds;
|
||||||
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.DatePicker;
|
import javafx.scene.control.DatePicker;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@@ -48,30 +64,29 @@ import javafx.stage.Modality;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
public class CompanyCustomerEvaluationFormFileWindowController extends BaseController {
|
@FxmlPath("/ui/company/customer/customer_evaluation_form.fxml")
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
|
public class CompanyCustomerEvaluationFormFileWindowController
|
||||||
|
extends AbstEntityController<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> {
|
||||||
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
|
||||||
|
|
||||||
public static void show(CompanyCustomerFile saved, Window window) {
|
public static void show(CustomerFileViewModel item, Window window) {
|
||||||
CompanyCustomerFileViewModel model = new CompanyCustomerFileViewModel();
|
show(CompanyCustomerEvaluationFormFileWindowController.class, window, controller -> {
|
||||||
model.update(saved);
|
controller.fileViewModel = item;
|
||||||
show(model, window);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void show(CompanyCustomerFileViewModel viewModel, Window window) {
|
|
||||||
String key = viewModel.getClass().getName() + "-" + viewModel.getId().get();
|
|
||||||
if (toFront(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FxmlUtils.newLoaderAsyncWithRunLater("/ui/company/customer/customer_evaluation_form.fxml", null, loader -> {
|
|
||||||
CompanyCustomerEvaluationFormFileWindowController controller = loader.getController();
|
|
||||||
controller.viewModel = viewModel;
|
|
||||||
controller.show(loader, window, Modality.NONE, key);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void show(CompanyCustomerEvaluationFormFileVo saved, Window window) {
|
||||||
|
show(CompanyCustomerEvaluationFormFileViewModel.from(saved), window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(CompanyCustomerEvaluationFormFileViewModel viewModel, Window window) {
|
||||||
|
show(CompanyCustomerEvaluationFormFileWindowController.class, viewModel, window);
|
||||||
|
}
|
||||||
|
|
||||||
public Label idField;
|
public Label idField;
|
||||||
public TextField filePathField;
|
public TextField filePathField;
|
||||||
@@ -91,60 +106,66 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
public ScrollPane leftPane;
|
public ScrollPane leftPane;
|
||||||
public Label totalCreditScoreLabel;
|
public Label totalCreditScoreLabel;
|
||||||
|
|
||||||
private CompanyCustomerFileViewModel viewModel;
|
|
||||||
|
|
||||||
private final SimpleStringProperty catalogProperty = new SimpleStringProperty("");
|
|
||||||
private final SimpleStringProperty levelProperty = new SimpleStringProperty("");
|
|
||||||
private final SimpleIntegerProperty score1Property = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty score2Property = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty score3Property = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty score4Property = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty score5Property = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty creditLevelProperty = new SimpleIntegerProperty(-1);
|
|
||||||
private final SimpleIntegerProperty totalCreditScoreProperty = new SimpleIntegerProperty(-1);
|
private final SimpleIntegerProperty totalCreditScoreProperty = new SimpleIntegerProperty(-1);
|
||||||
|
|
||||||
private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
|
private CustomerFileViewModel fileViewModel;
|
||||||
|
|
||||||
|
private SimpleBooleanProperty changed = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
private CompletableFuture<CompanyCustomerEvaluationFormFile> loadedFuture;
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Autowired
|
@Autowired
|
||||||
private CompanyCustomerFileService companyCustomerFileService;
|
private CompanyCustomerEvaluationFormFileService evaluationFormFileService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show(Stage stage) {
|
public void show(Stage stage) {
|
||||||
super.show(stage);
|
super.show(stage);
|
||||||
stage.setFullScreen(false);
|
stage.setFullScreen(false);
|
||||||
// Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
|
|
||||||
//
|
|
||||||
// stage.setX(screenBounds.getMinX());
|
|
||||||
// stage.setY(screenBounds.getMinY());
|
|
||||||
// stage.setWidth(screenBounds.getWidth());
|
|
||||||
// stage.setHeight(screenBounds.getHeight());
|
|
||||||
//
|
|
||||||
// stage.isMaximized();
|
|
||||||
stage.setMaximized(true);
|
stage.setMaximized(true);
|
||||||
getTitle().set("客户评估表单");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShown(WindowEvent windowEvent) {
|
public void onShown(WindowEvent windowEvent) {
|
||||||
super.onShown(windowEvent);
|
super.onShown(windowEvent);
|
||||||
if (logger.isDebugEnabled()) {
|
getTitle().set("客户评估表单");
|
||||||
logger.debug("onShown");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
initializePane();
|
@Override
|
||||||
|
public ViewModelService<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> getViewModelService() {
|
||||||
|
return evaluationFormFileService;
|
||||||
|
}
|
||||||
|
|
||||||
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
@Override
|
||||||
int id = viewModel.getId().get();
|
protected void initializeData() {
|
||||||
CompanyCustomerFile customerFile = companyCustomerFileService.findById(id);
|
CompanyCustomerEvaluationFormFileViewModel viewModel = new CompanyCustomerEvaluationFormFileViewModel();
|
||||||
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileByCustomerFile(customerFile);
|
setViewModel(viewModel);
|
||||||
Platform.runLater(() -> update(formFile));
|
runAsync(() -> {
|
||||||
return formFile;
|
CompanyCustomerEvaluationFormFileVo item = getCachedBean(CompanyCustomerEvaluationFormFileService.class)
|
||||||
|
.findByCustomerFile(fileViewModel.getId().get());
|
||||||
|
viewModel.getId().set(item.getId());
|
||||||
|
updateViewModel(item);
|
||||||
|
super.initializeData();
|
||||||
|
Platform.runLater(this::initializePane);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateViewModel(CompanyCustomerEvaluationFormFileVo entity) {
|
||||||
|
super.updateViewModel(entity);
|
||||||
|
changed.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BooleanBinding createTabSkinChangedBindings() {
|
||||||
|
return viewModel.getChanged().or(fileViewModel.getChanged()).or(changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveTabSkins() {
|
||||||
|
save();
|
||||||
|
changed.setValue(false);
|
||||||
|
}
|
||||||
|
|
||||||
BiConsumer<ToggleGroup, String> stringRadioGroupUpdater = (group, newValue) -> {
|
BiConsumer<ToggleGroup, String> stringRadioGroupUpdater = (group, newValue) -> {
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
for (Toggle toggle : group.getToggles()) {
|
for (Toggle toggle : group.getToggles()) {
|
||||||
@@ -160,14 +181,15 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
changed.set(true);
|
||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
group.selectToggle(null);
|
group.selectToggle(null);
|
||||||
// Toggle first = group.getToggles().getFirst();
|
// Toggle first = group.getToggles().getFirst();
|
||||||
// first.setSelected(true);
|
// first.setSelected(true);
|
||||||
// first.setSelected(false);
|
// first.setSelected(false);
|
||||||
// RadioButton btn = (RadioButton) first;
|
// RadioButton btn = (RadioButton) first;
|
||||||
// btn.setText(newValue);
|
// btn.setText(newValue);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,9 +200,11 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}
|
}
|
||||||
String data = (String) toggle.getUserData();
|
String data = (String) toggle.getUserData();
|
||||||
property.set(data);
|
property.set(data);
|
||||||
|
changed.set(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
BiConsumer<ToggleGroup, Number> numberRadioGroupUpdater = (group, newValue) -> {
|
BiConsumer<ToggleGroup, Number> numberRadioGroupUpdater = (group, newValue) -> {
|
||||||
|
System.out.println("group = " + group + ", newValue = " + newValue);
|
||||||
String value = String.valueOf(newValue);
|
String value = String.valueOf(newValue);
|
||||||
for (Toggle toggle : group.getToggles()) {
|
for (Toggle toggle : group.getToggles()) {
|
||||||
String data = (String) toggle.getUserData();
|
String data = (String) toggle.getUserData();
|
||||||
@@ -191,12 +215,13 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
group.selectToggle(null);
|
group.selectToggle(null);
|
||||||
// Toggle first = group.getToggles().getFirst();
|
// Toggle first = group.getToggles().getFirst();
|
||||||
// first.setSelected(true);
|
// first.setSelected(true);
|
||||||
// first.setSelected(false);
|
// first.setSelected(false);
|
||||||
// RadioButton btn = (RadioButton) first;
|
// RadioButton btn = (RadioButton) first;
|
||||||
// btn.setText(String.valueOf(newValue));
|
// btn.setText(String.valueOf(newValue));
|
||||||
});
|
});
|
||||||
|
changed.set(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
BiConsumer<SimpleIntegerProperty, Toggle> numberPropertyUpdater = (property, toggle) -> {
|
BiConsumer<SimpleIntegerProperty, Toggle> numberPropertyUpdater = (property, toggle) -> {
|
||||||
@@ -206,9 +231,9 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}
|
}
|
||||||
String data = (String) toggle.getUserData();
|
String data = (String) toggle.getUserData();
|
||||||
property.set(Integer.parseInt(data));
|
property.set(Integer.parseInt(data));
|
||||||
|
changed.set(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int totalScoreToLevel(int score) {
|
int totalScoreToLevel(int score) {
|
||||||
if (score >= 200) {
|
if (score >= 200) {
|
||||||
return 4;
|
return 4;
|
||||||
@@ -223,63 +248,73 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean calcValid() {
|
boolean calcValid() {
|
||||||
if (creditLevelProperty.get() <= 0) {
|
if (viewModel.getCreditLevel().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!StringUtils.hasText(catalogProperty.get())) {
|
if (!StringUtils.hasText(viewModel.getCatalog().get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!StringUtils.hasText(levelProperty.get())) {
|
if (!StringUtils.hasText(viewModel.getLevel().get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (score1Property.get() <= 0) {
|
|
||||||
|
if (viewModel.getScore1().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (score2Property.get() <= 0) {
|
if (viewModel.getScore2().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (score3Property.get() <= 0) {
|
if (viewModel.getScore3().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (score4Property.get() <= 0) {
|
if (viewModel.getScore4().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (score5Property.get() <= 0) {
|
if (viewModel.getScore5().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (creditLevelProperty.get() <= 0) {
|
if (viewModel.getCreditLevel().get() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializePane() {
|
private void initializePane() {
|
||||||
|
setStatus("");
|
||||||
idField.textProperty().bind(viewModel.getId().asString());
|
idField.textProperty().bind(viewModel.getId().asString());
|
||||||
filePathField.textProperty().bind(viewModel.getFilePath());
|
filePathField.textProperty().bind(fileViewModel.getFilePath());
|
||||||
editFilePathField.textProperty().bind(viewModel.getEditFilePath());
|
editFilePathField.textProperty().bind(fileViewModel.getEditFilePath());
|
||||||
signDateField.valueProperty().bindBidirectional(viewModel.getSignDate());
|
String pattern = "yyyy-MM-dd";
|
||||||
validField.selectedProperty().bindBidirectional(viewModel.getValid());
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
signDateField.setConverter(new LocalDateStringConverter(formatter, null));
|
||||||
initializeRadioGroup(catalog, catalogProperty);
|
signDateField.valueProperty().bindBidirectional(fileViewModel.getSignDate());
|
||||||
initializeRadioGroup(level, levelProperty);
|
signDateField.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
changed.set(true);
|
||||||
initializeRadioGroup(score1, score1Property);
|
|
||||||
initializeRadioGroup(score2, score2Property);
|
|
||||||
initializeRadioGroup(score3, score3Property);
|
|
||||||
initializeRadioGroup(score4, score4Property);
|
|
||||||
initializeRadioGroup(score5, score5Property);
|
|
||||||
|
|
||||||
creditLevelProperty.addListener((observable, oldValue, newValue) -> {
|
|
||||||
numberRadioGroupUpdater.accept(creditLevel, newValue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SimpleIntegerProperty[] scores = new SimpleIntegerProperty[]{score1Property, score2Property, score3Property, score4Property, score5Property};
|
initializeRadioGroup(catalog, viewModel.getCatalog());
|
||||||
|
initializeRadioGroup(level, viewModel.getLevel());
|
||||||
|
|
||||||
|
initializeRadioGroup(score1, viewModel.getScore1());
|
||||||
|
initializeRadioGroup(score2, viewModel.getScore2());
|
||||||
|
initializeRadioGroup(score3, viewModel.getScore3());
|
||||||
|
initializeRadioGroup(score4, viewModel.getScore4());
|
||||||
|
initializeRadioGroup(score5, viewModel.getScore5());
|
||||||
|
|
||||||
|
// 信用等级
|
||||||
|
viewModel.getCreditLevel().addListener((observable, oldValue, newValue) -> {
|
||||||
|
numberRadioGroupUpdater.accept(creditLevel, newValue);
|
||||||
|
});
|
||||||
|
numberRadioGroupUpdater.accept(creditLevel, viewModel.getCreditLevel().get());
|
||||||
|
|
||||||
|
SimpleIntegerProperty[] scores = new SimpleIntegerProperty[]{viewModel.getScore1(), viewModel.getScore2(),
|
||||||
|
viewModel.getScore3(), viewModel.getScore4(), viewModel.getScore5()};
|
||||||
totalCreditScoreProperty.bind(Bindings.createIntegerBinding(() -> {
|
totalCreditScoreProperty.bind(Bindings.createIntegerBinding(() -> {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (SimpleIntegerProperty score : scores) {
|
for (SimpleIntegerProperty score : scores) {
|
||||||
total += score.get();
|
total += score.get();
|
||||||
}
|
}
|
||||||
creditLevelProperty.set(totalScoreToLevel(total));
|
viewModel.getCreditLevel().set(totalScoreToLevel(total));
|
||||||
return total;
|
return total;
|
||||||
}, scores));
|
}, scores));
|
||||||
|
|
||||||
@@ -287,70 +322,205 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
return "合计总分:" + score;
|
return "合计总分:" + score;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Bindings.createBooleanBinding(() -> {
|
Bindings.createBooleanBinding(this::calcValid, viewModel.getCatalog(), viewModel.getLevel(),
|
||||||
boolean valid = calcValid();
|
viewModel.getScore1(), viewModel.getScore2(), viewModel.getScore3(), viewModel.getScore4(),
|
||||||
viewModel.getValid().set(valid);
|
viewModel.getScore5(), viewModel.getCreditLevel())
|
||||||
return valid;
|
.addListener((observable, oldValue, newValue) -> {
|
||||||
}, catalogProperty, levelProperty, score1Property, score2Property, score3Property, score4Property, score5Property, creditLevelProperty).addListener(((observable, oldValue, newValue) -> {
|
fileViewModel.getValid().set(newValue);
|
||||||
logger.info("valid:{}", newValue);
|
changed.set(true);
|
||||||
}));
|
});
|
||||||
|
validField.selectedProperty().bindBidirectional(fileViewModel.getValid());
|
||||||
|
validField.setSelected(fileViewModel.getValid().getValue());
|
||||||
|
|
||||||
|
fileViewModel.getFilePath().addListener((observable, oldValue, newValue) -> {
|
||||||
imageView.imageProperty().bind(viewModel.getFilePath().map(path -> {
|
File file = new File(newValue);
|
||||||
if (FileUtils.withExtensions(path, FileUtils.PDF)) {
|
loadFile(file);
|
||||||
File pdfFile = new File(path);
|
});
|
||||||
try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) {
|
if (StringUtils.hasText(fileViewModel.getFilePath().get())) {
|
||||||
PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);
|
loadFile(new File(fileViewModel.getFilePath().get()));
|
||||||
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
|
}
|
||||||
|
|
||||||
// 获取 BufferedImage 的宽度和高度
|
|
||||||
int width = bufferedImage.getWidth();
|
|
||||||
int height = bufferedImage.getHeight();
|
|
||||||
WritableImage writableImage = new WritableImage(width, height);
|
|
||||||
PixelWriter pixelWriter = writableImage.getPixelWriter();
|
|
||||||
// 将 BufferedImage 的像素数据复制到 WritableImage
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
int argb = bufferedImage.getRGB(x, y);
|
|
||||||
pixelWriter.setArgb(x, y, argb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writableImage;
|
|
||||||
} catch (Exception e) {
|
|
||||||
setStatus(e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
File file = new File(path);
|
|
||||||
Image image = new Image(file.toURI().toString());
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
leftPane.widthProperty().addListener((observable, oldValue, newValue) -> {
|
leftPane.widthProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.setFitWidth(leftPane.getWidth());
|
imageView.setFitWidth(leftPane.getWidth());
|
||||||
imageView.setFitHeight(leftPane.getHeight());
|
imageView.setFitHeight(-1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
imageView.setFitWidth(leftPane.getWidth());
|
imageView.setFitWidth(leftPane.getWidth());
|
||||||
imageView.setFitHeight(leftPane.getHeight());
|
imageView.setFitHeight(-1);
|
||||||
|
|
||||||
imageView.setOnScroll(event -> {
|
imageView.setOnScroll(event -> {
|
||||||
System.out.println("event = " + event);
|
|
||||||
System.out.println("event.getDeltaY() = " + event.getDeltaY());
|
|
||||||
Bounds bounds = imageView.getBoundsInLocal();
|
Bounds bounds = imageView.getBoundsInLocal();
|
||||||
// Bounds latestBounds = (Bounds) imageView.getProperties().get("latestBounds");
|
|
||||||
// if (latestBounds != null) {
|
|
||||||
// double latestBoundsWidth = latestBounds.getWidth();
|
|
||||||
// }
|
|
||||||
// if (bounds.getWidth() < leftPane.getWidth()) {
|
|
||||||
imageView.setFitWidth(bounds.getWidth() + event.getDeltaY());
|
imageView.setFitWidth(bounds.getWidth() + event.getDeltaY());
|
||||||
// } else {
|
imageView.setFitHeight(-1);
|
||||||
imageView.setFitHeight(bounds.getHeight() + event.getDeltaY());
|
event.consume();
|
||||||
// }
|
});
|
||||||
|
|
||||||
|
imageView.setOnMouseClicked(event -> {
|
||||||
|
System.out.println("imageView.getFitWidth() = " + imageView.getFitWidth());
|
||||||
|
System.out.println("imageView.getFitHeight() = " + imageView.getFitHeight());
|
||||||
|
|
||||||
|
System.out.println("leftPane.getWidth() = " + leftPane.getWidth());
|
||||||
|
System.out.println("leftPane.getViewportBounds().getWidth() = " + leftPane.getViewportBounds().getWidth());
|
||||||
|
|
||||||
|
if (event.getClickCount() == 2 && event.getButton() == MouseButton.PRIMARY) {
|
||||||
|
Image image = imageView.getImage();
|
||||||
|
if (image != null) {
|
||||||
|
System.out.println("image.getWidth() = " + image.getWidth());
|
||||||
|
if (image.getWidth() > imageView.getFitWidth()) {
|
||||||
|
imageView.setFitWidth(image.getWidth());
|
||||||
|
} else {
|
||||||
|
imageView.setFitWidth(leftPane.getWidth());
|
||||||
|
}
|
||||||
|
imageView.setFitHeight(-1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFile(File file) {
|
||||||
|
setStatus("文件" + file.getAbsolutePath() + " 加载中...");
|
||||||
|
if (FileUtils.withExtensions(file.getName(), FileUtils.PDF)) {
|
||||||
|
loadPdf(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Image image = new Image(file.toURI().toString(), true);
|
||||||
|
imageView.setImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPdf(File pdfFile) {
|
||||||
|
// 绘制文字, 等待加载
|
||||||
|
// 创建画布并绘制备用占位文字
|
||||||
|
javafx.scene.canvas.Canvas canvas = new javafx.scene.canvas.Canvas(leftPane.getWidth(), leftPane.getHeight());
|
||||||
|
GraphicsContext gc = canvas.getGraphicsContext2D();
|
||||||
|
gc.setFill(javafx.scene.paint.Color.RED);
|
||||||
|
var h1 = javafx.scene.text.Font.font("Microsoft YaHei", FontWeight.BOLD, 24);
|
||||||
|
var h2 = javafx.scene.text.Font.font("Microsoft YaHei", FontWeight.NORMAL, 18);
|
||||||
|
gc.setFont(h1);
|
||||||
|
gc.fillText(fileViewModel.getType().get().name(), 50, 100);
|
||||||
|
|
||||||
|
Runnable updateImage = () -> {
|
||||||
|
WritableImage writableImage = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
|
||||||
|
// 将画布内容转为图像
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
canvas.snapshot(null, writableImage);
|
||||||
|
imageView.setImage(writableImage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
runAsync(() -> {
|
||||||
|
CustomerFileTypeLocalVo localVo = getCachedBean(CompanyCustomerFileTypeService.class).findByLocaleAndType(getLocale(), fileViewModel.getType().get());
|
||||||
|
gc.setFill(Color.WHITE);
|
||||||
|
// 覆盖 fileViewModel.getType() 文字
|
||||||
|
gc.fillRect(0, 55, canvas.getWidth(), 55);
|
||||||
|
|
||||||
|
// 绘制 文件类型
|
||||||
|
gc.setFill(javafx.scene.paint.Color.RED);
|
||||||
|
gc.setFont(h1);
|
||||||
|
gc.fillText(localVo.getValue(), 50, 100);
|
||||||
|
|
||||||
|
updateImage.run();
|
||||||
|
});
|
||||||
|
|
||||||
|
gc.setStroke(javafx.scene.paint.Color.BLACK);
|
||||||
|
gc.setFont(h2);
|
||||||
|
gc.strokeText("正在加载文件..." + pdfFile.getName(), 50, 150);
|
||||||
|
updateImage.run();
|
||||||
|
|
||||||
|
runAsync(() -> {
|
||||||
|
|
||||||
|
//FileSystemUtils.
|
||||||
|
long fileSize = pdfFile.length();
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try (java.io.FileInputStream fis = new java.io.FileInputStream(pdfFile);
|
||||||
|
java.io.BufferedInputStream bis = new java.io.BufferedInputStream(fis)) {
|
||||||
|
|
||||||
|
bytes = new byte[(int) fileSize];
|
||||||
|
int totalBytesRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
byte[] buffer = new byte[8192]; // 8KB buffer
|
||||||
|
|
||||||
|
while ((bytesRead = bis.read(buffer)) != -1) {
|
||||||
|
System.arraycopy(buffer, 0, bytes, totalBytesRead, bytesRead);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
double progress = (double) totalBytesRead / fileSize * 100;
|
||||||
|
final String status = String.format("正在加载文件... %s (%.1f%%)",
|
||||||
|
pdfFile.getName(), progress);
|
||||||
|
|
||||||
|
gc.setFill(Color.WHITE);
|
||||||
|
gc.fillRect(0, 200, canvas.getWidth(), 80);
|
||||||
|
|
||||||
|
|
||||||
|
gc.setFill(Color.BLACK);
|
||||||
|
gc.setFont(h2);
|
||||||
|
gc.fillText(status, 50, 250);
|
||||||
|
|
||||||
|
|
||||||
|
// 绘制进度条背景
|
||||||
|
gc.setFill(Color.LIGHTGRAY);
|
||||||
|
gc.fillRect(50, 270, 400, 20);
|
||||||
|
|
||||||
|
// 绘制进度条
|
||||||
|
gc.setFill(Color.GREEN);
|
||||||
|
gc.fillRect(50, 270, 400 * (totalBytesRead / (double) fileSize), 20);
|
||||||
|
|
||||||
|
// 绘制进度条边框
|
||||||
|
gc.setStroke(Color.BLACK);
|
||||||
|
gc.setLineWidth(1);
|
||||||
|
gc.strokeRect(50, 270, 400, 20);
|
||||||
|
|
||||||
|
|
||||||
|
updateImage.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.setFill(Color.BLACK);
|
||||||
|
gc.setFont(h2);
|
||||||
|
gc.fillText("Loading file: " + pdfFile.getName() + ", size: " + bytes.length + " bytes", 50, 320);
|
||||||
|
updateImage.run();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (PDDocument pdDocument = Loader.loadPDF(bytes)) {
|
||||||
|
gc.setFill(Color.BLACK);
|
||||||
|
gc.setFont(h2);
|
||||||
|
gc.fillText("PDF has " + pdDocument.getNumberOfPages() + " pages", 50, 380);
|
||||||
|
updateImage.run();
|
||||||
|
|
||||||
|
PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);
|
||||||
|
|
||||||
|
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300);
|
||||||
|
|
||||||
|
// 获取 BufferedImage 的宽度和高度
|
||||||
|
int width = bufferedImage.getWidth();
|
||||||
|
int height = bufferedImage.getHeight();
|
||||||
|
canvas.resize(width, height);
|
||||||
|
GraphicsContext graphic = canvas.getGraphicsContext2D();
|
||||||
|
|
||||||
|
WritableImage writableImage1 = new WritableImage(width, height);
|
||||||
|
|
||||||
|
PixelWriter pixelWriter = writableImage1.getPixelWriter();
|
||||||
|
// 将 BufferedImage 的像素数据复制到 WritableImage
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int argb = bufferedImage.getRGB(x, y);
|
||||||
|
pixelWriter.setArgb(x, y, argb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setStatus();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
imageView.setImage(writableImage1);
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
setStatus(e.getMessage());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,6 +531,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
stringPropertyUpdater.accept(property, newValue);
|
stringPropertyUpdater.accept(property, newValue);
|
||||||
});
|
});
|
||||||
|
stringRadioGroupUpdater.accept(toggleGroup, property.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeRadioGroup(ToggleGroup toggleGroup, SimpleIntegerProperty property) {
|
private void initializeRadioGroup(ToggleGroup toggleGroup, SimpleIntegerProperty property) {
|
||||||
@@ -371,73 +542,8 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
numberPropertyUpdater.accept(property, newValue);
|
numberPropertyUpdater.accept(property, newValue);
|
||||||
});
|
});
|
||||||
|
numberRadioGroupUpdater.accept(toggleGroup, property.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update(CompanyCustomerEvaluationFormFile formFile) {
|
|
||||||
|
|
||||||
|
|
||||||
viewModel.update(formFile.getCustomerFile());
|
|
||||||
|
|
||||||
// formFile.getScoreTemplateVersion();
|
|
||||||
|
|
||||||
catalogProperty.set(formFile.getCatalog());
|
|
||||||
levelProperty.set(formFile.getLevel());
|
|
||||||
score1Property.set(formFile.getScore1());
|
|
||||||
score2Property.set(formFile.getScore2());
|
|
||||||
score3Property.set(formFile.getScore3());
|
|
||||||
score4Property.set(formFile.getScore4());
|
|
||||||
score5Property.set(formFile.getScore5());
|
|
||||||
creditLevelProperty.set(formFile.getCreditLevel());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSaveAction(ActionEvent event) {
|
|
||||||
boolean modified = false;
|
|
||||||
|
|
||||||
int id = viewModel.getId().get();
|
|
||||||
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileById(id);
|
|
||||||
CompanyCustomerFile customerFile = formFile.getCustomerFile();
|
|
||||||
|
|
||||||
if (!Objects.equals(catalogProperty.get(), formFile.getCatalog())) {
|
|
||||||
formFile.setCatalog(catalogProperty.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(levelProperty.get(), formFile.getLevel())) {
|
|
||||||
formFile.setLevel(levelProperty.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(score1Property.get(), formFile.getScore1())) {
|
|
||||||
formFile.setScore1(score1Property.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(score2Property.get(), formFile.getScore2())) {
|
|
||||||
formFile.setScore2(score2Property.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(score3Property.get(), formFile.getScore3())) {
|
|
||||||
formFile.setScore3(score3Property.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(score4Property.get(), formFile.getScore4())) {
|
|
||||||
formFile.setScore4(score4Property.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(score5Property.get(), formFile.getScore5())) {
|
|
||||||
formFile.setScore5(score5Property.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(creditLevelProperty.get(), formFile.getCreditLevel())) {
|
|
||||||
formFile.setCreditLevel(creditLevelProperty.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (viewModel.copyTo(customerFile)) {
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modified) {
|
|
||||||
companyCustomerFileService.save(formFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
|
||||||
|
|
||||||
import static com.ecep.contract.util.ExcelUtils.setCellValue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.ecep.contract.service.*;
|
|
||||||
import com.ecep.contract.util.CompanyUtils;
|
|
||||||
import org.apache.poi.ss.usermodel.Sheet;
|
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
|
||||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
|
||||||
import org.apache.poi.ss.util.CellRangeAddress;
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.ecep.contract.CompanyCustomerFileType;
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.model.CloudTyc;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
|
|
||||||
import com.ecep.contract.model.CompanyCustomerFile;
|
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
public class CompanyCustomerEvaluationFormUpdateTask extends Task<Object> {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class);
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
private CompanyCustomer customer;
|
|
||||||
@Setter
|
|
||||||
private CompanyService companyService;
|
|
||||||
private CompanyContactService companyContactService;
|
|
||||||
|
|
||||||
private CompanyCustomerService companyCustomerService;
|
|
||||||
@Setter
|
|
||||||
private CompanyCustomerFileService companyCustomerFileService;
|
|
||||||
|
|
||||||
private CompanyService getCompanyService() {
|
|
||||||
if (companyService == null) {
|
|
||||||
companyService = SpringApp.getBean(CompanyService.class);
|
|
||||||
}
|
|
||||||
return companyService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompanyCustomerService getCompanyCustomerService() {
|
|
||||||
if (companyCustomerService == null) {
|
|
||||||
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
|
|
||||||
}
|
|
||||||
return companyCustomerService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompanyContactService getCompanyContactService() {
|
|
||||||
if (companyContactService == null) {
|
|
||||||
companyContactService = SpringApp.getBean(CompanyContactService.class);
|
|
||||||
}
|
|
||||||
return companyContactService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompanyCustomerFileService getCompanyCustomerFileService() {
|
|
||||||
if (companyCustomerFileService == null) {
|
|
||||||
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
|
|
||||||
}
|
|
||||||
return companyCustomerFileService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object call() throws Exception {
|
|
||||||
try {
|
|
||||||
updateEvaluationForm();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
updateMessage(ex.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getEvaluationFormTemplate() {
|
|
||||||
return getCompanyCustomerFileService().getEvaluationFormTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateEvaluationForm() {
|
|
||||||
if (!StringUtils.hasText(customer.getPath())) {
|
|
||||||
updateMessage("供应商目录未设置,请先设置供应商目录");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File template = getEvaluationFormTemplate();
|
|
||||||
if (template == null) {
|
|
||||||
updateMessage("评价表模板文件未设置,请先设置评价表模板文件");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!template.exists()) {
|
|
||||||
updateMessage("评价表模板文件 " + template.getAbsolutePath() + " 不存在,请检查");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File dir = new File(customer.getPath());
|
|
||||||
String template_file_name = template.getName();
|
|
||||||
File destFile = new File(dir, template_file_name);
|
|
||||||
|
|
||||||
if (destFile.exists()) {
|
|
||||||
updateMessage("表单文件已经存在," + destFile.getName());
|
|
||||||
try (
|
|
||||||
InputStream inp = new FileInputStream(destFile);
|
|
||||||
Workbook wb = WorkbookFactory.create(inp)) {
|
|
||||||
updateEvaluationForm(wb, destFile);
|
|
||||||
updateMessage("评价表已更新");
|
|
||||||
} catch (Exception e) {
|
|
||||||
updateMessage(e.getMessage());
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateMessage("根据模板 " + template_file_name + " 创建表单 " + destFile.getName());
|
|
||||||
try (
|
|
||||||
InputStream inp = new FileInputStream(template);
|
|
||||||
Workbook wb = WorkbookFactory.create(inp)) {
|
|
||||||
updateEvaluationForm(wb, destFile);
|
|
||||||
updateMessage("评价表已创建");
|
|
||||||
CompanyCustomerFile customerFile = new CompanyCustomerFile();
|
|
||||||
customerFile.setCustomer(customer);
|
|
||||||
customerFile.setFilePath(destFile.getAbsolutePath());
|
|
||||||
customerFile.setType(CompanyCustomerFileType.General);
|
|
||||||
save(customerFile);
|
|
||||||
} catch (Exception e) {
|
|
||||||
updateMessage(e.getMessage());
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateProgress(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void save(CompanyCustomerFile customerFile) {
|
|
||||||
getCompanyCustomerFileService().save(customerFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新客户评估表,依据模板创建,如果已经存在生成的文件,则更新评估表
|
|
||||||
*
|
|
||||||
* @param wb work book
|
|
||||||
* @param destFile 目标文件
|
|
||||||
*/
|
|
||||||
public void updateEvaluationForm(
|
|
||||||
Workbook wb, File destFile) throws IOException {
|
|
||||||
Company company = customer.getCompany();
|
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
customer.setCompany(company);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sheet sheet = wb.getSheetAt(0);
|
|
||||||
updateSheet(company, sheet);
|
|
||||||
// 输出到文件
|
|
||||||
try (OutputStream fileOut = new FileOutputStream(destFile)) {
|
|
||||||
wb.write(fileOut);
|
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
updateMessage("写评估表时发生文件错误,请检查评估表是否被打开中");
|
|
||||||
updateMessage(e.getMessage());
|
|
||||||
logger.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSheet(Company company, Sheet sheet) {
|
|
||||||
setCellValue(sheet, "B3", "客户编号:" + CompanyUtils.formatCompanyVendorId(customer.getId()));
|
|
||||||
setCellValue(sheet, "B4", "客户名称:" + company.getName());
|
|
||||||
|
|
||||||
LocalDate suggestDate = getCompanyCustomerFileService().getNextSignDate(customer, (level, msg) -> {
|
|
||||||
updateMessage(" - " + msg);
|
|
||||||
});
|
|
||||||
if (suggestDate == null) {
|
|
||||||
suggestDate = LocalDate.now();
|
|
||||||
}
|
|
||||||
setCellValue(sheet, "H3", "评定时间:" + suggestDate);
|
|
||||||
setCellValue(sheet, "H4", "统一社会信用代码:");
|
|
||||||
setCellValue(sheet, "H5", company.getUniscid());
|
|
||||||
// 注册所属地
|
|
||||||
setCellValue(sheet, "B5", "注册所属地:" + company.getDistrict());
|
|
||||||
// 经营状态
|
|
||||||
setCellValue(sheet, "D6", "经营状态:" + company.getEntStatus());
|
|
||||||
// 成立日期
|
|
||||||
setCellValue(sheet, "H6", "成立日期:" + company.getSetupDate());
|
|
||||||
// 所属行业
|
|
||||||
setCellValue(sheet, "D7", "所属行业:" + company.getIndustry());
|
|
||||||
setCellValue(sheet, "D8",
|
|
||||||
"注册资金:" + company.getRegisteredCapital() + " " + company.getRegisteredCapitalCurrency());
|
|
||||||
// 企业类型
|
|
||||||
setCellValue(sheet, "H10", "企业类型:" + company.getEntType());
|
|
||||||
// 天眼评分
|
|
||||||
CloudTycService cloudTycService = SpringApp.getBean(CloudTycService.class);
|
|
||||||
CloudTyc cloudTyc = cloudTycService.getOrCreateCloudTyc(company);
|
|
||||||
setCellValue(sheet, "D10", "天眼评分:" + (cloudTyc.getScore() > 0 ? cloudTyc.getScore() : ""));
|
|
||||||
|
|
||||||
// 检索评估表
|
|
||||||
List<CompanyCustomerEvaluationFormFile> evaluationFormFiles = getCompanyCustomerFileService()
|
|
||||||
.findAllCustomerEvaluationFormFiles(customer);
|
|
||||||
List<CompanyCustomerEvaluationFormFile> filteredList = evaluationFormFiles.stream()
|
|
||||||
.filter(v -> {
|
|
||||||
CompanyCustomerFile file = v.getCustomerFile();
|
|
||||||
return file.getSignDate() != null && file.isValid();
|
|
||||||
})
|
|
||||||
.sorted(Comparator.comparing(v -> v.getCustomerFile().getSignDate()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (filteredList.isEmpty()) {
|
|
||||||
setCellValue(sheet, "C40", "首次评价");
|
|
||||||
try {
|
|
||||||
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:K40"));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setCellValue(sheet, "C40", "评价日期");
|
|
||||||
try {
|
|
||||||
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:D40"));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
setCellValue(sheet, "E40", "经济指标");
|
|
||||||
setCellValue(sheet, "F40", "综合指标");
|
|
||||||
setCellValue(sheet, "G40", "资信等级");
|
|
||||||
String[] CreditLevelTitles = new String[] { "-", "差★", " 一般★★", " 较好★★★", " 好★★★★", " " };
|
|
||||||
int baseRow = 40;
|
|
||||||
for (CompanyCustomerEvaluationFormFile form : filteredList) {
|
|
||||||
CompanyCustomerFile customerFile = form.getCustomerFile();
|
|
||||||
setCellValue(sheet, baseRow, 2, String.valueOf(customerFile.getSignDate()));
|
|
||||||
setCellValue(sheet, baseRow, 4, form.getCatalog());
|
|
||||||
setCellValue(sheet, baseRow, 5, form.getLevel());
|
|
||||||
if (form.getCreditLevel() == null) {
|
|
||||||
setCellValue(sheet, baseRow, 6, "-");
|
|
||||||
} else {
|
|
||||||
setCellValue(sheet, baseRow, 6, CreditLevelTitles[form.getCreditLevel()]);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
sheet.addMergedRegion(new CellRangeAddress(baseRow, baseRow, 2, 3));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
baseRow++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,50 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
package com.ecep.contract.controller.customer;
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
import static com.ecep.contract.util.ExcelUtils.setCellValue;
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
|
||||||
import com.ecep.contract.model.*;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerEntityService;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerFileService;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
|
||||||
import com.ecep.contract.task.Tasker;
|
|
||||||
import com.ecep.contract.util.UITools;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.apache.poi.ss.SpreadsheetVersion;
|
|
||||||
import org.apache.poi.ss.usermodel.*;
|
|
||||||
import org.apache.poi.ss.util.AreaReference;
|
|
||||||
import org.apache.poi.ss.util.CellRangeAddress;
|
|
||||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
|
||||||
import org.apache.poi.xssf.usermodel.XSSFTable;
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.ecep.contract.util.ExcelUtils.*;
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.PrintSetup;
|
||||||
|
import org.apache.poi.ss.usermodel.Row;
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||||
|
import org.apache.poi.ss.util.AreaReference;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFTable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerEntityService;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerFileService;
|
||||||
|
import com.ecep.contract.service.CustomerService;
|
||||||
|
import com.ecep.contract.task.Tasker;
|
||||||
|
import com.ecep.contract.util.ExcelUtils;
|
||||||
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vo.CompanyCustomerEntityVo;
|
||||||
|
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
||||||
|
import com.ecep.contract.vo.CustomerFileVo;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerExportExcelTasker.class);
|
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerExportExcelTasker.class);
|
||||||
@@ -35,13 +52,14 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
|||||||
@Setter
|
@Setter
|
||||||
File destFile;
|
File destFile;
|
||||||
|
|
||||||
CompanyCustomerService customerService;
|
CustomerService customerService;
|
||||||
CompanyCustomerEntityService customerEntityService;
|
CompanyCustomerEntityService customerEntityService;
|
||||||
CompanyCustomerFileService customerFileService;
|
CompanyCustomerFileService customerFileService;
|
||||||
|
CompanyCustomerEvaluationFormFileService customerEvaluationFormFileService;
|
||||||
|
|
||||||
CompanyCustomerService getCustomerService() {
|
CustomerService getCustomerService() {
|
||||||
if (customerService == null)
|
if (customerService == null)
|
||||||
customerService = getBean(CompanyCustomerService.class);
|
customerService = getBean(CustomerService.class);
|
||||||
return customerService;
|
return customerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +75,12 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
|||||||
return customerEntityService;
|
return customerEntityService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompanyCustomerEvaluationFormFileService getCustomerEvaluationFormFileService() {
|
||||||
|
if (customerEvaluationFormFileService == null)
|
||||||
|
customerEvaluationFormFileService = getBean(CompanyCustomerEvaluationFormFileService.class);
|
||||||
|
return customerEvaluationFormFileService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object execute(MessageHolder holder) throws Exception {
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
if (destFile.exists()) {
|
if (destFile.exists()) {
|
||||||
@@ -94,39 +118,33 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
|||||||
setCellValue(sheet, "A19", "Build by CMS @ " + MyDateTimeUtils.format(LocalDateTime.now()));
|
setCellValue(sheet, "A19", "Build by CMS @ " + MyDateTimeUtils.format(LocalDateTime.now()));
|
||||||
|
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
for (CompanyCustomer customer : getCustomerService().findAll(null, Pageable.unpaged())) {
|
for (CustomerVo customer : getCustomerService().findAll(null, Pageable.unpaged())) {
|
||||||
Company company = customer.getCompany();
|
Integer companyId = customer.getCompanyId();
|
||||||
|
;
|
||||||
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
if (company == null) {
|
if (company == null) {
|
||||||
holder.warn("客户 #" + customer.getId() + " 不存在对应公司");
|
holder.warn("客户 #" + customer.getId() + " 不存在对应公司");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
// VO类不需要延迟加载代理,直接使用即可
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate devDate = null;
|
LocalDate devDate = null;
|
||||||
List<CompanyCustomerEntity> entities = getCustomerEntityService().findAllByCustomer(customer);
|
List<CompanyCustomerEntityVo> entities = getCustomerEntityService().findAllByCustomer(customer);
|
||||||
for (CompanyCustomerEntity entity : entities) {
|
for (CompanyCustomerEntityVo entity : entities) {
|
||||||
if (devDate == null || devDate.isAfter(entity.getDevelopDate())) {
|
if (devDate == null || devDate.isAfter(entity.getDevelopDate())) {
|
||||||
devDate = entity.getDevelopDate();
|
devDate = entity.getDevelopDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompanyCustomerEvaluationFormFile evaluationFormFile = getCustomerFileService()
|
CustomerFileVo customerFile = getCustomerFileService()
|
||||||
.findAllCustomerEvaluationFormFiles(customer).stream().filter(v -> {
|
.findAllByCustomer(customer).stream().filter(v -> v.isValid())
|
||||||
CompanyCustomerFile customerFile = v.getCustomerFile();
|
.max(Comparator.comparing(v -> v.getSignDate())).orElse(null);
|
||||||
if (customerFile == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return customerFile.isValid();
|
|
||||||
}).max(Comparator.comparing(v -> v.getCustomerFile().getSignDate())).orElse(null);
|
|
||||||
|
|
||||||
if (evaluationFormFile == null) {
|
if (customerFile == null) {
|
||||||
holder.warn(company.getName() + " 未匹配的客户评估");
|
holder.warn(company.getName() + " 未匹配的客户评估");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompanyCustomerFile customerFile = evaluationFormFile.getCustomerFile();
|
|
||||||
if (devDate != null && devDate.isAfter(customerFile.getSignDate())) {
|
if (devDate != null && devDate.isAfter(customerFile.getSignDate())) {
|
||||||
holder.debug(company.getName() + " 最新评估日期早于客户开发日期,评估日期:" + customerFile.getSignDate() + ", 开发日期:"
|
holder.debug(company.getName() + " 最新评估日期早于客户开发日期,评估日期:" + customerFile.getSignDate() + ", 开发日期:"
|
||||||
+ devDate);
|
+ devDate);
|
||||||
@@ -137,12 +155,12 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
|||||||
if (rowIndex > 11) {
|
if (rowIndex > 11) {
|
||||||
// 插入行,并复制行的格式
|
// 插入行,并复制行的格式
|
||||||
sheet.shiftRows(rowIndex + 3, sheet.getLastRowNum(), 1);
|
sheet.shiftRows(rowIndex + 3, sheet.getLastRowNum(), 1);
|
||||||
Row templateRow = getRow(sheet, rowIndex + 2, true);
|
Row templateRow = ExcelUtils.getRow(sheet, rowIndex + 2, true);
|
||||||
Row newRow = getRow(sheet, rowIndex + 3, true);
|
Row newRow = ExcelUtils.getRow(sheet, rowIndex + 3, true);
|
||||||
if (templateRow != null && newRow != null) {
|
if (templateRow != null && newRow != null) {
|
||||||
for (int i = 0; i < templateRow.getLastCellNum(); i++) {
|
for (int i = 0; i < templateRow.getLastCellNum(); i++) {
|
||||||
Cell templateCell = templateRow.getCell(i);
|
Cell templateCell = templateRow.getCell(i);
|
||||||
Cell newCell = getCell(newRow, i, true);
|
Cell newCell = ExcelUtils.getCell(newRow, i, true);
|
||||||
if (templateCell != null && newCell != null) {
|
if (templateCell != null && newCell != null) {
|
||||||
newCell.setCellStyle(templateCell.getCellStyle());
|
newCell.setCellStyle(templateCell.getCellStyle());
|
||||||
}
|
}
|
||||||
@@ -150,7 +168,9 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row row = getRow(sheet, rowIndex + 3, true);
|
Row row = ExcelUtils.getRow(sheet, rowIndex + 3, true);
|
||||||
|
CompanyCustomerEvaluationFormFileVo evaluationFormFile = getCustomerEvaluationFormFileService()
|
||||||
|
.findByCustomerFile(customerFile);
|
||||||
|
|
||||||
setCellValue(row, 0, rowIndex);
|
setCellValue(row, 0, rowIndex);
|
||||||
setCellValue(row, 1, company.getName());
|
setCellValue(row, 1, company.getName());
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ package com.ecep.contract.controller.customer;
|
|||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
import com.ecep.contract.controller.table.cell.CompanyTableCell;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
import com.ecep.contract.controller.table.cell.CustomerCatalogTableCell;
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
|
import com.ecep.contract.service.CustomerCatalogService;
|
||||||
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
|
||||||
public class CompanyCustomerManagerSkin
|
public class CompanyCustomerManagerSkin
|
||||||
extends
|
extends
|
||||||
AbstEntityManagerSkin<CompanyCustomer, CompanyCustomerViewModel, CompanyCustomerManagerSkin, CompanyCustomerManagerWindowController> {
|
AbstEntityManagerSkin<CustomerVo, CompanyCustomerViewModel, CompanyCustomerManagerSkin, CompanyCustomerManagerWindowController> {
|
||||||
|
|
||||||
public CompanyCustomerManagerSkin(CompanyCustomerManagerWindowController controller) {
|
public CompanyCustomerManagerSkin(CompanyCustomerManagerWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -22,7 +25,7 @@ public class CompanyCustomerManagerSkin
|
|||||||
return getBean(CompanyService.class);
|
return getBean(CompanyService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompanyCustomerService getCompanyCustomerService() {
|
public CustomerService getCompanyCustomerService() {
|
||||||
return controller.getViewModelService();
|
return controller.getViewModelService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +33,10 @@ public class CompanyCustomerManagerSkin
|
|||||||
public void initializeTable() {
|
public void initializeTable() {
|
||||||
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
|
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
|
||||||
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
|
controller.companyColumn.setCellFactory(CompanyTableCell.forTableColumn(getCompanyService()));
|
||||||
|
|
||||||
|
controller.catalogColumn.setCellValueFactory(param -> param.getValue().getCatalog());
|
||||||
|
controller.catalogColumn.setCellFactory(CustomerCatalogTableCell.forTableColumn(getBean(CustomerCatalogService.class)));
|
||||||
|
|
||||||
controller.developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
|
controller.developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
|
||||||
controller.pathColumn.setCellValueFactory(param -> param.getValue().getPath());
|
controller.pathColumn.setCellValueFactory(param -> param.getValue().getPath());
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import java.time.LocalDate;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
@@ -15,12 +17,9 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
||||||
|
|
||||||
@@ -45,23 +44,25 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/company/customer/customer_manager.fxml")
|
@FxmlPath("/ui/company/customer/customer_manager.fxml")
|
||||||
public class CompanyCustomerManagerWindowController
|
public class CompanyCustomerManagerWindowController
|
||||||
extends AbstManagerWindowController<CompanyCustomer, CompanyCustomerViewModel, CompanyCustomerManagerSkin> {
|
extends AbstManagerWindowController<CustomerVo, CompanyCustomerViewModel, CompanyCustomerManagerSkin> {
|
||||||
|
|
||||||
// columns
|
// columns
|
||||||
public TableColumn<CompanyCustomerViewModel, Number> idColumn;
|
public TableColumn<CompanyCustomerViewModel, Number> idColumn;
|
||||||
public TableColumn<CompanyCustomerViewModel, Company> companyColumn;
|
/**
|
||||||
public TableColumn<CompanyCustomerViewModel, String> catalogColumn;
|
* 客户所属公司,Company
|
||||||
|
*/
|
||||||
|
public TableColumn<CompanyCustomerViewModel, Integer> companyColumn;
|
||||||
|
public TableColumn<CompanyCustomerViewModel, Integer> catalogColumn;
|
||||||
public TableColumn<CompanyCustomerViewModel, LocalDate> developDateColumn;
|
public TableColumn<CompanyCustomerViewModel, LocalDate> developDateColumn;
|
||||||
public TableColumn<CompanyCustomerViewModel, String> pathColumn;
|
public TableColumn<CompanyCustomerViewModel, String> pathColumn;
|
||||||
public TableColumn<CompanyCustomerViewModel, String> createdColumn;
|
public TableColumn<CompanyCustomerViewModel, String> createdColumn;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompanyCustomerService getViewModelService() {
|
public CustomerService getViewModelService() {
|
||||||
return getCachedBean(CompanyCustomerService.class);
|
return getCachedBean(CustomerService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CompanyService getCompanyService() {
|
private CompanyService getCompanyService() {
|
||||||
return getCachedBean(CompanyService.class);
|
return getCachedBean(CompanyService.class);
|
||||||
}
|
}
|
||||||
@@ -116,19 +117,16 @@ public class CompanyCustomerManagerWindowController
|
|||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
Pageable pageRequest = PageRequest.ofSize(50);
|
Pageable pageRequest = PageRequest.ofSize(50);
|
||||||
while (!canceled.get()) {
|
while (!canceled.get()) {
|
||||||
Page<CompanyCustomer> page = getViewModelService().findAll(null, pageRequest);
|
Page<CustomerVo> page = getViewModelService().findAll(null, pageRequest);
|
||||||
int index = page.getNumber() * page.getSize();
|
int index = page.getNumber() * page.getSize();
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (CompanyCustomer companyCustomer : page) {
|
for (CustomerVo companyCustomer : page) {
|
||||||
if (canceled.get()) {
|
if (canceled.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Company company = companyCustomer.getCompany();
|
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
CompanyVo company = getCompanyService().findById(companyCustomer.getCompanyId());
|
||||||
String prefix = (index + i) + "/" + page.getTotalElements() + ", " + company.getName() + "> ";
|
String prefix = (index + i) + "/" + page.getTotalElements() + ", " + company.getName() + "> ";
|
||||||
getViewModelService().reBuildingFiles(companyCustomer, (level, msg) -> {
|
getViewModelService().reBuildingFiles(companyCustomer, (level, msg) -> {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.service.CompanyContactService;
|
import com.ecep.contract.service.CompanyContactService;
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
|
||||||
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
@@ -36,16 +36,14 @@ import javafx.stage.WindowEvent;
|
|||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/company/customer/customer.fxml")
|
@FxmlPath("/ui/company/customer/customer.fxml")
|
||||||
public class CompanyCustomerWindowController extends AbstEntityController<CompanyCustomer, CompanyCustomerViewModel> {
|
public class CompanyCustomerWindowController extends AbstEntityController<CustomerVo, CompanyCustomerViewModel> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerWindowController.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示界面
|
* 显示界面
|
||||||
*/
|
*/
|
||||||
public static void show(CompanyCustomer customer, Window window) {
|
public static void show(CustomerVo customer, Window window) {
|
||||||
CompanyCustomerViewModel viewModel = new CompanyCustomerViewModel();
|
show(CompanyCustomerWindowController.class, CompanyCustomerViewModel.from(customer), window);
|
||||||
viewModel.update(customer);
|
|
||||||
show(CompanyCustomerWindowController.class, viewModel, window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tab baseInfoTab;
|
public Tab baseInfoTab;
|
||||||
@@ -71,29 +69,26 @@ public class CompanyCustomerWindowController extends AbstEntityController<Compan
|
|||||||
@Override
|
@Override
|
||||||
public void show(Stage stage) {
|
public void show(Stage stage) {
|
||||||
super.show(stage);
|
super.show(stage);
|
||||||
getTitle().bind(viewModel.getCompany().map(company -> {
|
getTitle().bind(viewModel.getCompany().map(companyId -> {
|
||||||
if (company == null) {
|
if (companyId == null) {
|
||||||
return "-";
|
return "-";
|
||||||
}
|
}
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
company = getCompanyService().findById(company.getId());
|
|
||||||
viewModel.getCompany().set(company);
|
|
||||||
}
|
|
||||||
return getMessage("ui.customer.title", String.valueOf(viewModel.getId().get()), company.getName());
|
return getMessage("ui.customer.title", String.valueOf(viewModel.getId().get()), company.getName());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void registerTabSkins() {
|
protected void registerTabSkins() {
|
||||||
registerTabSkin(baseInfoTab, tab -> new CompanyCustomerTabSkinBase(this));
|
registerTabSkin(baseInfoTab, tab -> new CustomerTabSkinBase(this));
|
||||||
registerTabSkin(fileTab, tab -> new CustomerTabSkinFile(this));
|
registerTabSkin(fileTab, tab -> new CustomerTabSkinFile(this));
|
||||||
registerTabSkin(entityTab, tab -> new CustomerTabSkinEntity(this));
|
registerTabSkin(entityTab, tab -> new CustomerTabSkinEntity(this));
|
||||||
registerTabSkin(satisfactionTab, tab -> new CustomerTabSkinSatisfactionSurvey(this));
|
registerTabSkin(satisfactionTab, tab -> new CustomerTabSkinSatisfactionSurvey(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompanyCustomerService getViewModelService() {
|
public CustomerService getViewModelService() {
|
||||||
return getCachedBean(CompanyCustomerService.class);
|
return getCachedBean(CustomerService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompanyService getCompanyService() {
|
public CompanyService getCompanyService() {
|
||||||
|
|||||||
@@ -1,36 +1,42 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
package com.ecep.contract.controller.customer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.controller.company.CompanyWindowController;
|
import com.ecep.contract.controller.company.CompanyWindowController;
|
||||||
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.converter.CompanyStringConverter;
|
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
|
||||||
import com.ecep.contract.model.Company;
|
|
||||||
import com.ecep.contract.model.CompanyContact;
|
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.service.CompanyContactService;
|
import com.ecep.contract.service.CompanyContactService;
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
import com.ecep.contract.service.CustomerService;
|
||||||
import com.ecep.contract.service.CompanyService;
|
import com.ecep.contract.service.CompanyService;
|
||||||
|
import com.ecep.contract.service.QueryService;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
|
import com.ecep.contract.vm.CompanyContactViewModel;
|
||||||
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
import com.ecep.contract.vm.CompanyCustomerViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyContactVo;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
import com.ecep.contract.vo.VendorVo;
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.stage.DirectoryChooser;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
import javafx.util.converter.LocalDateStringConverter;
|
import javafx.util.converter.LocalDateStringConverter;
|
||||||
|
|
||||||
public class CompanyCustomerTabSkinBase
|
public class CustomerTabSkinBase
|
||||||
extends AbstEntityBasedTabSkin<CompanyCustomerWindowController, CompanyCustomer, CompanyCustomerViewModel>
|
extends AbstEntityBasedTabSkin<CompanyCustomerWindowController, CustomerVo, CompanyCustomerViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public CompanyCustomerTabSkinBase(CompanyCustomerWindowController controller) {
|
public CustomerTabSkinBase(CompanyCustomerWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +50,26 @@ public class CompanyCustomerTabSkinBase
|
|||||||
initializeCompanyFieldAutoCompletion(controller.companyField);
|
initializeCompanyFieldAutoCompletion(controller.companyField);
|
||||||
initializeContactFieldAutoCompletion(controller.contactField);
|
initializeContactFieldAutoCompletion(controller.contactField);
|
||||||
|
|
||||||
|
UITools.autoCompletion(controller.contactField, viewModel.getContact(),
|
||||||
|
new QueryService<CompanyContactVo, CompanyContactViewModel>() {
|
||||||
|
@Override
|
||||||
|
public CompanyContactVo findById(Integer id) {
|
||||||
|
return getCompanyContactService().findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CompanyContactVo> search(String searchText) {
|
||||||
|
CustomerVo vendor = getEntity();
|
||||||
|
CompanyVo company = controller.getCompanyService().findById(vendor.getCompanyId());
|
||||||
|
return getCompanyContactService().searchByCompany(company, searchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringConverter<CompanyContactVo> getStringConverter() {
|
||||||
|
return getCompanyContactService().getStringConverter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
|
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
|
||||||
|
|
||||||
controller.developDateField.setConverter(converter);
|
controller.developDateField.setConverter(converter);
|
||||||
@@ -59,9 +85,12 @@ public class CompanyCustomerTabSkinBase
|
|||||||
|
|
||||||
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());
|
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());
|
||||||
controller.relativeCompanyBtn.setOnAction(event -> {
|
controller.relativeCompanyBtn.setOnAction(event -> {
|
||||||
Company company = viewModel.getCompany().get();
|
Integer companyId = viewModel.getCompany().get();
|
||||||
if (company != null) {
|
if (companyId != null) {
|
||||||
CompanyWindowController.show(company, controller.root.getScene().getWindow());
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
|
if (company != null) {
|
||||||
|
CompanyWindowController.show(company, controller.root.getScene().getWindow());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,20 +108,15 @@ public class CompanyCustomerTabSkinBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeContactFieldAutoCompletion(TextField textField) {
|
private void initializeContactFieldAutoCompletion(TextField textField) {
|
||||||
EntityStringConverter<CompanyContact> stringConverter = new EntityStringConverter<>();
|
UITools.autoCompletion(textField, viewModel.getContact(), getCompanyContactService());
|
||||||
stringConverter.setInitialized(cc -> getCompanyContactService().findById(cc.getId()));
|
|
||||||
UITools.autoCompletion(textField, viewModel.getContact(),
|
|
||||||
p -> getCompanyContactService().searchByCompany(viewModel.getCompany().get(), p.getUserText()),
|
|
||||||
stringConverter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeCompanyFieldAutoCompletion(TextField textField) {
|
private void initializeCompanyFieldAutoCompletion(TextField textField) {
|
||||||
CompanyStringConverter converter = SpringApp.getBean(CompanyStringConverter.class);
|
UITools.autoCompletion(textField, viewModel.getCompany(), getCompanyService());
|
||||||
UITools.autoCompletion(textField, viewModel.getCompany(), converter::suggest, converter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCompanyCustomerCreatePathAction(ActionEvent event) {
|
public void onCompanyCustomerCreatePathAction(ActionEvent event) {
|
||||||
CompanyCustomer companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
|
CustomerVo companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
|
||||||
if (getCompanyCustomerService().makePathAbsent(companyCustomer)) {
|
if (getCompanyCustomerService().makePathAbsent(companyCustomer)) {
|
||||||
companyCustomer = getCompanyCustomerService().save(companyCustomer);
|
companyCustomer = getCompanyCustomerService().save(companyCustomer);
|
||||||
viewModel.update(companyCustomer);
|
viewModel.update(companyCustomer);
|
||||||
@@ -102,15 +126,41 @@ public class CompanyCustomerTabSkinBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onCompanyCustomerChangePathAction(ActionEvent event) {
|
public void onCompanyCustomerChangePathAction(ActionEvent event) {
|
||||||
setStatus("未实现");
|
DirectoryChooser chooser = new DirectoryChooser();
|
||||||
|
CustomerVo entity = getEntity();
|
||||||
|
String path = entity.getPath();
|
||||||
|
File initialDirectory = null;
|
||||||
|
|
||||||
|
// 如果当前已经设置了目录并且路径有效,则设置初始目录为该目录
|
||||||
|
if (StringUtils.hasText(path)) {
|
||||||
|
File dir = new File(path);
|
||||||
|
if (dir.exists()) {
|
||||||
|
initialDirectory = dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有有效的初始目录,则使用基础路径
|
||||||
|
if (initialDirectory == null) {
|
||||||
|
initialDirectory = getCompanyCustomerService().getBasePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initialDirectory != null) {
|
||||||
|
chooser.setInitialDirectory(initialDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
File newDirectory = chooser.showDialog(getTab().getContent().getScene().getWindow());
|
||||||
|
if (newDirectory != null) {
|
||||||
|
entity.setPath(newDirectory.getAbsolutePath());
|
||||||
|
save(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCompanyCustomerPathSameAsNameAction(ActionEvent event) {
|
public void onCompanyCustomerPathSameAsNameAction(ActionEvent event) {
|
||||||
setStatus("未实现");
|
setStatus("未实现");
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompanyCustomerService getCompanyCustomerService() {
|
public CustomerService getCompanyCustomerService() {
|
||||||
return controller.getCachedBean(CompanyCustomerService.class);
|
return controller.getCachedBean(CustomerService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompanyContactService getCompanyContactService() {
|
public CompanyContactService getCompanyContactService() {
|
||||||
@@ -1,38 +1,36 @@
|
|||||||
package com.ecep.contract.controller.customer;
|
package com.ecep.contract.controller.customer;
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
|
||||||
import com.ecep.contract.model.CompanyCustomerEntity;
|
|
||||||
import com.ecep.contract.model.CustomerCatalog;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerEntityService;
|
|
||||||
import com.ecep.contract.service.CustomerCatalogService;
|
|
||||||
import com.ecep.contract.util.FxmlPath;
|
|
||||||
import com.ecep.contract.vm.CustomerEntityViewModel;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.Tab;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Map;
|
|
||||||
|
import com.ecep.contract.controller.table.cell.CustomerCatalogTableCell;
|
||||||
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerEntityService;
|
||||||
|
import com.ecep.contract.service.CustomerCatalogService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
|
import com.ecep.contract.vm.CustomerEntityViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyCustomerEntityVo;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
|
||||||
@FxmlPath("/ui/company/customer/customer-tab-entity.fxml")
|
@FxmlPath("/ui/company/customer/customer-tab-entity.fxml")
|
||||||
public class CustomerTabSkinEntity
|
public class CustomerTabSkinEntity
|
||||||
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerEntity, CustomerEntityViewModel> {
|
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerEntityVo, CustomerEntityViewModel> {
|
||||||
|
|
||||||
// 关联项 tab
|
// 关联项 tab
|
||||||
public TableColumn<CustomerEntityViewModel, Number> entityTable_idColumn;
|
public TableColumn<CustomerEntityViewModel, Number> entityTable_idColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_catalogColumn;
|
public TableColumn<CustomerEntityViewModel, Integer> entityTable_catalogColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_nameColumn;
|
public TableColumn<CustomerEntityViewModel, String> entityTable_nameColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_abbNameColumn;
|
public TableColumn<CustomerEntityViewModel, String> entityTable_abbNameColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_codeColumn;
|
public TableColumn<CustomerEntityViewModel, String> entityTable_codeColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_creatorColumn;
|
public TableColumn<CustomerEntityViewModel, Integer> entityTable_creatorColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_developDateColumn;
|
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_developDateColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, String> entityTable_modifierColumn;
|
public TableColumn<CustomerEntityViewModel, Integer> entityTable_modifierColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_modifyDateColumn;
|
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_modifyDateColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_updatedDateColumn;
|
public TableColumn<CustomerEntityViewModel, LocalDate> entityTable_updatedDateColumn;
|
||||||
public TableColumn<CustomerEntityViewModel, LocalDateTime> fetchedTimeColumn;
|
public TableColumn<CustomerEntityViewModel, LocalDateTime> fetchedTimeColumn;
|
||||||
@@ -40,9 +38,6 @@ public class CustomerTabSkinEntity
|
|||||||
public MenuItem entityTable_menu_refresh;
|
public MenuItem entityTable_menu_refresh;
|
||||||
public MenuItem entityTable_menu_del;
|
public MenuItem entityTable_menu_del;
|
||||||
|
|
||||||
@Setter
|
|
||||||
private CompanyCustomerEntityService customerEntityService;
|
|
||||||
|
|
||||||
public CustomerTabSkinEntity(CompanyCustomerWindowController controller) {
|
public CustomerTabSkinEntity(CompanyCustomerWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
}
|
}
|
||||||
@@ -60,35 +55,28 @@ public class CustomerTabSkinEntity
|
|||||||
entityTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
|
entityTable_nameColumn.setCellValueFactory(param -> param.getValue().getName());
|
||||||
entityTable_abbNameColumn.setCellValueFactory(param -> param.getValue().getAbbName());
|
entityTable_abbNameColumn.setCellValueFactory(param -> param.getValue().getAbbName());
|
||||||
entityTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
entityTable_codeColumn.setCellValueFactory(param -> param.getValue().getCode());
|
||||||
initializeEntityTabCatalogColumn(entityTable_catalogColumn);
|
|
||||||
|
|
||||||
EmployeeStringConverter stringConverter = SpringApp.getBean(EmployeeStringConverter.class);
|
entityTable_catalogColumn.setCellValueFactory(param -> param.getValue().getCatalog());
|
||||||
|
entityTable_catalogColumn.setCellFactory(CustomerCatalogTableCell.forTableColumn(getCustomerCatalogService()));
|
||||||
|
|
||||||
entityTable_developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
|
entityTable_developDateColumn.setCellValueFactory(param -> param.getValue().getDevelopDate());
|
||||||
entityTable_modifyDateColumn.setCellValueFactory(param -> param.getValue().getModifyDate());
|
entityTable_modifyDateColumn.setCellValueFactory(param -> param.getValue().getModifyDate());
|
||||||
entityTable_creatorColumn
|
|
||||||
.setCellValueFactory(param -> param.getValue().getCreator().map(stringConverter::toString));
|
|
||||||
entityTable_modifierColumn
|
|
||||||
.setCellValueFactory(param -> param.getValue().getModifier().map(stringConverter::toString));
|
|
||||||
entityTable_updatedDateColumn.setCellValueFactory(param -> param.getValue().getUpdatedDate());
|
entityTable_updatedDateColumn.setCellValueFactory(param -> param.getValue().getUpdatedDate());
|
||||||
|
|
||||||
fetchedTimeColumn.setCellValueFactory(param -> param.getValue().getFetchedTime());
|
fetchedTimeColumn.setCellValueFactory(param -> param.getValue().getFetchedTime());
|
||||||
fetchedTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
fetchedTimeColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
|
||||||
|
|
||||||
|
entityTable_creatorColumn.setCellValueFactory(param -> param.getValue().getCreator());
|
||||||
|
entityTable_creatorColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
entityTable_modifierColumn.setCellValueFactory(param -> param.getValue().getModifier());
|
||||||
|
entityTable_modifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
|
||||||
entityTable_menu_refresh.setOnAction(this::onTableRefreshAction);
|
entityTable_menu_refresh.setOnAction(this::onTableRefreshAction);
|
||||||
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
|
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeEntityTabCatalogColumn(TableColumn<CustomerEntityViewModel, String> column) {
|
|
||||||
EntityStringConverter<CustomerCatalog> converter = new EntityStringConverter<>();
|
|
||||||
converter.setInitialized(v -> getCachedBean(CustomerCatalogService.class).findById(v.getId()));
|
|
||||||
column.setCellValueFactory(param -> param.getValue().getCatalog().map(converter::toString));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompanyCustomerEntityService getCompanyCustomerEntityService() {
|
CompanyCustomerEntityService getCompanyCustomerEntityService() {
|
||||||
if (customerEntityService == null) {
|
return getCachedBean(CompanyCustomerEntityService.class);
|
||||||
customerEntityService = getBean(CompanyCustomerEntityService.class);
|
|
||||||
}
|
|
||||||
return customerEntityService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,10 +84,14 @@ public class CustomerTabSkinEntity
|
|||||||
return getCompanyCustomerEntityService();
|
return getCompanyCustomerEntityService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomerCatalogService getCustomerCatalogService() {
|
||||||
|
return getCachedBean(CustomerCatalogService.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(CompanyCustomer parent) {
|
public ParamUtils.Builder getSpecification(CustomerVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("customer", parent.getId());
|
params.equals("customer", parent.getId());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,47 +2,42 @@ package com.ecep.contract.controller.customer;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import com.ecep.contract.service.CompanyCustomerFileTypeService;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.CompanyCustomerFileType;
|
import com.ecep.contract.CustomerFileType;
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.Message;
|
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
import com.ecep.contract.SpringApp;
|
import com.ecep.contract.SpringApp;
|
||||||
import com.ecep.contract.constant.CompanyCustomerConstant;
|
import com.ecep.contract.constant.CompanyCustomerConstant;
|
||||||
|
import com.ecep.contract.controller.customer.tasker.CustomerEvaluationFormUpdateTask;
|
||||||
|
import com.ecep.contract.controller.customer.tasker.CustomerNextSignDateTask;
|
||||||
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
||||||
import com.ecep.contract.model.BaseEnumEntity;
|
import com.ecep.contract.controller.table.cell.CompanyCustomerFileTableTypeTableCell;
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.controller.table.cell.FilePathTableCell;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
|
||||||
import com.ecep.contract.model.CompanyCustomerFile;
|
|
||||||
import com.ecep.contract.model.CompanyCustomerFileTypeLocal;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerFileService;
|
import com.ecep.contract.service.CompanyCustomerFileService;
|
||||||
import com.ecep.contract.service.CompanyCustomerService;
|
import com.ecep.contract.service.CompanyCustomerFileTypeService;
|
||||||
|
import com.ecep.contract.service.CustomerService;
|
||||||
|
import com.ecep.contract.task.CustomerFileMoveTasker;
|
||||||
import com.ecep.contract.util.FileUtils;
|
import com.ecep.contract.util.FileUtils;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.CompanyCustomerFileViewModel;
|
import com.ecep.contract.vm.CustomerFileViewModel;
|
||||||
|
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
||||||
|
import com.ecep.contract.vo.CustomerFileVo;
|
||||||
|
import com.ecep.contract.vo.CompanyVo;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableMap;
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TableCell;
|
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
import javafx.scene.control.TableView;
|
import javafx.scene.control.TableView;
|
||||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||||
@@ -52,19 +47,19 @@ import lombok.Setter;
|
|||||||
|
|
||||||
@FxmlPath("/ui/company/customer/customer-tab-file.fxml")
|
@FxmlPath("/ui/company/customer/customer-tab-file.fxml")
|
||||||
public class CustomerTabSkinFile
|
public class CustomerTabSkinFile
|
||||||
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerFile, CompanyCustomerFileViewModel>
|
extends AbstCompanyCustomerTableTabSkin<CustomerFileVo, CustomerFileViewModel>
|
||||||
implements EditableEntityTableTabSkin<CompanyCustomerFile, CompanyCustomerFileViewModel> {
|
implements EditableEntityTableTabSkin<CustomerFileVo, CustomerFileViewModel> {
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private CompanyCustomerFileService companyCustomerFileService;
|
private CompanyCustomerFileService companyCustomerFileService;
|
||||||
|
|
||||||
public TableColumn<CompanyCustomerFileViewModel, Number> fileTable_idColumn;
|
public TableColumn<CustomerFileViewModel, Number> fileTable_idColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_typeColumn;
|
public TableColumn<CustomerFileViewModel, CustomerFileType> fileTable_typeColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_filePathColumn;
|
public TableColumn<CustomerFileViewModel, String> fileTable_filePathColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_editFilePathColumn;
|
public TableColumn<CustomerFileViewModel, String> fileTable_editFilePathColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, LocalDate> fileTable_signDateColumn;
|
public TableColumn<CustomerFileViewModel, LocalDate> fileTable_signDateColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, Boolean> fileTable_validColumn;
|
public TableColumn<CustomerFileViewModel, Boolean> fileTable_validColumn;
|
||||||
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_descriptionColumn;
|
public TableColumn<CustomerFileViewModel, String> fileTable_descriptionColumn;
|
||||||
|
|
||||||
public Button fileTable_reBuildBtn;
|
public Button fileTable_reBuildBtn;
|
||||||
public Button fileTable_updateEvaluationFormBuildBtn;
|
public Button fileTable_updateEvaluationFormBuildBtn;
|
||||||
@@ -90,10 +85,14 @@ public class CustomerTabSkinFile
|
|||||||
return getCompanyCustomerFileService();
|
return getCompanyCustomerFileService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompanyCustomerEvaluationFormFileService getEvaluationFormFileService() {
|
||||||
|
return getCachedBean(CompanyCustomerEvaluationFormFileService.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(CompanyCustomer parent) {
|
public ParamUtils.Builder getSpecification(CustomerVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("customer", parent.getId());
|
params.equals("customer", parent.getId());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,20 +100,20 @@ public class CustomerTabSkinFile
|
|||||||
public void initializeTable() {
|
public void initializeTable() {
|
||||||
super.initializeTable();
|
super.initializeTable();
|
||||||
|
|
||||||
TableView<CompanyCustomerFileViewModel> table = getTableView();
|
TableView<CustomerFileViewModel> table = getTableView();
|
||||||
|
|
||||||
table.disableProperty().bind(viewModel.getPath().isEmpty());
|
table.disableProperty().bind(viewModel.getPath().isEmpty());
|
||||||
fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
CompanyCustomerFileTypeService fileTypeService = getCachedBean(CompanyCustomerFileTypeService.class);
|
CompanyCustomerFileTypeService fileTypeService = getCachedBean(CompanyCustomerFileTypeService.class);
|
||||||
ObservableMap<CompanyCustomerFileType, CompanyCustomerFileTypeLocal> observableMapByLocal = FXCollections
|
fileTable_typeColumn.setCellValueFactory(param -> param.getValue().getType());
|
||||||
.observableMap(fileTypeService.findAll(getLocale()));
|
fileTable_typeColumn.setCellFactory(CompanyCustomerFileTableTypeTableCell.forTableColumn(fileTypeService));
|
||||||
fileTable_typeColumn.setCellValueFactory(param -> Bindings.valueAt(observableMapByLocal,
|
|
||||||
param.getValue().getType()).map(BaseEnumEntity::getValue));
|
|
||||||
|
|
||||||
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath());
|
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath());
|
||||||
fileTable_filePathColumn.setCellFactory(param -> new FileTableFilePathTableCell());
|
fileTable_filePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
|
||||||
|
|
||||||
fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath());
|
fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath());
|
||||||
fileTable_editFilePathColumn.setCellFactory(param -> new FileTableFilePathTableCell());
|
fileTable_editFilePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
|
||||||
|
|
||||||
fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate());
|
fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate());
|
||||||
fileTable_validColumn.setEditable(true);
|
fileTable_validColumn.setEditable(true);
|
||||||
fileTable_validColumn.setCellValueFactory(param -> param.getValue().getValid());
|
fileTable_validColumn.setCellValueFactory(param -> param.getValue().getValid());
|
||||||
@@ -155,13 +154,15 @@ public class CustomerTabSkinFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableRowDoubleClickedAction(CompanyCustomerFileViewModel item) {
|
protected void onTableRowDoubleClickedAction(CustomerFileViewModel item) {
|
||||||
CompanyCustomerFileType fileType = item.getType().get();
|
CustomerFileType fileType = item.getType().get();
|
||||||
if (fileType == CompanyCustomerFileType.EvaluationForm) {
|
if (fileType == CustomerFileType.EvaluationForm) {
|
||||||
|
|
||||||
// 文件不是 Excel 文件时,打开编辑UI
|
// 文件不是 Excel 文件时,打开编辑UI
|
||||||
if (!FileUtils.withExtensions(item.getFilePath().get(), FileUtils.XLS,
|
if (!FileUtils.withExtensions(item.getFilePath().get(), FileUtils.XLS,
|
||||||
FileUtils.XLSX)) {
|
FileUtils.XLSX)) {
|
||||||
CompanyCustomerEvaluationFormFileWindowController.show(item, controller.root.getScene().getWindow());
|
CompanyCustomerEvaluationFormFileWindowController.show(item,
|
||||||
|
controller.root.getScene().getWindow());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +185,7 @@ public class CustomerTabSkinFile
|
|||||||
setStatus("目录错误,不存在");
|
setStatus("目录错误,不存在");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CompanyCustomer companyCustomer = getParent();
|
CustomerVo companyCustomer = getParent();
|
||||||
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer,
|
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer,
|
||||||
((level, message) -> setStatus(message)));
|
((level, message) -> setStatus(message)));
|
||||||
if (nextSignDate != null && files.size() == 1) {
|
if (nextSignDate != null && files.size() == 1) {
|
||||||
@@ -196,17 +197,18 @@ public class CustomerTabSkinFile
|
|||||||
+ "." + StringUtils.getFilenameExtension(fileName);
|
+ "." + StringUtils.getFilenameExtension(fileName);
|
||||||
File dest = new File(dir, destFileName);
|
File dest = new File(dir, destFileName);
|
||||||
if (file.renameTo(dest)) {
|
if (file.renameTo(dest)) {
|
||||||
CompanyCustomerFile ccf = new CompanyCustomerFile();
|
CustomerFileVo ccf = new CustomerFileVo();
|
||||||
ccf.setCustomer(companyCustomer);
|
ccf.setCustomer(companyCustomer.getId());
|
||||||
ccf.setType(CompanyCustomerFileType.EvaluationForm);
|
ccf.setType(CustomerFileType.EvaluationForm);
|
||||||
ccf.setFilePath(dest.getAbsolutePath());
|
ccf.setFilePath(dest.getAbsolutePath());
|
||||||
ccf.setSignDate(nextSignDate);
|
ccf.setSignDate(nextSignDate);
|
||||||
ccf.setValid(false);
|
ccf.setValid(false);
|
||||||
CompanyCustomerFile saved = getCompanyCustomerFileService().save(ccf);
|
CustomerFileVo saved = getCompanyCustomerFileService().save(ccf);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
CompanyCustomerFileViewModel model = new CompanyCustomerFileViewModel();
|
CustomerFileViewModel model = new CustomerFileViewModel();
|
||||||
model.update(saved);
|
model.update(saved);
|
||||||
dataSet.add(model);
|
dataSet.add(model);
|
||||||
|
|
||||||
CompanyCustomerEvaluationFormFileWindowController.show(model,
|
CompanyCustomerEvaluationFormFileWindowController.show(model,
|
||||||
getTableView().getScene().getWindow());
|
getTableView().getScene().getWindow());
|
||||||
});
|
});
|
||||||
@@ -219,9 +221,9 @@ public class CustomerTabSkinFile
|
|||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
File dest = new File(dir, file.getName());
|
File dest = new File(dir, file.getName());
|
||||||
if (file.renameTo(dest)) {
|
if (file.renameTo(dest)) {
|
||||||
CompanyCustomerFile ccf = new CompanyCustomerFile();
|
CustomerFileVo ccf = new CustomerFileVo();
|
||||||
ccf.setCustomer(companyCustomer);
|
ccf.setCustomer(companyCustomer.getId());
|
||||||
ccf.setType(CompanyCustomerFileType.General);
|
ccf.setType(CustomerFileType.General);
|
||||||
ccf.setFilePath(dest.getAbsolutePath());
|
ccf.setFilePath(dest.getAbsolutePath());
|
||||||
ccf.setValid(false);
|
ccf.setValid(false);
|
||||||
getCompanyCustomerFileService().save(ccf);
|
getCompanyCustomerFileService().save(ccf);
|
||||||
@@ -231,36 +233,20 @@ public class CustomerTabSkinFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onFileReBuildingAction(ActionEvent event) {
|
public void onFileReBuildingAction(ActionEvent event) {
|
||||||
CompletableFuture.runAsync(() -> {
|
|
||||||
CompanyCustomerService customerService = getCompanyCustomerService();
|
CustomerService customerService = getCompanyCustomerService();
|
||||||
try {
|
try {
|
||||||
CompanyCustomer companyCustomer = customerService.findById(viewModel.getId().get());
|
CustomerVo companyCustomer = customerService.findById(viewModel.getId().get());
|
||||||
if (customerService.reBuildingFiles(companyCustomer, (level, message) -> setStatus(message))) {
|
if (customerService.reBuildingFiles(companyCustomer, (level, message) -> setStatus(message))) {
|
||||||
loadTableDataSet();
|
loadTableDataSet();
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
});
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompanyCustomerFile loadRowData(CompanyCustomerFileViewModel row) {
|
protected boolean deleteRow(CustomerFileViewModel row) {
|
||||||
return getCompanyCustomerFileService().findById(row.getId().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompanyCustomerFile saveRowData(CompanyCustomerFile entity) {
|
|
||||||
return getCompanyCustomerFileService().save(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteRowData(CompanyCustomerFile entity) {
|
|
||||||
getCompanyCustomerFileService().delete(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean deleteRow(CompanyCustomerFileViewModel row) {
|
|
||||||
String path = row.getFilePath().get();
|
String path = row.getFilePath().get();
|
||||||
if (super.deleteRow(row)) {
|
if (super.deleteRow(row)) {
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
@@ -280,11 +266,14 @@ public class CustomerTabSkinFile
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onFileTableMoveToCompanyPathAction(ActionEvent event) {
|
public void onFileTableMoveToCompanyPathAction(ActionEvent event) {
|
||||||
Company company = viewModel.getCompany().get();
|
CustomerFileViewModel selectedItem = getSelectedItem();
|
||||||
if (!ProxyUtils.isInitialized(company)) {
|
if (selectedItem == null) {
|
||||||
company = getCompanyService().findById(company.getId());
|
return;
|
||||||
}
|
}
|
||||||
viewModel.getCompany().set(company);
|
|
||||||
|
// 检查公司目录设置
|
||||||
|
Integer companyId = viewModel.getCompany().get();
|
||||||
|
CompanyVo company = getCompanyService().findById(companyId);
|
||||||
|
|
||||||
if (!StringUtils.hasText(company.getPath())) {
|
if (!StringUtils.hasText(company.getPath())) {
|
||||||
setStatus("公司目录未设置");
|
setStatus("公司目录未设置");
|
||||||
@@ -293,104 +282,30 @@ public class CustomerTabSkinFile
|
|||||||
|
|
||||||
File companyPath = new File(company.getPath());
|
File companyPath = new File(company.getPath());
|
||||||
if (!companyPath.exists()) {
|
if (!companyPath.exists()) {
|
||||||
setStatus("公司目录设置设置异常,无法访问");
|
setStatus("公司目录设置异常,无法访问");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompanyCustomerFileViewModel selectedItem = getSelectedItem();
|
// 创建并启动任务
|
||||||
if (selectedItem == null) {
|
CustomerFileMoveTasker task = new CustomerFileMoveTasker();
|
||||||
return;
|
task.setFileId(selectedItem.getId().get());
|
||||||
}
|
UITools.showTaskDialogAndWait("移动文件到公司目录", task, null);
|
||||||
String filePath = selectedItem.getFilePath().get();
|
|
||||||
String editFilePath = selectedItem.getEditFilePath().get();
|
|
||||||
|
|
||||||
if (StringUtils.hasText(filePath)) {
|
// 刷新表格数据
|
||||||
File file = new File(filePath);
|
loadTableDataSet();
|
||||||
if (file.exists()) {
|
|
||||||
File dest = new File(companyPath, file.getName());
|
|
||||||
if (file.renameTo(dest)) {
|
|
||||||
setStatus(file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.hasText(editFilePath)) {
|
|
||||||
File file = new File(editFilePath);
|
|
||||||
if (file.exists()) {
|
|
||||||
File dest = new File(companyPath, file.getName());
|
|
||||||
if (file.renameTo(dest)) {
|
|
||||||
setStatus(file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRow(selectedItem);
|
|
||||||
|
|
||||||
// getCompanyCustomerService().deleteFileById(selectedItem.getId().get());
|
|
||||||
// dataSet.remove(selectedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeTask(Task<Object> task, String prefix, Consumer<String> consumer) {
|
|
||||||
task.setOnScheduled(e -> {
|
|
||||||
consumer.accept("正在" + prefix + ",请稍后...");
|
|
||||||
});
|
|
||||||
task.setOnRunning(e -> {
|
|
||||||
consumer.accept("开始" + prefix + "...");
|
|
||||||
});
|
|
||||||
task.setOnSucceeded(e -> {
|
|
||||||
consumer.accept(prefix + "同步完成...");
|
|
||||||
});
|
|
||||||
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
consumer.accept(newValue.getMessage());
|
|
||||||
});
|
|
||||||
SpringApp.getBean(ScheduledExecutorService.class).submit(task);
|
|
||||||
consumer.accept("任务已创建...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpdateEvaluationFormAction(ActionEvent event) {
|
public void onUpdateEvaluationFormAction(ActionEvent event) {
|
||||||
CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask();
|
CustomerEvaluationFormUpdateTask task = new CustomerEvaluationFormUpdateTask();
|
||||||
task.setCompanyService(getCompanyService());
|
|
||||||
task.setCompanyCustomerFileService(getCompanyCustomerFileService());
|
|
||||||
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get()));
|
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get()));
|
||||||
UITools.showTaskDialogAndWait("更新评价表", task, consumer -> {
|
UITools.showTaskDialogAndWait("更新评价表", task, null);
|
||||||
initializeTask(task, "更新评价表", msg -> consumer.accept(Message.info(msg)));
|
|
||||||
});
|
|
||||||
loadTableDataSet();
|
loadTableDataSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCalcNextSignDateAction(ActionEvent event) {
|
public void onCalcNextSignDateAction(ActionEvent event) {
|
||||||
UITools.showDialogAndWait("计算客户下一个评价日期", "依据已有的客户评估表和登记采购的合同计算下一个评估日期", ds -> {
|
CustomerNextSignDateTask task = new CustomerNextSignDateTask();
|
||||||
CompanyCustomer companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
|
task.setCustomer(getEntity());
|
||||||
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer, (level, msg) -> {
|
UITools.showTaskDialogAndWait("计算客户的下一个评价日期", task, null);
|
||||||
Platform.runLater(() -> {
|
|
||||||
ds.add(msg);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (nextSignDate != null) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
ds.add("下一个评价日期:" + nextSignDate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileTableFilePathTableCell extends TableCell<CompanyCustomerFileViewModel, String> {
|
|
||||||
@Override
|
|
||||||
protected void updateItem(String item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
if (empty || !StringUtils.hasText(item)) {
|
|
||||||
setText("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = viewModel.getPath().get();
|
|
||||||
if (StringUtils.hasText(path)) {
|
|
||||||
if (item.startsWith(path)) {
|
|
||||||
item = "~" + item.substring(path.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setText(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompanyCustomerFileService getCompanyCustomerFileService() {
|
private CompanyCustomerFileService getCompanyCustomerFileService() {
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import com.ecep.contract.controller.project.satisfaction_survey.CustomerSatisfac
|
|||||||
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.ProjectTableCell;
|
import com.ecep.contract.controller.table.cell.ProjectTableCell;
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
import com.ecep.contract.converter.EntityStringConverter;
|
||||||
import com.ecep.contract.model.CompanyCustomer;
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
import com.ecep.contract.model.CustomerCatalog;
|
import com.ecep.contract.vo.CustomerCatalogVo;
|
||||||
import com.ecep.contract.model.CustomerSatisfactionSurvey;
|
import com.ecep.contract.vo.CustomerSatisfactionSurveyVo;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
import com.ecep.contract.model.Project;
|
import com.ecep.contract.vo.ProjectVo;
|
||||||
import com.ecep.contract.service.CustomerSatisfactionSurveyService;
|
import com.ecep.contract.service.CustomerSatisfactionSurveyService;
|
||||||
import com.ecep.contract.service.ProjectService;
|
import com.ecep.contract.service.ProjectService;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.CustomerEntityViewModel;
|
import com.ecep.contract.vm.CustomerEntityViewModel;
|
||||||
import com.ecep.contract.vm.CustomerSatisfactionSurveyViewModel;
|
import com.ecep.contract.vm.CustomerSatisfactionSurveyViewModel;
|
||||||
|
|
||||||
@@ -27,14 +28,14 @@ import lombok.Setter;
|
|||||||
|
|
||||||
@FxmlPath("/ui/company/customer/customer-tab-satisfaction-survey.fxml")
|
@FxmlPath("/ui/company/customer/customer-tab-satisfaction-survey.fxml")
|
||||||
public class CustomerTabSkinSatisfactionSurvey
|
public class CustomerTabSkinSatisfactionSurvey
|
||||||
extends AbstCompanyCustomerTableTabSkin<CustomerSatisfactionSurvey, CustomerSatisfactionSurveyViewModel> {
|
extends AbstCompanyCustomerTableTabSkin<CustomerSatisfactionSurveyVo, CustomerSatisfactionSurveyViewModel> {
|
||||||
|
|
||||||
// 关联项 tab
|
// 关联项 tab
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> idColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> idColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, Project> projectColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, Integer> projectColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, String> codeColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, String> codeColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> totalScoreColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, Number> totalScoreColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, Employee> applicantColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, Integer> applicantColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDateTime> applyTimeColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDateTime> applyTimeColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, String> descriptionColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, String> descriptionColumn;
|
||||||
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDate> dateColumn;
|
public TableColumn<CustomerSatisfactionSurveyViewModel, LocalDate> dateColumn;
|
||||||
@@ -61,12 +62,13 @@ public class CustomerTabSkinSatisfactionSurvey
|
|||||||
|
|
||||||
bindNumberColumn(idColumn, CustomerSatisfactionSurveyViewModel::getId);
|
bindNumberColumn(idColumn, CustomerSatisfactionSurveyViewModel::getId);
|
||||||
bindColumn(codeColumn, CustomerSatisfactionSurveyViewModel::getCode);
|
bindColumn(codeColumn, CustomerSatisfactionSurveyViewModel::getCode);
|
||||||
|
|
||||||
projectColumn.setCellValueFactory(param -> param.getValue().getProject());
|
projectColumn.setCellValueFactory(param -> param.getValue().getProject());
|
||||||
projectColumn.setCellFactory(cell -> new ProjectTableCell<>(getProjectService()));
|
projectColumn.setCellFactory(ProjectTableCell.forTableColumn(getProjectService()));
|
||||||
bindLocalDateColumn(dateColumn, CustomerSatisfactionSurveyViewModel::getDate);
|
bindLocalDateColumn(dateColumn, CustomerSatisfactionSurveyViewModel::getDate);
|
||||||
bindNumberColumn(totalScoreColumn, CustomerSatisfactionSurveyViewModel::getTotalScore);
|
bindNumberColumn(totalScoreColumn, CustomerSatisfactionSurveyViewModel::getTotalScore);
|
||||||
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
|
applicantColumn.setCellValueFactory(param -> param.getValue().getApplicant());
|
||||||
applicantColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
|
applicantColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
bindLocalDateTimeColumn(applyTimeColumn, CustomerSatisfactionSurveyViewModel::getApplyTime);
|
bindLocalDateTimeColumn(applyTimeColumn, CustomerSatisfactionSurveyViewModel::getApplyTime);
|
||||||
bindColumn(descriptionColumn, CustomerSatisfactionSurveyViewModel::getDescription);
|
bindColumn(descriptionColumn, CustomerSatisfactionSurveyViewModel::getDescription);
|
||||||
|
|
||||||
@@ -74,12 +76,6 @@ public class CustomerTabSkinSatisfactionSurvey
|
|||||||
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
|
entityTable_menu_del.setOnAction(this::onTableDeleteAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeEntityTabCatalogColumn(TableColumn<CustomerEntityViewModel, String> column) {
|
|
||||||
EntityStringConverter<CustomerCatalog> converter = new EntityStringConverter<>();
|
|
||||||
converter.setInitialized(v -> getCachedBean(CustomerCatalogService.class).findById(v.getId()));
|
|
||||||
column.setCellValueFactory(param -> param.getValue().getCatalog().map(converter::toString));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerSatisfactionSurveyService getCustomerSatisfactionSurveyService() {
|
private CustomerSatisfactionSurveyService getCustomerSatisfactionSurveyService() {
|
||||||
if (satisfactionSurveyService == null) {
|
if (satisfactionSurveyService == null) {
|
||||||
satisfactionSurveyService = getBean(CustomerSatisfactionSurveyService.class);
|
satisfactionSurveyService = getBean(CustomerSatisfactionSurveyService.class);
|
||||||
@@ -100,9 +96,9 @@ public class CustomerTabSkinSatisfactionSurvey
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(CompanyCustomer parent) {
|
public ParamUtils.Builder getSpecification(CustomerVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("project.customer", parent.getId());
|
params.equals("project.customer", parent.getId());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ecep.contract.controller.customer.tasker;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
import com.ecep.contract.task.Tasker;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户评估表更新任务
|
||||||
|
*/
|
||||||
|
public class CustomerEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
@Setter
|
||||||
|
private CustomerVo customer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "CustomerEvaluationFormUpdateTask";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("客户评估表更新任务");
|
||||||
|
return callRemoteTask(holder, getLocale(), customer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ecep.contract.controller.customer.tasker;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
import com.ecep.contract.task.Tasker;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class CustomerNextSignDateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CustomerNextSignDateTask.class);
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private CustomerVo customer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "CustomerNextSignDateTask";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("计算客户下一个评价日期");
|
||||||
|
return callRemoteTask(holder, getLocale(), customer.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.ecep.contract.controller.customer.tasker;
|
||||||
|
|
||||||
|
import com.ecep.contract.MessageHolder;
|
||||||
|
import com.ecep.contract.WebSocketClientTasker;
|
||||||
|
import com.ecep.contract.task.Tasker;
|
||||||
|
import com.ecep.contract.vo.CustomerVo;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户文件重建任务类
|
||||||
|
* 用于通过WebSocket与服务器通信,重建客户相关文件
|
||||||
|
*/
|
||||||
|
public class CustomerRebuildFilesTasker extends Tasker<Object> implements WebSocketClientTasker {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private CustomerVo companyCustomer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
protected boolean filesUpdated = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskName() {
|
||||||
|
return "CustomerRebuildFilesTasker";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateProgress(long current, long total) {
|
||||||
|
super.updateProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object execute(MessageHolder holder) throws Exception {
|
||||||
|
updateTitle("重建客户文件");
|
||||||
|
return callRemoteTask(holder, getLocale(), companyCustomer.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,11 @@ import org.springframework.data.domain.Pageable;
|
|||||||
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.controller.ManagerSkin;
|
import com.ecep.contract.controller.ManagerSkin;
|
||||||
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
||||||
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
import com.ecep.contract.converter.EmployeeStringConverter;
|
||||||
import com.ecep.contract.model.Department;
|
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.model.Employee;
|
||||||
import com.ecep.contract.util.ParamUtils;
|
import com.ecep.contract.util.ParamUtils;
|
||||||
|
import com.ecep.contract.vo.DepartmentVo;
|
||||||
import com.ecep.contract.vm.DepartmentViewModel;
|
import com.ecep.contract.vm.DepartmentViewModel;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@@ -21,9 +22,9 @@ import javafx.scene.control.cell.ComboBoxTableCell;
|
|||||||
import javafx.scene.control.cell.TextFieldTableCell;
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
|
||||||
public class DepartmentManagerSkin
|
public class DepartmentManagerSkin
|
||||||
extends AbstEntityManagerSkin<Department, DepartmentViewModel, DepartmentManagerSkin, DepartmentManagerWindowController>
|
extends
|
||||||
implements ManagerSkin, EditableEntityTableTabSkin<Department, DepartmentViewModel> {
|
AbstEntityManagerSkin<DepartmentVo, DepartmentViewModel, DepartmentManagerSkin, DepartmentManagerWindowController>
|
||||||
|
implements ManagerSkin, EditableEntityTableTabSkin<DepartmentVo, DepartmentViewModel> {
|
||||||
|
|
||||||
public DepartmentManagerSkin(DepartmentManagerWindowController controller) {
|
public DepartmentManagerSkin(DepartmentManagerWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -33,7 +34,7 @@ public class DepartmentManagerSkin
|
|||||||
public void initializeTable() {
|
public void initializeTable() {
|
||||||
getTableView().setEditable(true);
|
getTableView().setEditable(true);
|
||||||
|
|
||||||
List<Employee> employees = controller.getEmployeeService().findAll(ParamUtils.equal("isActive", true), Pageable.ofSize(30)).getContent();
|
// 不再需要获取所有员工列表,因为现在使用的是leaderId和leaderName
|
||||||
|
|
||||||
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
|
|
||||||
@@ -46,8 +47,7 @@ public class DepartmentManagerSkin
|
|||||||
controller.codeColumn.setOnEditCommit(this::onCodeColumnEditCommit);
|
controller.codeColumn.setOnEditCommit(this::onCodeColumnEditCommit);
|
||||||
|
|
||||||
controller.leaderColumn.setCellValueFactory(param -> param.getValue().getLeader());
|
controller.leaderColumn.setCellValueFactory(param -> param.getValue().getLeader());
|
||||||
controller.leaderColumn.setCellFactory(ComboBoxTableCell.forTableColumn(getBean(EmployeeStringConverter.class), FXCollections.observableArrayList(employees)));
|
controller.leaderColumn.setCellFactory(param -> new EmployeeTableCell<>(controller.getEmployeeService()));
|
||||||
controller.leaderColumn.setOnEditCommit(this::onLeaderColumnEditCommit);
|
|
||||||
|
|
||||||
controller.activeColumn.setCellValueFactory(param -> param.getValue().getIsActive());
|
controller.activeColumn.setCellValueFactory(param -> param.getValue().getIsActive());
|
||||||
controller.activeColumn.setEditable(true);
|
controller.activeColumn.setEditable(true);
|
||||||
@@ -55,7 +55,6 @@ public class DepartmentManagerSkin
|
|||||||
controller.activeColumn.setOnEditCommit(this::onActiveColumnEditCommit);
|
controller.activeColumn.setOnEditCommit(this::onActiveColumnEditCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void onCodeColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, String> event) {
|
private void onCodeColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, String> event) {
|
||||||
DepartmentViewModel row = event.getRowValue();
|
DepartmentViewModel row = event.getRowValue();
|
||||||
row.getCode().set(event.getNewValue());
|
row.getCode().set(event.getNewValue());
|
||||||
@@ -68,9 +67,11 @@ public class DepartmentManagerSkin
|
|||||||
saveRowData(row);
|
saveRowData(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLeaderColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, Employee> event) {
|
private void onLeaderColumnEditCommit(TableColumn.CellEditEvent<DepartmentViewModel, Integer> event) {
|
||||||
DepartmentViewModel row = event.getRowValue();
|
DepartmentViewModel row = event.getRowValue();
|
||||||
row.getLeader().set(event.getNewValue());
|
row.getLeader().set(event.getNewValue());
|
||||||
|
// 注意:这里我们只设置了leaderName,但没有设置leaderId
|
||||||
|
// 在实际应用中,您可能需要根据leaderName查找对应的leaderId
|
||||||
saveRowData(row);
|
saveRowData(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,14 +83,14 @@ public class DepartmentManagerSkin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableRowDoubleClickedAction(DepartmentViewModel item) {
|
protected void onTableRowDoubleClickedAction(DepartmentViewModel item) {
|
||||||
//TODO 显示详情
|
// TODO 显示详情
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableCreateNewAction(ActionEvent event) {
|
protected void onTableCreateNewAction(ActionEvent event) {
|
||||||
Department employee = new Department();
|
DepartmentVo department = new DepartmentVo();
|
||||||
employee = controller.getViewModelService().save(employee);
|
department = controller.getViewModelService().save(department);
|
||||||
DepartmentViewModel viewModel = DepartmentViewModel.from(employee);
|
DepartmentViewModel viewModel = DepartmentViewModel.from(department);
|
||||||
dataSet.add(viewModel);
|
dataSet.add(viewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
package com.ecep.contract.controller.department;
|
package com.ecep.contract.controller.department;
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.model.Department;
|
|
||||||
import com.ecep.contract.model.Employee;
|
|
||||||
import com.ecep.contract.service.DepartmentService;
|
import com.ecep.contract.service.DepartmentService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.DepartmentViewModel;
|
import com.ecep.contract.vm.DepartmentViewModel;
|
||||||
|
import com.ecep.contract.vo.DepartmentVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
@@ -22,12 +20,12 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath(value = "/ui/employee/department-manager.fxml")
|
@FxmlPath(value = "/ui/employee/department-manager.fxml")
|
||||||
public class DepartmentManagerWindowController
|
public class DepartmentManagerWindowController
|
||||||
extends AbstManagerWindowController<Department, DepartmentViewModel, DepartmentManagerSkin> {
|
extends AbstManagerWindowController<DepartmentVo, DepartmentViewModel, DepartmentManagerSkin> {
|
||||||
|
|
||||||
public TableColumn<DepartmentViewModel, Number> idColumn;
|
public TableColumn<DepartmentViewModel, Number> idColumn;
|
||||||
public TableColumn<DepartmentViewModel, String> nameColumn;
|
public TableColumn<DepartmentViewModel, String> nameColumn;
|
||||||
public TableColumn<DepartmentViewModel, String> codeColumn;
|
public TableColumn<DepartmentViewModel, String> codeColumn;
|
||||||
public TableColumn<DepartmentViewModel, Employee> leaderColumn;
|
public TableColumn<DepartmentViewModel, Integer> leaderColumn;
|
||||||
public TableColumn<DepartmentViewModel, Boolean> activeColumn;
|
public TableColumn<DepartmentViewModel, Boolean> activeColumn;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -8,35 +8,22 @@ import com.ecep.contract.service.EmployeeService;
|
|||||||
import com.ecep.contract.service.PermissionService;
|
import com.ecep.contract.service.PermissionService;
|
||||||
import com.ecep.contract.vm.EmployeeViewModel;
|
import com.ecep.contract.vm.EmployeeViewModel;
|
||||||
|
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public abstract class AbstEmployeeBasedTabSkin
|
public abstract class AbstEmployeeBasedTabSkin
|
||||||
extends AbstEntityBasedTabSkin<EmployeeWindowController, Employee, EmployeeViewModel>
|
extends AbstEntityBasedTabSkin<EmployeeWindowController, EmployeeVo, EmployeeViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
@Setter
|
|
||||||
private PermissionService permissionService;
|
|
||||||
@Setter
|
|
||||||
private EmployeeRoleService employeeRoleService;
|
|
||||||
|
|
||||||
public AbstEmployeeBasedTabSkin(EmployeeWindowController controller) {
|
public AbstEmployeeBasedTabSkin(EmployeeWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EmployeeService getEmployeeService() {
|
|
||||||
return controller.employeeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EmployeeRoleService getEmployeeRoleService() {
|
protected EmployeeRoleService getEmployeeRoleService() {
|
||||||
if (employeeRoleService == null) {
|
return getCachedBean(EmployeeRoleService.class);
|
||||||
employeeRoleService = getBean(EmployeeRoleService.class);
|
|
||||||
}
|
|
||||||
return employeeRoleService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PermissionService getPermissionService() {
|
protected PermissionService getPermissionService() {
|
||||||
if (permissionService == null) {
|
return getCachedBean(PermissionService.class);
|
||||||
permissionService = getBean(PermissionService.class);
|
|
||||||
}
|
|
||||||
return permissionService;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
|||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.model.Employee;
|
||||||
import com.ecep.contract.model.IdentityEntity;
|
import com.ecep.contract.model.IdentityEntity;
|
||||||
import com.ecep.contract.service.EmployeeService;
|
import com.ecep.contract.service.EmployeeService;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.EmployeeBasedViewModel;
|
import com.ecep.contract.vm.EmployeeBasedViewModel;
|
||||||
import com.ecep.contract.vm.EmployeeViewModel;
|
import com.ecep.contract.vm.EmployeeViewModel;
|
||||||
import com.ecep.contract.vm.IdentityViewModel;
|
import com.ecep.contract.vm.IdentityViewModel;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
|
|
||||||
public abstract class AbstEmployeeTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
public abstract class AbstEmployeeTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
|
||||||
extends AbstEntityTableTabSkin<EmployeeWindowController, Employee, EmployeeViewModel, T, TV>
|
extends AbstEntityTableTabSkin<EmployeeWindowController, EmployeeVo, EmployeeViewModel, T, TV>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
public AbstEmployeeTableTabSkin(EmployeeWindowController controller) {
|
public AbstEmployeeTableTabSkin(EmployeeWindowController controller) {
|
||||||
@@ -27,16 +29,16 @@ public abstract class AbstEmployeeTableTabSkin<T extends IdentityEntity, TV exte
|
|||||||
protected TV createNewViewModel() {
|
protected TV createNewViewModel() {
|
||||||
TV model = super.createNewViewModel();
|
TV model = super.createNewViewModel();
|
||||||
if (model instanceof EmployeeBasedViewModel m) {
|
if (model instanceof EmployeeBasedViewModel m) {
|
||||||
m.getEmployee().set(getEntity());
|
m.getEmployee().set(getEntity().getId());
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(Employee parent) {
|
public ParamUtils.Builder getSpecification(EmployeeVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("employee", parent.getId());
|
params.equals("employee", parent.getId());
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
package com.ecep.contract.controller.employee;
|
package com.ecep.contract.controller.employee;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
import com.ecep.contract.controller.ManagerSkin;
|
import com.ecep.contract.controller.ManagerSkin;
|
||||||
import com.ecep.contract.controller.table.cell.DepartmentTableCell;
|
import com.ecep.contract.controller.table.cell.DepartmentTableCell;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.model.Employee;
|
||||||
import com.ecep.contract.service.DepartmentService;
|
import com.ecep.contract.service.DepartmentService;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.EmployeeViewModel;
|
import com.ecep.contract.vm.EmployeeViewModel;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||||
|
|
||||||
public class EmployeeManagerSkin
|
public class EmployeeManagerSkin
|
||||||
extends AbstEntityManagerSkin<Employee, EmployeeViewModel, EmployeeManagerSkin, EmployeeManagerWindowController>
|
extends
|
||||||
|
AbstEntityManagerSkin<EmployeeVo, EmployeeViewModel, EmployeeManagerSkin, EmployeeManagerWindowController>
|
||||||
implements ManagerSkin {
|
implements ManagerSkin {
|
||||||
public EmployeeManagerSkin(EmployeeManagerWindowController controller) {
|
public EmployeeManagerSkin(EmployeeManagerWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -29,10 +30,10 @@ public class EmployeeManagerSkin
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification() {
|
public ParamUtils.Builder getSpecification() {
|
||||||
Map<String, Object> params = super.getSpecification();
|
ParamUtils.Builder params = super.getSpecification();
|
||||||
if (controller.activeCheckBox.isSelected()) {
|
if (controller.activeCheckBox.isSelected()) {
|
||||||
params.put("isActive", true);
|
params.equals("isActive", true);
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ public class EmployeeManagerSkin
|
|||||||
controller.accountColumn.setCellValueFactory(param -> param.getValue().getAccount());
|
controller.accountColumn.setCellValueFactory(param -> param.getValue().getAccount());
|
||||||
|
|
||||||
controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment());
|
controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment());
|
||||||
controller.departmentColumn.setCellFactory(param -> new DepartmentTableCell<>(getDepartmentService()));
|
controller.departmentColumn.setCellFactory(DepartmentTableCell.forTableColumn(getDepartmentService()));
|
||||||
|
|
||||||
controller.emailColumn.setCellValueFactory(param -> param.getValue().getEmail());
|
controller.emailColumn.setCellValueFactory(param -> param.getValue().getEmail());
|
||||||
controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
|
controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
|
||||||
@@ -64,7 +65,7 @@ public class EmployeeManagerSkin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableCreateNewAction(ActionEvent event) {
|
protected void onTableCreateNewAction(ActionEvent event) {
|
||||||
Employee employee = new Employee();
|
EmployeeVo employee = new EmployeeVo();
|
||||||
employee = controller.getViewModelService().save(employee);
|
employee = controller.getViewModelService().save(employee);
|
||||||
EmployeeViewModel viewModel = EmployeeViewModel.from(employee);
|
EmployeeViewModel viewModel = EmployeeViewModel.from(employee);
|
||||||
dataSet.add(viewModel);
|
dataSet.add(viewModel);
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import org.springframework.stereotype.Component;
|
|||||||
import com.ecep.contract.constant.CloudServiceConstant;
|
import com.ecep.contract.constant.CloudServiceConstant;
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.model.Department;
|
import com.ecep.contract.model.Department;
|
||||||
import com.ecep.contract.model.Employee;
|
|
||||||
import com.ecep.contract.service.EmployeeService;
|
import com.ecep.contract.service.EmployeeService;
|
||||||
import com.ecep.contract.task.EmployeesSyncTask;
|
import com.ecep.contract.task.EmployeesSyncTask;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.EmployeeViewModel;
|
import com.ecep.contract.vm.EmployeeViewModel;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
@@ -27,14 +27,14 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/employee/employee-manager.fxml")
|
@FxmlPath("/ui/employee/employee-manager.fxml")
|
||||||
public class EmployeeManagerWindowController
|
public class EmployeeManagerWindowController
|
||||||
extends AbstManagerWindowController<Employee, EmployeeViewModel, EmployeeManagerSkin> {
|
extends AbstManagerWindowController<EmployeeVo, EmployeeViewModel, EmployeeManagerSkin> {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<EmployeeViewModel, Number> idColumn;
|
public TableColumn<EmployeeViewModel, Number> idColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<EmployeeViewModel, String> accountColumn;
|
public TableColumn<EmployeeViewModel, String> accountColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<EmployeeViewModel, Department> departmentColumn;
|
public TableColumn<EmployeeViewModel, Integer> departmentColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<EmployeeViewModel, String> nameColumn;
|
public TableColumn<EmployeeViewModel, String> nameColumn;
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
package com.ecep.contract.controller.employee;
|
package com.ecep.contract.controller.employee;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import com.ecep.contract.Desktop;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.data.domain.Sort;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.model.Employee;
|
||||||
import com.ecep.contract.model.EmployeeAuthBind;
|
import com.ecep.contract.model.EmployeeAuthBind;
|
||||||
import com.ecep.contract.service.EmployeeAuthBindService;
|
import com.ecep.contract.service.EmployeeAuthBindService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.EmployeeAuthBindViewModel;
|
import com.ecep.contract.vm.EmployeeAuthBindViewModel;
|
||||||
|
import com.ecep.contract.vo.EmployeeAuthBindVo;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
@@ -19,15 +15,18 @@ import javafx.scene.control.Tab;
|
|||||||
import javafx.scene.control.TableColumn;
|
import javafx.scene.control.TableColumn;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@FxmlPath("/ui/employee/employee-auth-bind.fxml")
|
@FxmlPath("/ui/employee/employee-auth-bind.fxml")
|
||||||
public class EmployeeTabSkinAuthBind
|
public class EmployeeTabSkinAuthBind
|
||||||
extends AbstEmployeeTableTabSkin<EmployeeAuthBind, EmployeeAuthBindViewModel> {
|
extends AbstEmployeeTableTabSkin<EmployeeAuthBindVo, EmployeeAuthBindViewModel> {
|
||||||
public TableColumn<EmployeeAuthBindViewModel, Number> idColumn;
|
public TableColumn<EmployeeAuthBindViewModel, Number> idColumn;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, String> ipColumn;
|
public TableColumn<EmployeeAuthBindViewModel, String> ipColumn;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, String> macColumn;
|
public TableColumn<EmployeeAuthBindViewModel, String> macColumn;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> createTime;
|
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> createTime;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> updateTimeColumn;
|
public TableColumn<EmployeeAuthBindViewModel, LocalDateTime> updateTimeColumn;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, Employee> updaterColumn;
|
public TableColumn<EmployeeAuthBindViewModel, Integer> updaterColumn;
|
||||||
public TableColumn<EmployeeAuthBindViewModel, String> descriptionColumn;
|
public TableColumn<EmployeeAuthBindViewModel, String> descriptionColumn;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@@ -81,13 +80,18 @@ public class EmployeeTabSkinAuthBind
|
|||||||
protected void createContextMenu(ContextMenu contextMenu) {
|
protected void createContextMenu(ContextMenu contextMenu) {
|
||||||
super.createContextMenu(contextMenu);
|
super.createContextMenu(contextMenu);
|
||||||
MenuItem menuItem = new MenuItem("导入未关联");
|
MenuItem menuItem = new MenuItem("导入未关联");
|
||||||
|
int activeEmployeeId = Desktop.instance.getActiveEmployeeId();
|
||||||
|
if (activeEmployeeId <= 0) {
|
||||||
|
logger.warn("未登录员工{}", activeEmployeeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
menuItem.setOnAction(event -> {
|
menuItem.setOnAction(event -> {
|
||||||
EmployeeAuthBindService service = getEmployeeAuthBindService();
|
EmployeeAuthBindService service = getEmployeeAuthBindService();
|
||||||
List<EmployeeAuthBind> authBinds = service.findAllByEmployee(null);
|
List<EmployeeAuthBindVo> authBinds = service.findAllByEmployee(null);
|
||||||
for (EmployeeAuthBind authBind : authBinds) {
|
for (EmployeeAuthBindVo authBind : authBinds) {
|
||||||
authBind.setEmployee(getEntity());
|
authBind.setEmployeeId(getEntity().getId());
|
||||||
authBind.setUpdateTime(LocalDateTime.now());
|
authBind.setUpdateTime(LocalDateTime.now());
|
||||||
authBind.setUpdater(controller.getCurrentUser());
|
authBind.setUpdaterId(activeEmployeeId);
|
||||||
service.save(authBind);
|
service.save(authBind);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package com.ecep.contract.controller.employee;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
|
||||||
import com.ecep.contract.model.Department;
|
|
||||||
import com.ecep.contract.service.DepartmentService;
|
import com.ecep.contract.service.DepartmentService;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
|
|
||||||
@@ -25,11 +23,8 @@ public class EmployeeTabSkinBase
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTab() {
|
public void initializeTab() {
|
||||||
EntityStringConverter<Department> departmentEntityStringConverter = new EntityStringConverter<>();
|
|
||||||
DepartmentService departmentService = getBean(DepartmentService.class);
|
|
||||||
departmentEntityStringConverter.setInitialized(department -> departmentService.findById(department.getId()));
|
|
||||||
UITools.autoCompletion(controller.departmentField, viewModel.getDepartment(),
|
UITools.autoCompletion(controller.departmentField, viewModel.getDepartment(),
|
||||||
p -> departmentService.search(p.getUserText()), departmentEntityStringConverter);
|
getCachedBean(DepartmentService.class));
|
||||||
|
|
||||||
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
|
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
|
||||||
controller.aliasField.textProperty().bindBidirectional(viewModel.getAlias());
|
controller.aliasField.textProperty().bindBidirectional(viewModel.getAlias());
|
||||||
@@ -49,23 +44,25 @@ public class EmployeeTabSkinBase
|
|||||||
|
|
||||||
controller.isActiveField.selectedProperty().bindBidirectional(viewModel.getIsActive());
|
controller.isActiveField.selectedProperty().bindBidirectional(viewModel.getIsActive());
|
||||||
|
|
||||||
|
// Callback<ListView<EmployeeRole>, ListCell<EmployeeRole>> cellFactory =
|
||||||
|
// controller.rolesField.getCellFactory();
|
||||||
|
// StringConverter<EmployeeRole> employeeRoleStringConverter = new
|
||||||
|
// EntityStringConverter<>();
|
||||||
|
// controller.rolesField.setCellFactory(param -> {
|
||||||
|
// ListCell<EmployeeRole> cell = cellFactory.call(param);
|
||||||
|
// if (cell instanceof CheckBoxListCell<EmployeeRole> list) {
|
||||||
|
// list.setConverter(employeeRoleStringConverter);
|
||||||
|
// }
|
||||||
|
// return cell;
|
||||||
|
// });
|
||||||
|
|
||||||
// Callback<ListView<EmployeeRole>, ListCell<EmployeeRole>> cellFactory = controller.rolesField.getCellFactory();
|
// controller.rolesField.getCheckModel().getCheckedItems().setAll();
|
||||||
// StringConverter<EmployeeRole> employeeRoleStringConverter = new EntityStringConverter<>();
|
// Property<IndexedCheckModel<EmployeeRole>> selectedRoles = new
|
||||||
// controller.rolesField.setCellFactory(param -> {
|
// SimpleObjectProperty<>();
|
||||||
// ListCell<EmployeeRole> cell = cellFactory.call(param);
|
// controller.rolesField.getCheckModel().getCheckedItems().addListener((ListChangeListener<?
|
||||||
// if (cell instanceof CheckBoxListCell<EmployeeRole> list) {
|
// super EmployeeRole>) changed -> {
|
||||||
// list.setConverter(employeeRoleStringConverter);
|
// System.out.println("newValue = " + changed);
|
||||||
// }
|
// });
|
||||||
// return cell;
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
// controller.rolesField.getCheckModel().getCheckedItems().setAll();
|
|
||||||
// Property<IndexedCheckModel<EmployeeRole>> selectedRoles = new SimpleObjectProperty<>();
|
|
||||||
// controller.rolesField.getCheckModel().getCheckedItems().addListener((ListChangeListener<? super EmployeeRole>) changed -> {
|
|
||||||
// System.out.println("newValue = " + changed);
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import java.time.LocalDateTime;
|
|||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.model.EmployeeLoginHistory;
|
|
||||||
import com.ecep.contract.service.EmployeeLoginHistoryService;
|
import com.ecep.contract.service.EmployeeLoginHistoryService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.vo.EmployeeLoginHistoryVo;
|
||||||
import com.ecep.contract.vm.EmployeeLoginHistoryViewModel;
|
import com.ecep.contract.vm.EmployeeLoginHistoryViewModel;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -15,7 +15,7 @@ import javafx.scene.control.TableColumn;
|
|||||||
|
|
||||||
@FxmlPath("/ui/employee/employee-login-history.fxml")
|
@FxmlPath("/ui/employee/employee-login-history.fxml")
|
||||||
public class EmployeeTabSkinLoginHistory
|
public class EmployeeTabSkinLoginHistory
|
||||||
extends AbstEmployeeTableTabSkin<EmployeeLoginHistory, EmployeeLoginHistoryViewModel>
|
extends AbstEmployeeTableTabSkin<EmployeeLoginHistoryVo, EmployeeLoginHistoryViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
public TableColumn<EmployeeLoginHistoryViewModel, Number> idColumn;
|
public TableColumn<EmployeeLoginHistoryViewModel, Number> idColumn;
|
||||||
public TableColumn<EmployeeLoginHistoryViewModel, String> ipColumn;
|
public TableColumn<EmployeeLoginHistoryViewModel, String> ipColumn;
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
package com.ecep.contract.controller.employee;
|
package com.ecep.contract.controller.employee;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.vo.EmployeeRoleVo;
|
||||||
import com.ecep.contract.model.EmployeeRole;
|
|
||||||
import com.ecep.contract.service.EmployeeRoleService;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class EmployeeTabSkinRole
|
public class EmployeeTabSkinRole
|
||||||
extends AbstEmployeeBasedTabSkin
|
extends AbstEmployeeBasedTabSkin
|
||||||
@@ -41,21 +43,21 @@ public class EmployeeTabSkinRole
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadSelectedRoles() {
|
private void loadSelectedRoles() {
|
||||||
List<EmployeeRole> selectedRoles = getEmployeeService().getRolesByEmployeeId(viewModel.getId().get());
|
List<EmployeeRoleVo> selectedRoles = getEmployeeService().getRolesByEmployeeId(viewModel.getId().get());
|
||||||
controller.rolesField.getTargetItems().setAll(selectedRoles);
|
controller.rolesField.getTargetItems().setAll(selectedRoles);
|
||||||
changed.set(false);
|
changed.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeListView() {
|
private void initializeListView() {
|
||||||
// 非系统内置账户
|
// 非系统内置账户
|
||||||
HashMap<String, Object> params = new HashMap<>();
|
Map<String, Object> params = ParamUtils.builder().build();
|
||||||
List<EmployeeRole> roles = getEmployeeRoleService().findAll(params, Pageable.ofSize(500)).getContent();
|
List<EmployeeRoleVo> roles = getEmployeeRoleService().findAll(params, Pageable.ofSize(500)).getContent();
|
||||||
|
|
||||||
controller.rolesField.getSourceItems().setAll(roles);
|
controller.rolesField.getSourceItems().setAll(roles);
|
||||||
controller.rolesField.setCellFactory(param -> {
|
controller.rolesField.setCellFactory(param -> {
|
||||||
return new ListCell<>() {
|
return new ListCell<EmployeeRoleVo>() {
|
||||||
@Override
|
@Override
|
||||||
protected void updateItem(EmployeeRole item, boolean empty) {
|
protected void updateItem(EmployeeRoleVo item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item == null || empty) {
|
if (item == null || empty) {
|
||||||
setText(null);
|
setText(null);
|
||||||
@@ -66,10 +68,10 @@ public class EmployeeTabSkinRole
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.rolesField.getTargetItems().addListener((ListChangeListener<EmployeeRole>) change -> {
|
controller.rolesField.getTargetItems().addListener((ListChangeListener<EmployeeRoleVo>) change -> {
|
||||||
while (change.next()) {
|
while (change.next()) {
|
||||||
List<? extends EmployeeRole> added = change.getAddedSubList();
|
List<? extends EmployeeRoleVo> added = change.getAddedSubList();
|
||||||
List<? extends EmployeeRole> removed = change.getRemoved();
|
List<? extends EmployeeRoleVo> removed = change.getRemoved();
|
||||||
if (!added.isEmpty() || !removed.isEmpty()) {
|
if (!added.isEmpty() || !removed.isEmpty()) {
|
||||||
changed.set(true);
|
changed.set(true);
|
||||||
}
|
}
|
||||||
@@ -79,9 +81,7 @@ public class EmployeeTabSkinRole
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save() {
|
public void save() {
|
||||||
Employee entity = getEntity();
|
getEmployeeService().getUpdateEmployeeRoles(viewModel.getId().get(), controller.rolesField.getTargetItems());
|
||||||
entity.setRoles(controller.rolesField.getTargetItems());
|
|
||||||
save(entity);
|
|
||||||
loadSelectedRoles();
|
loadSelectedRoles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import org.springframework.context.annotation.Scope;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstEntityController;
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
import com.ecep.contract.model.Employee;
|
|
||||||
import com.ecep.contract.model.EmployeeRole;
|
|
||||||
import com.ecep.contract.service.EmployeeService;
|
import com.ecep.contract.service.EmployeeService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.vm.EmployeeViewModel;
|
import com.ecep.contract.vm.EmployeeViewModel;
|
||||||
|
import com.ecep.contract.vo.EmployeeRoleVo;
|
||||||
|
import com.ecep.contract.vo.EmployeeVo;
|
||||||
|
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.DatePicker;
|
import javafx.scene.control.DatePicker;
|
||||||
@@ -30,7 +30,7 @@ import lombok.Getter;
|
|||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
@FxmlPath("/ui/employee/employee.fxml")
|
@FxmlPath("/ui/employee/employee.fxml")
|
||||||
public class EmployeeWindowController extends AbstEntityController<Employee, EmployeeViewModel> {
|
public class EmployeeWindowController extends AbstEntityController<EmployeeVo, EmployeeViewModel> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(EmployeeWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(EmployeeWindowController.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,7 +60,7 @@ public class EmployeeWindowController extends AbstEntityController<Employee, Emp
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
public Tab rolesTab;
|
public Tab rolesTab;
|
||||||
public ListSelectionView<EmployeeRole> rolesField;
|
public ListSelectionView<EmployeeRoleVo> rolesField;
|
||||||
|
|
||||||
public Tab loginHistoryTab;
|
public Tab loginHistoryTab;
|
||||||
public Tab authBindTab;
|
public Tab authBindTab;
|
||||||
@@ -71,7 +71,7 @@ public class EmployeeWindowController extends AbstEntityController<Employee, Emp
|
|||||||
public TableView<Tab> permissionsTable;
|
public TableView<Tab> permissionsTable;
|
||||||
|
|
||||||
|
|
||||||
public static void show(Employee employee, Window owner) {
|
public static void show(EmployeeVo employee, Window owner) {
|
||||||
EmployeeViewModel model = EmployeeViewModel.from(employee);
|
EmployeeViewModel model = EmployeeViewModel.from(employee);
|
||||||
show(model, owner);
|
show(model, owner);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ package com.ecep.contract.controller.inventory;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
import com.ecep.contract.controller.AbstEntityManagerSkin;
|
||||||
|
import com.ecep.contract.controller.table.cell.InventoryCatalogTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
|
||||||
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
import com.ecep.contract.converter.EntityStringConverter;
|
||||||
import com.ecep.contract.model.Inventory;
|
|
||||||
import com.ecep.contract.model.InventoryCatalog;
|
|
||||||
import com.ecep.contract.service.InventoryCatalogService;
|
import com.ecep.contract.service.InventoryCatalogService;
|
||||||
import com.ecep.contract.service.InventoryService;
|
import com.ecep.contract.service.InventoryService;
|
||||||
import com.ecep.contract.vm.InventoryViewModel;
|
import com.ecep.contract.vm.InventoryViewModel;
|
||||||
|
import com.ecep.contract.vo.InventoryCatalogVo;
|
||||||
|
import com.ecep.contract.vo.InventoryVo;
|
||||||
|
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -21,7 +22,7 @@ import javafx.util.converter.NumberStringConverter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class InventoryManagerSkin extends
|
public class InventoryManagerSkin extends
|
||||||
AbstEntityManagerSkin<Inventory, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController> {
|
AbstEntityManagerSkin<InventoryVo, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController> {
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private InventoryCatalogService catalogService;
|
private InventoryCatalogService catalogService;
|
||||||
@@ -56,13 +57,7 @@ public class InventoryManagerSkin extends
|
|||||||
controller.codeColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCode));
|
controller.codeColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCode));
|
||||||
|
|
||||||
controller.catalogColumn.setCellValueFactory(param -> param.getValue().getCatalog());
|
controller.catalogColumn.setCellValueFactory(param -> param.getValue().getCatalog());
|
||||||
EntityStringConverter<InventoryCatalog> catalogStringConverter = new EntityStringConverter<>();
|
controller.catalogColumn.setCellFactory(param-> new InventoryCatalogTableCell<>(getInventoryCatalogService()));
|
||||||
catalogStringConverter.setInitialized(v -> getInventoryCatalogService().findById(v.getId()));
|
|
||||||
catalogStringConverter.setFormater(InventoryCatalog::getName);
|
|
||||||
catalogStringConverter.setFromString(v -> getInventoryCatalogService().findByName(v));
|
|
||||||
catalogStringConverter.setSuggestion(getInventoryCatalogService()::search);
|
|
||||||
|
|
||||||
controller.catalogColumn.setCellFactory(TextFieldTableCell.forTableColumn(catalogStringConverter));
|
|
||||||
controller.catalogColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCatalog));
|
controller.catalogColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCatalog));
|
||||||
|
|
||||||
controller.specificationColumn.setCellValueFactory(param -> param.getValue().getSpecification());
|
controller.specificationColumn.setCellValueFactory(param -> param.getValue().getSpecification());
|
||||||
@@ -125,7 +120,7 @@ public class InventoryManagerSkin extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTableCreateNewAction(ActionEvent event) {
|
protected void onTableCreateNewAction(ActionEvent event) {
|
||||||
Inventory inventory = getService().save(getService().createNewInstance());
|
InventoryVo inventory = getService().save(getService().createNewEntity());
|
||||||
InventoryViewModel viewModel = InventoryViewModel.from(inventory);
|
InventoryViewModel viewModel = InventoryViewModel.from(inventory);
|
||||||
dataSet.add(viewModel);
|
dataSet.add(viewModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ import org.springframework.context.annotation.Scope;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.ecep.contract.controller.AbstManagerWindowController;
|
import com.ecep.contract.controller.AbstManagerWindowController;
|
||||||
import com.ecep.contract.model.Inventory;
|
|
||||||
import com.ecep.contract.model.InventoryCatalog;
|
|
||||||
import com.ecep.contract.service.InventoryService;
|
import com.ecep.contract.service.InventoryService;
|
||||||
|
import com.ecep.contract.task.InventoryAllSyncTask;
|
||||||
import com.ecep.contract.task.InventorySyncTask;
|
import com.ecep.contract.task.InventorySyncTask;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.InventoryViewModel;
|
import com.ecep.contract.vm.InventoryViewModel;
|
||||||
|
import com.ecep.contract.vo.InventoryCatalogVo;
|
||||||
|
import com.ecep.contract.vo.InventoryVo;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
@@ -27,7 +28,7 @@ import javafx.stage.Stage;
|
|||||||
@Component
|
@Component
|
||||||
@FxmlPath(value = "/ui/inventory/inventory-manager.fxml")
|
@FxmlPath(value = "/ui/inventory/inventory-manager.fxml")
|
||||||
public class InventoryManagerWindowController
|
public class InventoryManagerWindowController
|
||||||
extends AbstManagerWindowController<Inventory, InventoryViewModel, InventoryManagerSkin> {
|
extends AbstManagerWindowController<InventoryVo, InventoryViewModel, InventoryManagerSkin> {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<InventoryViewModel, Number> idColumn;
|
public TableColumn<InventoryViewModel, Number> idColumn;
|
||||||
@@ -35,8 +36,11 @@ public class InventoryManagerWindowController
|
|||||||
public TableColumn<InventoryViewModel, String> nameColumn;
|
public TableColumn<InventoryViewModel, String> nameColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<InventoryViewModel, String> codeColumn;
|
public TableColumn<InventoryViewModel, String> codeColumn;
|
||||||
|
/**
|
||||||
|
* InventoryCatalogVo
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<InventoryViewModel, InventoryCatalog> catalogColumn;
|
public TableColumn<InventoryViewModel, Integer> catalogColumn;
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<InventoryViewModel, String> specificationColumn;
|
public TableColumn<InventoryViewModel, String> specificationColumn;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -76,7 +80,7 @@ public class InventoryManagerWindowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onSyncAction(ActionEvent event) {
|
public void onSyncAction(ActionEvent event) {
|
||||||
InventorySyncTask task = new InventorySyncTask();
|
InventoryAllSyncTask task = new InventoryAllSyncTask();
|
||||||
UITools.showTaskDialogAndWait("同步数据", task, null);
|
UITools.showTaskDialogAndWait("同步数据", task, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.text.NumberFormat;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.ecep.contract.task.InventorySyncTask;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.MessageHolder;
|
import com.ecep.contract.MessageHolder;
|
||||||
@@ -13,12 +14,12 @@ import com.ecep.contract.controller.tab.TabSkin;
|
|||||||
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
import com.ecep.contract.converter.EmployeeStringConverter;
|
||||||
import com.ecep.contract.converter.EntityStringConverter;
|
import com.ecep.contract.converter.EntityStringConverter;
|
||||||
import com.ecep.contract.model.Inventory;
|
|
||||||
import com.ecep.contract.model.InventoryCatalog;
|
|
||||||
import com.ecep.contract.service.InventoryCatalogService;
|
import com.ecep.contract.service.InventoryCatalogService;
|
||||||
import com.ecep.contract.service.InventoryService;
|
import com.ecep.contract.service.InventoryService;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.InventoryViewModel;
|
import com.ecep.contract.vm.InventoryViewModel;
|
||||||
|
import com.ecep.contract.vo.InventoryCatalogVo;
|
||||||
|
import com.ecep.contract.vo.InventoryVo;
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
@@ -31,8 +32,8 @@ import javafx.util.converter.LocalDateTimeStringConverter;
|
|||||||
import javafx.util.converter.NumberStringConverter;
|
import javafx.util.converter.NumberStringConverter;
|
||||||
|
|
||||||
public class InventoryTabSkinBase
|
public class InventoryTabSkinBase
|
||||||
extends AbstEntityBasedTabSkin<InventoryWindowController, Inventory, InventoryViewModel>
|
extends AbstEntityBasedTabSkin<InventoryWindowController, InventoryVo, InventoryViewModel>
|
||||||
implements TabSkin, EditableEntityTableTabSkin<Inventory, InventoryViewModel> {
|
implements TabSkin, EditableEntityTableTabSkin<InventoryVo, InventoryViewModel> {
|
||||||
|
|
||||||
public InventoryTabSkinBase(InventoryWindowController controller) {
|
public InventoryTabSkinBase(InventoryWindowController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
@@ -67,12 +68,7 @@ public class InventoryTabSkinBase
|
|||||||
controller.specificationField.textProperty().bindBidirectional(viewModel.getSpecification());
|
controller.specificationField.textProperty().bindBidirectional(viewModel.getSpecification());
|
||||||
controller.specificationLockField.selectedProperty().bindBidirectional(viewModel.getSpecificationLock());
|
controller.specificationLockField.selectedProperty().bindBidirectional(viewModel.getSpecificationLock());
|
||||||
|
|
||||||
EntityStringConverter<InventoryCatalog> catalogConverter = new EntityStringConverter<>();
|
UITools.autoCompletion(controller.catalogField, viewModel.getCatalog(), getCatalogService());
|
||||||
catalogConverter.setInitialized(v -> getCatalogService().findById(v.getId()));
|
|
||||||
catalogConverter.setFormater(InventoryCatalog::getName);
|
|
||||||
catalogConverter.setSuggestion(getCatalogService()::search);
|
|
||||||
catalogConverter.setFromString(getCatalogService()::findByName);
|
|
||||||
UITools.autoCompletion(controller.catalogField, viewModel.getCatalog(), catalogConverter);
|
|
||||||
|
|
||||||
controller.purchaseTaxRateField.textProperty().bindBidirectional(viewModel.getPurchaseTaxRate(),
|
controller.purchaseTaxRateField.textProperty().bindBidirectional(viewModel.getPurchaseTaxRate(),
|
||||||
new NumberStringConverter());
|
new NumberStringConverter());
|
||||||
@@ -112,13 +108,12 @@ public class InventoryTabSkinBase
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
EmployeeStringConverter employeeStringConverter = getBean(EmployeeStringConverter.class);
|
UITools.autoCompletion(controller.creatorField, viewModel.getCreator(), controller.getEmployeeService());
|
||||||
UITools.autoCompletion(controller.creatorField, viewModel.getCreator(), employeeStringConverter);
|
|
||||||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATE_FORMAT_PATTERN);
|
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATE_FORMAT_PATTERN);
|
||||||
controller.createTimeField.textProperty().bindBidirectional(viewModel.getCreateTime(),
|
controller.createTimeField.textProperty().bindBidirectional(viewModel.getCreateTime(),
|
||||||
new LocalDateStringConverter(dateFormatter, null));
|
new LocalDateStringConverter(dateFormatter, null));
|
||||||
|
|
||||||
UITools.autoCompletion(controller.updaterField, viewModel.getUpdater(), employeeStringConverter);
|
UITools.autoCompletion(controller.updaterField, viewModel.getUpdater(), controller.getEmployeeService());
|
||||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
|
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
|
||||||
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
|
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
|
||||||
controller.updateDateField.textProperty().bindBidirectional(viewModel.getUpdateDate(),
|
controller.updateDateField.textProperty().bindBidirectional(viewModel.getUpdateDate(),
|
||||||
@@ -162,34 +157,24 @@ public class InventoryTabSkinBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onSyncAction(ActionEvent event) {
|
private void onSyncAction(ActionEvent event) {
|
||||||
Inventory inventory = getEntity();
|
InventoryVo inventory = getEntity();
|
||||||
setStatus("开始同步数据...");
|
InventorySyncTask task = new InventorySyncTask();
|
||||||
if (inventory == null) {
|
task.setInventory(inventory);
|
||||||
setStatus("请选择要同步的数据.");
|
UITools.showTaskDialogAndWait("同步数据", task, null);
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!StringUtils.hasText(inventory.getCode())) {
|
|
||||||
setStatus("请填写商品编码.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setStatus("正在同步数据...");
|
|
||||||
MessageHolder holder = (lv, msg) -> setStatus(msg);
|
|
||||||
getService().syncInventory(inventory, holder);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteRowData(Inventory entity) {
|
public void deleteRowData(InventoryVo entity) {
|
||||||
getService().delete(entity);
|
getService().delete(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Inventory loadRowData(InventoryViewModel row) {
|
public InventoryVo loadRowData(InventoryViewModel row) {
|
||||||
return getService().findById(row.getId().get());
|
return getService().findById(row.getId().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Inventory saveRowData(Inventory entity) {
|
public InventoryVo saveRowData(InventoryVo entity) {
|
||||||
return getService().save(entity);
|
return getService().save(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ package com.ecep.contract.controller.inventory;
|
|||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.ecep.contract.controller.contract.ContractWindowController;
|
import com.ecep.contract.controller.contract.ContractWindowController;
|
||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
|
||||||
import com.ecep.contract.model.Contract;
|
|
||||||
import com.ecep.contract.model.Inventory;
|
|
||||||
import com.ecep.contract.service.ContractItemService;
|
import com.ecep.contract.service.ContractItemService;
|
||||||
import com.ecep.contract.service.ContractService;
|
import com.ecep.contract.service.ContractService;
|
||||||
import com.ecep.contract.service.InventoryHistoryPriceService;
|
import com.ecep.contract.service.InventoryHistoryPriceService;
|
||||||
import com.ecep.contract.util.FxmlPath;
|
import com.ecep.contract.util.FxmlPath;
|
||||||
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.ContractViewModel;
|
import com.ecep.contract.vm.ContractViewModel;
|
||||||
import com.ecep.contract.vm.InventoryViewModel;
|
import com.ecep.contract.vm.InventoryViewModel;
|
||||||
|
import com.ecep.contract.vo.ContractVo;
|
||||||
|
import com.ecep.contract.vo.InventoryVo;
|
||||||
|
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
@@ -26,7 +26,7 @@ import lombok.Setter;
|
|||||||
@FxmlPath("/ui/inventory/inventory-contract.fxml")
|
@FxmlPath("/ui/inventory/inventory-contract.fxml")
|
||||||
public class InventoryTabSkinContracts
|
public class InventoryTabSkinContracts
|
||||||
extends
|
extends
|
||||||
AbstEntityTableTabSkin<InventoryWindowController, Inventory, InventoryViewModel, Contract, ContractViewModel>
|
AbstEntityTableTabSkin<InventoryWindowController, InventoryVo, InventoryViewModel, ContractVo, ContractViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
public TableColumn<ContractViewModel, Number> idColumn;
|
public TableColumn<ContractViewModel, Number> idColumn;
|
||||||
public TableColumn<ContractViewModel, String> nameColumn;
|
public TableColumn<ContractViewModel, String> nameColumn;
|
||||||
@@ -90,21 +90,9 @@ public class InventoryTabSkinContracts
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getSpecification(Inventory parent) {
|
public ParamUtils.Builder getSpecification(InventoryVo parent) {
|
||||||
Map<String, Object> params = getSpecification();
|
ParamUtils.Builder params = getSpecification();
|
||||||
params.put("inventory", parent);
|
params.equals("inventory", parent.getId());
|
||||||
|
|
||||||
// return SpecificationUtils.and(getSpecification(), (root, query, builder) -> {
|
|
||||||
// // 创建ContractItem的子查询
|
|
||||||
// Subquery<Integer> subquery = query.subquery(Integer.class);
|
|
||||||
// Root<ContractItem> from = subquery.from(ContractItem.class);
|
|
||||||
// // 子查询选择与指定库存相关的合同ID
|
|
||||||
// subquery.select(from.get("contract").get("id"))
|
|
||||||
// .where(builder.equal(from.get("inventory"), parent));
|
|
||||||
|
|
||||||
// // 主查询筛选ID在子查询结果中的合同
|
|
||||||
// return builder.in(root.get("id")).value(subquery);
|
|
||||||
// });
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user