Compare commits

...

97 Commits

Author SHA1 Message Date
eea4d93ae1 feat(资金计划): 完善资金计划功能并优化界面显示
- 在ProjectFundPlanService中添加payWay和payDate字段映射
- 为资金计划添加项目关联处理
- 优化付款计划表格的列名和显示
- 重构ProjectTabSkinFundPlan的service获取逻辑
2025-10-16 18:25:28 +08:00
71a358fa77 feat(contract): 新增合同发票管理功能
实现合同发票的增删改查功能,包括:
1. 添加ContractInvoiceVo实体类及相关ViewModel
2. 创建合同发票数据库表CONTRACT_INVOICE
3. 实现前后端发票管理服务ContractInvoiceService
4. 开发发票管理界面及标签页
5. 添加发票表格单元格组件
6. 完善销售订单表结构,增加客户联系人字段
7. 更新pom.xml版本至0.0.122-SNAPSHOT

修复销售订单界面搜索字段命名不一致问题
2025-10-16 15:47:33 +08:00
cf0a7e18ea feat(contract): 新增合同内容管理功能
- 添加合同内容窗口控制器及相关视图模型
- 实现合同内容基础信息标签页
- 添加金额计算功能
- 完善合同内容相关字段绑定
- 优化合同内容界面布局
2025-10-16 00:09:01 +08:00
d2e0dc4555 refactor(controller): 移除冗余的service注入,使用BeanContext统一管理
feat(sales-order): 补充SalesOrderVo缺失字段并完善相关功能
feat(sales-order): 添加客户、税率等字段到销售订单界面
refactor(tab-skin): 重构TabSkin基类,统一bean获取方式
fix(fxml): 修正controller包路径和字段绑定
feat(repository): 添加findAllByOrder方法优化查询
2025-10-15 00:40:02 +08:00
1c1ff678a5 feat(contract): 添加采购订单和销售订单的合同标签页实现
实现采购订单和销售订单的合同标签页功能,包括表格列定义、单元格渲染和基本操作。采购订单标签页在合同为付款方式时启用,销售订单标签页在合同为收款方式时启用。两个标签页都支持双击行查看详情和刷新操作,销售订单标签页额外提供搜索功能。
2025-10-14 17:36:55 +08:00
5b3ab3ed00 refactor(util): 重构BeanContext接口及相关实现
将ContextUtils重命名为BeanContext,并统一客户端和服务端的实现
添加DefaultBeanContext作为默认实现
优化Inventory同步任务逻辑,支持WebSocket远程调用
更新tasker_mapper.json添加新的任务映射
移除未使用的syncInventory方法
2025-10-12 22:39:32 +08:00
59d78619da refactor(CloudRkService): 优化导入语句顺序并移除未使用的导入
移除未使用的Bank类导入,并按照标准顺序重新组织导入语句,提高代码可读性和维护性
2025-10-12 12:52:47 +08:00
86e18632aa feat(采购订单): 添加合同条目关联及税率绑定功能
新增采购订单条目与合同条目的关联字段,实现税率和税率锁定的UI绑定
优化采购订单同步逻辑,支持从U8系统获取更多字段信息
调整界面文本显示,修复部分字段绑定问题
2025-10-12 12:49:18 +08:00
ddd9dad945 refactor: 优化采购订单相关功能及代码结构
重构采购订单模块,包括以下改进:
1. 移除PurchaseOrderItemVo中冗余字段
2. 在ContractCtx中添加调试日志
3. 修改InventoryTabSkinContracts和PurchaseOrderTabSkinBillVoucher中的equals比较逻辑
4. 调整PurchaseOrderVo的税率字段类型并添加锁定标志
5. 修正FXML文件中的控制器路径
6. 优化InventoryStringConverter的toString方法格式
7. 在PurchaseBillVoucherItemService中添加凭证查询条件
8. 改进ContractRepairTasker的进度更新逻辑
9. 修复AbstContractRepairTasker中的子合同同步问题
10. 优化PurchaseOrderTabSkinItems的表格列显示
11. 添加InventoryCatalogStringConverter及相关缓存支持
12. 完善PurchaseBillVoucherService的查询逻辑
13. 增强ContractService的库存查询功能
14. 改进PurchaseOrderItemService的合同项查询逻辑
15. 为InventoryService添加缓存支持
16. 优化ContractTabSkinPurchaseOrders的员工列显示
17. 改进ContractTabSkinItemsV2的数量显示逻辑
18. 重构PurchaseOrderViewModel的数据绑定逻辑
2025-10-12 00:52:01 +08:00
333feb0d5a feat: 添加按ID查询的仓库方法并优化合同同步逻辑
添加了多个仓库的按ID查询方法,如findByCompanyId和findAllByContractId
优化了合同同步任务,使用ContractVo替代Contract减少数据库访问
重构了部分服务方法,支持通过ID直接操作,提升性能
修复了ProjectCostService中字段映射错误的问题
2025-10-11 17:56:02 +08:00
553feac0a4 UP 2025-10-11 09:21:14 +08:00
bda92193d4 feat: 添加ContextUtils接口和VendorReBuildFilesTasker类 2025-10-11 09:21:03 +08:00
0dcc9236a8 feat: 实现节假日服务功能并更新项目版本
refactor(CloudRkManagerWindowController): 简化任务初始化逻辑
feat(HolidayService): 添加节假日调整和检查功能
feat(HolidayTable): 实现Voable接口并添加toVo方法
feat(HolidayTableRepository): 添加节假日查询方法
docs: 添加服务器端Service类规则文档
build: 更新项目版本至0.0.101-SNAPSHOT
2025-10-10 10:21:51 +08:00
e49952a63c docs(entity_rules): 更新实体规则文档中的注解和接口描述
- 合并特定领域接口的描述为单一条目
- 明确指定@Table注解的schema默认值为"supplier_ms"
2025-10-09 18:32:50 +08:00
35e8fba805 docs(实体规则): 更新实体类目录结构和接口规范
- 将业务领域实体类按领域划分到不同目录
- 明确基础实体类和特定领域接口的存放位置
- 补充业务领域目录结构示例
2025-10-09 18:32:16 +08:00
c4eec0a9dd refactor(model): 重构模型类包结构并优化序列化处理
重构模型类包结构,将模型类按功能模块划分到不同的子包中。优化序列化处理,为VO类添加serialVersionUID并实现Serializable接口。移除部分冗余的serialVersionUID字段,简化模型类代码。同时修复UITools中空值处理的问题,并更新pom版本至0.0.100-SNAPSHOT。

- 将模型类按功能模块划分到ds子包中
- 为VO类添加序列化支持
- 移除冗余的serialVersionUID字段
- 修复UITools空值处理问题
- 更新项目版本号
2025-10-09 18:27:48 +08:00
51b8c16798 feat: 添加多个实体类实现基础接口和Voable接口
添加公司、项目、合同、供应商、客户等相关实体类,实现IdentityEntity、BasedEntity、CompanyBasedEntity等基础接口和Voable接口
包含公司文件、合同文件、供应商文件、客户文件等实体类
实现equals、hashCode和toVo方法
添加相关字段和JPA注解
2025-10-09 18:26:04 +08:00
49413ad473 refactor(service): 统一Service缓存为VO对象并优化关联实体处理
重构Service类实现,将QueryService泛型参数调整为VO类型,确保缓存VO对象而非实体。优化关联实体处理逻辑,减少重复代码。修改findById方法返回VO对象,新增getById方法获取实体。更新相关调用点以适配新接口。

调整WebSocket处理、控制器及Service实现,确保数据类型一致性。完善文档记录重构过程及发现的问题。为后续优化提供基础架构支持。
2025-09-29 19:31:51 +08:00
64471b46f8 docs: 更新server模块service缓存调整为Vo对象的文档标题
将所有相关文档标题从"IEntityService接口泛型修改任务"统一修改为"Server模块Service缓存调整为Vo对象",保持文档命名一致性
2025-09-28 19:19:01 +08:00
1f354853b0 refactor(service): 调整接口泛型参数并优化缓存策略
docs(task): 更新接口泛型修改相关文档

- 重构QueryService接口,移除未使用的Specification相关方法
- InventoryCatalogService实现IEntityService和QueryService接口
- 新增delete方法实现并调整缓存配置
- 删除过时的任务文档并重新组织文档结构
- 更新ALIGNMENT、CONSENSUS等文档,添加WebSocket服务兼容性说明
2025-09-28 19:11:53 +08:00
b03b5385a5 refactor(service): 修改IEntityService泛型为VO类型并优化缓存策略
重构所有注解@CacheConfig的Service类,将IEntityService泛型从实体类改为VO类
实现实体与VO之间的转换逻辑,使用VO替代实体进行缓存以避免序列化问题
更新相关依赖组件和测试用例,确保功能完整性和系统兼容性
优化Redis缓存配置,清理旧缓存数据并验证新缓存策略有效性
2025-09-28 18:19:00 +08:00
df6188db40 feat: 新增WebSocket任务管理器及相关任务实现
实现WebSocketServerTaskManager用于管理WebSocket任务,并添加多个任务类:
- CompanyContext和CloudRkContext接口定义
- WebSocketServerTasker接口及多个具体任务实现类
- ContractVerifyTasker合同验证任务
- ContractRepairTasker合同修复任务
- CompanyCustomerRebuildFilesTasker客户文件重建任务
- CompanyVerifyTasker企业验证任务
- CustomerFileMoveTasker客户文件移动任务
- CompanyCompositeUpdateTasker企业综合更新任务
- ProjectCostImportItemsFromContractsTasker项目成本导入任务
- 其他相关辅助任务类

这些任务类通过WebSocket与前端交互,实现各种业务功能
2025-09-28 18:18:32 +08:00
510952d72e feat: 添加企业文件管理功能及相关任务类
refactor: 重构企业文件验证和移动逻辑

fix: 修复企业合规验证逻辑及路径处理问题

docs: 添加VerifyContext工具类及相关文档

style: 优化代码格式及注释
2025-09-26 19:40:34 +08:00
a4db8a1644 docs: 更新VoableService接口实现分析文档
将详细的服务实现列表转换为表格形式,简化文档结构并提高可读性
2025-09-26 12:50:01 +08:00
42a8f9ab30 refactor(service): 实现VoableService接口以统一VO与实体映射逻辑
refactor(model): 重构实体类与VO类的字段映射关系
style: 调整代码格式与注释
fix: 修复部分字段映射错误
2025-09-26 12:31:08 +08:00
045a1e9eed docs: 添加Service层合规性分析报告文档
添加Contract-Manager项目Service层合规性分析报告,评估各Service实现是否符合规范要求,并列出不符合项和改进建议
2025-09-26 12:16:17 +08:00
97a2586c21 更新策略 2025-09-25 19:31:55 +08:00
45f7b611c5 feat: 实现VoableService接口并重构相关服务
refactor: 优化WebSocket通信和任务处理逻辑

fix: 修复客户和供应商路径选择功能

docs: 更新任务通信规则文档

build: 更新项目版本至0.0.86-SNAPSHOT

style: 清理无用导入和日志输出

test: 添加CustomerFileMoveTasker测试类

chore: 更新tasker_mapper.json注册信息
2025-09-25 18:57:17 +08:00
bf90117116 docs: 添加客户端模块Service层规则文档
添加客户端模块Service层的架构设计、缓存管理、数据操作模式等详细规则文档,总结项目中的最佳实践和规范要求
2025-09-25 17:56:36 +08:00
928d10c681 Merge branch 'main' of http://10.84.210.110/songqq/contract-manager 2025-09-25 11:02:48 +08:00
fa6920806d refactor(客户文件): 重构客户文件相关任务处理逻辑
- 移除CompanyCustomerFileService中未实现的searchEvaluationFile方法
- 删除已不再使用的tasker_mapper.json配置文件
- 重构CompanyCustomerEvaluationFormUpdateTask,移除无用日志记录
- 更新客户端与服务端Tasker通信文档中的文件路径格式
- 重构CustomerTabSkinFile中的文件重建和日期计算逻辑,改为直接调用服务方法
2025-09-25 11:02:45 +08:00
c21269975d 更新 README.md 2025-09-25 10:08:17 +08:00
ad42a49858 docs(task): 更新任务通信规则文档并添加任务注册描述
添加任务注册信息的描述字段到tasker_mapper.json
完善WebSocket通信机制文档,补充核心组件说明
修正属性同步机制中的空指针问题
优化代码格式和注释
2025-09-25 09:56:27 +08:00
2057c3ca67 feat: 实现客户端与服务器端Tasker通信机制及文件管理功能
refactor: 重构Tasker基类与服务获取逻辑
fix: 修复文件路径显示问题及任务注册加载机制
docs: 添加客户端与服务器端Tasker通信规则文档
style: 优化代码格式与日志输出
build: 添加tasker_mapper.json配置文件
chore: 清理无用代码与文件
2025-09-25 00:14:34 +08:00
dc764e6ed8 feat(表格单元格): 添加文件路径表格单元格基础类 2025-09-25 00:14:13 +08:00
09b0da498b feat(service): 实现国际化支持并优化Service层
重构文件类型相关Service以支持国际化查询
添加findOneByLang辅助方法统一查询逻辑
实现StringConverter支持UI控件显示
优化缓存配置和查询性能
新增UnitStringConverter和CustomerCatalogStringConverter
完善文档和测试用例
2025-09-24 16:20:49 +08:00
45eed8281f docs 2025-09-24 00:11:21 +08:00
7b023fd07b docs: 添加数据库结构文档和任务说明文档
添加多个数据库表结构SQL文件,包括供应商评分、企业报告等JSON数据模板
添加任务说明文档,包括枚举类型本地化实现方案、VO创建指南和异步任务监控方案
删除无用的CustomerFileTypeLocalRepository.java文件
2025-09-24 00:11:05 +08:00
9561ad99e6 refactor(vendor): 重构供应商相关类命名和包结构
将CompanyVendor前缀的类重命名为Vendor前缀,优化包结构
调整供应商相关控制器、服务、仓库类的命名和位置
修复TableViewUtils中的类型安全警告
更新FXML文件中的控制器引用路径
2025-09-23 23:37:44 +08:00
9c3306eea3 feat: 添加供应商管理相关功能及数据库表结构
新增供应商名录管理功能,包括合格供应商名录、供应商文件、供应商关联实体等模块。主要变更包括:

1. 添加COMPANY_VENDOR_ENTITY表的CREATOR_ID、MODIFIER_ID和MODIFY_DATE字段
2. 实现供应商同步任务类(VendorClassSyncTask等)
3. 新增供应商相关VO类(VendorApprovedVo、VendorFileVo等)
4. 添加供应商相关Repository接口(VendorRepository、VendorFileRepository等)
5. 实现供应商相关服务类(VendorApprovedService、VendorFileService等)
6. 添加供应商管理界面控制器及皮肤类
7. 新增供应商文件类型枚举和本地化配置
2025-09-23 23:37:04 +08:00
71d3ecab52 CompanyVendor 改为 Vendor 2025-09-23 22:50:30 +08:00
5919636c04 feat(供应商管理): 新增供应商实体类及VO转换方法
添加CompanyVendor实体类,包含供应商基本信息、关联关系和VO转换功能
2025-09-23 22:50:16 +08:00
57fbae90c5 feat: 实现文件类型枚举的本地化支持
新增文件类型枚举的本地化功能,包括供应商、项目、公司、合同和客户文件类型。添加了相关的SQL表结构、Repository、Service、ViewModel和StringConverter实现。同时更新了文档说明如何创建和使用枚举类型的本地化功能。

修改了客户文件类型的相关代码,统一使用CustomerFileTypeLocalVo替代原有的CompanyCustomerFileTypeLocal,优化了代码结构和一致性。添加了文件类型枚举的缓存支持,提高了性能。

更新了create_enum.md文档,详细说明了文件类型枚举本地化的实现方式和相关组件清单。
2025-09-23 22:34:59 +08:00
4b8c1d4038 refactor(文件类型): 重构文件类型相关仓库和服务
- 将CompanyVendorFileTypeLocalRepository重命名为VendorFileTypeLocalRepository
- 新增CustomerFileTypeLocalRepository
- 更新VendorFileTypeService使用新的仓库名称
- 新增CustomerFileTypeLocal实体类
- 更新文档结构
2025-09-23 18:42:46 +08:00
543311c676 refactor(controller): 重构枚举类型相关代码,优化类型转换和显示逻辑
重构枚举类型的处理逻辑,统一使用Vo对象替代Model对象
- 修改ComboBoxUtils初始化逻辑,支持更多类型转换
- 实现VendorCatalogStringConverter用于类型转换
- 更新ContractFileTypeListCell和ContractFileTypeTableCell显示逻辑
- 调整VendorTabSkinFile和CompanyVendorTabSkinBase使用新的类型转换方式
- 更新相关服务类接口,添加类型转换方法
- 修改FXML文件添加类型选择控件
2025-09-23 18:24:00 +08:00
73cbb4e19e feat: 添加合同文件类型相关类和文档
添加了VendorCatalogStringConverter转换器类、ContractFileTypeListCell列表单元格类和ContractFileTypeTableCell表格单元格类,并创建了相关文档文件
2025-09-23 18:21:26 +08:00
515b255567 feat: 添加供应商类型本地化支持及优化表格单元格显示
refactor: 重构供应商类型相关服务及控制器
fix: 修复供应商类型表格单元格初始化问题
style: 优化代码格式及导入顺序
2025-09-23 14:12:09 +08:00
386b6d01b4 feat: 添加项目文件和客户文件类型服务类
添加 ProjectFileTypeService 和 CompanyCustomerFileTypeService 服务类,用于处理文件和客户类型相关逻辑
2025-09-23 14:11:49 +08:00
39dbce013f feat(converter): 实现通用枚举转换器和供应商类型转换器
添加EnumEntityStringConverter作为通用枚举转换基类
实现VendorTypeStringConverter用于供应商类型本地化转换
在VendorTypeService中添加findByLocaleAndValue方法支持转换器
优化ComboBoxUtils的绑定逻辑使其支持可选属性
新增VendorCatalogService提供供应商目录CRUD功能
2025-09-22 23:54:50 +08:00
b84e011857 feat: 添加枚举和供应商类型转换器及供应商目录服务
添加三个新类:
1. EnumEntityStringConverter - 枚举实体字符串转换器
2. VendorTypeStringConverter - 供应商类型字符串转换器
3. VendorCatalogService - 供应商目录服务
2025-09-22 23:54:23 +08:00
866e08224a refactor(vo): 重构VO类及相关模型,添加Voable接口实现
feat(constant): 添加WebSocket错误码常量
docs(model): 为模型类添加注释
fix(service): 修复ProductUsageService缓存键问题
refactor(converter): 重构字符串转换器,移除EntityStringConverter依赖
feat(tab): 添加ComboBoxUtils工具类,优化下拉框初始化
style: 移除无用导入和字段
2025-09-22 23:11:21 +08:00
8aac509e51 feat(vendor): 添加VendorTypeService基础类结构 2025-09-22 23:10:45 +08:00
35a15f4702 refactor(contract): 重构客户文件类型相关代码,统一命名和继承结构
- 将 CompanyCustomerFileType 重命名为 CustomerFileType
- 统一相关 VO 和 model 的继承结构,使用 BaseEnumEntity
- 更新所有引用点,保持代码一致性
- 优化表格单元格显示逻辑,使用专用单元格工厂
2025-09-22 17:25:24 +08:00
3c3003fdf3 feat(contract): 添加供应商和客户文件类型相关类
添加VendorFileTypeLocalVo、CustomerFileType枚举和CustomerFileTypeLocalVo类,用于处理文件类型分类和本地化展示
2025-09-22 17:25:03 +08:00
35b33d401b feat: 添加VendorGroupRequireFileTypeVo及相关服务功能
refactor: 重构多个服务类和方法,优化代码结构
fix: 修复PermissionVo中code字段更名为key的问题
docs: 更新create_vo.md文档,添加新创建的VO记录
perf: 优化WebSocketClientService中的session关闭逻辑
style: 清理无用导入和注释,统一代码格式
2025-09-21 23:08:34 +08:00
039d753bab refactor(vo): 重构VO对象结构,统一字段命名和接口实现
重构所有VO对象,统一字段命名规范,移除冗余字段,优化接口实现
新增Voable接口用于VO对象转换
调整BaseViewModel和ProjectBasedViewModel接口定义
更新相关服务和控制器以适应VO对象变更
2025-09-21 17:47:52 +08:00
07c3f39a95 feat(vo): 新增并更新多个VO类实现ContractBasedVo接口
新增CloudTycVo、CloudYuVo、ExtendVendorInfoVo等VO类
更新SalesOrderVo、PurchaseOrderVo等实现ContractBasedVo接口
统一布尔类型字段为boolean并设置默认值false
2025-09-18 09:19:45 +08:00
f113cd8c48 feat(contract): 添加ContractBasedVo接口作为合同相关VO的基类 2025-09-18 09:19:29 +08:00
2752828094 refactor(client): 重构银行和公司相关代码
- 更新了多个类中的导入语句,替换了模型类为对应的VO类
- 优化了部分方法的参数和返回类型,使用VO类替代模型类
- 重构了自动补全功能,使用统一的泛型方法
- 添加了缓存注解,提高了数据访问性能
- 优化了部分代码结构,提高了可维护性
2025-09-18 09:08:57 +08:00
d0645c33f1 feat(common): 新增多个 VO 类
- 新增了多个与公司、合同、项目、库存等相关的 VO 类
- 这些类用于数据传输和接口定义,提高了系统的可维护性和扩展性
- 使用了 Lombok 注解,简化了类的编写
2025-09-18 08:45:08 +08:00
588779d611 feat(contract): 新增合同管理相关 VO 类
- 新增 CompanyContractVo 类,用于公司合同信息的传输
- 新增 CompanyCustomerEntityVo 类,用于公司客户实体信息的传输
- 新增 CompanyCustomerEvaluationFormFileVo 类,用于公司客户评估表文件信息的传输
2025-09-18 02:03:21 +08:00
9ffaac39cb feat(common): 新增 BankVo、CompanyBlackReasonVo 和 CompanyContactVo 类
- 新增 BankVo 类,用于表示银行信息
- 新增 CompanyBlackReasonVo 类,用于表示公司黑名单原因
- 新增 CompanyContactVo 类,用于表示公司联系人信息
2025-09-18 02:03:01 +08:00
9ef90f98c1 feat(contract): 添加公司银行账户价值对象
新增 CompanyBankAccountVo 类,用于表示公司银行账户相关信息。该类包含以下属性:
- id: 主键
- companyId:公司 ID,关联 CompanyVo- bankId: 银行 ID,关联 BankVo
- account: 银行账号
- openingBank: 开户行
- description: 描述信息
- active: 账户状态

此添加为合同模块提供了新的数据传输对象,有助于管理和展示公司银行账户信息。
2025-09-18 02:01:35 +08:00
c0e4916474 refactor: 重构供应商文件类型枚举及相关服务
feat: 为多个服务添加缓存支持
fix: 修复WebSocket任务管理和远程调用异常处理
refactor: 重命名CompanyVendorFileType为VendorFileType
refactor: 优化项目成本导入任务实现
fix: 修复ContractTabSkinBase中的空指针问题
refactor: 统一WebSocket客户端任务调用接口
2025-09-17 22:28:17 +08:00
7560250036 feat(contract): 添加供应商文件类型相关类及服务实现
添加供应商文件类型枚举、模型、视图模型及服务类
实现供应商文件类型的缓存查询、保存和删除功能
2025-09-17 22:27:05 +08:00
c42ff7501d refactor: 重构WebSocket服务及相关实体类
重构WebSocket服务名称从WebSocketService改为WebSocketClientService,并实现Serializable接口
添加WebSocket常量定义和消息处理实现
优化实体类equals和hashCode方法
修复控制器路径和日志配置
添加查询服务和任务接口方法
2025-09-17 11:45:50 +08:00
30deb0a280 feat: 实现WebSocket通信框架及任务管理功能
新增WebSocket客户端和服务端通信框架,包括会话管理、心跳检测和自动重连机制
添加任务管理器用于处理WebSocket任务创建和执行
实现消息回调处理和错误处理机制
重构销售类型服务并添加缓存支持
移除旧的销售类型服务实现
2025-09-17 11:44:39 +08:00
ada539bebf refactor(controller): 重构控制器类名和路径,优化代码结构
feat(service): 新增QueryService接口实现,支持通用查询功能

docs(util): 完善ProxyUtils工具类的注释说明

fix(model): 修复CustomerCatalog实现IdentityEntity接口

style: 优化代码格式和导入顺序

perf(util): 提升FileUtils工具类功能,新增文件处理方法

chore: 更新README.md文件,补充UI资源路径说明

build: 更新pom.xml文件中的mainClass配置

test: 调整测试类命名和路径

ci: 更新CI配置文件中的类引用

refactor(controller): 重构表格单元格异步更新逻辑

docs(constant): 新增常量定义和注释

style: 统一代码风格和命名规范

refactor(service): 重构服务类继承关系

perf(controller): 优化表格数据加载性能

fix(service): 修复文件类型服务缓存问题

docs(model): 完善视图模型类注释

refactor(util): 重构文件工具类方法

style: 清理无用导入和代码

chore: 更新.gitignore文件

build: 调整项目依赖配置

refactor(controller): 重构控制器基类

perf(service): 优化查询服务性能

fix(controller): 修复表格数据加载异常

docs: 更新代码注释和文档

style: 统一代码缩进和格式
2025-09-13 01:02:43 +08:00
5edb44f619 feat: 添加新模块的基础类和主应用类
添加了多个基础服务类和视图模型类,包括SysConfViewModel、CompanyConstant等
实现了客户端和服务端的主应用类,包含启动配置和默认连接参数
2025-09-12 21:29:59 +08:00
98e48c520f feat(proxy): 实现代理对象初始化检查与懒加载机制
添加ProxyUtils工具类用于检查代理对象初始化状态
实现代理对象创建和标记初始化功能
添加ProxyObjectDeserializerModifier处理反序列化时的代理对象创建
修改WebSocketService错误消息字段从errorMsg改为message
实现ContractItemService.findAllByInventory方法
优化ContractService查询条件处理并添加缓存支持
重构InventoryTabSkinHistoryPrice的service获取方式
2025-09-12 16:00:45 +08:00
422994efcd refactor: 重构服务依赖注入和上下文管理
移除硬编码的服务注入,改为使用缓存机制动态获取Bean
优化上下文类结构,统一服务获取方式
添加PageContent类支持分页数据封装
实现异步数据加载功能
2025-09-12 12:20:15 +08:00
fc263288e4 feat: 添加合约文件类型服务及错误处理改进
refactor: 重构合约文件类型相关代码,优化错误处理逻辑

fix: 修复WebSocket会话未绑定用户时的错误处理

style: 调整代码格式,提高可读性

docs: 更新部分代码注释

test: 添加合约文件类型服务的测试用例

chore: 移除无用代码,清理项目结构
2025-09-12 00:12:51 +08:00
a1b87de7c0 feat: 添加日志配置和Logback依赖
refactor: 重构实体类equals和hashCode方法

fix: 修复WebSocketService消息发送逻辑

style: 格式化代码和优化导入

docs: 更新JacksonConfig日期序列化格式

test: 添加CompanyFilePathTableCell测试类

chore: 清理无用代码和注释
2025-09-11 19:44:28 +08:00
375de610ef refactor(client): 重构服务类继承关系并统一使用QueryService
重构所有服务类,使其继承自QueryService接口,统一数据查询逻辑。同时为服务类添加@Service注解,确保Spring容器管理。更新相关FXML文件的控制器路径,从manager.ds调整为controller目录结构。调整pom.xml版本号至0.0.84-SNAPSHOT。新增MessageNotitfication和SimpleMessage消息类,提供基础消息结构支持。
2025-09-11 00:06:22 +08:00
23e1f98ae5 feat: 实现基于JSON的登录API和安全认证
refactor: 重构登录逻辑和会话管理

fix: 修复会话ID类型和WebSocket连接问题

build: 更新项目版本号和添加Servlet API依赖

style: 清理无用导入和注释代码
2025-09-08 17:46:48 +08:00
3b90db0450 refactor: 移除Hibernate依赖并重构代理对象初始化检查逻辑
feat(controller): 新增多个任务类用于合同和客户相关操作
feat(service): 新增ProxyUtils工具类替代Hibernate.isInitialized检查
refactor(controller): 重构多个控制器和皮肤类,使用ProxyUtils替代Hibernate
refactor(service): 重构服务类,移除Hibernate依赖并优化方法实现
fix(controller): 修复表格单元格初始化逻辑,确保代理对象正确加载
chore: 更新项目版本号至0.0.58-SNAPSHOT
docs: 添加MyProperties类用于管理下载路径配置
2025-09-06 17:22:12 +08:00
effd7b103c feat: 新增多个服务类及工具类,重构部分代码结构
重构服务类结构,将分散的服务统一整合到service包下
新增ProjectConstant常量类及多个实体服务类
添加SecurityUtils安全工具类和BeanCacher工具类
优化部分UI控件和转换器的实现
2025-09-06 13:43:52 +08:00
0e444508ff feat: 重构员工控制器并优化JSON序列化配置
refactor(EmployeeController): 重命名EmployyeController为EmployeeController并优化代码结构
feat(EmployeeController): 添加@JsonIgnoreProperties注解解决循环引用问题
feat(JacksonConfig): 新增Jackson配置类处理Hibernate代理和循环引用
fix(EmployeeService): 修复缓存注解格式问题
feat(Employee): 添加@JsonIgnoreProperties注解忽略可能导致循环引用的字段
feat(EmployeeRole): 添加@JsonIgnore注解忽略关联字段
fix(application.properties): 调整Redis缓存配置和错误处理设置
refactor(IndexController): 移除错误处理方法
feat(GlobalExceptionHandler): 新增全局异常处理类
refactor(SecurityConfig): 优化安全配置并启用方法级安全注解
refactor(AbstractCtx): 优化日期时间处理方法
build: 更新项目版本至0.0.53-SNAPSHOT
2025-09-04 16:06:47 +08:00
acb63116d5 docs: 优化README.md的格式和可读性
调整项目介绍中的模块说明格式,使用更清晰的列表展示方式
2025-09-03 23:11:54 +08:00
72f8e2e209 Merge branch 'main' of http://10.84.210.110/songqq/contract-manager 2025-09-03 23:09:48 +08:00
b927f9d572 build: 添加FixPackageNames相关编译文件
添加FixPackageNames.class及其内部类FixPackageNames$1.class的编译输出文件
2025-09-03 23:09:40 +08:00
a2f5e4864b 拆分模块 2025-09-03 20:56:44 +08:00
08cc2c29a5 拆分模块 2025-09-03 20:48:39 +08:00
7d684a5006 删除 null 2025-08-31 23:10:08 +08:00
8139a45f06 更新 docs/异步任务监控实现方案.md 2025-08-31 23:09:54 +08:00
cca51c6fcc feat(project): 新增项目资金计划功能 #2
添加项目资金计划模块,包括以下内容:
- 新增项目资金计划实体类及相关数据库表结构
- 实现资金计划Repository和服务层
- 添加资金计划Tab页及FXML界面
- 实现从合同付款计划同步资金计划功能
- 添加资金计划表格展示及更新操作
2025-08-28 19:54:20 +08:00
1514cb0f9f refactor(CloudTycService): 优化配置获取方式并移除多余空行
将SysConfRepository替换为SysConfService以获取配置参数,提升代码可维护性
清理类中多余的空行,保持代码整洁
2025-08-28 16:07:03 +08:00
cf73769ef2 refactor(配置管理): 重构配置绑定与保存逻辑
将配置绑定逻辑抽象到StringConfig类中,统一处理配置的保存与控件绑定
提取配置键常量到对应服务类中,提高代码可维护性
使用LocalDateTime替代Instant处理时间字段
简化SysConfRepository继承结构,使用自定义基础仓库
优化SysConfWindowController的配置管理逻辑,减少重复代码
2025-08-28 15:55:24 +08:00
c793c0925e refactor(配置管理): 重构配置绑定组件结构并添加布尔类型支持
将原有配置绑定组件重构为泛型抽象基类,统一处理不同类型配置的绑定逻辑。新增BooleanConfig支持布尔类型配置,并优化了LocalDateTimeConfig的转换器处理。

重构后的AbstractConfigBounder提供通用属性绑定和变更处理,子类只需实现特定类型的双向绑定逻辑。同时调整了相关UI界面,添加了布尔配置开关控件。
2025-08-28 15:09:54 +08:00
c69d3f1af2 refactor(ui): 重构BaseController及相关组件,优化代码结构和性能
重构BaseController,移除冗余属性,引入bean缓存机制
新增LocalDateTimeConfig类处理日期时间配置
优化AbstEntityManagerSkin的分页和表格数据处理逻辑
调整ContractTabSkinBase的依赖注入方式,使用缓存bean
统一日期、数字和货币的格式化处理,使用CurrentEmployee的配置
2025-08-27 19:36:30 +08:00
f810532824 fix: 将 AsyncUpdateTableCell 的日志级别从 debug 调整为 warn
修改日志级别以减少不必要的调试信息输出,仅保留警告及以上级别的日志
2025-08-27 14:44:45 +08:00
fb28bac53a refactor(ui): 优化表格数据加载和异步单元格更新逻辑
修复表格高度调整时频繁触发数据加载的问题,增加调整阈值判断
改进异步单元格更新逻辑,增加取消机制和状态检查
重构常量命名,使用更清晰的KEY_SYNC_BY前缀
添加日志记录和注释说明
2025-08-27 14:43:55 +08:00
9ff84ebe8a refactor(u8配置): 重构配置管理相关代码
将配置管理相关代码重构为ConfigBounder接口及其实现类,提取公共逻辑到AbstractConfigBounder基类
修复公司创建日期配置的显示问题,使用更直观的中文描述
将合同同步相关配置常量集中到ContractCtx中管理
2025-08-25 19:40:07 +08:00
6711657663 feat(u8): 添加用友U8配置窗口和数据迁移功能
新增用友U8配置窗口,支持日期和文本配置项的编辑与保存。实现从CloudInfo到CloudYu的数据迁移任务,优化任务执行方式。重构多个同步任务类继承Tasker基类,统一任务处理逻辑。扩展YongYouU8Service功能,添加配置相关接口。调整UI布局和菜单项,增加配置入口。

refactor: 重命名CompanyTableCell为EmployeeRoleTableCell

style: 清理无用导入和格式化代码

fix: 修复ContractTypeSyncTask中分类和方向字段设置错误
2025-08-25 18:57:17 +08:00
17e326b35c refactor(ui): 重命名并移动EvaluationFileTableCell为CompanyTableCell
将EvaluationFileTableCell从project.controller包移动到table.cell包,并重命名为CompanyTableCell。同时修改其实现以使用CompanyService而非CompanyCustomerFileService,使其更符合新的业务需求。
2025-08-25 18:56:55 +08:00
danyz
fa25130c9f Merge branch 'main' of http://10.84.210.110/songqq/contract-manager into main 2025-08-25 00:41:45 +08:00
danyz
b0b67b5d7f feat(任务监控): 添加Executor信息监控面板并重构界面
重构任务监控界面布局,将演示任务功能移至监控窗口工具栏
新增Executor信息监控面板,显示线程池详细状态信息
移除主界面中的演示任务按钮,更新项目版本号
2025-08-25 00:41:34 +08:00
1523 changed files with 73455 additions and 31033 deletions

View File

@@ -0,0 +1,157 @@
# 客户端 Controller 类规则
## 1. 目录结构
客户端控制器位于`client/src/main/java/com/ecep/contract/controller/`目录下,按业务模块组织:
- **根目录**:包含基础控制器、抽象控制器和主窗口控制器
- **业务子包**:按业务领域组织,如`company/``project/``contract/``vendor/`
- **tab/子包**包含所有Tab皮肤控制器和相关接口、抽象类
- **table/子包**:包含表格相关控制器和单元格实现
## 2. 命名规范
- **基础控制器**`BaseController.java`
- **抽象控制器**:以`Abst`开头,如`AbstEntityController.java``AbstManagerWindowController.java`
- **窗口控制器**:以`WindowController`结尾,如`HomeWindowController.java`
- **管理窗口控制器**:以`ManagerWindowController`结尾,如`CompanyManagerWindowController.java`
- **Tab皮肤控制器**:以`TabSkin`结尾,如`CompanyTabSkinBase.java`
- **管理器皮肤**:以`ManagerSkin`结尾,如`CompanyManagerSkin.java`
- **组件控制器**:如`OkHttpLoginController.java`
## 3. 继承关系
控制器类遵循以下继承层次结构:
- `BaseController`:所有控制器的基础类,提供窗口显示、异常处理等通用功能
- `AbstEntityBasedController`:基于实体的控制器抽象类
- `AbstManagerWindowController`:管理窗口控制器的抽象类,包含搜索、分页等功能
- `AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>>`:实体详情窗口控制器的抽象类
- 具体业务窗口控制器,如`CompanyWindowController``ProjectWindowController`
Tab相关控制器继承关系
- `Skin`:皮肤接口基础
- `TabSkin`Tab皮肤接口
- `AbstGenericTabSkin<C extends BaseController>`通用Tab皮肤抽象类
- `AbstEntityBasedTabSkin<C extends AbstEntityController<T, V>, T extends IdentityEntity, V extends IdentityViewModel<T>>`基于实体的Tab皮肤抽象类
- 具体业务Tab皮肤控制器`CompanyTabSkinBase.java``ContractTabSkinFiles.java`
## 4. 注解使用
客户端控制器类应使用以下注解:
- `@Component`声明为Spring组件使其可被Spring容器管理
- `@Scope("prototype")`:设置为原型作用域,确保每次请求创建新实例
- `@Lazy`:延迟加载,提高应用启动性能
- `@FxmlPath("/ui/[业务模块]/[文件名].fxml")`指定对应的FXML文件路径
- `@Autowired`:自动注入依赖的服务层组件
## 5. FXML文件规范
- **文件位置**FXML文件通常位于`/client/src/main/resources/ui/`目录下,按业务模块组织子目录
- **命名规范**:使用小写字母和下划线,如`home.fxml``company/company.fxml`
- **关联方式**:通过`@FxmlPath`注解指定控制器对应的FXML文件路径
- **组件ID**FXML文件中组件的id应与控制器中对应的字段名保持一致驼峰命名法
## 6. 控制器方法规范
### 6.1 初始化方法
- `initialize()`控制器初始化方法用于设置UI组件初始状态、绑定事件监听器等
- 此方法会在FXML加载完成后自动调用
### 6.2 窗口生命周期方法
- `onShown(WindowEvent windowEvent)`:窗口显示时触发的方法
- 用于执行窗口显示后的初始化操作,如数据加载、组件状态更新等
- 通常需要调用`super.onShown(windowEvent)`确保父类逻辑执行
### 6.3 窗口显示方法
- `public static void show()`:静态方法,用于便捷地显示控制器对应的窗口
- 通常有多个重载版本支持不同参数如viewModel、owner窗口等
- 内部调用`BaseController.show()`方法实现窗口显示逻辑
### 6.4 Tab相关方法
Tab皮肤控制器包含以下核心方法
- `install()`安装Tab皮肤加载FXML并初始化UI组件
- `initializeUIComponents()`初始化UI组件设置数据绑定和事件监听
- `getTab()`获取对应的Tab对象
- `save()`保存Tab中的数据变更
- `dispose()`:释放资源,清理监听器等
## 7. 字段规范
- **UI组件字段**与FXML文件中的组件id对应使用`public`访问修饰符
- **服务依赖字段**:使用`@Autowired`注解注入,通常使用`private`访问修饰符
- **ViewModel字段**:通常作为控制器的核心数据模型,提供`getter/setter`方法
- **命名规范**:使用驼峰命名法,如`nameField``tabPane``saveBtn`
## 8. 数据绑定与更新
- **ViewModel绑定**控制器通常通过ViewModel与实体数据进行绑定
- **异步数据加载**:使用`CompletableFuture`进行异步数据加载避免阻塞UI线程
- **UI更新**在JavaFX应用线程中更新UI组件可使用`Platform.runLater()`确保线程安全
## 9. 事件处理
- **按钮点击事件**:通过`setOnAction()`方法绑定事件处理器
- **表单字段变更**可通过JavaFX的属性绑定机制监听字段变更
- **Tab切换事件**:实现`onTabShown()`方法处理Tab显示事件
## 10. 异常处理
- 使用`handleException()`方法统一处理异常
- 异常信息通常会显示在状态栏并记录日志
- 异步操作中的异常应通过`exceptionally()``handle()`方法捕获处理
## 11. 最佳实践
- **职责分离**控制器应专注于UI交互和数据展示业务逻辑应委托给服务层
- **避免重复代码**:利用抽象类和接口提取通用逻辑
- **资源管理**:及时释放资源,避免内存泄漏
- **线程安全**确保在JavaFX应用线程中更新UI
- **代码可读性**:添加适当的注释,遵循命名规范
## 12. 示例代码结构
```java
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/[模块名]/[文件名].fxml")
public class [业务]WindowController extends AbstEntityController<[Vo类型], [ViewModel类型]> {
private static final Logger logger = LoggerFactory.getLogger([业务]WindowController.class);
@Autowired
private [业务]Service [业务]Service;
// UI组件字段
public BorderPane root;
public TabPane tabPane;
public TextField nameField;
// ...其他UI组件
public static void show([Vo类型] entity, Window window) {
show([ViewModel类型].from(entity), window);
}
public static void show([ViewModel类型] viewModel, Window window) {
BaseController.show([业务]WindowController.class, viewModel, window);
}
@Override
public void initialize() {
// 初始化UI组件和事件监听
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
// 窗口显示后的逻辑
}
}
```

View 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);
}
}
```

View File

@@ -0,0 +1,146 @@
# 客户端 Service 类规则
## 1. 目录结构
- 所有客户端 Service 类位于 `client/src/main/java/com/ecep/contract/service/` 目录下
- 按业务领域组织,直接放置在 service 包下,不进行子包划分
- 服务类命名与实体类一一对应
## 2. 命名规范
- 服务类命名格式为:`[实体名称]Service.java`
- 例如:`CompanyService.java``ContractService.java``ProjectService.java`
- 基础服务接口命名为:`IEntityService.java``ViewModelService.java`
- 泛型基础服务类命名为:`QueryService.java`
## 3. 继承关系
- 业务服务类通常继承自泛型基础服务类 `QueryService<T, TV>`
- `T` 表示 VO 类型(实现了 IdentityEntity 接口)
- `TV` 表示 ViewModel 类型(实现了 IdentityViewModel<T> 接口)
- `QueryService` 实现了 `ViewModelService<T, TV>` 接口
- `ViewModelService` 继承了 `IEntityService<T>` 接口
- 特定场景下可以不继承 `QueryService`,直接实现所需接口或创建独立服务类
```java
@Service
@CacheConfig(cacheNames = "company")
public class CompanyService extends QueryService<CompanyVo, CompanyViewModel> {
// 业务方法实现
}
```
## 4. 注解使用
- **@Service**:标记为 Spring 服务组件,使其可被自动发现和注入
- **@CacheConfig**:配置缓存名称,通常与服务类名对应
- **@Cacheable**标记方法结果可缓存需指定缓存键key
- **@CacheEvict**:标记方法执行后清除缓存,可指定缓存键或清除所有
- **@Caching**:组合多个缓存操作(如同时清除多个缓存条目)
- **@Autowired**:用于自动注入依赖的其他服务
```java
@Service
@CacheConfig(cacheNames = "contract")
public class ContractService extends QueryService<ContractVo, ContractViewModel> {
@Autowired
private SysConfService confService;
@Cacheable(key = "#p0")
public ContractVo findById(Integer id) {
return super.findById(id);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'code-'+#p0.code")
})
public ContractVo save(ContractVo contract) {
return super.save(contract);
}
}
```
## 5. 缓存机制
- 每个服务类应有独立的缓存名称空间
- 缓存键key应具有唯一性通常使用 ID、代码或名称等唯一标识
- 保存和删除操作时应清除相关缓存,保持数据一致性
- 可使用 SpEL 表达式动态生成缓存键
- 频繁查询的数据应考虑缓存,提高性能
## 6. 异步调用机制
- 使用 `async()` 方法进行异步远程调用
- 方法参数通常包括:方法名、参数值、参数类型列表
- 使用 `CompletableFuture` 处理异步结果
- 使用 `handle()` 方法处理响应和异常
- 远程调用异常应包装为 RuntimeException 并提供详细错误信息
```java
@Cacheable(key = "'code-'+#p0")
public ContractVo findByCode(String code) {
try {
return async("findByCode", code, String.class).handle((response, ex) -> {
if (ex != null) {
throw new RuntimeException("远程方法+findByCode+调用失败", ex);
}
if (response != null) {
return updateValue(createNewEntity(), response);
}
return null;
}).get();
} catch (Exception e) {
throw new RuntimeException("查询失败: " + code, e);
}
}
```
## 7. 基础方法实现
- 应实现 `IEntityService<T>` 接口定义的核心方法:
- `findById(Integer id)`:根据 ID 查询实体
- `save(T entity)`:保存实体
- `delete(T entity)`:删除实体
- `findAll()`:查询所有实体
- `findAll(Map<String, Object> params, Pageable pageable)`:条件分页查询
- `getStringConverter()`:获取类型转换器
- 通常通过继承 `QueryService` 来复用这些基础方法的实现
- 可根据业务需求重写或扩展基础方法
## 8. 业务方法规范
- 业务方法应与服务端对应,保持方法名和参数一致
- 方法命名应清晰表达其功能,如 `findByName`, `findByCode`
- 复杂业务逻辑应封装为独立方法
- 参数校验应在方法开始处进行
- 返回值类型应明确,避免使用过于泛化的类型
## 9. 类型转换器
- 实现 `getStringConverter()` 方法,返回对应的 StringConverter 实例
- 通常创建专用的 Converter 类,如 `CustomerCatalogStringConverter`
- 转换器实例应作为服务类的成员变量,避免重复创建
```java
public class CustomerCatalogService extends QueryService<CustomerCatalogVo, CustomerCatalogViewModel> {
private final CustomerCatalogStringConverter stringConverter = new CustomerCatalogStringConverter(this);
@Override
public StringConverter<CustomerCatalogVo> getStringConverter() {
return stringConverter;
}
}
```
## 10. 错误处理
- 远程调用异常应捕获并包装为更具描述性的 RuntimeException
- 提供详细的错误信息,包括调用的方法名和参数
- 对于可预期的业务异常,可添加专门的处理逻辑
- 不推荐使用 printStackTrace(),应使用日志记录异常
## 11. 工具方法和辅助功能
- 通用功能可封装为工具方法
- 配置相关操作可通过 `SysConfService` 实现
- 文件路径处理应使用 `File` 类和相关工具方法
- 日期时间处理应使用 Java 8+ 的日期时间 API
## 12. 最佳实践
- 遵循单一职责原则,每个服务类专注于一个业务领域
- 优先使用继承和接口实现来复用代码
- 合理使用缓存提高性能,但注意缓存一致性
- 异步调用应正确处理异常和超时情况
- 服务类之间的依赖应通过 `@Autowired` 注入,避免硬编码
- 方法实现应简洁明了,复杂逻辑应拆分
- 为重要方法添加 JavaDoc 注释,说明其功能和参数含义

View File

View 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以提高性能

View File

@@ -1,13 +1,284 @@
Java 21 # 项目规则
Spring Boot 3.3.7
Spring Data JPA 3.3.7
JavaFX 21
ControlsFX 11.1.2
MySQL 8.0.33
Lombok 1.18.32
POI 5.2.5
PDFBox 3.0.1
## 技术栈规范
### 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文件管理
## 文档同步
- 代码变更同时更新相关文档
## 测试策略
- 测试优先:先写测试,后写实现
- 边界覆盖:覆盖正常流程、边界条件、异常情况
## 交互体验优化
### 进度反馈
- 显示当前执行阶段
- 提供详细的执行步骤
- 标示完成情况
- 突出需要关注的问题
### 异常处理机制
#### 中断条件
- 遇到无法自主决策的问题
- 觉得需要询问用户的问题
- 技术实现出现阻塞
- 文档不一致需要确认修正
#### 恢复策略
- 保存当前执行状态
- 记录问题详细信息
- 询问并等待人工干预
- 从中断点任务继续执行

View File

@@ -0,0 +1,260 @@
# 服务器端 Service 类规则文档
## 1. 概述
本规则文档定义了 Contract-Manager 项目服务器端server模块Service 类的设计规范、实现标准和最佳实践。所有服务器端 Service 类必须严格遵循本规则,以确保代码的一致性、可维护性和性能。
## 2. 目录结构
Service 类按业务领域组织,位于 `server/src/main/java/com/ecep/contract/ds/{业务领域}/service/` 目录下。其中 `{业务领域}` 对应具体的业务模块,如 `customer``contract``company``project``other` 等。
**示例:**
- 客户分类服务:`server/src/main/java/com/ecep/contract/ds/customer/service/CustomerCatalogService.java`
- 员工服务:`server/src/main/java/com/ecep/contract/ds/other/service/EmployeeService.java`
## 3. 命名规范
- **类名**:采用驼峰命名法,首字母大写,以 `Service` 结尾,表示这是一个服务类。
**示例**`CustomerCatalogService``EmployeeService`
## 4. 接口实现
所有业务领域的 Service 类必须实现以下三个核心接口:
### 4.1 IEntityService<T>
提供实体类的基本 CRUD 操作。泛型 `T` 表示实体类类型。
**主要方法:**
- `T getById(Integer id)`:根据 ID 查询实体对象
- `Page<T> findAll(Specification<T> spec, Pageable pageable)`:根据条件和分页参数查询实体列表
- `Specification<T> getSpecification(String searchText)`:构建搜索条件
- `void delete(T entity)`:删除实体
- `T save(T entity)`:保存实体
### 4.2 QueryService<Vo>
提供 VO 对象的查询能力。泛型 `Vo` 表示视图对象类型。
**主要方法:**
- `Vo findById(Integer id)`:根据 ID 查询 VO 对象
- `Page<Vo> findAll(JsonNode paramsNode, Pageable pageable)`:根据 JSON 查询参数和分页条件查询 VO 列表
- `default long count(JsonNode paramsNode)`:根据查询参数统计数据总数
### 4.3 VoableService<M, Vo>
提供从 VO 对象更新实体对象的能力。泛型 `M` 表示实体类类型,`Vo` 表示视图对象类型。
**主要方法:**
- `void updateByVo(M model, Vo vo)`:根据 VO 对象更新实体对象
**实现示例:**
```java
@Lazy
@Service
@CacheConfig(cacheNames = "customer-catalog")
public class CustomerCatalogService implements IEntityService<CustomerCatalog>, QueryService<CustomerCatalogVo>,
VoableService<CustomerCatalog, CustomerCatalogVo> {
// 实现方法...
}
```
## 5. 注解规范
Service 类必须使用以下注解:
### 5.1 类级别注解
- `@Service`:标记这是一个 Spring 服务类,使其能够被自动扫描和管理
- `@Lazy`:延迟加载服务类,提高应用启动性能
- `@CacheConfig(cacheNames = "缓存名称")`:配置缓存名称,缓存名称通常与业务领域相关
**示例**`@CacheConfig(cacheNames = "customer-catalog")``@CacheConfig(cacheNames = "employee")`
### 5.2 方法级别注解
#### 5.2.1 查询方法注解
- `@Cacheable(key = "缓存键")`:将查询结果缓存起来,下次相同查询可以直接从缓存获取
**示例**`@Cacheable(key = "#p0")`(使用方法第一个参数作为缓存键)、`@Cacheable(key = "'code-'+#p0")`(使用前缀+参数值作为缓存键)
#### 5.2.2 数据修改方法注解
- `@Caching(evict = { ... })`:在保存或删除操作时清除相关缓存
**示例**
```java
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'code-'+#p0.code"),
@CacheEvict(key = "'name-'+#p0.name"),
@CacheEvict(key = "'all'")
})
```
- `@Transactional`:标记方法需要在事务中执行,确保数据一致性
**示例**:用于包含多个数据操作的复杂业务方法
## 6. 缓存策略
Service 类必须遵循以下缓存策略:
### 6.1 查询缓存
- 所有 `findById`、`findByCode`、`findByName` 等单条查询方法都应使用 `@Cacheable` 注解缓存结果
- 缓存键设计应具有唯一性和可读性,通常包含参数值和适当的前缀
- 列表查询(如 `findAll`)可以考虑使用 `@Cacheable`,但需谨慎管理缓存失效
### 6.2 缓存清理
- 所有 `save`、`delete` 等修改数据的方法都应使用 `@Caching` 和 `@CacheEvict` 注解清理相关缓存
- 清理缓存时应考虑所有可能影响的查询,确保缓存数据的一致性
## 7. 方法实现规范
### 7.1 IEntityService 方法实现
- `getById`:直接调用 Repository 的 `findById` 方法,返回实体对象
```java
@Override
public CustomerCatalog getById(Integer id) {
return repository.findById(id).orElse(null);
}
```
- `findAll(Specification<T>, Pageable)`:直接调用 Repository 的 `findAll` 方法,返回实体分页对象
```java
@Override
public Page<CustomerCatalog> findAll(Specification<CustomerCatalog> spec, Pageable pageable) {
return repository.findAll(spec, pageable);
}
```
- `save/delete`:调用 Repository 的相应方法,并添加缓存清理注解
```java
@Caching(evict = { ... })
@Override
public CustomerCatalog save(CustomerCatalog catalog) {
return repository.save(catalog);
}
```
### 7.2 QueryService 方法实现
- `findById`:调用 Repository 的 `findById` 方法,然后调用实体类的 `toVo` 方法转换为 VO 对象
```java
@Cacheable(key = "#p0")
@Override
public CustomerCatalogVo findById(Integer id) {
return repository.findById(id).map(CustomerCatalog::toVo).orElse(null);
}
```
- `findAll(JsonNode, Pageable)`:构建 Specification调用 IEntityService 的 `findAll` 方法,然后使用 Stream API 的 `map` 方法将结果转换为 VO 对象
```java
@Override
public Page<CustomerCatalogVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CustomerCatalog> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
// 字段等值查询
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "code", "name", "description");
return repository.findAll(spec, pageable).map(CustomerCatalog::toVo);
}
```
### 7.3 VoableService 方法实现
- `updateByVo`:将 VO 对象的属性逐个复制到实体对象,实现 VO 到实体的转换
```java
@Override
public void updateByVo(CustomerCatalog model, CustomerCatalogVo vo) {
// 参数校验
if (model == null) {
throw new ServiceException("实体对象不能为空");
}
if (vo == null) {
throw new ServiceException("VO对象不能为空");
}
// 映射基本属性
model.setCode(vo.getCode());
model.setName(vo.getName());
model.setDescription(vo.getDescription());
}
```
## 8. 依赖注入规范
- Service 类应使用 `@Autowired` 注解注入所需的 Repository 和其他依赖
- 为了避免循环依赖,所有注入的依赖都应使用 `@Lazy` 注解标记为延迟加载
```java
@Lazy
@Autowired
private CustomerCatalogRepository repository;
```
## 9. 查询条件构建
- 使用 `SpecificationUtils` 工具类辅助构建查询条件
- 实现 `getSpecification(String searchText)` 方法,提供基于搜索文本的模糊查询能力
```java
@Override
public Specification<CustomerCatalog> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {
return null;
}
String likeText = "%" + searchText + "%";
return (root, query, builder) -> {
return builder.or(
builder.like(root.get("code"), likeText),
builder.like(root.get("name"), likeText),
builder.like(root.get("description"), likeText));
};
}
```
## 10. 异常处理
- Service 类应适当处理异常,特别是对输入参数的校验
- 对于业务逻辑异常,应抛出 `ServiceException`
```java
if (model == null) {
throw new ServiceException("实体对象不能为空");
}
```
## 11. 最佳实践
1. **VO优先原则**QueryService 接口应使用 VO 类型作为泛型参数,返回 VO 对象而不是实体对象
2. **缓存粒度**:缓存键应设计得足够细粒度,避免缓存过大或频繁失效
3. **事务管理**:包含多个数据操作的方法应使用 `@Transactional` 注解确保事务一致性
4. **延迟加载**:所有依赖都应使用 `@Lazy` 注解,避免循环依赖问题
5. **参数校验**:方法开始时应进行参数校验,确保输入数据的合法性
6. **文档注释**:关键方法应有清晰的 JavaDoc 注释,说明其功能、参数和返回值
7. **代码复用**:尽量使用 SpecificationUtils 等工具类辅助构建查询条件,提高代码复用性
## 12. 不符合规范的Service类示例
以下是不符合规范的Service类实现应避免
```java
// 错误QueryService泛型参数使用实体类型而非VO类型
@Service
@CacheConfig(cacheNames = "company")
public class CompanyService extends EntityService<Company, Integer>
implements IEntityService<Company>, QueryService<Company>, VoableService<Company, CompanyVo> {
// 实现方法...
}
// 错误:未使用缓存注解
@Service
public class ExampleService implements IEntityService<Example>, QueryService<ExampleVo>,
VoableService<Example, ExampleVo> {
// 未使用@Cacheable、@CacheEvict等缓存注解
}
```
## 13. 总结
本规则文档定义了服务器端 Service 类的完整规范,包括目录结构、命名规范、接口实现、注解规范、缓存策略、方法实现、依赖注入等方面。所有服务器端开发人员都必须严格遵循这些规则,以确保代码的一致性、可维护性和性能。

58
.trae/rules/vo_rules.md Normal file
View 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方法实体类可能包含更多业务相关方法

231
README.md
View File

@@ -1,33 +1,198 @@
## 项目改进与优化分析报告 # Contract-Manager 项目报告
### 1. 同步任务与调度优化
- 调度策略优化 :各同步任务(如 ContractSyncTask 、 VendorSyncTask )使用不同的调度间隔和策略,建议统一调度配置,避免重复执行和资源浪费 ## 项目概述
- 增量同步实现 多数同步任务缺乏断点续传机制建议添加基于最后同步ID或时间戳的增量同步功能 Contract-Manager是一个企业级合同管理系统提供完整的合同生命周期管理包括合同的创建、审批、执行、归档等功能同时集成了供应商管理、客户管理、项目管理等相关业务模块。
- 任务依赖管理 :部分任务存在隐式依赖关系(如 OldVersionSyncVendorTask 中的多步骤执行),建议引入任务依赖管理机制
### 2. 线程池与异步处理 ## 项目架构
- 线程池配置 :当前项目未明确配置线程池参数(核心线程数、最大线程数等),建议在 SpringApp 中添加显式线程池配置
- 线程池隔离 不同类型的任务IO密集型、CPU密集型应使用不同线程池避免资源竞争 ### 模块划分
- 异步任务监控 :添加异步任务执行状态监控和超时处理机制,避免任务长时间阻塞 项目采用模块化设计,分为三个主要模块:
### 3. 缓存机制优化
- 缓存策略细化 :当前缓存配置较为简单( CaffeineCacheManager ),建议为不同类型数据配置差异化缓存策略(过期时间、最大容量等) - **common**:公共模块,包含实体类、枚举、异常、工具类等共享组件
- 缓存一致性 :修复 CloudRkService 中缓存更新问题(注释中提到的"这个可以无法更新缓存" - **server**服务端模块基于Spring Boot开发提供HTTP服务和WebSocket通信
- 缓存预热 :添加关键数据缓存预热机制,提高系统启动后响应速度 - **client**客户端模块基于JavaFX开发提供图形用户界面
### 4. 依赖注入与懒加载
- 依赖注入规范化 :减少 SpringApp.getBean() 手动获取bean的方式推广构造函数注入或字段注入 ### 技术栈
- 懒加载优化 :充分利用 @Lazy 注解和 BootstrapMode.LAZY 配置,优化应用启动性能
- 服务解耦 :继续推进服务拆分工作(如之前的 ContractService 拆分为专用服务),减少服务间耦合 #### 服务端技术栈
### 5. 异常处理与重试机制 - Java 21
- 异常处理统一 :规范异常处理策略,确保所有异常都被适当记录和处理 - Spring Boot 3.3.7
- 重试机制添加 :为网络请求和数据库操作添加重试机制(如使用 Spring Retry - Spring Data JPA 3.3.7
- 熔断降级 对外部系统调用如用友U8接口添加熔断降级机制提高系统稳定性 - MySQL 8.0.33
### 6. 数据库操作优化 - Lombok 1.18.32
- 批量操作引入 对大量数据的CRUD操作建议使用批量处理API提高性能 - POI 5.2.5 (Office文档处理)
- 查询优化 :添加适当索引,优化复杂查询,避免全表扫描 - PDFBox 3.0.1 (PDF文档处理)
- 连接池配置 :优化 HikariDataSource 配置参数(如连接池大小、超时时间等) - Redis (缓存)
### 7. 代码质量与维护性 - WebSocket (实时通信)
- 重复代码消除 :提取同步任务中的共同逻辑(如进度更新、异常处理)为抽象基类
- 注释完善 :补充关键类和方法的文档注释,特别是复杂业务逻辑和优化点 #### 客户端技术栈
- 技术债务清理 解决代码中的TODO项如 CloudRkService 中的缓存更新问题) - Java 21
### 8. 配置管理优化 - 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创建安装包
## 开发环境要求
- JDK 21
- Maven 3.6+
- MySQL 8.0+
- Redis 6.0+
- IntelliJ IDEA或Eclipse推荐使用IDEA
## 后续优化方向
1. 完善单元测试和集成测试
2. 优化数据库查询性能
3. 增强系统安全机制
4. 改进用户界面体验
5. 扩展更多云端服务集成

143
client/pom.xml Normal file
View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecep.contract</groupId>
<artifactId>Contract-Manager</artifactId>
<version>0.0.122-SNAPSHOT</version>
</parent>
<groupId>com.ecep.contract</groupId>
<artifactId>client</artifactId>
<version>0.0.122-SNAPSHOT</version>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.ecep.contract</groupId>
<artifactId>common</artifactId>
<version>0.0.122-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- JavaFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>11.2.0</version>
</dependency>
<!-- Caffeine Cache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<!-- Spring Context Support for CaffeineCacheManager -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>3.3.7</version>
</dependency>
<!-- OkHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Logback 日志实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>com.ecep.contract.ClientV2</mainClass>
<launcher>app</launcher>
<jlinkZipName>app-jlink</jlinkZipName>
<jlinkImageName>app-jlink-image</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<compress>2</compress>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.16.2</version>
<executions>
<execution>
<inherited>true</inherited>
<id>increment-version</id>
<phase>install</phase>
<goals>
<goal>set</goal>
</goals>
<configuration>
<generateBackupPoms>false</generateBackupPoms>
<processAllModules>true</processAllModules>
<!-- <newVersion>${project.version}</newVersion> -->
<nextSnapshot>true</nextSnapshot>
<nextSnapshotIndexToIncrement>3</nextSnapshotIndexToIncrement>
<!-- <removeSnapshot>true</removeSnapshot>-->
</configuration>
</execution>
</executions>
</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>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package com.ecep.contract;
import javafx.application.Application;
import java.io.File;
/**
* Created by Administrator on 2017/4/16.
*/
public class ClientV2 {
public static void main(String[] args) {
System.out.println("当前目录 = " + new File(".").getAbsolutePath());
Application.launch(Desktop.class, args);
}
public static final String DEFAULT_HOST = "10.84.209.154";
}

View File

@@ -1,27 +1,4 @@
package com.ecep.contract.manager; package com.ecep.contract;
import com.ecep.contract.manager.ds.other.controller.LoginWidowController;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.task.TaskMonitorCenter;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -29,8 +6,42 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.*; import java.util.concurrent.CompletableFuture;
import java.util.logging.Level; import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.controller.HomeWindowController;
import com.ecep.contract.controller.OkHttpLoginController;
import com.ecep.contract.task.TaskMonitorCenter;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.util.TextMessageHolder;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CurrentEmployee;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import lombok.Getter;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
/** /**
* JavaFx 应用程序 * JavaFx 应用程序
@@ -58,9 +69,11 @@ public class Desktop extends Application {
private ScheduledExecutorService scheduledExecutorService = null; private ScheduledExecutorService scheduledExecutorService = null;
private final TaskMonitorCenter taskMonitorCenter = new TaskMonitorCenter(); private final TaskMonitorCenter taskMonitorCenter = new TaskMonitorCenter();
private final SimpleIntegerProperty sessionId = new SimpleIntegerProperty(0); private final SimpleStringProperty sessionId = new SimpleStringProperty("");
@Getter @Getter
private final CurrentEmployee activeEmployee = new CurrentEmployee(); private final CurrentEmployee activeEmployee = new CurrentEmployee();
@Getter
private OkHttpClient httpClient;
public void setActiveEmployeeId(int activeEmployeeId) { public void setActiveEmployeeId(int activeEmployeeId) {
activeEmployee.getId().set(activeEmployeeId); activeEmployee.getId().set(activeEmployeeId);
@@ -70,11 +83,11 @@ public class Desktop extends Application {
return activeEmployee.getId().get(); return activeEmployee.getId().get();
} }
public int getSessionId() { public String getSessionId() {
return sessionId.get(); return sessionId.get();
} }
public void setSessionId(int sessionId) { public void setSessionId(String sessionId) {
this.sessionId.set(sessionId); this.sessionId.set(sessionId);
} }
@@ -122,8 +135,12 @@ public class Desktop extends Application {
} }
} }
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, getExecutorService());
}
private void startSpringApp(Stage primaryStage, Parent root, FXMLLoader loader) { private void startSpringApp(Stage primaryStage, Parent root, FXMLLoader loader) {
System.out.println("Desktop.startSpringApp"); logger.debug("startSpringApp");
// 更新窗口标题 // 更新窗口标题
Node titleNode = root.lookup("#title"); Node titleNode = root.lookup("#title");
if (titleNode != null) { if (titleNode != null) {
@@ -138,34 +155,26 @@ public class Desktop extends Application {
ScrollPane logPane = (ScrollPane) root.lookup("#logPane"); ScrollPane logPane = (ScrollPane) root.lookup("#logPane");
logBox.getChildren().clear(); logBox.getChildren().clear();
MessageHolder holder = (level, message) -> { TextMessageHolder holder = new TextMessageHolder() {
Text text = new Text(message); @Override
if (Level.WARNING == level) { // warning public void addTextMessage(Text text) {
text.setFill(Color.YELLOW);
} else if (Level.SEVERE == level) {// error
text.setFill(Color.RED);
} else if (Level.FINE == level) { // debug
text.setFill(Color.GRAY);
} else {
text.setFill(Color.WHITE);
}
Platform.runLater(() -> { Platform.runLater(() -> {
logBox.getChildren().add(text); logBox.getChildren().add(text);
logPane.layout(); logPane.layout();
logPane.setVvalue(1.0); logPane.setVvalue(1.0);
}); });
}
}; };
holder.info("启动中,请稍后..."); holder.info("启动中,请稍后...");
CompletableFuture.runAsync(() -> { runAsync(() -> {
try { try {
// //
holder.info("读取配置文件..."); holder.info("读取配置文件...");
Properties properties = new Properties(); Properties properties = new Properties();
File configFile = new File("config.properties"); File configFile = new File("config.properties");
if (configFile.exists()) { if (configFile.exists()) {
holder.debug("读取配置文件 " + configFile.getName() + "..."); holder.info("读取配置文件 " + configFile.getAbsolutePath() + "...");
try (FileInputStream input = new FileInputStream(configFile)) { try (FileInputStream input = new FileInputStream(configFile)) {
properties.load(input); properties.load(input);
holder.info("配置文件读取成功."); holder.info("配置文件读取成功.");
@@ -174,9 +183,12 @@ public class Desktop extends Application {
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
return; return;
} }
} else {
logger.warn("配置文件{}不存在", configFile.getAbsolutePath());
} }
HibernateProxyUtils.useProxy(false);
CompletableFuture.runAsync(() -> { runAsync(() -> {
SpringApp.launch(properties, holder); SpringApp.launch(properties, holder);
ConfigurableListableBeanFactory beanFactory = SpringApp.context.getBeanFactory(); ConfigurableListableBeanFactory beanFactory = SpringApp.context.getBeanFactory();
@@ -185,16 +197,37 @@ public class Desktop extends Application {
}); });
try { try {
LoginWidowController controller = new LoginWidowController(); initHttpClient();
OkHttpLoginController controller = new OkHttpLoginController();
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) {
controller.tryLogin(); try {
break; controller.tryLogin().get();
holder.info("登录成功");
try {
while (!SpringApp.isRunning()) {
System.out.println("等待启动");
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
// 必须要等待启动成功后才能关闭主场景否则进程结束程序退出
HomeWindowController.show().thenRun(() -> Platform.runLater(primaryStage::close));
break;
} catch (Exception ex) {
holder.error(ex.getMessage());
Thread.sleep(3000);
} }
if (logger.isDebugEnabled()) {
logger.debug("login in");
} }
} catch (Exception e) { } catch (Exception e) {
holder.error("登录失败:" + e.getMessage()); holder.error("登录失败:" + e.getMessage());
@@ -206,6 +239,27 @@ public class Desktop extends Application {
} }
}); });
System.out.println("Desktop.startSpringApp."); System.out.println("Desktop.startSpringApp.");
}
private void initHttpClient() {
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
@@ -253,5 +307,4 @@ public class Desktop extends Application {
} }
scheduledExecutorService.close(); scheduledExecutorService.close();
} }
} }

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager.util; package com.ecep.contract;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -6,8 +6,6 @@ import java.util.function.Consumer;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.ecep.contract.manager.Desktop;
public class DesktopUtils { public class DesktopUtils {
/** /**

View File

@@ -0,0 +1,236 @@
package com.ecep.contract;
import java.io.File;
import java.io.FileInputStream;
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.util.StringUtils;
import lombok.Getter;
import lombok.Setter;
/**
* 应用程序配置类,用于管理系统配置信息
*/
@Component
public class MyProperties implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(MyProperties.class);
private static final String FILE_NAME = "config.properties";
@Getter
@Setter
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");
}
}
/**
* 尝试返回当前用户的下载文件夹
*/
public File getDownloadDirectory() {
String downloadsPath = getDownloadsPath();
if (StringUtils.hasText(downloadsPath)) {
// 确保目录存在
File dir = new File(downloadsPath);
if (!dir.exists()) {
boolean created = dir.mkdirs();
logger.debug("创建下载目录: {}, 结果: {}", downloadsPath, created);
}
return dir;
}
// 没有配置下载目录时,尝试使用默认设置
String home = System.getProperty("user.home");
Path path = Paths.get(home, "Downloads");
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);
}
}
}

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager; package com.ecep.contract;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
@@ -25,7 +25,6 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
@@ -36,11 +35,7 @@ import org.springframework.core.metrics.StartupStep;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import com.ecep.contract.manager.cloud.CloudRepositoriesConfig; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.ds.DsRepositoriesConfig;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.UITools;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
@@ -50,15 +45,20 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
@SpringBootApplication(exclude = { org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration.class }) @SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.class,
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.class,
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.class
})
@EnableScheduling @EnableScheduling
@EnableAsync @EnableAsync
@EnableCaching @EnableCaching
public class SpringApp { public class SpringApp {
private static final Logger logger = LoggerFactory.getLogger(SpringApp.class); private static final Logger logger = LoggerFactory.getLogger(SpringApp.class);
static SpringApplication application; public static SpringApplication application;
static ConfigurableApplicationContext context; public static ConfigurableApplicationContext context;
public static <T> T getBean(Class<T> requiredType) throws BeansException { public static <T> T getBean(Class<T> requiredType) throws BeansException {
return context.getBean(requiredType); return context.getBean(requiredType);
@@ -92,23 +92,29 @@ public class SpringApp {
environment.getPropertySources().addLast(new PropertiesPropertySource("dynamicProperties", properties)); environment.getPropertySources().addLast(new PropertiesPropertySource("dynamicProperties", properties));
// app.getBeanFactory().registerSingleton("dataSource", dataSource()); // app.getBeanFactory().registerSingleton("dataSource", dataSource());
logger.debug("app = {}", app); logger.debug("app = {}", app);
if (app instanceof AnnotationConfigApplicationContext ctx) {
ctx.register(DsRepositoriesConfig.class);
ctx.register(CloudRepositoriesConfig.class);
}
}); });
startup.start(""); startup.start("");
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(() -> {
// 在这里调用 startup 性能分析 // 在这里调用 startup 性能分析
analyzeStartupPerformance(startup); analyzeStartupPerformance(startup);
}); }, Desktop.instance.getExecutorService());
} }
/** /**
@@ -242,14 +248,18 @@ public class SpringApp {
public ObjectMapper objectMapper() { public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule(); JavaTimeModule javaTimeModule = new JavaTimeModule();
// LocalDate
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE)); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(
DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE)); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer( // LocalTime
DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN))); javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME)); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
// LocalDateTime
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
objectMapper.registerModule(javaTimeModule); objectMapper.registerModule(javaTimeModule);
return objectMapper; return objectMapper;
} }

View File

@@ -0,0 +1,376 @@
package com.ecep.contract;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
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消息的功能
*/
@Service
public class WebSocketClientService {
private static final Logger logger = LoggerFactory.getLogger(WebSocketClientService.class);
@Getter
@Setter
private WebSocket webSocket;
@Getter
@Autowired
private ObjectMapper objectMapper;
private static final int RECONNECT_DELAY_MS = 5000; // 重连延迟时间(毫秒)
private static final int HEARTBEAT_INTERVAL_MS = 30000; // 心跳间隔时间(毫秒)
@Getter
@Setter
private long readTimeout = 30000;
private String webSocketUrl = "ws://localhost:8080/ws";
private boolean isActive = false; // 标记连接是否活跃
private ScheduledFuture<?> heartbeatTask; // 心跳任务
private ScheduledFuture<?> reconnectFuture; // 修改类型为CompletableFuture<Void>
private SimpleBooleanProperty online = new SimpleBooleanProperty(false);
private SimpleStringProperty message = new SimpleStringProperty("");
// 存储所有活跃的WebSocket会话
private final Map<String, WebSocketClientSession> sessions = Collections.synchronizedMap(new HashMap<>());
// 存储所有活跃的WebSocket会话
private final Map<String, CompletableFuture<JsonNode>> callbacks = Collections.synchronizedMap(new HashMap<>());
WebSocketListener listener = new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
Platform.runLater(() -> {
online.setValue(true);
message.setValue("已连接");
});
startHeartbeat(); // 启动心跳
}
@Override
public void onMessage(WebSocket webSocket, String text) {
// 处理收到的文本消息
logger.debug("收到WebSocket消息: {}", text);
// 这里可以根据需要处理从服务器接收的数据
if ("pong".equals(text)) {
// 收到pong响应说明连接正常
logger.debug("收到心跳响应");
return;
}
try {
JsonNode node = objectMapper.readTree(text);
if (node.has(WebSocketConstant.MESSAGE_ID_FIELD_NAME)) {
String messageId = node.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
CompletableFuture<JsonNode> future = callbacks.remove(messageId);
if (future != null) {
onCallbackMessage(future, node);
} else {
logger.error("未找到对应的回调future: {}", messageId);
}
return;
}
if (node.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
String sessionId = node.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
WebSocketClientSession session = sessions.get(sessionId);
if (session != null) {
try {
session.onMessage(node);
} catch (Exception e) {
session.updateMessage(java.util.logging.Level.SEVERE, "会话异常: " + e.getMessage());
}
}
return;
}
if (node.has(WebSocketConstant.ERROR_CODE_FIELD_NAME)) {
int errorCode = node.get(WebSocketConstant.ERROR_CODE_FIELD_NAME).asInt();
String errorMsg = node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText();
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) {
logger.error("处理WebSocket消息失败: {}", e.getMessage(), e);
}
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
// 处理收到的二进制消息
logger.debug("收到二进制WebSocket消息长度: " + bytes.size());
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
logger.debug("WebSocket连接正在关闭: 代码=" + code + ", 原因=" + reason);
stopHeartbeat(); // 停止心跳
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
logger.debug("WebSocket连接已关闭: 代码=" + code + ", 原因=" + reason);
stopHeartbeat(); // 停止心跳
// 处理重连逻辑
scheduleReconnect();
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
logger.error("WebSocket连接失败: " + t.getMessage() + ", response=" + response, t);
Platform.runLater(() -> {
online.setValue(false);
message.set("连接失败: " + t.getMessage());
});
stopHeartbeat(); // 停止心跳
// 处理重连逻辑
scheduleReconnect();
}
};
private void onCallbackMessage(CompletableFuture<JsonNode> future, JsonNode node) {
if (node.has(WebSocketConstant.SUCCESS_FIELD_NAME)) {
if (!node.get(WebSocketConstant.SUCCESS_FIELD_NAME).asBoolean()) {
future.completeExceptionally(
new RuntimeException(
"请求失败:来自服务器的消息=" + node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText()));
return;
}
}
// 使用具体类型后,这里不会再出现类型不匹配的错误
if (node.has("data")) {
future.complete(node.get("data"));
} else {
future.complete(node);
}
}
public void send(String string) {
if (webSocket != null && webSocket.send(string)) {
logger.debug("send message success:{}", string);
} else if (webSocket == null) {
logger.warn("Failed to send message: WebSocket is not initialized");
}
}
public void send(Object message) throws JsonProcessingException {
send(objectMapper.writeValueAsString(message));
}
public CompletableFuture<JsonNode> send(SimpleMessage msg) {
CompletableFuture<JsonNode> future = new CompletableFuture<>();
try {
if (webSocket == null) {
throw new IllegalStateException("WebSocket is not initialized");
}
if (!online.get()) {
throw new IllegalStateException("WebSocket is not online");
}
String json = objectMapper.writeValueAsString(msg);
callbacks.put(msg.getMessageId(), future);
if (webSocket.send(json)) {
logger.debug("send json success:{}", json);
} else {
if (isActive) {
future.completeExceptionally(new RuntimeException("Failed to send WebSocket message"));
} else {
future.completeExceptionally(new RuntimeException("WebSocket is not active"));
}
}
} catch (Exception e) {
logger.error("Failed to send WebSocket message: {}", e.getMessage());
future.completeExceptionally(e);
}
return future;
}
public CompletableFuture<JsonNode> invoke(String service, String method, Object... params) {
SimpleMessage msg = new SimpleMessage();
msg.setService(service);
msg.setMethod(method);
msg.setArguments(params);
return send(msg).orTimeout(getReadTimeout(), TimeUnit.MILLISECONDS);
}
public void initWebSocket() {
isActive = true;
OkHttpClient httpClient = Desktop.instance.getHttpClient();
try {
// 构建WebSocket请求包含认证信息
Request request = new Request.Builder()
.url(webSocketUrl)
.build();
webSocket = httpClient.newWebSocket(request, listener);
} catch (Exception e) {
logger.error("建立WebSocket连接失败: " + e.getMessage());
Platform.runLater(() -> {
online.setValue(false);
message.set("连接失败: " + e.getMessage());
});
// 处理重连逻辑
scheduleReconnect();
}
}
/**
* 启动心跳任务定期发送ping消息保持连接
*/
private void startHeartbeat() {
// 先停止可能存在的心跳任务
stopHeartbeat();
ScheduledExecutorService executorService = Desktop.instance.getExecutorService();
heartbeatTask = executorService.scheduleAtFixedRate(this::heartbeat, RECONNECT_DELAY_MS, HEARTBEAT_INTERVAL_MS,
TimeUnit.MILLISECONDS);
}
void heartbeat() {
if (!isActive) {
return;
}
try {
if (webSocket != null) {
logger.debug("发送心跳 ping");
webSocket.send("ping");
}
} catch (Exception e) {
logger.error("发送心跳失败: {}", e.getMessage());
}
}
/**
* 停止心跳任务
*/
private void stopHeartbeat() {
if (heartbeatTask != null && !heartbeatTask.isCancelled()) {
heartbeatTask.cancel(true);
heartbeatTask = null;
}
}
/**
* 安排重连任务
*/
private void scheduleReconnect() {
if (!isActive) {
return; // 如果连接已被主动关闭,则不再重连
}
// 取消之前可能存在的重连任务
if (reconnectFuture != null && !reconnectFuture.isDone()) {
reconnectFuture.cancel(true);
}
// 创建新的重连任务s
logger.info("计划在 {} 毫秒后尝试重连WebSocket", RECONNECT_DELAY_MS);
reconnectFuture = Desktop.instance.getExecutorService().schedule(() -> {
if (isActive) {
logger.info("尝试重新连接WebSocket");
Platform.runLater(() -> {
online.setValue(false);
message.set("正在重新连接WebSocket...");
});
initWebSocket();
}
}, RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
}
/**
* 关闭WebSocket连接
*/
public void closeWebSocket() {
isActive = false;
stopHeartbeat();
if (reconnectFuture != null && !reconnectFuture.isDone()) {
reconnectFuture.cancel(false);
reconnectFuture = null;
}
if (webSocket != null) {
webSocket.close(1000, "主动关闭连接");
webSocket = null;
}
}
public StringProperty getMessageProperty() {
return message;
}
public BooleanProperty getOnlineProperty() {
return online;
}
public void withSession(Consumer<WebSocketClientSession> sessionConsumer) {
WebSocketClientSession session = createSession();
try {
sessionConsumer.accept(session);
} finally {
// closeSession(session);vvvv
}
}
public void closeSession(WebSocketClientSession session) {
if (session != null) {
sessions.remove(session.getSessionId());
// session.close();
}
}
public WebSocketClientSession createSession() {
WebSocketClientSession session = new WebSocketClientSession(this);
sessions.put(session.getSessionId(), session);
return session;
}
}

View File

@@ -0,0 +1,131 @@
package com.ecep.contract;
import java.beans.PropertyDescriptor;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import javafx.application.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import com.ecep.contract.constant.WebSocketConstant;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Getter;
/**
*
*/
public class WebSocketClientSession {
private static final Logger logger = LoggerFactory.getLogger(WebSocketClientSession.class);
/**
* 会话ID由客户端创建服务器不保存会话只回传会话ID
*/
@Getter
private final String sessionId = UUID.randomUUID().toString();
private WebSocketClientTasker tasker;
private final WebSocketClientService webSocketService;
public WebSocketClientSession(WebSocketClientService webSocketService) {
this.webSocketService = webSocketService;
}
public void close() {
webSocketService.closeSession(this);
}
public void submitTask(WebSocketClientTasker tasker, Locale locale, Object... args) throws JsonProcessingException {
this.tasker = tasker;
send("createTask", tasker.getTaskName(), locale.toLanguageTag(), args);
}
public void send(String type, Object... args) throws JsonProcessingException {
Map<String, Object> arguments = Map.of(
WebSocketConstant.SESSION_ID_FIELD_NAME, getSessionId(),
"type", type,
WebSocketConstant.ARGUMENTS_FIELD_NAME, args);
webSocketService.send(arguments);
}
public void onMessage(JsonNode node) {
if (node.has("type")) {
String type = node.get("type").asText();
JsonNode args = node.get(WebSocketConstant.ARGUMENTS_FIELD_NAME);
if (type.equals("message")) {
handleAsMessage(args);
} else if (type.equals("title")) {
handleAsTitle(args);
} else if (type.equals("property")) {
handleAsProperty(args);
} else if (type.equals("progress")) {
handleAsProgress(args);
} else if (type.equals("start")) {
handleAsStart(args);
} else if (type.equals("done")) {
handleAsDone(args);
} else {
tasker.updateMessage(java.util.logging.Level.INFO, "未知的消息类型: " + node.toString());
}
} else {
tasker.updateMessage(java.util.logging.Level.INFO, "未知的消息: " + node.toString());
}
}
private void handleAsStart(JsonNode args) {
tasker.updateMessage(java.util.logging.Level.INFO, "任务开始");
}
private void handleAsDone(JsonNode args) {
tasker.updateMessage(java.util.logging.Level.INFO, "任务完成");
close();
}
private void handleAsProgress(JsonNode args) {
long current = args.get(0).asLong();
long total = args.get(1).asLong();
tasker.updateProgress(current, total);
}
private void handleAsProperty(JsonNode args) {
String name = args.get(0).asText();
Object value = args.get(1);
try {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(tasker.getClass(), name);
if (descriptor == null) {
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性 " + name + " 不存在");
return;
}
Object object = webSocketService.getObjectMapper().convertValue(value, descriptor.getPropertyType());
if (descriptor.getWriteMethod() == null) {
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性 " + name + " 不可写");
} else {
descriptor.getWriteMethod().invoke(tasker, object);
}
} catch (Exception e) {
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性设置失败: " + name + " = " + value);
logger.error("set {} = {}", name, value, e);
}
}
private void handleAsTitle(JsonNode args) {
String message = args.get(0).asText();
tasker.updateTitle(message);
}
private void handleAsMessage(JsonNode args) {
String level = args.get(0).asText();
String message = args.get(1).asText();
updateMessage(java.util.logging.Level.parse(level), "[R] " + message);
}
public void updateMessage(Level level, String message) {
tasker.updateMessage(level, message);
}
}

View File

@@ -0,0 +1,115 @@
package com.ecep.contract;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
/**
* WebSocket客户端任务接口
* 定义了所有通过WebSocket与服务器通信的任务的通用方法
* 包括任务名称、更新消息、更新标题、更新进度等操作
*/
public interface WebSocketClientTasker {
/**
* 获取任务名称
*
* @return 任务名称
*/
String getTaskName();
/**
* 更新任务执行过程中的消息
*
* @param level 消息级别
* @param message 消息内容
*/
void updateMessage(Level level, String message);
/**
* 更新任务标题
*
* @param title 任务标题
*/
void updateTitle(String title);
/**
* 更新任务进度
*
* @param current 当前进度
* @param total 总进度
*/
void updateProgress(long current, long total);
/**
* 取消任务执行
* 默认实现为空,可由具体任务重写以提供取消逻辑
*/
default void cancelTask() {
// 默认实现为空,由具体任务重写
}
/**
* 调用远程WebSocket任务
*
* @param holder 消息持有者,用于记录任务执行过程中的消息
* @param locale 语言环境
* @param args 任务参数
* @return 任务执行结果
*/
default Object callRemoteTask(MessageHolder holder, Locale locale, Object... args) {
WebSocketClientService webSocketService = SpringApp.getBean(WebSocketClientService.class);
// 检查WebSocket连接是否可用
if (!webSocketService.getOnlineProperty().get()) {
String errorMsg = "WebSocket连接不可用请检查网络连接或服务器状态";
holder.addMessage(Level.SEVERE, errorMsg);
return null;
}
webSocketService.withSession(session -> {
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);
}
});
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();
}
}

View File

@@ -1,7 +1,10 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import com.ecep.contract.manager.ds.other.model.IdentityEntity; import org.apache.poi.ss.formula.functions.T;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.stage.WindowEvent; import javafx.stage.WindowEvent;

View File

@@ -1,11 +1,21 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.RefreshableSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.QueryService;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.BaseViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.vo.BaseViewModel;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.manager.ui.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
@@ -18,12 +28,6 @@ import javafx.stage.WindowEvent;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public abstract class AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>> public abstract class AbstEntityController<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends BaseController { extends BaseController {
@@ -43,15 +47,28 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
@Override @Override
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
Class<?> aClass = getClass();
super.onShown(windowEvent); super.onShown(windowEvent);
loadedFuture = CompletableFuture.supplyAsync(() -> { ViewModelService<T, TV> service = getViewModelService();
T entity = loadEntity();
if (entity == null) { if (service instanceof QueryService<T, TV> queryService) {
setStatus("读取...");
loadedFuture = queryService.asyncFindById(viewModel.getId().get());
loadedFuture.thenAccept(entity -> {
// fixed, bind change if new view model create // fixed, bind change if new view model create
if (viewModel != null) { if (viewModel != null) {
viewModel.bindListener(); viewModel.bindListener();
} }
setStatus();
// BaseViewModel.updateInFxApplicationThread(entity, viewModel);
});
loadedFuture.exceptionally(ex -> {
handleException("载入失败,#" + viewModel.getId().get(), ex);
return null;
});
} else {
loadedFuture = CompletableFuture.supplyAsync(() -> {
T entity = getViewModelService().findById(viewModel.getId().get());
if (entity == null) {
return null; return null;
} }
Platform.runLater(() -> { Platform.runLater(() -> {
@@ -61,6 +78,7 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
viewModel.bindListener(); viewModel.bindListener();
return entity; return entity;
}); });
}
registerTabSkins(); registerTabSkins();
if (saveBtn != null) { if (saveBtn != null) {
@@ -71,10 +89,6 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
installTabSkins(); installTabSkins();
} }
protected T loadEntity() {
return getViewModelService().findById(viewModel.getId().get());
}
public T getEntity() { public T getEntity() {
return getLoadedFuture().join(); return getLoadedFuture().join();
} }
@@ -190,10 +204,20 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
public CompletableFuture<Void> refresh() { public CompletableFuture<Void> refresh() {
CompletableFuture<Void> future = new CompletableFuture<>(); CompletableFuture<Void> future = new CompletableFuture<>();
T entity = loadEntity(); ViewModelService<T, TV> service = getViewModelService();
if (service instanceof QueryService<T, TV> queryService) {
Platform.runLater(() -> { loadedFuture = queryService.asyncFindById(viewModel.getId().get());
loadedFuture.whenComplete((entity, ex) -> {
if (ex != null) {
future.completeExceptionally(ex);
return;
}
BaseViewModel.updateInFxApplicationThread(entity, viewModel);
});
} else {
T entity = service.findById(viewModel.getId().get());
setEntity(entity); setEntity(entity);
}
List<RefreshableSkin> list = tabSkins.stream() List<RefreshableSkin> list = tabSkins.stream()
.filter(v -> v instanceof RefreshableSkin) .filter(v -> v instanceof RefreshableSkin)
@@ -201,10 +225,10 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
if (list.isEmpty()) { if (list.isEmpty()) {
future.complete(null); future.complete(null);
return; return future;
} }
CompletableFuture.allOf(list return CompletableFuture.allOf(list
.stream() .stream()
.map(RefreshableSkin::refresh) .map(RefreshableSkin::refresh)
.filter(Objects::nonNull) .filter(Objects::nonNull)
@@ -216,9 +240,6 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
future.complete(null); future.complete(null);
} }
}); });
});
return future;
} }
public abstract ViewModelService<T, TV> getViewModelService(); public abstract ViewModelService<T, TV> getViewModelService();

View File

@@ -1,27 +1,31 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledFuture; 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.manager.ui.table.EditableEntityTableTabSkin; import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.manager.ui.table.TableTabSkin;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.manager.ds.other.model.IdentityEntity; import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel; import com.ecep.contract.controller.table.TableTabSkin;
import com.ecep.contract.manager.util.TableViewUtils; import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.manager.util.UITools; import com.ecep.contract.service.QueryService;
import com.ecep.contract.service.ViewModelService;
import com.ecep.contract.util.TableViewUtils;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.IdentityViewModel;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.Property; import javafx.beans.property.Property;
@@ -43,12 +47,15 @@ import javafx.scene.input.KeyEvent;
import javafx.util.converter.NumberStringConverter; import javafx.util.converter.NumberStringConverter;
/** /**
* 实体管理器皮肤
* 提供了实体管理器的基本功能如查询新增删除修改分页等
*
* @param <T> Entity 的类型 * @param <T> Entity 的类型
* @param <TV> Entity 对应的ViewModel * @param <TV> Entity 对应的ViewModel
* @param <Skin> Skin 的类型 * @param <SKIN> Skin 的类型
* @param <C> * @param <C>
*/ */
public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>, Skin extends ManagerSkin, C extends AbstManagerWindowController<T, TV, Skin>> public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>, SKIN extends ManagerSkin, C extends AbstManagerWindowController<T, TV, SKIN>>
implements ManagerSkin, TableTabSkin<T, TV>, EditableEntityTableTabSkin<T, TV> { implements ManagerSkin, TableTabSkin<T, TV>, EditableEntityTableTabSkin<T, TV> {
private static final Logger logger = LoggerFactory.getLogger(AbstEntityManagerSkin.class); private static final Logger logger = LoggerFactory.getLogger(AbstEntityManagerSkin.class);
/** /**
@@ -61,11 +68,20 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
protected PageRequest currentPageable = PageRequest.ofSize(25); protected PageRequest currentPageable = PageRequest.ofSize(25);
protected final SimpleIntegerProperty currentPageNumber = new SimpleIntegerProperty(); protected final SimpleIntegerProperty currentPageNumber = new SimpleIntegerProperty();
// 是否允许调整表格高度
private boolean allowResize = true;
// 记录延时任务信息
private ScheduledFuture<?> loadTableDataSetFuture;
public AbstEntityManagerSkin(C controller) { public AbstEntityManagerSkin(C controller) {
this.controller = controller; this.controller = controller;
} }
@Override
public <TT> TT getBean(Class<TT> requiredType) throws BeansException {
return controller.getCachedBean(requiredType);
}
@Override @Override
public TableView<TV> getTableView() { public TableView<TV> getTableView() {
return controller.table; return controller.table;
@@ -199,6 +215,9 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
* 根据表格高度重新计算分页的页大小 * 根据表格高度重新计算分页的页大小
*/ */
private void resizeTable(Object observable, Bounds old, Bounds newBounds) { private void resizeTable(Object observable, Bounds old, Bounds newBounds) {
if (!allowResize) {
return;
}
double tableHeight = newBounds.getHeight(); double tableHeight = newBounds.getHeight();
if (tableHeight <= 0) { if (tableHeight <= 0) {
return; return;
@@ -208,8 +227,13 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
if (lookup != null) { if (lookup != null) {
double rowHeight = lookup.prefHeight(-1); double rowHeight = lookup.prefHeight(-1);
int rows = (int) Math.round(table.getHeight() / rowHeight) - 1; int rows = (int) Math.round(table.getHeight() / rowHeight) - 1;
int pageNumber = (int) Math.abs(currentPageable.getOffset() / rows); // 只有当行数变化超过一定阈值时才重新加载数据
int currentRows = currentPageable.getPageSize();
if (Math.abs(rows - currentRows) <= 2) {
return; // 避免微小变化导致频繁刷新
}
int pageNumber = (int) Math.abs(currentPageable.getOffset() / rows);
if (currentPageable.getPageNumber() == pageNumber && currentPageable.getPageSize() == rows) { if (currentPageable.getPageNumber() == pageNumber && currentPageable.getPageSize() == rows) {
return; return;
} }
@@ -259,9 +283,17 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
} }
} }
protected <K> void acceptCellEditEvent(TableColumn.CellEditEvent<TV, K> event, Function<TV, Property<K>> function) { /**
* 处理单元格编辑事件
*
* @param <K>
* @param event
* @param propGetter
*/
protected <K> void acceptCellEditEvent(TableColumn.CellEditEvent<TV, K> event,
Function<TV, Property<K>> propGetter) {
TV row = event.getRowValue(); TV row = event.getRowValue();
Property<K> property = function.apply(row); Property<K> property = propGetter.apply(row);
property.setValue(event.getNewValue()); property.setValue(event.getNewValue());
try { try {
saveRowData(row); saveRowData(row);
@@ -317,21 +349,47 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
row.saveInFxApplicationThread(service); row.saveInFxApplicationThread(service);
} }
/**
* 加载行数据
*
* @param row
* @return
*/
public T loadRowData(TV row) { public T loadRowData(TV row) {
return getViewModelService().findById(row.getId().get()); if (row.getId() == null) {
return null;
}
T entity = getViewModelService().findById(row.getId().get());
return entity;
} }
/**
* 删除行数据
*
* @param entity
*/
public void deleteRowData(T entity) { public void deleteRowData(T entity) {
if (entity == null) {
return;
}
ViewModelService<T, TV> service = getViewModelService();
getViewModelService().delete(entity); getViewModelService().delete(entity);
} }
/**
* 保存行数据
*
* @param entity
* @return
*/
public T saveRowData(T entity) { public T saveRowData(T entity) {
if (entity == null) {
return null;
}
ViewModelService<T, TV> service = getViewModelService();
return getViewModelService().save(entity); return getViewModelService().save(entity);
} }
// 记录延时任务信息
private ScheduledFuture<?> loadTableDataSetFuture;
@Override @Override
public void loadTableDataSet() { public void loadTableDataSet() {
loadTableDataSet(false); loadTableDataSet(false);
@@ -359,18 +417,18 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
private CompletableFuture<Void> _reloadTableData() { private CompletableFuture<Void> _reloadTableData() {
CompletableFuture<Void> future = new CompletableFuture<>(); CompletableFuture<Void> future = new CompletableFuture<>();
Platform.runLater(() -> { Platform.runLater(() -> {
allowResize = false; // 禁用调整
dataSet.clear(); dataSet.clear();
runAsync(() -> { runAsync(() -> {
controller.setStatus("载入中..."); controller.setStatus("载入中...");
List<TV> models = loadTableData(); // 异步加载数据
Platform.runLater(() -> { if (getViewModelService() instanceof QueryService<T, TV> queryService) {
try { asyncLoadTableData(queryService, future);
updateTableDataSet(models); return;
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
} }
}); // 同步加载方法
List<TV> models = loadTableData();
_updateModels(models, future);
}).exceptionally(ex -> { }).exceptionally(ex -> {
future.completeExceptionally(ex); future.completeExceptionally(ex);
return null; return null;
@@ -379,20 +437,60 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return future; return future;
} }
private void _updateModels(List<TV> models, CompletableFuture<Void> future) {
Platform.runLater(() -> {
try {
updateTableDataSet(models);
allowResize = true; // 恢复调整
future.complete(null);
} catch (Exception e) {
allowResize = true; // 恢复调整
future.completeExceptionally(e);
}
});
}
/**
* 更新表格数据
*
* @param models
*/
protected void updateTableDataSet(List<TV> models) { protected void updateTableDataSet(List<TV> models) {
long timeMillis = System.currentTimeMillis(); long timeMillis = System.currentTimeMillis();
dataSet.setAll(models); // 清除所有选择状态避免选择状态混乱
if (getTableView() != null && getTableView().getSelectionModel() != null) {
getTableView().getSelectionModel().clearSelection();
}
// 先清空再设置新数据避免数据叠加
dataSet.clear();
if (models != null) {
dataSet.addAll(models);
}
// 强制刷新表格布局
if (getTableView() != null) {
getTableView().requestLayout();
getTableView().refresh();
}
long timeCost = System.currentTimeMillis() - timeMillis; long timeCost = System.currentTimeMillis() - timeMillis;
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("update table dataSet cost: {} ms", timeCost); logger.debug("update table dataSet cost: {} ms", timeCost);
} }
} }
/**
* 加载表格数据
*
* @return
*/
protected List<TV> loadTableData() { protected List<TV> loadTableData() {
Specification<T> spec = 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(spec, 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);
@@ -404,17 +502,45 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return page.map(service::from).toList(); return page.map(service::from).toList();
} }
/**
* 异步加载表格数据
*
* @param queryService
* @param future
*/
private void asyncLoadTableData(QueryService<T, TV> queryService, CompletableFuture<Void> future) {
ParamUtils.Builder params = getSpecification();
queryService.asyncFindAll(params == null ? null : params.build(), getPageable()).whenComplete((result, ex) -> {
if (ex != null) {
future.completeExceptionally(ex);
return;
}
updateFooter(result);
List<TV> models = result.map(getViewModelService()::from).toList();
_updateModels(models, future);
});
}
/**
* 获取ViewModelService
*
* @return
*/
protected ViewModelService<T, TV> getViewModelService() { protected ViewModelService<T, TV> getViewModelService() {
ViewModelService<T, TV> service = controller.getViewModelService(); ViewModelService<T, TV> service = controller.getViewModelService();
if (service == null) { if (service == null) {
if (logger.isWarnEnabled()) { throw new IllegalArgumentException("ViewModelService is null");
logger.warn("ViewModelService is null");
}
} }
return service; return service;
} }
public Specification<T> getSpecification() { /**
* 获取查询条件
*
* @return
*/
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());
@@ -422,8 +548,14 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return null; return null;
} }
protected Specification<T> getSpecification(String searchText) { /**
return controller.getViewModelService().getSpecification(searchText); * 获取查询条件
*
* @param searchText
* @return
*/
protected ParamUtils.Builder getSpecification(String searchText) {
return getViewModelService().getSpecification(searchText);
} }
/** /**
@@ -434,6 +566,11 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
protected void onTableRowDoubleClickedAction(TV item) { protected void onTableRowDoubleClickedAction(TV item) {
} }
/**
* 更新页脚
*
* @param page
*/
protected void updateFooter(Page<T> page) { protected void updateFooter(Page<T> page) {
Platform.runLater(() -> { Platform.runLater(() -> {
controller.previousPageBtn.setDisable(!page.hasPrevious()); controller.previousPageBtn.setDisable(!page.hasPrevious());
@@ -444,19 +581,44 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
}); });
} }
/**
* 获取表格排序
*
* @return
*/
public List<Sort.Order> getTableOrders() { public List<Sort.Order> getTableOrders() {
return TableViewUtils.getOrders(getTableView()); return TableViewUtils.getOrders(getTableView());
} }
/**
* 获取表格排序
*
* @return
*/
public Sort getSortByTable() { public Sort getSortByTable() {
if (getTableView() == null) {
return Sort.unsorted();
}
return Sort.by(getTableOrders()); return Sort.by(getTableOrders());
} }
/**
* 获取分页参数
*
* @return
*/
public Pageable getPageable() { public Pageable getPageable() {
Sort sort = getSortByTable(); Sort sort = getSortByTable();
return currentPageable.withSort(sort); return currentPageable.withSort(sort);
} }
/**
* 显示在当前窗口为父窗口的新窗口
*
* @param <Controller> 控制器类型
* @param clz 控制器类
* @param model 数据
*/
protected <Controller extends AbstEntityController<T, TV>> void showInOwner(Class<Controller> clz, TV model) { protected <Controller extends AbstEntityController<T, TV>> void showInOwner(Class<Controller> clz, TV model) {
BaseController.show(clz, model, getTableView().getScene().getWindow()); BaseController.show(clz, model, getTableView().getScene().getWindow());
} }

View File

@@ -1,7 +1,7 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import com.ecep.contract.manager.ds.other.model.IdentityEntity; import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel; import com.ecep.contract.vm.IdentityViewModel;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@@ -41,6 +41,4 @@ public abstract class AbstManagerWindowController<T extends IdentityEntity, TV e
} }
} }

View File

@@ -1,14 +1,35 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
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.FxmlUtils;
import com.ecep.contract.vo.EmployeeVo;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import com.ecep.contract.Desktop;
import com.ecep.contract.SpringApp;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.SysConfService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CurrentEmployee;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.other.service.SysConfService;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.manager.util.FxmlUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@@ -21,23 +42,8 @@ import javafx.stage.Stage;
import javafx.stage.Window; import javafx.stage.Window;
import javafx.stage.WindowEvent; import javafx.stage.WindowEvent;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import java.beans.PropertyDescriptor; public class BaseController implements BeanContext {
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class BaseController {
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<>();
@@ -45,7 +51,8 @@ public class BaseController {
return show(clz, owner, null); return show(clz, owner, null);
} }
public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner, Consumer<T> consumer) { public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner,
Consumer<T> consumer) {
String key = clz.getName(); String key = clz.getName();
if (toFront(key)) { if (toFront(key)) {
return null; return null;
@@ -65,8 +72,8 @@ public class BaseController {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController> public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController> void show(
void show(Class<T> clz, M viewModel, Window owner) { Class<T> clz, M viewModel, Window owner) {
String key = getKey(clz, viewModel); String key = getKey(clz, viewModel);
if (toFront(key)) { if (toFront(key)) {
return; return;
@@ -76,7 +83,6 @@ public class BaseController {
throw new RuntimeException("@FxmlPath is required"); throw new RuntimeException("@FxmlPath is required");
} }
FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> { FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
T controller = loader.getController(); T controller = loader.getController();
if (controller instanceof AbstEntityController<?, ?>) { if (controller instanceof AbstEntityController<?, ?>) {
@@ -114,7 +120,6 @@ public class BaseController {
}); });
} }
public static boolean toFront(String key) { public static boolean toFront(String key) {
Stage stage = stages.get(key); Stage stage = stages.get(key);
if (stage != null) { if (stage != null) {
@@ -161,6 +166,11 @@ public class BaseController {
return future; return future;
} }
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, Desktop.instance.getExecutorService())
.exceptionally(this::handleException);
}
/** /**
* 窗口标题 * 窗口标题
*/ */
@@ -177,42 +187,44 @@ public class BaseController {
private String stageKey; private String stageKey;
public Label leftStatusLabel; public Label leftStatusLabel;
public Label rightStatusLabel; public Label rightStatusLabel;
private EmployeeVo currentUser;
@Getter private BeanContext beanContext = new DefaultBeanContext();
@Setter public <T> T getBean(Class<T> requiredType) throws BeansException {
private Locale locale = Locale.getDefault(); return beanContext.getBean(requiredType);
}
@Setter public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
private SysConfService confService; return beanContext.getCachedBean(requiredType);
@Setter
private EmployeeService employeeService;
private Employee currentUser;
protected <T> T getBean(Class<T> requiredType) throws BeansException {
return SpringApp.getBean(requiredType);
} }
public SysConfService getConfService() { public SysConfService getConfService() {
if (confService == null) { return getCachedBean(SysConfService.class);
confService = getBean(SysConfService.class);
}
return confService;
} }
public EmployeeService getEmployeeService() { public EmployeeService getEmployeeService() {
if (employeeService == null) { return getCachedBean(EmployeeService.class);
employeeService = getBean(EmployeeService.class);
}
return employeeService;
} }
public Employee getCurrentUser() { public EmployeeVo getCurrentUser() {
if (currentUser == null) { if (currentUser == null) {
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId()); currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
} }
return currentUser; return currentUser;
} }
public CurrentEmployee getCurrentEmployee() {
return Desktop.instance.getActiveEmployee();
}
public Locale getLocale() {
CurrentEmployee currentEmployee = getCurrentEmployee();
if (currentEmployee == null) {
return Locale.getDefault();
}
return currentEmployee.localeProperty().get();
}
public String getMessage(String code, Object... args) { public String getMessage(String code, Object... args) {
return SpringApp.getMessage(code, args, getLocale()); return SpringApp.getMessage(code, args, getLocale());
} }

View File

@@ -1,7 +1,10 @@
package com.ecep.contract.manager; package com.ecep.contract.controller;
import org.springframework.context.ApplicationEvent;
import com.ecep.contract.vm.CurrentEmployee;
import lombok.Getter; import lombok.Getter;
import org.springframework.context.ApplicationEvent;
@Getter @Getter
public class CurrentEmployeeInitialedEvent extends ApplicationEvent { public class CurrentEmployeeInitialedEvent extends ApplicationEvent {

View File

@@ -1,11 +1,16 @@
package com.ecep.contract.manager.ds.other.controller; package com.ecep.contract.controller;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@@ -14,32 +19,26 @@ import org.springframework.context.annotation.Scope;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ecep.contract.manager.CurrentEmployee; import com.ecep.contract.Desktop;
import com.ecep.contract.manager.CurrentEmployeeInitialedEvent; import com.ecep.contract.DesktopUtils;
import com.ecep.contract.manager.Desktop; import com.ecep.contract.WebSocketClientService;
import com.ecep.contract.manager.cloud.old.OldVersionService; import com.ecep.contract.controller.bank.BankManagerWindowController;
import com.ecep.contract.manager.cloud.rk.CloudRkManagerWindowController; import com.ecep.contract.controller.company.CompanyManagerWindowController;
import com.ecep.contract.manager.cloud.rk.CloudRkService; import com.ecep.contract.controller.contract.ContractManagerWindowController;
import com.ecep.contract.manager.cloud.tyc.CloudTycManagerWindowController; import com.ecep.contract.controller.customer.CompanyCustomerManagerWindowController;
import com.ecep.contract.manager.cloud.u8.ContractSyncTask; import com.ecep.contract.controller.department.DepartmentManagerWindowController;
import com.ecep.contract.manager.cloud.u8.YongYouU8ManagerWindowController; import com.ecep.contract.controller.employee.EmployeeManagerWindowController;
import com.ecep.contract.manager.cloud.u8.YongYouU8Service; import com.ecep.contract.controller.inventory.InventoryManagerWindowController;
import com.ecep.contract.manager.ds.company.controller.CompanyManagerWindowController; import com.ecep.contract.controller.permission.EmployeeFunctionsManagerWindowController;
import com.ecep.contract.manager.ds.contract.controller.ContractManagerWindowController; import com.ecep.contract.controller.permission.EmployeeRoleManagerWindowController;
import com.ecep.contract.manager.ds.customer.controller.CompanyCustomerManagerWindowController; import com.ecep.contract.controller.project.ProjectManagerWindowController;
import com.ecep.contract.manager.ds.other.controller.bank.BankManagerWindowController; import com.ecep.contract.controller.vendor.VendorManagerWindowController;
import com.ecep.contract.manager.ds.other.controller.department.DepartmentManagerWindowController; import com.ecep.contract.service.CloudRkService;
import com.ecep.contract.manager.ds.other.controller.employee.EmployeeManagerWindowController; import com.ecep.contract.service.YongYouU8Service;
import com.ecep.contract.manager.ds.other.controller.inventory.InventoryManagerWindowController; import com.ecep.contract.task.ContractSyncTask;
import com.ecep.contract.manager.ds.other.controller.permission.EmployeeFunctionsManagerWindowController; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ds.other.controller.permission.EmployeeRoleManagerWindowController; import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.manager.ds.project.controller.ProjectManagerWindowController; import com.ecep.contract.vm.CurrentEmployee;
import com.ecep.contract.manager.ds.vendor.controller.CompanyVendorManagerWindowController;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.task.TaskMonitorViewController;
import com.ecep.contract.manager.util.DesktopUtils;
import com.ecep.contract.manager.util.FxmlUtils;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@@ -75,13 +74,15 @@ public class HomeWindowController extends BaseController {
public Button openCustomManagerWindow; public Button openCustomManagerWindow;
public TaskProgressView<Task<?>> taskProgressView; public TaskProgressView<Task<?>> taskProgressView;
public Label taskMonitorLabel; public Label taskMonitorLabel;
public Label webSocketMonitorLabel;
public Glyph webSocketMonitorIcon;
public Label employeeStatusLabel; public Label employeeStatusLabel;
public void initialize() { public void initialize() {
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));
} }
@@ -102,20 +103,26 @@ public class HomeWindowController extends BaseController {
employeeStatusLabel.textProperty().bind(Desktop.instance.getActiveEmployee().getName()); employeeStatusLabel.textProperty().bind(Desktop.instance.getActiveEmployee().getName());
Desktop.instance.getTaskMonitorCenter().bindStatusLabel(taskMonitorLabel); Desktop.instance.getTaskMonitorCenter().bindStatusLabel(taskMonitorLabel);
Desktop.instance.getActiveEmployee().initialize(); Desktop.instance.getActiveEmployee().initialize();
WebSocketClientService webSocketService = getBean(WebSocketClientService.class);
webSocketMonitorIcon.iconProperty()
.bind(webSocketService.getOnlineProperty().map(b -> b ? "CHAIN" : "CHAIN_BROKEN"));
webSocketMonitorLabel.textProperty().bind(webSocketService.getMessageProperty());
webSocketMonitorLabel.setOnMouseClicked(event -> {
webSocketService.send("webSocketUrl - " + LocalDateTime.now().toString());
});
webSocketService.initWebSocket();
} }
@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()) {
logger.info("You are administrator, try schedule sync tasks."); logger.info("You are administrator, try schedule sync tasks.");
} }
Desktop.instance.getExecutorService().schedule(() -> { Desktop.instance.getExecutorService().schedule(() -> {
try {
getBean(OldVersionService.class).scheduledTasks(taskProgressView);
} catch (BeansException ignored) {
}
try { try {
getBean(YongYouU8Service.class).scheduledTasks(taskProgressView); getBean(YongYouU8Service.class).scheduledTasks(taskProgressView);
} catch (BeansException ignored) { } catch (BeansException ignored) {
@@ -140,14 +147,6 @@ public class HomeWindowController extends BaseController {
// scheduledExecutorService.shutdownNow(); // scheduledExecutorService.shutdownNow();
} }
@Override
public void onHidden(WindowEvent windowEvent) {
System.out.println("windowEvent = " + windowEvent);
super.onHidden(windowEvent);
// Platform.exit();
}
/** /**
* 打开 配置 窗口 * 打开 配置 窗口
*/ */
@@ -221,12 +220,11 @@ public class HomeWindowController extends BaseController {
showInOwner(TaskMonitorViewController.class); showInOwner(TaskMonitorViewController.class);
} }
/** @Override
* 运行任务监控演示 public void onHidden(WindowEvent windowEvent) {
*/ System.out.println("windowEvent = " + windowEvent);
public void onRunTaskMonitorDemo(ActionEvent event) { WebSocketClientService webSocketService = getBean(WebSocketClientService.class);
com.ecep.contract.manager.ui.task.DemoTask.runDemo(); webSocketService.closeWebSocket(); // 在窗口隐藏时关闭WebSocket连接
// 自动打开任务监控窗口 super.onHidden(windowEvent);
showInOwner(TaskMonitorViewController.class);
} }
} }

View File

@@ -1,4 +1,6 @@
package com.ecep.contract.manager.ui; package com.ecep.contract.controller;
import com.ecep.contract.controller.tab.Skin;
public interface ManagerSkin extends Skin { public interface ManagerSkin extends Skin {

View File

@@ -0,0 +1,369 @@
package com.ecep.contract.controller;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import javafx.beans.property.SimpleStringProperty;
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.MyProperties;
import com.ecep.contract.SpringApp;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import javafx.application.Platform;
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.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import lombok.Setter;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.WebSocket;
public class OkHttpLoginController implements MessageHolder {
private static final Logger logger = LoggerFactory.getLogger(OkHttpLoginController.class);
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
@Setter
private MessageHolder holder;
@Setter
private Stage primaryStage;
@Setter
private MyProperties properties;
@Setter
private OkHttpClient httpClient;
private SimpleStringProperty serverUrl = new SimpleStringProperty();
private String webSocketUrl;
public OkHttpLoginController() {
}
@Override
public void addMessage(Level level, String message) {
if (holder != null) {
holder.addMessage(level, message);
}
}
private void initServerUrls() {
String host = properties.getServerHost();
String port = properties.getServerPort();
serverUrl.set("http://" + host + ":" + port);
this.webSocketUrl = "ws://" + host + ":" + port + "/ws";
}
public CompletableFuture<Void> tryLogin() {
initServerUrls();
// 检查配置文件中是否保存用户名和密码
String userName = getUserName();
String password = getPassword();
if (StringUtils.hasText(userName) && StringUtils.hasText(password)) {
return login(userName, password);
} else {
CompletableFuture<Void> loginFuture = new CompletableFuture<>();
Platform.runLater(() -> {
showLoginDialog();
loginFuture.complete(null);
});
return loginFuture;
}
}
private String getUserName() {
return properties.getUserName();
}
private String getPassword() {
return properties.getPassword();
}
private void showLoginDialog() {
Stage stage = new Stage();
stage.initOwner(primaryStage);
stage.setTitle("系统登录");
// 创建 BorderPane 并设置边距
BorderPane borderPane = new BorderPane();
borderPane.setPadding(new Insets(20));
// 创建布局
GridPane grid = new GridPane();
grid.setHgap(10);
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("用户名:");
TextField userField = new TextField();
{
String username = getUserName();
if (StringUtils.hasText(username)) {
userField.setText(username);
}
grid.add(userLabel, 0, 1);
grid.add(userField, 1, 1);
}
// 密码输入框
Label passwordLabel = new Label("密码:");
PasswordField passwordField = new PasswordField();
{
String password = getPassword();
if (StringUtils.hasText(password)) {
passwordField.setText(password);
}
grid.add(passwordLabel, 0, 2);
grid.add(passwordField, 1, 2);
}
// 记住密码复选框
CheckBox rememberCheckBox = new CheckBox("记住密码");
{
boolean remember = properties.isRememberPassword();
if (remember) {
rememberCheckBox.setSelected(true);
}
}
grid.add(rememberCheckBox, 1, 3);
// 错误消息提示
Label errorLabel = new Label();
errorLabel.setStyle("-fx-text-fill: red;");
errorLabel.setVisible(false);
grid.add(errorLabel, 0, 4);
GridPane.setColumnSpan(errorLabel, 2);
borderPane.setCenter(grid);
// 登录按钮
Button loginButton = new Button("登录");
loginButton.setDefaultButton(true);
loginButton.setPrefWidth(100);
borderPane.setBottom(loginButton);
BorderPane.setAlignment(loginButton, javafx.geometry.Pos.CENTER);
BorderPane.setMargin(loginButton, new Insets(20, 0, 0, 0));
// 登录按钮点击事件
loginButton.setOnAction(event -> {
String username = userField.getText();
String password = passwordField.getText();
boolean remember = rememberCheckBox.isSelected();
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
errorLabel.setText("用户名和密码不能为空");
errorLabel.setVisible(true);
return;
}
errorLabel.setVisible(false);
// 保存配置
if (remember) {
properties.setUserName(username);
properties.setPassword(password);
properties.setRememberPassword(true);
} else {
properties.setUserName(username);
properties.setPassword("");
properties.setRememberPassword(false);
}
properties.save();
// 执行登录
login(username, password).whenComplete((v, e) -> {
if (e != null) {
Platform.runLater(() -> {
errorLabel.setText(e.getMessage());
errorLabel.setVisible(true);
// showError("登录失败", e.getMessage());
});
return;
}
Platform.runLater(() -> {
stage.close();
});
});
});
// 创建场景并设置到窗口
Scene scene = new Scene(borderPane, 400, 280);
stage.setScene(scene);
stage.setResizable(false);
stage.showAndWait();
System.out.println("登录窗口关闭");
}
private CompletableFuture<Void> login(String username, String password) {
// 添加详细日志记录服务器URL和请求准备情况
info("正在连接服务器: " + serverUrl.get());
logger.debug("用户名: {}", username);
CompletableFuture<Void> future = new CompletableFuture<>();
try {
while (!SpringApp.isRunning()) {
holder.info("环境准备中,请稍后...");
Thread.sleep(1000);
}
ObjectMapper objectMapper = SpringApp.getBean(ObjectMapper.class);
ObjectNode objectNode = objectMapper.createObjectNode();
objectNode.put("username", username);
objectNode.put("password", password);
objectNode.put("type", "client");
// 将MacIP列表转换为Map<String, String>格式MAC地址->IP地址
List<MacIP> macIpList = getMacAndIP().join();
ObjectNode signNode = objectMapper.createObjectNode();
for (MacIP macIp : macIpList) {
signNode.put(macIp.mac, macIp.ip);
}
objectNode.set("sign", signNode);
// 构建JSON格式的登录请求
RequestBody body = RequestBody.create(objectNode.toString(), JSON);
// 构建并记录完整的请求URL
String loginUrl = serverUrl.get() + "/api/login";
logger.debug("构建登录请求URL: " + loginUrl);
Request request = new Request.Builder()
.url(loginUrl)
.post(body)
.build();
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
future.completeExceptionally(
new IOException("登录失败: 无法连接到服务器,请检查网络连接或服务器配置 - " + e.getMessage(), e));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try {
if (!response.isSuccessful()) {
future.completeExceptionally(
new IOException("登录失败: 服务器返回错误码 - " + response.toString()));
return;
}
ResponseBody body = response.body();
System.out.println("contentType = " + body.contentType());
JsonNode jsonNode = objectMapper.readTree(body.string());
boolean success = jsonNode.get("success").asBoolean(false);
if (!success) {
future.completeExceptionally(
new IOException("登录失败: 服务器返回错误 - " + jsonNode.get("error").asText()));
return;
}
System.out.println("登录成功: " + jsonNode.toString());
// 登录成功后调用新的API端点获取用户信息
Desktop.instance.setActiveEmployeeId(jsonNode.get("employeeId").asInt());
Desktop.instance.setSessionId(jsonNode.get("sessionId").asText());
future.complete(null);
} finally {
// 确保主响应体被关闭
response.close();
}
}
});
} catch (Exception e) {
future.completeExceptionally(new IOException("登录过程中发生错误: " + e.getMessage(), e));
}
return future;
}
CompletableFuture<List<MacIP>> getMacAndIP() {
return CompletableFuture.supplyAsync(() -> {
List<MacIP> list = new ArrayList<>();
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface anInterface = interfaces.nextElement();
if (anInterface.isLoopback() || !anInterface.isUp()) {
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 ? "-" : ""));
}
String mac = sb.toString();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress.getHostAddress().contains(":")) {
continue; // 跳过IPv6地址
}
list.add(new MacIP(mac, inetAddress.getHostAddress()));
}
}
} catch (SocketException e) {
throw new RuntimeException(e);
}
return list;
});
}
static class MacIP {
String mac;
String ip;
public MacIP(String mac, String ip) {
this.mac = mac;
this.ip = ip;
}
}
}

View File

@@ -0,0 +1,133 @@
package com.ecep.contract.controller;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.constant.CompanyVendorConstant;
import com.ecep.contract.constant.ContractConstant;
import com.ecep.contract.util.StringConfig;
import jakarta.annotation.PreDestroy;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
@Lazy
@Scope("prototype")
@Component
public class SysConfWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(SysConfWindowController.class);
public Label companyContractPathLabel;
public TextField u8DataBaseServerHostField;
public TextField u8DataBaseCatalogField;
public TextField u8DataBaseUserNameField;
public TextField u8DataBasePasswordField;
public Label vendorPathLabel;
public Label vendorEvaluationFormTemplateLabel;
public Label customerPathLabel;
public Label customerEvaluationFormTemplateLabel;
public Label customerSaleBookPathLabel;
StringConfig contractPathConfig = new StringConfig(ContractConstant.KEY_BASE_PATH);
StringConfig vendorPathConfig = new StringConfig(CompanyVendorConstant.KEY_BASE_PATH);
StringConfig vendorEvaluationFormTemplateConfig = new StringConfig(
CompanyVendorConstant.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerPathConfig = new StringConfig(CompanyCustomerConstant.KEY_BASE_PATH);
StringConfig customerEvaluationFormTemplateConfig = new StringConfig(
CompanyCustomerConstant.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerSaleBookPathConfig = new StringConfig(CompanyCustomerConstant.KEY_SALEBOOK_PATH);
public void initialize() {
contractPathConfig.setControl(companyContractPathLabel);
contractPathConfig.initialize();
vendorPathConfig.setControl(vendorPathLabel);
vendorPathConfig.initialize();
vendorEvaluationFormTemplateConfig.setControl(vendorEvaluationFormTemplateLabel);
vendorEvaluationFormTemplateConfig.initialize();
customerPathConfig.setControl(customerPathLabel);
customerPathConfig.initialize();
customerEvaluationFormTemplateConfig.setControl(customerEvaluationFormTemplateLabel);
customerEvaluationFormTemplateConfig.initialize();
customerSaleBookPathConfig.setControl(customerSaleBookPathLabel);
customerSaleBookPathConfig.initialize();
logger.debug("#initialize()");
}
private void directoryChoose(StringConfig config, String title, String key, ActionEvent event) {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(title);
File value = new File(config.getProperty().getValue());
chooser.setInitialDirectory(value);
Node node = (Node) event.getSource();
File selected = chooser.showDialog(node.getScene().getWindow());
if (selected != null) {
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCompanyContractPath(ActionEvent actionEvent) {
directoryChoose(contractPathConfig, "请选择合同目录", ContractConstant.KEY_BASE_PATH, actionEvent);
}
public void changeVendorPath(ActionEvent actionEvent) {
directoryChoose(vendorPathConfig, "请选择供应商目录", CompanyVendorConstant.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerPath(ActionEvent actionEvent) {
directoryChoose(customerPathConfig, "请选择客户目录", CompanyCustomerConstant.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerSaleBookPath(ActionEvent actionEvent) {
directoryChoose(customerSaleBookPathConfig, "请选择销售台账目录", CompanyCustomerConstant.KEY_SALEBOOK_PATH, actionEvent);
}
// 模拟销毁方法
@PreDestroy
public void destroy() {
}
private void fileChoose(StringConfig config, String title, String key, ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.setTitle(title);
File value = new File(config.getProperty().getValue());
chooser.setInitialDirectory(value.getParentFile());
chooser.setInitialFileName(value.getName());
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(" 模板文件(*.xlsx)", "*.xlsx"));
Node node = (Node) event.getSource();
File selected = chooser.showOpenDialog(node.getScene().getWindow());
if (selected != null) {
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCustomerEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(customerEvaluationFormTemplateConfig, "请选择客户资信评估表模板",
CompanyCustomerConstant.KEY_EVALUATION_FORM_TEMPLATE, actionEvent);
}
public void changeVendorEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(vendorEvaluationFormTemplateConfig, "请选择供方调查评价表模板",
CompanyVendorConstant.KEY_EVALUATION_FORM_TEMPLATE,
actionEvent);
}
}

View File

@@ -1,18 +1,25 @@
package com.ecep.contract.manager.ui.task; package com.ecep.contract.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import com.ecep.contract.util.FxmlPath;
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.manager.Desktop; import com.ecep.contract.Desktop;
import com.ecep.contract.manager.ui.BaseController; import com.ecep.contract.Message;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.manager.ui.Message; import com.ecep.contract.task.MonitoredTask;
import com.ecep.contract.manager.util.MyDateTimeUtils; import com.ecep.contract.task.TaskHistory;
import com.ecep.contract.task.TaskMonitorCenter;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@@ -69,10 +76,47 @@ public class TaskMonitorViewController extends BaseController {
private Button cancelTaskButton; private Button cancelTaskButton;
@FXML @FXML
private Button clearHistoryButton; private Button clearHistoryButton;
@FXML
private Button refreshExecutorInfoButton;
@FXML
private TableView<ExecutorInfo> executorInfoTable;
@FXML
private TableColumn<ExecutorInfo, String> executorFieldColumn;
@FXML
private TableColumn<ExecutorInfo, String> executorValueColumn;
@FXML
private TableColumn<ExecutorInfo, String> executorDescriptionColumn;
public TaskMonitorViewController() { public TaskMonitorViewController() {
} }
/**
* 用于存储ExecutorService信息的内部类
*/
public static class ExecutorInfo {
private final String field;
private final String value;
private final String description;
public ExecutorInfo(String field, String value, String description) {
this.field = field;
this.value = value;
this.description = description;
}
public String getField() {
return field;
}
public String getValue() {
return value;
}
public String getDescription() {
return description;
}
}
@Override @Override
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent); super.onShown(windowEvent);
@@ -144,6 +188,94 @@ public class TaskMonitorViewController extends BaseController {
}); });
cancelTaskButton.disableProperty().bind(activeTasksTable.getSelectionModel().selectedItemProperty().isNull()); cancelTaskButton.disableProperty().bind(activeTasksTable.getSelectionModel().selectedItemProperty().isNull());
// 初始化Executor信息表格
initializeExecutorInfoTable();
}
/**
* 初始化Executor信息表格
*/
private void initializeExecutorInfoTable() {
executorFieldColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getField()));
executorValueColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getValue()));
executorDescriptionColumn
.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getDescription()));
// 初始加载Executor信息
refreshExecutorInfo();
// 绑定刷新按钮事件
refreshExecutorInfoButton.setOnAction(event -> refreshExecutorInfo());
}
/**
* 刷新Executor信息
*/
@FXML
private void refreshExecutorInfo() {
executorInfoTable.getItems().clear();
try {
ExecutorService executorService = Desktop.instance.getExecutorService();
if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
executorInfoTable.getItems().add(new ExecutorInfo(
"PoolSize",
String.valueOf(threadPoolExecutor.getPoolSize()),
"PoolSize"));
executorInfoTable.getItems().add(new ExecutorInfo(
"活动线程数",
String.valueOf(threadPoolExecutor.getActiveCount()),
"当前正在执行任务的线程数"));
// 添加线程池信息
executorInfoTable.getItems().add(new ExecutorInfo(
"核心线程数",
String.valueOf(threadPoolExecutor.getCorePoolSize()),
"线程池维护的最小线程数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"最大线程数",
String.valueOf(threadPoolExecutor.getMaximumPoolSize()),
"线程池允许的最大线程数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"KeepAliveTime",
String.valueOf(threadPoolExecutor.getKeepAliveTime(TimeUnit.SECONDS)),
"the thread keep-alive time, which is the amount of time that threads may remain idle before being terminated. Threads that wait this amount of time without processing a task will be terminated if there are more than the core number of threads currently in the pool, or if this pool allows core thread timeout."));
executorInfoTable.getItems().add(new ExecutorInfo(
"任务队列大小",
String.valueOf(threadPoolExecutor.getQueue().size()),
"等待执行的任务数量"));
executorInfoTable.getItems().add(new ExecutorInfo(
"已完成任务数",
String.valueOf(threadPoolExecutor.getCompletedTaskCount()),
"已完成执行的任务总数"));
executorInfoTable.getItems().add(new ExecutorInfo(
"LargestPoolSize",
String.valueOf(threadPoolExecutor.getLargestPoolSize()),
"线程池当前状态"));
// 如果是ScheduledExecutorService添加额外信息
if (executorService instanceof ScheduledExecutorService) {
executorInfoTable.getItems().add(new ExecutorInfo(
"线程池类型",
"ScheduledExecutorService",
"可调度的线程池"));
} else {
executorInfoTable.getItems().add(new ExecutorInfo(
"线程池类型",
"ThreadPoolExecutor",
"普通线程池"));
}
} else {
executorInfoTable.getItems().add(new ExecutorInfo(
"错误",
"未知的ExecutorService类型",
"无法获取线程池详细信息"));
}
} catch (Exception e) {
executorInfoTable.getItems().add(new ExecutorInfo(
"异常",
e.getMessage(),
"获取ExecutorService信息时发生错误"));
}
} }
/** /**
@@ -218,4 +350,11 @@ public class TaskMonitorViewController extends BaseController {
alert.getDialogPane().setContent(scrollPane); alert.getDialogPane().setContent(scrollPane);
alert.showAndWait(); alert.showAndWait();
} }
/**
* 运行任务监控演示
*/
public void onRunTaskMonitorDemo(ActionEvent event) {
com.ecep.contract.task.DemoTask.runDemo();
}
} }

View File

@@ -1,17 +1,18 @@
package com.ecep.contract.manager.ds.other.controller.bank; package com.ecep.contract.controller.bank;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.vm.BankViewModel;
import com.ecep.contract.vo.BankVo;
import com.ecep.contract.manager.ds.other.model.Bank;
import com.ecep.contract.manager.ds.other.vo.BankViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.control.cell.TextFieldTableCell;
public class BankManagerSkin public class BankManagerSkin
extends AbstEntityManagerSkin<Bank, BankViewModel, BankManagerSkin, BankManagerWindowController> extends AbstEntityManagerSkin<BankVo, BankViewModel, BankManagerSkin, BankManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<Bank, BankViewModel> { implements ManagerSkin, EditableEntityTableTabSkin<BankVo, BankViewModel> {
public BankManagerSkin(BankManagerWindowController controller) { public BankManagerSkin(BankManagerWindowController controller) {
super(controller); super(controller);

View File

@@ -1,23 +1,25 @@
package com.ecep.contract.manager.ds.other.controller.bank; package com.ecep.contract.controller.bank;
import com.ecep.contract.manager.ds.other.model.Bank;
import com.ecep.contract.manager.ds.other.service.BankService;
import com.ecep.contract.manager.ds.other.vo.BankViewModel;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
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.service.BankService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.BankViewModel;
import com.ecep.contract.vo.BankVo;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
@FxmlPath("/ui/bank-manager.fxml") @FxmlPath("/ui/bank-manager.fxml")
public class BankManagerWindowController public class BankManagerWindowController
extends AbstManagerWindowController<Bank, BankViewModel, BankManagerSkin> { extends AbstManagerWindowController<BankVo, BankViewModel, BankManagerSkin> {
@Autowired @Autowired
private BankService bankService; private BankService bankService;

View File

@@ -1,14 +1,15 @@
package com.ecep.contract.manager.cloud.rk; package com.ecep.contract.controller.cloud.rk;
import org.hibernate.Hibernate; import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.manager.SpringApp; import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.manager.ui.table.cell.CompanyTableCell; import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.manager.ds.company.controller.CompanyWindowController; import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.manager.ds.company.model.Company; import com.ecep.contract.service.CloudRkService;
import com.ecep.contract.manager.ds.company.service.CompanyService; import com.ecep.contract.service.CompanyService;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin; import com.ecep.contract.vm.CloudRkViewModel;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell; 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;
@@ -19,7 +20,7 @@ import javafx.scene.control.cell.CheckBoxTableCell;
import lombok.Setter; import lombok.Setter;
public class CloudRkManagerSkin public class CloudRkManagerSkin
extends AbstEntityManagerSkin<CloudRk, CloudRkInfoViewModel, CloudRkManagerSkin, CloudRkManagerWindowController> { extends AbstEntityManagerSkin<CloudRkVo, CloudRkViewModel, CloudRkManagerSkin, CloudRkManagerWindowController> {
@Setter @Setter
private CompanyService companyService; private CompanyService companyService;
@@ -47,13 +48,14 @@ public class CloudRkManagerSkin
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());
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>()); controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudBlackListUpdatedColumn.setCellValueFactory(param -> param.getValue().getCloudBlackListUpdated()); controller.cloudBlackListUpdatedColumn
.setCellValueFactory(param -> param.getValue().getCloudBlackListUpdated());
controller.cloudBlackListUpdatedColumn.setCellFactory(param -> new LocalDateTimeTableCell<>()); controller.cloudBlackListUpdatedColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudEntUpdateColumn.setCellValueFactory(param -> param.getValue().getCloudEntUpdate()); controller.cloudEntUpdateColumn.setCellValueFactory(param -> param.getValue().getCloudEntUpdate());
@@ -65,13 +67,12 @@ public class CloudRkManagerSkin
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription()); controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
} }
private void onAutoUpdateColumnEditCommit(TableColumn.CellEditEvent<CloudRkInfoViewModel, Boolean> event) { private void onAutoUpdateColumnEditCommit(TableColumn.CellEditEvent<CloudRkViewModel, Boolean> event) {
CloudRkInfoViewModel row = event.getRowValue(); CloudRkViewModel row = event.getRowValue();
row.getAutoUpdate().set(event.getNewValue()); row.getAutoUpdate().set(event.getNewValue());
saveRowData(row); saveRowData(row);
} }
@Override @Override
protected void createContextMenu(ContextMenu contextMenu) { protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新"); MenuItem item2 = new MenuItem("刷新");
@@ -89,23 +90,21 @@ public class CloudRkManagerSkin
* @param event event * @param event event
*/ */
public void onTableClearDescriptionAction(ActionEvent event) { public void onTableClearDescriptionAction(ActionEvent event) {
ObservableList<CloudRkInfoViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems(); ObservableList<CloudRkViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) { if (selectedItems.isEmpty()) {
return; return;
} }
CloudRkService service = getCloudRkService(); CloudRkService service = getCloudRkService();
for (CloudRkInfoViewModel selectedItem : selectedItems) { for (CloudRkViewModel selectedItem : selectedItems) {
selectedItem.getDescription().set(""); selectedItem.getDescription().set("");
selectedItem.saveInFxApplicationThread(service); selectedItem.saveInFxApplicationThread(service);
} }
} }
@Override @Override
protected void onTableRowDoubleClickedAction(CloudRkInfoViewModel item) { protected void onTableRowDoubleClickedAction(CloudRkViewModel item) {
Company company = item.getCompany().get(); Integer companyId = item.getCompany().get();
if (!Hibernate.isInitialized(item)) { CompanyVo company = getCompanyService().findById(companyId);
company = getCompanyService().findById(company.getId());
}
CompanyWindowController.show(company, getTableView().getScene().getWindow()); CompanyWindowController.show(company, getTableView().getScene().getWindow());
} }
} }

View File

@@ -0,0 +1,80 @@
package com.ecep.contract.controller.cloud.rk;
import java.time.LocalDateTime;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import com.ecep.contract.controller.AbstManagerWindowController;
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.Message;
import com.ecep.contract.SpringApp;
import com.ecep.contract.service.CloudRkService;
import com.ecep.contract.task.CloudRkSyncTask;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CloudRkViewModel;
import com.ecep.contract.vo.CloudRkVo;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/rk_manager.fxml")
public class CloudRkManagerWindowController
extends AbstManagerWindowController<CloudRkVo, CloudRkViewModel, CloudRkManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(CloudRkManagerWindowController.class);
public static void show() {
show(CloudRkManagerWindowController.class, null);
}
@Autowired
private CloudRkService cloudRkService;
public TableColumn<CloudRkViewModel, Number> idColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> latestUpdateColumn;
/**
* 集团相关方, Company
*/
public TableColumn<CloudRkViewModel, Integer> companyColumn;
public TableColumn<CloudRkViewModel, String> cloudIdColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudLatestColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudBlackListUpdatedColumn;
public TableColumn<CloudRkViewModel, LocalDateTime> cloudEntUpdateColumn;
public TableColumn<CloudRkViewModel, Boolean> autoUpdateColumn;
public TableColumn<CloudRkViewModel, String> descriptionColumn;
@Override
protected CloudRkManagerSkin createDefaultSkin() {
return new CloudRkManagerSkin(this);
}
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("数据源:集团相关方");
}
public void onDataRepairAction(ActionEvent event) {
}
public void onSyncAction(ActionEvent event) {
CloudRkSyncTask task = new CloudRkSyncTask();
UITools.showTaskDialogAndWait("同步数据", task, null);
}
@Override
public CloudRkService getViewModelService() {
return cloudRkService;
}
}

View File

@@ -1,21 +1,16 @@
package com.ecep.contract.manager.cloud.tyc; package com.ecep.contract.controller.cloud.tyc;
import java.util.List; import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.service.CloudTycService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CloudTycInfoViewModel;
import com.ecep.contract.vo.CloudTycVo;
import com.ecep.contract.vo.CompanyVo;
import org.hibernate.Hibernate;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ui.table.cell.CompanyTableCell;
import com.ecep.contract.manager.ds.company.controller.CompanyWindowController;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell;
import jakarta.persistence.criteria.Path;
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;
@@ -24,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;
@@ -49,31 +44,6 @@ public class CloudTycManagerSkin
return companyService; return companyService;
} }
@Override
protected List<CloudTycInfoViewModel> loadTableData() {
String searchText = controller.searchKeyField.getText();
Specification<CloudTyc> spec = null;
if (StringUtils.hasText(searchText)) {
Specification<CloudTyc> companySpec = (root, query, builder) -> {
Path<Object> company = root.get("company");
return builder.or(
builder.like(company.get("name"), "%" + searchText + "%"),
builder.like(company.get("shortName"), "%" + searchText + "%"));
};
Specification<CloudTyc> cloudIdSpec = (root, query, builder) -> {
return builder.like(root.get("cloudId"), "%" + searchText + "%");
};
spec = Specification.anyOf(companySpec, cloudIdSpec);
}
Page<CloudTyc> page = getCloudTycService().findAll(spec, getPageable());
updateFooter(page);
return page.map(CloudTycInfoViewModel::from).toList();
}
@Override @Override
public void initializeTable() { public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId()); controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
@@ -82,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());
@@ -114,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 (!Hibernate.isInitialized(item)) { CompanyVo company = getCompanyService().findById(companyId);
company = getCompanyService().findById(company.getId());
}
CompanyWindowController.show(company, getTableView().getScene().getWindow()); CompanyWindowController.show(company, getTableView().getScene().getWindow());
} }
} }

View File

@@ -0,0 +1,76 @@
package com.ecep.contract.controller.cloud.tyc;
import java.time.LocalDateTime;
import com.ecep.contract.controller.AbstManagerWindowController;
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.service.CloudTycService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.CloudTycInfoViewModel;
import com.ecep.contract.vo.CloudTycVo;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
/**
* 天眼查信息管理窗口控制器
*/
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/tyc_manager.fxml")
public class CloudTycManagerWindowController
extends AbstManagerWindowController<CloudTycVo, CloudTycInfoViewModel, CloudTycManagerSkin> {
public static void show() {
show(CloudTycManagerWindowController.class, null);
}
@Autowired
private CloudTycService cloudTycService;
@Autowired
private CompanyService companyService;
@FXML
public TableColumn<CloudTycInfoViewModel, Number> idColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, LocalDateTime> latestUpdateColumn;
/**
* 公司, Company
*/
@FXML
public TableColumn<CloudTycInfoViewModel, Integer> companyColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, String> cloudIdColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, LocalDateTime> cloudLatestColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, Number> scoreColumn;
@FXML
public TableColumn<CloudTycInfoViewModel, String> descriptionColumn;
@Override
public CloudTycService getViewModelService() {
return cloudTycService;
}
@Override
protected CloudTycManagerSkin createDefaultSkin() {
return new CloudTycManagerSkin(this);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("数据源:天眼查");
}
}

View File

@@ -0,0 +1,70 @@
package com.ecep.contract.controller.cloud.u8;
import com.ecep.contract.controller.BaseController;
import org.controlsfx.control.ToggleSwitch;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.constant.CloudYuConstant;
import com.ecep.contract.util.BooleanConfig;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.LocalDateConfig;
import com.ecep.contract.util.LocalDateTimeConfig;
import com.ecep.contract.util.StringConfig;
import javafx.fxml.FXML;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import javafx.stage.WindowEvent;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/u8_config.fxml")
public class YongYouU8ConfigWindowController extends BaseController {
@FXML
private DatePicker auto_create_company_after;
@FXML
private TextField contract_latest_date;
@FXML
private TextField contract_latest_id;
@FXML
private TextField sync_elapse;
@FXML
private ToggleSwitch use_latest_id;
LocalDateConfig config1 = new LocalDateConfig(CloudYuConstant.KEY_AUTO_CREATE_COMPANY_AFTER);
LocalDateTimeConfig config2 = new LocalDateTimeConfig(CloudYuConstant.KEY_SYNC_BY_LATEST_DATE);
StringConfig config3 = new StringConfig(CloudYuConstant.KEY_SYNC_BY_LATEST_ID);
StringConfig config4 = new StringConfig(CloudYuConstant.KEY_SYNC_ELAPSE);
BooleanConfig config5 = new BooleanConfig(CloudYuConstant.KEY_SYNC_USE_LATEST_ID);
@Override
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
getTitle().set("用友U8配置");
auto_create_company_after.setConverter(getCurrentEmployee().getLocalDateStringConverter());
config1.setControl(auto_create_company_after);
config1.initialize();
config2.setControl(contract_latest_date);
config2.setControlConverter(getCurrentEmployee().getLocalDateTimeStringConverter());
config2.initialize();
config3.setControl(contract_latest_id);
config3.initialize();
config4.setControl(sync_elapse);
config4.initialize();
config5.setControl(use_latest_id);
config5.initialize();
}
}

View File

@@ -0,0 +1,90 @@
package com.ecep.contract.controller.cloud.u8;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.YongYouU8Service;
import com.ecep.contract.vm.CloudYuInfoViewModel;
import com.ecep.contract.vo.CloudYuVo;
import com.ecep.contract.vo.CompanyVo;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
public class YongYouU8ManagerSkin
extends
AbstEntityManagerSkin<CloudYuVo, CloudYuInfoViewModel, YongYouU8ManagerSkin, YongYouU8ManagerWindowController>
implements ManagerSkin {
public YongYouU8ManagerSkin(YongYouU8ManagerWindowController controller) {
super(controller);
}
YongYouU8Service getU8Service() {
return getBean(YongYouU8Service.class);
}
CompanyService getCompanyService() {
return getBean(CompanyService.class);
}
@Override
public void initializeTable() {
controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
controller.companyColumn.setCellValueFactory(param -> param.getValue().getCompany());
controller.companyColumn.setCellFactory(param -> new CompanyTableCell<>(getCompanyService()));
controller.cloudIdColumn.setCellValueFactory(param -> param.getValue().getCloudId());
controller.latestUpdateColumn.setCellValueFactory(param -> param.getValue().getLatestUpdate());
controller.latestUpdateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.cloudLatestColumn.setCellValueFactory(param -> param.getValue().getCloudLatest());
controller.cloudLatestColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getVendorCode());
}
@Override
protected void createContextMenu(ContextMenu contextMenu) {
MenuItem item2 = new MenuItem("刷新");
item2.setOnAction(this::onTableRefreshAction);
MenuItem item3 = new MenuItem("清空备注");
item3.setOnAction(this::onTableClearDescriptionAction);
contextMenu.getItems().addAll(item2, item3);
}
/**
* 请空选择行的注释
*
* @param event event
*/
public void onTableClearDescriptionAction(ActionEvent event) {
ObservableList<CloudYuInfoViewModel> selectedItems = getTableView().getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
for (CloudYuInfoViewModel selectedItem : selectedItems) {
CloudYuVo yongYouU8Vo = getU8Service().findById(selectedItem.getId().get());
selectedItem.getCustomerCode().set("");
if (selectedItem.copyTo(yongYouU8Vo)) {
CloudYuVo saved = getU8Service().save(yongYouU8Vo);
selectedItem.update(saved);
}
}
}
@Override
protected void onTableRowDoubleClickedAction(CloudYuInfoViewModel item) {
Integer companyId = item.getCompany().get();
CompanyVo company = getCompanyService().findById(companyId);
CompanyWindowController.show(company, getTableView().getScene().getWindow());
}
}

View File

@@ -0,0 +1,117 @@
package com.ecep.contract.controller.cloud.u8;
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.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.Desktop;
import com.ecep.contract.service.YongYouU8Service;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CloudYuInfoViewModel;
import com.ecep.contract.vo.CloudYuVo;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@Component
@FxmlPath("/ui/cloud/u8_manager.fxml")
public class YongYouU8ManagerWindowController
extends AbstManagerWindowController<CloudYuVo, CloudYuInfoViewModel, YongYouU8ManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(YongYouU8ManagerWindowController.class);
public static void show() {
show(YongYouU8ManagerWindowController.class, null);
}
public TableColumn<CloudYuInfoViewModel, Number> idColumn;
public TableColumn<CloudYuInfoViewModel, LocalDateTime> latestUpdateColumn;
public TableColumn<CloudYuInfoViewModel, Integer> companyColumn;
public TableColumn<CloudYuInfoViewModel, String> cloudIdColumn;
public TableColumn<CloudYuInfoViewModel, LocalDateTime> cloudLatestColumn;
public TableColumn<CloudYuInfoViewModel, String> descriptionColumn;
@Override
public YongYouU8Service getViewModelService() {
return getSkin().getU8Service();
}
@Override
protected YongYouU8ManagerSkin createDefaultSkin() {
return new YongYouU8ManagerSkin(this);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("用友U8");
}
/**
* 打开配置窗口
*/
public void onConfigAction(ActionEvent event) {
BaseController.show(YongYouU8ConfigWindowController.class, null);
}
public void onPersonSyncAction(ActionEvent event) {
EmployeesSyncTask task = new EmployeesSyncTask();
UITools.showTaskDialogAndWait("员工数据同步", task, null);
}
public void onContractSyncAction(ActionEvent event) {
ContractSyncTask task = new ContractSyncTask();
UITools.showTaskDialogAndWait("合同数据增量同步", task, null);
}
public void onContractAllSyncAction(ActionEvent event) {
ContractSyncAllTask task = new ContractSyncAllTask();
UITools.showTaskDialogAndWait("合同数据全量同步", task, null);
}
public void onVendorSyncAction(ActionEvent event) {
VendorSyncTask task = new VendorSyncTask();
UITools.showTaskDialogAndWait("供应商数据同步", task, null);
}
public void onCustomerSyncAction(ActionEvent event) {
CustomerSyncTask task = new CustomerSyncTask();
UITools.showTaskDialogAndWait("客户数据同步", task, null);
}
public void onContractGroupSyncAction(ActionEvent event) {
ContractGroupSyncTask task = new ContractGroupSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractTypeSyncAction(ActionEvent event) {
ContractTypeSyncTask task = new ContractTypeSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractKindSyncAction(ActionEvent event) {
ContractKindSyncTask task = new ContractKindSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onVendorClassSyncAction(ActionEvent event) {
VendorClassSyncTask task = new VendorClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onCustomerClassSyncAction(ActionEvent event) {
CustomerClassSyncTask task = new CustomerClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
}

View File

@@ -0,0 +1,28 @@
package com.ecep.contract.controller.company;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyViewModel;
import com.ecep.contract.vo.CompanyVo;
import javafx.concurrent.Task;
public abstract class AbstCompanyBasedTabSkin
extends AbstEntityBasedTabSkin<CompanyWindowController, CompanyVo, CompanyViewModel>
implements TabSkin {
public AbstCompanyBasedTabSkin(CompanyWindowController controller) {
super(controller);
}
protected CompanyService getCompanyService() {
Task<CompanyService> task = new Task<>() {
@Override
protected CompanyService call() throws Exception {
return controller.getViewModelService();
}
};
return controller.getViewModelService();
}
}

View File

@@ -0,0 +1,45 @@
package com.ecep.contract.controller.company;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.TableOfTabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.CompanyBasedViewModel;
import com.ecep.contract.vm.CompanyViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.vo.CompanyVo;
public abstract class AbstCompanyTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<CompanyWindowController, CompanyVo, CompanyViewModel, T, TV>
implements TabSkin, TableOfTabSkin<CompanyVo, T, TV> {
public AbstCompanyTableTabSkin(CompanyWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
@Override
protected TV createNewViewModel() {
TV model = super.createNewViewModel();
if (model instanceof CompanyBasedViewModel m) {
m.getCompanyId().set(getEntity().getId());
}
return model;
}
protected CompanyService getCompanyService() {
return getParentService();
}
protected CompanyService getParentService() {
return controller.getViewModelService();
}
@Override
public ParamUtils.Builder getSpecification(CompanyVo parent) {
return ParamUtils.builder().equals("company", parent.getId());
}
}

View File

@@ -1,11 +1,11 @@
package com.ecep.contract.manager.ds.company.controller.contact; package com.ecep.contract.controller.company;
import com.ecep.contract.manager.ds.company.model.CompanyContact; import com.ecep.contract.controller.BaseController;
import com.ecep.contract.manager.ds.company.repository.CompanyContactRepository; import com.ecep.contract.service.CompanyContactService;
import com.ecep.contract.manager.ui.BaseController; import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.manager.ds.company.vo.CompanyContactViewModel; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.util.FxmlUtils; import com.ecep.contract.vm.CompanyContactViewModel;
import com.ecep.contract.manager.util.UITools; import com.ecep.contract.vo.CompanyContactVo;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
@@ -25,13 +25,14 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
public class CompanyContactWindowController extends BaseController { public class CompanyContactWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(CompanyContactWindowController.class); private static final Logger logger = LoggerFactory.getLogger(CompanyContactWindowController.class);
/** /**
* 显示界面 * 显示界面
*/ */
@@ -59,8 +60,9 @@ public class CompanyContactWindowController extends BaseController {
@Getter @Getter
@Setter @Setter
private CompanyContactViewModel viewModel; private CompanyContactViewModel viewModel;
@Autowired @Autowired
private CompanyContactRepository companyContactRepository; private CompanyContactService companyContactService;
public TextField nameField; public TextField nameField;
public TextField positionField; public TextField positionField;
@@ -73,12 +75,13 @@ 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) {
super.show(stage); super.show(stage);
getTitle().bind(viewModel.getName().map(v -> "[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情")); getTitle().bind(viewModel.getName()
.map(v -> "[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情"));
} }
@Override @Override
@@ -91,9 +94,7 @@ public class CompanyContactWindowController extends BaseController {
initializeBaseTab(); initializeBaseTab();
companyContactLoadedFuture = CompletableFuture.supplyAsync(() -> { companyContactLoadedFuture = CompletableFuture.supplyAsync(() -> {
Optional<CompanyContact> optional = companyContactRepository.findById(viewModel.getId().get()); CompanyContactVo oldName = companyContactService.findById(viewModel.getId().get());
if (optional.isPresent()) {
CompanyContact oldName = optional.get();
Platform.runLater(() -> { Platform.runLater(() -> {
viewModel.update(oldName); viewModel.update(oldName);
viewModel.bindListener(); viewModel.bindListener();
@@ -103,12 +104,9 @@ public class CompanyContactWindowController extends BaseController {
tabPane.getSelectionModel().getSelectedItem().getOnSelectionChanged().handle(null); tabPane.getSelectionModel().getSelectedItem().getOnSelectionChanged().handle(null);
}); });
return oldName; return oldName;
}
return null;
}); });
} }
private void initializeBaseTab() { private void initializeBaseTab() {
baseInfoTab.setOnSelectionChanged(event -> { baseInfoTab.setOnSelectionChanged(event -> {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@@ -121,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 = companyContactRepository.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) {
@@ -176,4 +174,3 @@ public class CompanyContactWindowController extends BaseController {
super.onHidden(windowEvent); super.onHidden(windowEvent);
} }
} }

View File

@@ -1,22 +1,22 @@
package com.ecep.contract.manager.ds.company.controller; package com.ecep.contract.controller.company;
import java.util.List;
import java.util.Optional;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyViewModel;
import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyOldNameService;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.company.vo.CompanyViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.TextInputDialog; import javafx.scene.control.TextInputDialog;
import lombok.Setter; import lombok.Setter;
import java.util.List;
import java.util.Optional;
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);

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager.ds.company.controller; package com.ecep.contract.controller.company;
import java.time.LocalDate; import java.time.LocalDate;
@@ -7,13 +7,13 @@ 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.manager.ds.company.model.Company; import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.manager.ds.company.service.CompanyService; import com.ecep.contract.service.CompanyService;
import com.ecep.contract.manager.ds.company.tasker.CompanyFilesRebuildTasker; import com.ecep.contract.task.CompanyFilesRebuildTasker;
import com.ecep.contract.manager.ds.company.vo.CompanyViewModel; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ui.AbstManagerWindowController; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.manager.util.UITools; import com.ecep.contract.vm.CompanyViewModel;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@@ -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

View File

@@ -1,11 +1,12 @@
package com.ecep.contract.manager.ds.company.controller; package com.ecep.contract.controller.company;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.FxmlPath;
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.BaseController;
import com.ecep.contract.util.FxmlPath;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component

View File

@@ -1,32 +1,8 @@
package com.ecep.contract.manager.ds.company.controller; package com.ecep.contract.controller.company;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyFileService;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.company.tasker.CompanyCompositeUpdateTasker;
import com.ecep.contract.manager.ds.company.tasker.CompanyVerifyTasker;
import com.ecep.contract.manager.ds.company.vo.CompanyViewModel;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.customer.controller.CompanyCustomerWindowController;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.vendor.controller.CompanyVendorWindowController;
import com.ecep.contract.manager.ds.vendor.model.CompanyVendor;
import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ui.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.DesktopUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.Window;
import org.hibernate.Hibernate;
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;
@@ -35,25 +11,59 @@ import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.concurrent.CompletableFuture; import com.ecep.contract.DesktopUtils;
import java.util.function.Function; import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.controller.customer.CompanyCustomerWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.CompanyTabSkinBankAccount;
import com.ecep.contract.controller.tab.CompanyTabSkinBase;
import com.ecep.contract.controller.tab.CompanyTabSkinBlackReason;
import com.ecep.contract.controller.tab.CompanyTabSkinContact;
import com.ecep.contract.controller.tab.CompanyTabSkinContract;
import com.ecep.contract.controller.tab.CompanyTabSkinFile;
import com.ecep.contract.controller.tab.CompanyTabSkinInvoice;
import com.ecep.contract.controller.tab.CompanyTabSkinOldName;
import com.ecep.contract.controller.tab.CompanyTabSkinOther;
import com.ecep.contract.controller.tab.CompanyTabSkinPurchaseBillVoucher;
import com.ecep.contract.controller.vendor.VendorWindowController;
import com.ecep.contract.service.CustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.VendorService;
import com.ecep.contract.task.CompanyCompositeUpdateTasker;
import com.ecep.contract.task.CompanyVerifyTasker;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
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 javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
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.Pane;
import javafx.stage.Stage;
import javafx.stage.Window;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
@FxmlPath("/ui/company/company.fxml") @FxmlPath("/ui/company/company.fxml")
public class CompanyWindowController public class CompanyWindowController
extends AbstEntityController<Company, CompanyViewModel> { extends AbstEntityController<CompanyVo, CompanyViewModel> {
private static final Logger logger = LoggerFactory.getLogger(CompanyWindowController.class); private static final Logger logger = LoggerFactory.getLogger(CompanyWindowController.class);
public static void show(CompanyVo company, Window window) {
public static void show(Company company, Window window) { show(CompanyViewModel.from(company), window);
CompanyViewModel viewModel = new CompanyViewModel();
if (!Hibernate.isInitialized(company)) {
company = SpringApp.getBean(CompanyService.class).findById(company.getId());
}
viewModel.update(company);
show(viewModel, window);
} }
/** /**
@@ -65,14 +75,6 @@ public class CompanyWindowController
@Autowired @Autowired
private CompanyService companyService; private CompanyService companyService;
@Autowired
private CompanyFileService companyFileService;
@Autowired
private ContractService contractService;
@Autowired
private CompanyCustomerService companyCustomerService;
@Autowired
private CompanyVendorService companyVendorService;
public BorderPane root; public BorderPane root;
public TabPane tabPane; public TabPane tabPane;
@@ -117,12 +119,13 @@ public class CompanyWindowController
public Button companyPathChangeBtn; public Button companyPathChangeBtn;
public Button companyPathSameAsNameBtn; public Button companyPathSameAsNameBtn;
// private final CompanyCustomerViewModel companyCustomerViewModel = new
// CompanyCustomerViewModel();
// private final CompanyVendorViewModel companyVendorViewModel = new
// CompanyVendorViewModel();
// private final CompanyCustomerViewModel companyCustomerViewModel = new CompanyCustomerViewModel(); private final SimpleObjectProperty<CustomerVo> companyCustomerProperty = new SimpleObjectProperty<>();
// private final CompanyVendorViewModel companyVendorViewModel = new CompanyVendorViewModel(); private final SimpleObjectProperty<VendorVo> companyVendorProperty = new SimpleObjectProperty<>();
private final SimpleObjectProperty<CompanyCustomer> companyCustomerProperty = new SimpleObjectProperty<>();
private final SimpleObjectProperty<CompanyVendor> companyVendorProperty = new SimpleObjectProperty<>();
public Pane customerTab_pane1; public Pane customerTab_pane1;
public Button customerTab_openBtn; public Button customerTab_openBtn;
@@ -134,7 +137,6 @@ public class CompanyWindowController
public Pane vendorTab_pane2; public Pane vendorTab_pane2;
public Button vendorTab_createBtn; public Button vendorTab_createBtn;
@Override @Override
public void show(Stage stage) { public void show(Stage stage) {
super.show(stage); super.show(stage);
@@ -143,17 +145,6 @@ public class CompanyWindowController
getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 公司详情"); getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 公司详情");
} }
@Override
protected Company loadEntity() {
return companyService.findById(viewModel.getId().get());
}
@Override
protected Company saveEntity(Company entity) {
return companyService.save(entity);
}
@Override @Override
protected void registerTabSkins() { protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab1 -> new CompanyTabSkinBase(this)); registerTabSkin(baseInfoTab, tab1 -> new CompanyTabSkinBase(this));
@@ -161,18 +152,16 @@ public class CompanyWindowController
registerTabSkin(contactTab, tab1 -> new CompanyTabSkinContact(this)); registerTabSkin(contactTab, tab1 -> new CompanyTabSkinContact(this));
registerTabSkin(blackReasonTab, tab1 -> new CompanyTabSkinBlackReason(this)); registerTabSkin(blackReasonTab, tab1 -> new CompanyTabSkinBlackReason(this));
registerTabSkin(bankAccountTab, tab1 -> new CompanyTabSkinBankAccount(this)); registerTabSkin(bankAccountTab, tab1 -> new CompanyTabSkinBankAccount(this));
registerTabSkin(contractTab, this::createContractTabSkin); registerTabSkin(contractTab, tab -> new CompanyTabSkinContract(this));
registerTabSkin(fileTab, this::createFileTabSkin); registerTabSkin(fileTab, tab -> new CompanyTabSkinFile(this));
registerTabSkin(invoiceTab, tab -> new CompanyTabSkinInvoice(this)); registerTabSkin(invoiceTab, tab -> new CompanyTabSkinInvoice(this));
registerTabSkin(purchaseBillVoucherTab, tab -> new CompanyTabSkinPurchaseBillVoucher(this)); registerTabSkin(purchaseBillVoucherTab, tab -> new CompanyTabSkinPurchaseBillVoucher(this));
registerTabSkin(otherTab, tab -> new CompanyTabSkinOther(this)); registerTabSkin(otherTab, tab -> new CompanyTabSkinOther(this));
initializeVendorTab(); initializeVendorTab();
initializeCustomerTab(); initializeCustomerTab();
} }
@Override @Override
protected <K extends AbstEntityBasedTabSkin<?, ?, ?>> K registerTabSkin(Tab tab, Function<Tab, K> func) { protected <K extends AbstEntityBasedTabSkin<?, ?, ?>> K registerTabSkin(Tab tab, Function<Tab, K> func) {
K skin = super.registerTabSkin(tab, func); K skin = super.registerTabSkin(tab, func);
@@ -187,19 +176,6 @@ public class CompanyWindowController
return companyService; return companyService;
} }
private CompanyTabSkinContract createContractTabSkin(Tab tab) {
CompanyTabSkinContract skin = new CompanyTabSkinContract(this);
skin.setContractService(contractService);
return skin;
}
private CompanyTabSkinFile createFileTabSkin(Tab tab) {
CompanyTabSkinFile skin = new CompanyTabSkinFile(this);
skin.setCompanyFileService(companyFileService);
return skin;
}
private void initializeCustomerTab() { private void initializeCustomerTab() {
customerTab.setOnSelectionChanged(event -> { customerTab.setOnSelectionChanged(event -> {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@@ -210,7 +186,6 @@ public class CompanyWindowController
} }
}); });
customerTab_pane1.visibleProperty().bind(customerTab_pane2.visibleProperty().not()); customerTab_pane1.visibleProperty().bind(customerTab_pane2.visibleProperty().not());
customerTab_pane2.visibleProperty().bind(companyCustomerProperty.isNull()); customerTab_pane2.visibleProperty().bind(companyCustomerProperty.isNull());
customerTab_createBtn.setOnAction(event -> { customerTab_createBtn.setOnAction(event -> {
@@ -227,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;
@@ -251,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());
}); });
} }
@@ -263,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;
@@ -277,29 +254,29 @@ 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();
} }
/** /**
* 企业合规检查 * 企业合规检查
*/ */
public void onCompanyVerifyAction(ActionEvent event) { public void onCompanyVerifyAction(ActionEvent event) {
Company 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) {
Company company = getEntity(); CompanyVo company = getEntity();
String path = company.getPath(); String path = company.getPath();
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
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 {

View File

@@ -0,0 +1,59 @@
package com.ecep.contract.controller.company.bank_account;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.BankService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyBankAccountViewModel;
import com.ecep.contract.vo.CompanyBankAccountVo;
import com.ecep.contract.vo.CompanyVo;
import javafx.scene.control.Tab;
public class BankAccountBaseTabSkin
extends AbstEntityBasedTabSkin<BankAccountWindowController, CompanyBankAccountVo, CompanyBankAccountViewModel>
implements TabSkin {
public BankAccountBaseTabSkin(BankAccountWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
UITools.autoCompletion(controller.companyField, viewModel.getCompanyId(), getCompanyService());
UITools.autoCompletion(controller.bankField, viewModel.getBankId(), getBankService());
controller.openingBankField.textProperty().bindBidirectional(viewModel.getOpeningBank());
controller.bankAccountField.textProperty().bindBidirectional(viewModel.getAccount());
// controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.createdField.textProperty().bind(viewModel.getCreated().asString());
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompanyId().isNull());
controller.relativeCompanyBtn.setOnAction(event -> {
Integer companyId = viewModel.getCompanyId().get();
if (companyId != null) {
CompanyVo company = getCompanyService().findById(companyId);
CompanyWindowController.show(company, controller.root.getScene().getWindow());
}
});
}
public BankService getBankService() {
return getCachedBean(BankService.class);
}
public CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
}
}

View File

@@ -1,24 +1,31 @@
package com.ecep.contract.manager.ds.company.controller.bank_account; package com.ecep.contract.controller.company.bank_account;
import com.ecep.contract.manager.ds.company.model.CompanyBankAccount;
import com.ecep.contract.manager.ds.company.service.CompanyBankAccountService;
import com.ecep.contract.manager.ds.company.vo.CompanyBankAccountViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
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.AbstEntityController;
import com.ecep.contract.service.CompanyBankAccountService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.CompanyBankAccountViewModel;
import com.ecep.contract.vo.CompanyBankAccountVo;
import javafx.scene.control.Button;
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 @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
@FxmlPath("/ui/company/bank-account.fxml") @FxmlPath("/ui/company/bank-account.fxml")
public class BankAccountWindowController extends AbstEntityController<CompanyBankAccount, CompanyBankAccountViewModel> { public class BankAccountWindowController extends AbstEntityController<CompanyBankAccountVo, CompanyBankAccountViewModel> {
public BorderPane root; public BorderPane root;
public TabPane tabPane; public TabPane tabPane;

View File

@@ -1,20 +1,21 @@
package com.ecep.contract.manager.ds.company.controller.old_name; package com.ecep.contract.controller.company.old_name;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vo.CompanyOldNameVo;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import com.ecep.contract.manager.ds.company.model.CompanyOldName;
import com.ecep.contract.manager.ds.company.service.CompanyOldNameService;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.company.vo.CompanyOldNameViewModel;
import com.ecep.contract.manager.ui.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.manager.ui.tab.TabSkin;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.util.converter.LocalDateStringConverter; import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter; import lombok.Setter;
import java.time.format.DateTimeFormatter;
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;

View File

@@ -0,0 +1,366 @@
package com.ecep.contract.controller.company.old_name;
import java.io.File;
import java.util.List;
import java.util.function.Consumer;
import org.springframework.util.StringUtils;
import com.ecep.contract.CompanyFileType;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.service.CompanyFileService;
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.vm.CompanyFileViewModel;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import com.ecep.contract.vo.CompanyFileVo;
import com.ecep.contract.vo.CompanyOldNameVo;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableView;
import lombok.Setter;
/**
*
*/
public class CompanyOldNameTabSkinFile
extends
AbstEntityTableTabSkin<CompanyOldNameWindowController, CompanyOldNameVo, CompanyOldNameViewModel, CompanyFileVo, CompanyFileViewModel>
implements TabSkin {
@Setter
private CompanyOldNameService companyOldNameService;
@Setter
private CompanyFileService companyFileService;
public CompanyOldNameTabSkinFile(CompanyOldNameWindowController controller) {
super(controller);
setDragAndDrop(true);
setDragAndDropFileHandler(this::moveFileToCompany);
}
@Override
public Tab getTab() {
return controller.fileTab;
}
@Override
public TableView<CompanyFileViewModel> getTableView() {
return controller.fileTable;
}
@Override
protected CompanyFileService getViewModelService() {
return companyFileService;
}
@Override
public Builder getSpecification(CompanyOldNameVo parent) {
return ParamUtils.builder().equals("company", parent.getCompanyId());
}
@Override
public void initializeTab() {
// controller.fileTable_file_move_btn.setOnAction(this::onTableMoveFileAction);
// controller.fileTable_file_retrieve_from_download_dir_btn.setOnAction(this::onTableRetrieveFromDownloadDirAction);
// controller.fileTable_file_reset_btn.setOnAction(this::onTableResetAction);
//
//
// controller.fileTable_idColumn.setCellValueFactory(param ->
// param.getValue().getId());
// ObservableMap<CompanyFileType, CompanyFileTypeLocal> observableMapByLocal =
// getBean(CompanyFileTypeLocalRepository.class).getObservableMapByLocal();
// controller.fileTable_typeColumn.setCellValueFactory(param ->
// Bindings.valueAt(observableMapByLocal,
// param.getValue().getType()).map(CompanyFileTypeLocal::getValue));
// controller.fileTable_filePathColumn.setCellValueFactory(param ->
// param.getValue().getFilePath());
// controller.fileTable_filePathColumn.setCellFactory(param -> new
// FileTableFilePathTableCell());
// controller.fileTable_applyDateColumn.setCellValueFactory(param ->
// param.getValue().getApplyDate());
// controller.fileTable_expiringDateColumn.setCellValueFactory(param ->
// param.getValue().getExpiringDate());
//
//
// controller.fileTable_menu_refresh.setOnAction(this::onTableRefreshAction);
// controller.fileTable_menu_del.setOnAction(this::onTableDeleteAction);
// controller.fileTable_menu_copy_as_matched_by_contract.setOnAction(this::onTableCopyAsMatchedByContractAction);
// controller.fileTable_menu_copy_as_matched_by_contract.setOnMenuValidation(this::onTableCopyAsMatchedMenuValidation);
super.initializeTab();
}
private void onTableResetAction(ActionEvent event) {
CompanyOldNameVo oldName = getParent();
// CompletableFuture.runAsync(() -> {
// if (getCompanyFileService().reBuildingFiles(oldName, this::setStatus)) {
// loadTableDataSet();
// }
// });
}
/**
* 从 下载目录 中查找相关的资质文件
*/
private void onTableRetrieveFromDownloadDirAction(ActionEvent event) {
// CompanyOldName oldName = getParent();
// MyProperties myProperties = getMyProperties();
// File dir = myProperties.getDownloadDirectory();
// if (!dir.exists()) {
// setStatus("下载目录 " + dir.getAbsolutePath() + " 不存在,请检查");
// return;
// }
//
// setStatus("开始检索 下载 文件夹:" + dir.getAbsolutePath() + "...");
// File[] files = dir.listFiles(File::isFile);
// if (files == null) {
// setStatus("检索 下载 文件夹失败");
// return;
// }
// if (files.length == 0) {
// setStatus("下载 文件夹没有文件");
// return;
// }
// setStatus("下载 文件夹中共有文件 " + files.length + " 个文件");
//
// if (getCompanyOldNameService().retrieveFromDownloadFiles(oldName, files,
// this::setStatus)) {
// // fixed if update
// viewModel.update(oldName);
// loadTableDataSet();
// }
}
/**
* 把文件从 老系统中移到 \\10.84.209.8\项目信息\相关方信息 目录中
*/
private void onTableMoveFileAction(ActionEvent event) {
// CompanyFileService companyFileService = getCompanyFileService();
// CompanyOldName oldName = getParent();
// List<CompanyFile> list = companyFileService.findByCompany(oldName);
// if (list.isEmpty()) {
// return;
// }
// if (getCompanyService().makePathAbsent(oldName)) {
// save(oldName);
// }
//
// String path = oldName.getPath();
// if (!StringUtils.hasText(path)) {
// setStatus("异常, 企业目录未设置");
// return;
// }
// File companyPath = new File(path);
// for (CompanyFile companyFile : list) {
// String filePath = companyFile.getFilePath();
// if (StringUtils.hasText(filePath)) {
// File file = new File(filePath);
// if (file.exists()) {
// if (file.getParentFile().equals(companyPath)) {
// continue;
// }
// File dest = new File(companyPath, file.getName());
// if (file.renameTo(dest)) {
// companyFile.setFilePath(dest.getAbsolutePath());
// companyFileService.save(companyFile);
// setStatus(file.getName() + " 移动到 " + companyPath.getName());
// }
// }
// }
// }
}
/**
*
*/
private void onTableCopyAsMatchedByContractAction(ActionEvent event) {
UITools.showDialogAndWait("复制资信评估报告", "按当前评估报告复制一个合同中最匹配的", list -> {
onTableCopyAsMatchedAction_(msg -> {
Platform.runLater(() -> {
list.add(msg);
});
});
});
}
private void onTableCopyAsMatchedAction_(Consumer<String> state) {
// CompanyOldName oldName = getParent();
//
// CompanyFileViewModel selectedItem =
// table.getSelectionModel().getSelectedItem();
// if (selectedItem == null) {
// state.accept("未选择行");
// return;
// }
// if (selectedItem.getApplyDate().get() == null) {
// state.accept("有效日期不能未空");
// return;
// }
//
// LocalDate nextCreditReportDate = null;
// try {
// nextCreditReportDate = companyFileService.getNextCreditReportDate(oldName,
// state);
// if (nextCreditReportDate == null) {
// state.accept("没有找到下一个咨询评估日期");
// return;
// }
// } catch (Exception e) {
// state.accept("获取下一个咨询评估日期失败:" + e.getMessage());
// return;
// }
//
// state.accept("下一个咨询评估日期:" + nextCreditReportDate);
//
// if (!nextCreditReportDate.isBefore(selectedItem.getApplyDate().get())) {
// state.accept("咨询评估日期晚于下一个咨询评估日期");
// return;
// }
//
//
// File src = new File(selectedItem.getFilePath().get());
// if (!src.exists()) {
// state.accept("当前选择行的文件不存在");
// return;
// }
//
// String srcDate = MyDateTimeUtils.format(selectedItem.getApplyDate().get());
// String destDate = MyDateTimeUtils.format(nextCreditReportDate);
// String srcFileName = src.getName();
// String destFileName;
//
// // 天眼查的报告
// if (CloudTycService.isTycReport(srcFileName)) {
// state.accept("天眼查的报告按标准格式命名");
// String name = oldName.getName() + "_" + CloudTycService.NAME;
// if (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_BASIC_REPORT)) {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_BASIC_REPORT;
// } else if (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_MAJOR_REPORT))
// {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_MAJOR_REPORT;
// } else if
// (srcFileName.contains(CloudTycService.TYC_ENTERPRISE_ANALYSIS_REPORT)) {
// name = name + "_" + CloudTycService.TYC_ENTERPRISE_ANALYSIS_REPORT;
// }
// destFileName = name + "_" + destDate + "_cp." +
// StringUtils.getFilenameExtension(srcFileName);
// } else {
// if (srcFileName.contains(srcDate)) {
// // 如果文件名中包含日期,则替换为新日期
// destFileName = srcFileName.replace(srcDate, destDate + "_cp");
// } else {
// // 如果文件名中不包含日期,则添加日期
// destFileName = oldName.getName() + "_" + destDate + "_cp." +
// StringUtils.getFilenameExtension(srcFileName);
// }
// }
//
// state.accept("新文件名:" + destFileName);
//
// File dest = new File(src.getParent(), destFileName);
// try {
// FileSystemUtils.copyRecursively(src, dest);
// state.accept("新文件复制成功");
// } catch (IOException e) {
// state.accept("新文件复制失败:" + e.getMessage());
// }
//
// CompanyFile companyFile = new CompanyFile();
// companyFile.setFilePath(dest.getAbsolutePath());
// companyFile.setApplyDate(nextCreditReportDate);
// companyFile.setExpiringDate(nextCreditReportDate.plusYears(1));
// companyFile.setType(CompanyFileType.CreditReport);
// companyFile.setCompany(oldName);
// companyFileService.save(companyFile);
//
// state.accept("新文件已记录");
//
// loadTableDataSet();
// state.accept("文件表已刷新");
}
/**
* 当fileTable选中的行是咨询评估时可用
*
* @param event event
*/
public void onTableCopyAsMatchedMenuValidation(Event event) {
// 当fileTable选中的行是咨询评估时可用
CompanyFileViewModel selectedItem = getSelectedItem();
if (selectedItem == null) {
event.consume();
return;
}
CompanyFileType type = selectedItem.getType().get();
if (type != CompanyFileType.CreditReport) {
event.consume();
return;
}
}
private void moveFileToCompany(List<File> files) {
String path = viewModel.getPath().get();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File dir = new File(path);
if (!dir.exists()) {
setStatus("目录错误,不存在");
return;
}
}
@Override
protected void onTableRowDoubleClickedAction(CompanyFileViewModel item) {
String path = item.getFilePath().get();
if (StringUtils.hasText(path)) {
File file = new File(path);
if (!file.exists()) {
setStatus("文件不存在 " + file.getName());
return;
}
DesktopUtils.showInExplorer(file);
}
}
class FileTableFilePathTableCell extends TableCell<CompanyFileViewModel, String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
String path = viewModel.getPath().get();
if (StringUtils.hasText(path)) {
if (item.startsWith(path)) {
item = "~" + item.substring(path.length());
}
}
setText(item);
}
}
private CompanyFileService getCompanyFileService() {
if (companyFileService == null) {
companyFileService = getBean(CompanyFileService.class);
}
return companyFileService;
}
public CompanyOldNameService getCompanyOldNameService() {
if (companyOldNameService == null) {
companyOldNameService = getBean(CompanyOldNameService.class);
}
return companyOldNameService;
}
}

View File

@@ -1,36 +1,44 @@
package com.ecep.contract.manager.ds.company.controller.old_name; package com.ecep.contract.controller.company.old_name;
import java.io.File;
import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.ds.company.model.CompanyOldName;
import com.ecep.contract.manager.ds.company.service.CompanyOldNameService;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.company.vo.CompanyFileViewModel;
import com.ecep.contract.manager.ds.company.vo.CompanyOldNameViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.util.DesktopUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
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.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.StringUtils; import org.springframework.util.StringUtils;
import java.io.File; import com.ecep.contract.DesktopUtils;
import java.util.concurrent.CompletableFuture; import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vo.CompanyOldNameVo;
import com.ecep.contract.vm.CompanyFileViewModel;
import com.ecep.contract.vm.CompanyOldNameViewModel;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy @Lazy
@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);
/** /**
@@ -45,12 +53,6 @@ public class CompanyOldNameWindowController extends AbstEntityController<Company
public TabPane tabPane; public TabPane tabPane;
public Tab fileTab; public Tab fileTab;
@Autowired
private CompanyOldNameService companyOldNameService;
@Autowired
private CompanyService companyService;
public TextField nameField; public TextField nameField;
public CheckBox ambiguityField; public CheckBox ambiguityField;
public DatePicker startDateField; public DatePicker startDateField;
@@ -62,26 +64,14 @@ public class CompanyOldNameWindowController extends AbstEntityController<Company
public Button saveBtn; public Button saveBtn;
public Button saveBtn2; public Button saveBtn2;
public TableView<CompanyFileViewModel> fileTable; public TableView<CompanyFileViewModel> fileTable;
@Override @Override
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent); super.onShown(windowEvent);
getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情"); getTitle().set("[" + viewModel.getId().get() + "] " + viewModel.getName().getValue() + " 曾用名详情");
} }
@Override
protected CompanyOldName loadEntity() {
return companyOldNameService.findById(viewModel.getId().get());
}
@Override
protected CompanyOldName saveEntity(CompanyOldName entity) {
return companyOldNameService.save(entity);
}
@Override @Override
protected void registerTabSkins() { protected void registerTabSkins() {
registerTabSkin(baseInfoTab, this::createBaseTabSkin); registerTabSkin(baseInfoTab, this::createBaseTabSkin);
@@ -90,32 +80,31 @@ public class CompanyOldNameWindowController extends AbstEntityController<Company
@Override @Override
public CompanyOldNameService getViewModelService() { public CompanyOldNameService getViewModelService() {
return companyOldNameService; return getCachedBean(CompanyOldNameService.class);
} }
private CompanyOldNameTabSkinBase createBaseTabSkin(Tab tab) { private CompanyOldNameTabSkinBase createBaseTabSkin(Tab tab) {
CompanyOldNameTabSkinBase skin = new CompanyOldNameTabSkinBase(this); CompanyOldNameTabSkinBase skin = new CompanyOldNameTabSkinBase(this);
skin.setCompanyOldNameService(companyOldNameService); skin.setCompanyOldNameService(getViewModelService());
return skin; return skin;
} }
private CompanyOldNameTabSkinFile createFileTabSkin(Tab tab) { private CompanyOldNameTabSkinFile createFileTabSkin(Tab tab) {
CompanyOldNameTabSkinFile skin = new CompanyOldNameTabSkinFile(this); CompanyOldNameTabSkinFile skin = new CompanyOldNameTabSkinFile(this);
skin.setCompanyOldNameService(companyOldNameService); skin.setCompanyOldNameService(getViewModelService());
// skin.setCompanyFileService(companyFileService); // skin.setCompanyFileService(companyFileService);
return skin; return skin;
} }
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(() -> {
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 (companyOldNameService.makePathAbsent(companyOldName)) { if (getViewModelService().makePathAbsent(companyOldName)) {
save(companyOldName); save(companyOldName);
} }
} else { } else {
@@ -128,4 +117,3 @@ public class CompanyOldNameWindowController extends AbstEntityController<Company
}); });
} }
} }

View File

@@ -0,0 +1,22 @@
package com.ecep.contract.controller.contract;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.vo.ContractVo;
public abstract class AbstContractBasedTabSkin
extends AbstEntityBasedTabSkin<ContractWindowController, ContractVo, ContractViewModel>
implements TabSkin {
public AbstContractBasedTabSkin(ContractWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
public ContractService getContractService() {
return controller.getViewModelService();
}
}

View File

@@ -0,0 +1,32 @@
package com.ecep.contract.controller.contract;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.TableOfTabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.vo.ContractVo;
public abstract class AbstContractTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<ContractWindowController, ContractVo, ContractViewModel, T, TV>
implements TabSkin, TableOfTabSkin<ContractVo, T, TV> {
public AbstContractTableTabSkin(ContractWindowController controller) {
super(controller);
}
public ContractService getContractService() {
return controller.getViewModelService();
}
@Override
public ParamUtils.Builder getSpecification(ContractVo parent) {
ParamUtils.Builder params = getSpecification();
params.equals("contract", parent.getId());
return params;
}
}

View File

@@ -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'");
}
}

View File

@@ -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("合同发票管理");
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}
}

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager.ds.contract.controller; package com.ecep.contract.controller.contract;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -8,19 +8,16 @@ 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.manager.ds.company.model.Company; import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.manager.ds.contract.model.Contract; import com.ecep.contract.controller.tab.ContractManagerSkin;
import com.ecep.contract.manager.ds.contract.model.ContractGroup; import com.ecep.contract.service.ContractService;
import com.ecep.contract.manager.ds.contract.model.ContractKind; import com.ecep.contract.task.ContractFilesRebuildAllTasker;
import com.ecep.contract.manager.ds.contract.model.ContractType; import com.ecep.contract.task.ContractRepairAllTasker;
import com.ecep.contract.manager.ds.contract.service.ContractService; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ds.contract.tasker.ContractFilesRebuildAllTasker; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.ds.contract.tasker.ContractRepairAllTasker; import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.manager.ds.contract.vo.ContractViewModel; import com.ecep.contract.vo.ContractGroupVo;
import com.ecep.contract.manager.ds.other.model.Employee; import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.UITools;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
@@ -33,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;

View File

@@ -1,20 +1,16 @@
package com.ecep.contract.manager.ds.contract.controller; package com.ecep.contract.controller.contract;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.hibernate.Hibernate; import com.ecep.contract.util.ComboBoxUtils;
import com.ecep.contract.service.ExtendVendorInfoService;
import com.ecep.contract.manager.SpringApp; import com.ecep.contract.service.VendorGroupService;
import com.ecep.contract.manager.ds.contract.model.Contract; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ds.contract.model.ExtendVendorInfo; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.ds.contract.service.ExtendVendorInfoService; import com.ecep.contract.vm.ExtendVendorInfoViewModel;
import com.ecep.contract.manager.ds.contract.vo.ExtendVendorInfoViewModel; import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.manager.ds.vendor.model.VendorGroup; import com.ecep.contract.vo.ExtendVendorInfoVo;
import com.ecep.contract.manager.ds.vendor.service.VendorGroupService; import com.ecep.contract.vo.VendorGroupVo;
import com.ecep.contract.manager.ui.ComboBoxUtils;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@@ -26,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
@@ -48,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) {
@@ -76,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 {
@@ -90,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();
@@ -109,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 (!Hibernate.isInitialized(v)) {
v = getVendorGroupService().findById(v.getId());
viewModel.getGroup().set(v);
}
return v.getDescription(); return v.getDescription();
})); }));
@@ -127,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;
} }
@@ -140,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);
} }
@@ -150,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;
} }
} }

View File

@@ -1,37 +1,38 @@
package com.ecep.contract.manager.ds.contract.controller; package com.ecep.contract.controller.contract;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.controller.purchase_order.PurchaseOrderWindowController;
import com.ecep.contract.manager.ds.contract.model.PurchaseOrder;
import com.ecep.contract.manager.ds.contract.service.PurchaseOrdersService;
import com.ecep.contract.manager.ds.contract.vo.PurchaseOrderViewModel;
import com.ecep.contract.manager.ds.other.EmployeeStringConverter;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell;
import javafx.scene.control.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
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.controller.vendor.purchase.order.PurchaseOrderWindowController;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.PurchaseOrdersService;
import com.ecep.contract.vm.PurchaseOrderViewModel;
import com.ecep.contract.vo.PurchaseOrderVo;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
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;
@@ -68,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<>());
@@ -89,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();
} }
} }

View File

@@ -0,0 +1,146 @@
package com.ecep.contract.controller.contract;
import java.time.LocalDate;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.SaleOrdersService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.SalesOrderViewModel;
import com.ecep.contract.vo.SalesOrderVo;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
/**
* 子合同
*/
@FxmlPath("/ui/contract/contract-tab-sale-orders.fxml")
public class ContractTabSkinSaleOrders
extends AbstContractTableTabSkin<SalesOrderVo, SalesOrderViewModel>
implements TabSkin {
@Setter
private SaleOrdersService saleOrdersService;
private CompanyService companyService;
@Setter
private EmployeeStringConverter employeeStringConverter;
public TableColumn<SalesOrderViewModel, Number> idColumn;
public TableColumn<SalesOrderViewModel, String> codeColumn;
/**
* 业务员, Employee
*/
public TableColumn<SalesOrderViewModel, Integer> employeeColumn;
/**
* 创建人, Employee
*/
public TableColumn<SalesOrderViewModel, Integer> makerColumn;
public TableColumn<SalesOrderViewModel, LocalDate> makerDateColumn;
/**
* 审核人, Employee
*/
public TableColumn<SalesOrderViewModel, Integer> verifierColumn;
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 MenuItem subContractTable_menu_refresh;
private Tab tab;
public ContractTabSkinSaleOrders(ContractWindowController controller, Tab tab) {
super(controller);
this.tab = tab;
}
@Override
public void initializeUIComponents() {
super.initializeUIComponents();
// 合同是收时,有关联子合同
getTab().disableProperty().bind(viewModel.getPayWay().isEqualTo(ContractPayWay.RECEIVE).not());
}
@Override
public Tab getTab() {
return tab;
}
@Override
protected SaleOrdersService getViewModelService() {
return getSaleOrdersService();
}
@Override
public void initializeTab() {
super.initializeTab();
subContractTable_menu_refresh.setOnAction(this::onTableRefreshAction);
idColumn.setCellValueFactory(param -> param.getValue().getId());
codeColumn.setCellValueFactory(param -> param.getValue().getCode());
employeeColumn.setCellValueFactory(param -> param.getValue().getEmployee());
employeeColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
makerColumn.setCellValueFactory(param -> param.getValue().getMaker());
makerColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
makerDateColumn.setCellValueFactory(param -> param.getValue().getMakerDate());
makerDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
verifierColumn.setCellValueFactory(param -> param.getValue().getVerifier());
verifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
verifierDateColumn.setCellValueFactory(param -> param.getValue().getVerifierDate());
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());
}
@Override
protected void onTableRowDoubleClickedAction(SalesOrderViewModel item) {
SalesOrderWindowController.show(item, getTableView().getScene().getWindow());
}
SaleOrdersService getSaleOrdersService() {
if (saleOrdersService == null) {
saleOrdersService = getBean(SaleOrdersService.class);
}
return saleOrdersService;
}
CompanyService getCompanyService() {
if (companyService == null) {
companyService = getBean(CompanyService.class);
}
return companyService;
}
}

View File

@@ -1,37 +1,17 @@
package com.ecep.contract.manager.ds.contract.controller; package com.ecep.contract.controller.contract;
import static com.ecep.contract.util.TableViewUtils.bindDoubleClicked;
import static com.ecep.contract.util.TableViewUtils.bindEnterPressed;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.contract.tasker.ContractVerifyComm;
import com.ecep.contract.manager.ds.contract.tasker.ContractVerifyResultExportAsExcelFile;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.project.service.SaleTypeService;
import com.ecep.contract.manager.ds.vendor.service.VendorGroupService;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.Message;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.stage.*;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Hibernate;
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;
@@ -40,21 +20,52 @@ import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
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 com.ecep.contract.Message;
import java.time.LocalDate; import com.ecep.contract.MessageHolder;
import java.util.ArrayList; import com.ecep.contract.controller.BaseController;
import java.util.List; import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import java.util.concurrent.CompletableFuture; import com.ecep.contract.model.Employee;
import java.util.concurrent.atomic.AtomicInteger; import com.ecep.contract.service.ContractService;
import java.util.logging.Level; import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.ProjectSaleTypeService;
import com.ecep.contract.service.VendorGroupService;
import com.ecep.contract.task.ContractVerifyComm;
import com.ecep.contract.task.ContractVerifyResultExportAsExcelFileTasker;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.vo.EmployeeVo;
import static com.ecep.contract.manager.util.TableViewUtils.bindDoubleClicked; import javafx.application.Platform;
import static com.ecep.contract.manager.util.TableViewUtils.bindEnterPressed; import javafx.beans.property.SimpleListProperty;
import static java.util.concurrent.CompletableFuture.runAsync; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
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.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@@ -78,12 +89,11 @@ public class ContractVerifyWindowController extends BaseController {
} }
} }
@Data @Data
public static class Model implements MessageHolder { public static class Model implements MessageHolder {
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());
@@ -104,9 +114,6 @@ public class ContractVerifyWindowController extends BaseController {
} }
} }
static class StateTableCell extends TableCell<Model, ObservableList<MessageExt>> { static class StateTableCell extends TableCell<Model, ObservableList<MessageExt>> {
Label message2Label(MessageExt message) { Label message2Label(MessageExt message) {
Label label = new Label(message.getMessage()); Label label = new Label(message.getMessage());
@@ -151,16 +158,10 @@ public class ContractVerifyWindowController extends BaseController {
} }
} }
ContractVerifyComm comm = new ContractVerifyComm(); ContractVerifyComm comm = new ContractVerifyComm(this);
@Autowired
private SaleTypeService 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;
@@ -189,7 +190,6 @@ public class ContractVerifyWindowController extends BaseController {
@FXML @FXML
public CheckMenuItem onlyShowVerifiedChecker; public CheckMenuItem onlyShowVerifiedChecker;
@FXML @FXML
public TableView<Model> viewTable; public TableView<Model> viewTable;
private final ObservableList<Model> viewTableDataSet = FXCollections.observableArrayList(); private final ObservableList<Model> viewTableDataSet = FXCollections.observableArrayList();
@@ -198,7 +198,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
@@ -212,7 +212,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();
@@ -224,9 +223,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());
@@ -241,11 +238,9 @@ public class ContractVerifyWindowController extends BaseController {
comm.getVerifyCustomerFiles().bind(verifyCustomerFileChecker.selectedProperty()); comm.getVerifyCustomerFiles().bind(verifyCustomerFileChecker.selectedProperty());
comm.getVerifyCustomerSubContractDate().bind(verifyCustomerSubContractDateChecker.selectedProperty()); comm.getVerifyCustomerSubContractDate().bind(verifyCustomerSubContractDateChecker.selectedProperty());
bindDoubleClicked(viewTable, this::showContract); bindDoubleClicked(viewTable, this::showContract);
bindEnterPressed(viewTable, this::reVerifyContract); bindEnterPressed(viewTable, this::reVerifyContract);
} }
/** /**
@@ -257,22 +252,13 @@ public class ContractVerifyWindowController extends BaseController {
viewTableDataSet.clear(); viewTableDataSet.clear();
Pageable pageRequest = PageRequest.ofSize(200); Pageable pageRequest = PageRequest.ofSize(200);
AtomicInteger counter = new AtomicInteger(0); AtomicInteger counter = new AtomicInteger(0);
Specification<Contract> spec = (root, query, builder) -> {
return builder.and( Map<String, Object> params = ParamUtils.builder().
builder.or( between("setupDate", setupDateBeginSelector.getValue(), setupDateEndSelector.getValue())
builder.equal(root.get("payWay"), ContractPayWay.RECEIVE), .build();
builder.and(
builder.equal(root.get("payWay"), ContractPayWay.PAY),
builder.or( long total = contractService.count(params);
builder.isNull(root.get("parentCode")),
builder.equal(root.get("parentCode"), "")
)
)
),
builder.between(root.get("setupDate"), setupDateBeginSelector.getValue(), setupDateEndSelector.getValue())
);
};
long total = contractService.count(spec);
setStatus("合同:" + total + ""); setStatus("合同:" + total + "");
while (true) { while (true) {
@@ -280,17 +266,17 @@ public class ContractVerifyWindowController extends BaseController {
break; break;
} }
Page<Contract> page = contractService.findAll(spec, pageRequest); Page<ContractVo> page = contractService.findAll(params, pageRequest);
for (Contract contract : page) { for (ContractVo contract : page) {
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);
} }
@@ -298,6 +284,7 @@ public class ContractVerifyWindowController extends BaseController {
model.getCode().set(contract.getCode()); model.getCode().set(contract.getCode());
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, model);
setStatus("合同验证进度:" + counter.get() + " / " + total); setStatus("合同验证进度:" + counter.get() + " / " + total);
// 移除中间消息 // 移除中间消息
@@ -323,7 +310,6 @@ public class ContractVerifyWindowController extends BaseController {
}); });
} }
public void onContractReVerifyAction(ActionEvent event) { public void onContractReVerifyAction(ActionEvent event) {
Model selectedItem = viewTable.getSelectionModel().getSelectedItem(); Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
if (selectedItem == null) { if (selectedItem == null) {
@@ -338,7 +324,13 @@ public class ContractVerifyWindowController extends BaseController {
return; return;
} }
runAsync(() -> { runAsync(() -> {
Contract contract = contractService.findByCode(contractCode); ContractVo contract = null;
try {
contract = contractService.findByCode(contractCode);
} catch (Exception e) {
handleException("查找合同 " + contractCode + " 时发生错误", e);
return;
}
if (contract == null) { if (contract == null) {
return; return;
} }
@@ -362,7 +354,6 @@ public class ContractVerifyWindowController extends BaseController {
}); });
} }
public void onShowContractDetailAction(ActionEvent event) { public void onShowContractDetailAction(ActionEvent event) {
Model selectedItem = viewTable.getSelectionModel().getSelectedItem(); Model selectedItem = viewTable.getSelectionModel().getSelectedItem();
if (selectedItem == null) { if (selectedItem == null) {
@@ -376,7 +367,13 @@ public class ContractVerifyWindowController extends BaseController {
if (!StringUtils.hasText(contractCode)) { if (!StringUtils.hasText(contractCode)) {
return; return;
} }
Contract contract = contractService.findByCode(contractCode); ContractVo contract = null;
try {
contract = contractService.findByCode(contractCode);
} catch (Exception e) {
handleException("查找合同 " + contractCode + " 时发生错误", e);
return;
}
if (contract == null) { if (contract == null) {
return; return;
} }
@@ -389,7 +386,7 @@ public class ContractVerifyWindowController extends BaseController {
chooser.setInitialFileName("核验结果.xlsx"); chooser.setInitialFileName("核验结果.xlsx");
File selected = chooser.showSaveDialog(viewTable.getScene().getWindow()); File selected = chooser.showSaveDialog(viewTable.getScene().getWindow());
if (selected != null) { if (selected != null) {
ContractVerifyResultExportAsExcelFile task = new ContractVerifyResultExportAsExcelFile(); ContractVerifyResultExportAsExcelFileTasker task = new ContractVerifyResultExportAsExcelFileTasker();
task.setDestFile(selected); task.setDestFile(selected);
task.setModels(new ArrayList<>(viewTableDataSet)); task.setModels(new ArrayList<>(viewTableDataSet));
UITools.showTaskDialogAndWait("导出中...", task, null); UITools.showTaskDialogAndWait("导出中...", task, null);

View File

@@ -1,46 +1,56 @@
package com.ecep.contract.manager.ds.contract.controller; package com.ecep.contract.controller.contract;
import java.io.File;
import com.ecep.contract.manager.ds.company.controller.CompanyWindowController;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.contract.tasker.ContractRepairTask;
import com.ecep.contract.manager.ds.contract.tasker.ContractVerifyTasker;
import com.ecep.contract.manager.ds.contract.vo.ContractViewModel;
import com.ecep.contract.manager.ds.project.service.ProjectService;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.DesktopUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import javafx.util.converter.LocalDateStringConverter;
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.StringUtils; import org.springframework.util.StringUtils;
import java.io.File; import com.ecep.contract.ContractPayWay;
import java.time.format.DateTimeFormatter; import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.ContractTabSkinBase;
import com.ecep.contract.controller.tab.ContractTabSkinFiles;
import com.ecep.contract.controller.tab.ContractTabSkinInvoices;
import com.ecep.contract.controller.tab.ContractTabSkinItemsV2;
import com.ecep.contract.controller.tab.ContractTabSkinPayPlan;
import com.ecep.contract.controller.tab.ContractTabSkinSubContract;
import com.ecep.contract.controller.tab.ContractTabSkinVendorBid;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.task.ContractRepairTask;
import com.ecep.contract.task.ContractVerifyTasker;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.vo.ContractVo;
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 @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(ContractVo contract, Window owner) {
public static void show(Contract contract, Window owner) { show(ContractViewModel.from(contract), owner);
ContractViewModel model = new ContractViewModel();
model.update(contract);
show(model, owner);
} }
/** /**
@@ -63,16 +73,6 @@ public class ContractWindowController
public Button openRelativeCompanyCustomerBtn; public Button openRelativeCompanyCustomerBtn;
public Button openRelativeCompanyVendorBtn; public Button openRelativeCompanyVendorBtn;
@Autowired
CompanyService companyService;
@Autowired
ContractService contractService;
@Autowired
ProjectService projectService;
LocalDateStringConverter localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
public TextField nameField; public TextField nameField;
public TextField guidField; public TextField guidField;
public TextField codeField; public TextField codeField;
@@ -124,12 +124,17 @@ public class ContractWindowController
@Override @Override
public ContractService getViewModelService() { public ContractService getViewModelService() {
return contractService; return getCachedBean(ContractService.class);
}
public CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
} }
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent); super.onShown(windowEvent);
getTitle().bind(viewModel.getName().map(name -> "[" + viewModel.getId().get() + "] " + viewModel.getCode().get() + " " + name + " 合同详情")); getTitle().bind(viewModel.getName()
.map(name -> "[" + viewModel.getId().get() + "] " + viewModel.getCode().get() + " " + name + " 合同详情"));
root.getScene().getStylesheets().add("/ui/contract/contract.css"); root.getScene().getStylesheets().add("/ui/contract/contract.css");
} }
@@ -139,18 +144,23 @@ public class ContractWindowController
ObservableList<Tab> tabs = tabPane.getTabs(); ObservableList<Tab> tabs = tabPane.getTabs();
registerTabSkin(baseInfoTab, tab -> new ContractTabSkinBase(this)); registerTabSkin(baseInfoTab, tab -> new ContractTabSkinBase(this));
switch (viewModel.getPayWay().get()) { ContractPayWay payWay = viewModel.getPayWay().get();
case RECEIVE -> { if (payWay == ContractPayWay.RECEIVE) {
tabs.remove(extendVendorInfo); tabs.remove(extendVendorInfo);
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("");
break; tabs.add(invoiceTab);
} registerTabSkin(invoiceTab, tab -> new ContractTabSkinInvoices(this, tab));
case PAY -> { tabs.add(new Tab("出库单"));
tabs.add(new Tab("发货单"));
tabs.add(new Tab("签收单"));
} else if (payWay == ContractPayWay.PAY) {
registerTabSkin(extendVendorInfo, t -> new ContractTabSkinExtendVendorInfo(this)); registerTabSkin(extendVendorInfo, t -> new ContractTabSkinExtendVendorInfo(this));
tabs.remove(contractTab); tabs.remove(contractTab);
registerTabSkin(bidVendorTab, t -> new ContractTabSkinVendorBid(this)); registerTabSkin(bidVendorTab, t -> new ContractTabSkinVendorBid(this));
@@ -159,11 +169,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("付款单"));
break;
} payPlanTab.setText("付款计划");
} }
registerTabSkin(itemTab, tab -> new ContractTabSkinItemsV2(this)); registerTabSkin(itemTab, tab -> new ContractTabSkinItemsV2(this));
@@ -171,9 +180,8 @@ public class ContractWindowController
registerTabSkin(fileTab, tab -> new ContractTabSkinFiles(this)); registerTabSkin(fileTab, tab -> new ContractTabSkinFiles(this));
} }
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("未设置目录");
@@ -188,13 +196,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 = companyService.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());
} }
@@ -202,7 +210,6 @@ public class ContractWindowController
public void onSyncContractAction(ActionEvent event) { public void onSyncContractAction(ActionEvent event) {
ContractRepairTask task = new ContractRepairTask(); ContractRepairTask task = new ContractRepairTask();
task.setContractService(contractService);
task.setContract(getEntity()); task.setContract(getEntity());
UITools.showTaskDialogAndWait("同步合同", task, null); UITools.showTaskDialogAndWait("同步合同", task, null);
if (task.isRepaired()) { if (task.isRepaired()) {
@@ -221,7 +228,7 @@ public class ContractWindowController
} }
} }
if (task.isItemsUpdated()) { if (task.isItemsUpdated()) {
ContractTabSkinItems tabSkin = getTabSkin(ContractTabSkinItems.class); ContractTabSkinItemsV2 tabSkin = getTabSkin(ContractTabSkinItemsV2.class);
if (tabSkin != null) { if (tabSkin != null) {
tabSkin.loadTableDataSet(); tabSkin.loadTableDataSet();
} }
@@ -234,14 +241,12 @@ 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.setContractService(contractService);
task.setContract(contract); task.setContract(contract);
UITools.showTaskDialogAndWait("同步合规性验证", task, null); UITools.showTaskDialogAndWait("同步合规性验证", task, null);
} }

View File

@@ -1,25 +1,33 @@
package com.ecep.contract.manager.ds.contract.controller.sale_order; package com.ecep.contract.controller.contract.sale_order;
import com.ecep.contract.manager.ds.contract.model.SalesOrder; import com.ecep.contract.vo.SalesOrderVo;
import com.ecep.contract.manager.ds.contract.service.SaleOrdersService;
import com.ecep.contract.manager.ds.contract.vo.SalesOrderViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import javafx.scene.control.*;
import javafx.stage.Stage;
import javafx.stage.Window;
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.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.AbstEntityController;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.tab.SalesOrderTabSkinBase;
import com.ecep.contract.controller.tab.SalesOrderTabSkinBillVoucher;
import com.ecep.contract.controller.tab.SalesOrderTabSkinItems;
import com.ecep.contract.service.SaleOrdersService;
import com.ecep.contract.vm.SalesOrderViewModel;
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.Stage;
import javafx.stage.Window;
@Lazy @Lazy
@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;
@@ -35,25 +43,21 @@ 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) {
show(SalesOrderWindowController.class, viewModel, window); show(SalesOrderWindowController.class, viewModel, window);
} }
@Autowired
private SaleOrdersService service;
@Override
protected SalesOrder loadEntity() {
return service.findById(viewModel.getId().get());
}
@Override
protected SalesOrder saveEntity(SalesOrder entity) {
return service.save(entity);
}
@Override @Override
public void show(Stage stage) { public void show(Stage stage) {
super.show(stage); super.show(stage);
@@ -69,6 +73,6 @@ public class SalesOrderWindowController extends AbstEntityController<SalesOrder,
@Override @Override
public SaleOrdersService getViewModelService() { public SaleOrdersService getViewModelService() {
return service; return getCachedBean(SaleOrdersService.class);
} }
} }

View File

@@ -0,0 +1,29 @@
package com.ecep.contract.controller.customer;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.CustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.vo.CustomerVo;
public abstract class AbstCompanyCustomerTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends
AbstEntityTableTabSkin<CompanyCustomerWindowController, CustomerVo, CompanyCustomerViewModel, T, TV>
implements TabSkin {
public AbstCompanyCustomerTableTabSkin(CompanyCustomerWindowController controller) {
super(controller);
}
public CompanyService getCompanyService() {
return controller.getCompanyService();
}
protected CustomerService getCompanyCustomerService() {
return getCachedBean(CustomerService.class);
}
}

View File

@@ -1,27 +1,11 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerFile;
import com.ecep.contract.manager.ds.customer.repository.CompanyCustomerEvaluationFormFileRepository;
import com.ecep.contract.manager.ds.customer.repository.CompanyCustomerFileRepository;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ds.customer.vo.CompanyCustomerFileViewModel;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.manager.ds.company.CompanyFileUtils;
import com.ecep.contract.manager.util.FxmlUtils;
import javafx.application.Platform;
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.geometry.Bounds;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.stage.*;
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;
@@ -33,24 +17,50 @@ import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.awt.image.BufferedImage; import com.ecep.contract.controller.BaseController;
import java.io.File; import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
import java.util.Objects; import com.ecep.contract.vo.CustomerFileVo;
import java.util.concurrent.CompletableFuture; import com.ecep.contract.service.CompanyCustomerFileService;
import java.util.function.BiConsumer; import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.FxmlUtils;
import com.ecep.contract.vm.CompanyCustomerEvaluationFormFileViewModel;
import com.ecep.contract.vm.CustomerFileViewModel;
import javafx.application.Platform;
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.geometry.Bounds;
import javafx.scene.control.CheckBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
public class CompanyCustomerEvaluationFormFileWindowController extends BaseController { public class CompanyCustomerEvaluationFormFileWindowController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class); private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
public static void show(CompanyCustomerFile saved, Window window) { public static void show(CompanyCustomerEvaluationFormFileVo saved, Window window) {
CompanyCustomerFileViewModel model = new CompanyCustomerFileViewModel(); show(CompanyCustomerEvaluationFormFileViewModel.from(saved), window);
model.update(saved);
show(model, window);
} }
public static void show(CompanyCustomerFileViewModel viewModel, Window window) { public static void show(CompanyCustomerEvaluationFormFileViewModel viewModel, Window window) {
String key = viewModel.getClass().getName() + "-" + viewModel.getId().get(); String key = viewModel.getClass().getName() + "-" + viewModel.getId().get();
if (toFront(key)) { if (toFront(key)) {
return; return;
@@ -81,7 +91,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
public ScrollPane leftPane; public ScrollPane leftPane;
public Label totalCreditScoreLabel; public Label totalCreditScoreLabel;
private CompanyCustomerFileViewModel viewModel; private CompanyCustomerEvaluationFormFileViewModel viewModel;
private final SimpleStringProperty catalogProperty = new SimpleStringProperty(""); private final SimpleStringProperty catalogProperty = new SimpleStringProperty("");
private final SimpleStringProperty levelProperty = new SimpleStringProperty(""); private final SimpleStringProperty levelProperty = new SimpleStringProperty("");
@@ -95,15 +105,15 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>(); private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
private CompletableFuture<CompanyCustomerEvaluationFormFile> loadedFuture; private CompletableFuture<CompanyCustomerEvaluationFormFileVo> loadedFuture;
@Autowired
private CompanyCustomerFileRepository companyCustomerFileRepository;
@Autowired
private CompanyCustomerEvaluationFormFileRepository companyCustomerEvaluationFormFileRepository;
@Lazy @Lazy
@Autowired @Autowired
private CompanyCustomerFileService companyCustomerFileService; private CompanyCustomerFileService companyCustomerFileService;
@Lazy
@Autowired
private CompanyCustomerEvaluationFormFileService evaluationFormFileService;
@Override @Override
public void show(Stage stage) { public void show(Stage stage) {
@@ -132,8 +142,8 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
loadedFuture = CompletableFuture.supplyAsync(() -> { loadedFuture = CompletableFuture.supplyAsync(() -> {
int id = viewModel.getId().get(); int id = viewModel.getId().get();
CompanyCustomerFile customerFile = companyCustomerFileService.findById(id); CustomerFileVo customerFile = companyCustomerFileService.findById(id);
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileByCustomerFile(customerFile); CompanyCustomerEvaluationFormFileVo formFile = evaluationFormFileService.findByCustomerFile(customerFile);
Platform.runLater(() -> update(formFile)); Platform.runLater(() -> update(formFile));
return formFile; return formFile;
}); });
@@ -249,10 +259,10 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
private void initializePane() { private void initializePane() {
idField.textProperty().bind(viewModel.getId().asString()); idField.textProperty().bind(viewModel.getId().asString());
filePathField.textProperty().bind(viewModel.getFilePath()); // filePathField.textProperty().bind(viewModel.getFilePath());
editFilePathField.textProperty().bind(viewModel.getEditFilePath()); // editFilePathField.textProperty().bind(viewModel.getEditFilePath());
signDateField.valueProperty().bindBidirectional(viewModel.getSignDate()); // signDateField.valueProperty().bindBidirectional(viewModel.getSignDate());
validField.selectedProperty().bindBidirectional(viewModel.getValid()); // validField.selectedProperty().bindBidirectional(viewModel.getValid());
initializeRadioGroup(catalog, catalogProperty); initializeRadioGroup(catalog, catalogProperty);
initializeRadioGroup(level, levelProperty); initializeRadioGroup(level, levelProperty);
@@ -283,7 +293,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
Bindings.createBooleanBinding(() -> { Bindings.createBooleanBinding(() -> {
boolean valid = calcValid(); boolean valid = calcValid();
viewModel.getValid().set(valid); // viewModel.getValid().set(valid);
return valid; return valid;
}, catalogProperty, levelProperty, score1Property, score2Property, score3Property, score4Property, score5Property, creditLevelProperty).addListener(((observable, oldValue, newValue) -> { }, catalogProperty, levelProperty, score1Property, score2Property, score3Property, score4Property, score5Property, creditLevelProperty).addListener(((observable, oldValue, newValue) -> {
logger.info("valid:{}", newValue); logger.info("valid:{}", newValue);
@@ -291,7 +301,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
imageView.imageProperty().bind(viewModel.getFilePath().map(path -> { imageView.imageProperty().bind(viewModel.getFilePath().map(path -> {
if (CompanyFileUtils.withExtensions(path, CompanyFileUtils.PDF)) { if (FileUtils.withExtensions(path, FileUtils.PDF)) {
File pdfFile = new File(path); File pdfFile = new File(path);
try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) { try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) {
PDFRenderer pdfRenderer = new PDFRenderer(pdDocument); PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);
@@ -367,10 +377,9 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
}); });
} }
private void update(CompanyCustomerEvaluationFormFile formFile) { private void update(CompanyCustomerEvaluationFormFileVo formFile) {
viewModel.update(formFile);
viewModel.update(formFile.getCustomerFile());
// formFile.getScoreTemplateVersion(); // formFile.getScoreTemplateVersion();
@@ -383,55 +392,4 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
score5Property.set(formFile.getScore5()); score5Property.set(formFile.getScore5());
creditLevelProperty.set(formFile.getCreditLevel()); 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);
}
}
} }

View File

@@ -1,52 +1,65 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import com.ecep.contract.manager.ds.company.model.Company; import static com.ecep.contract.util.ExcelUtils.setCellValue;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerEntity;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerFile;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerEntityService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.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 org.hibernate.Hibernate;
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.manager.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);
@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;
} }
@@ -55,12 +68,19 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
customerFileService = getBean(CompanyCustomerFileService.class); customerFileService = getBean(CompanyCustomerFileService.class);
return customerFileService; return customerFileService;
} }
CompanyCustomerEntityService getCustomerEntityService() { CompanyCustomerEntityService getCustomerEntityService() {
if (customerEntityService == null) if (customerEntityService == null)
customerEntityService = getBean(CompanyCustomerEntityService.class); customerEntityService = getBean(CompanyCustomerEntityService.class);
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()) {
@@ -78,8 +98,7 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
} }
try ( try (
InputStream inp = new FileInputStream(template); InputStream inp = new FileInputStream(template);
Workbook wb = WorkbookFactory.create(inp); Workbook wb = WorkbookFactory.create(inp);) {
) {
String sheetName = "客户资信台账"; String sheetName = "客户资信台账";
Sheet sheet = null; Sheet sheet = null;
for (int i = 0; i < wb.getNumberOfSheets(); i++) { for (int i = 0; i < wb.getNumberOfSheets(); i++) {
@@ -99,55 +118,49 @@ 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 (!Hibernate.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();
} }
} }
CustomerFileVo customerFile = getCustomerFileService()
.findAllByCustomer(customer).stream().filter(v -> v.isValid())
.max(Comparator.comparing(v -> v.getSignDate())).orElse(null);
CompanyCustomerEvaluationFormFile evaluationFormFile = getCustomerFileService().findAllCustomerEvaluationFormFiles(customer).stream().filter(v -> {
CompanyCustomerFile customerFile = v.getCustomerFile();
if (customerFile == null) { if (customerFile == null) {
return false;
}
return customerFile.isValid();
}).max(Comparator.comparing(v -> v.getCustomerFile().getSignDate())).orElse(null);
if (evaluationFormFile == 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() + ", 开发日期:" + devDate); holder.debug(company.getName() + " 最新评估日期早于客户开发日期,评估日期:" + customerFile.getSignDate() + ", 开发日期:"
+ devDate);
} }
rowIndex++; rowIndex++;
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());
} }
@@ -155,8 +168,9 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
} }
} }
Row row = ExcelUtils.getRow(sheet, rowIndex + 3, true);
Row row = 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());
@@ -173,7 +187,8 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
for (XSSFTable table : ((XSSFSheet) sheet).getTables()) { for (XSSFTable table : ((XSSFSheet) sheet).getTables()) {
if ("表2".equals(table.getName())) { if ("表2".equals(table.getName())) {
holder.info("找到表=" + table.getName() + ", style=" + table.getStyleName()); holder.info("找到表=" + table.getName() + ", style=" + table.getStyleName());
table.setCellReferences(new AreaReference("$A$4:$H$" + (rowIndex + 4), SpreadsheetVersion.EXCEL2007)); table.setCellReferences(
new AreaReference("$A$4:$H$" + (rowIndex + 4), SpreadsheetVersion.EXCEL2007));
// table.setDataRowCount(rowIndex); // table.setDataRowCount(rowIndex);
// table.getCTTable().setRef("$A$4:$H$" + (rowIndex + 4)); // table.getCTTable().setRef("$A$4:$H$" + (rowIndex + 4));
@@ -200,7 +215,6 @@ public class CompanyCustomerExportExcelTasker extends Tasker<Object> {
UITools.showExceptionAndWait("保存失败", e); UITools.showExceptionAndWait("保存失败", e);
} }
return null; return null;
} }
} }

View File

@@ -1,35 +1,31 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import com.ecep.contract.manager.ui.table.cell.CompanyTableCell; import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.manager.ds.company.service.CompanyService; import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer; import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService; import com.ecep.contract.controller.table.cell.CustomerCatalogTableCell;
import com.ecep.contract.manager.ds.customer.vo.CompanyCustomerViewModel; import com.ecep.contract.service.CustomerService;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin; import com.ecep.contract.service.CompanyService;
import com.ecep.contract.manager.util.MyDateTimeUtils; import com.ecep.contract.service.CustomerCatalogService;
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;
import lombok.Setter;
public class CompanyCustomerManagerSkin public class CompanyCustomerManagerSkin
extends extends
AbstEntityManagerSkin<CompanyCustomer, CompanyCustomerViewModel, CompanyCustomerManagerSkin, CompanyCustomerManagerWindowController> { AbstEntityManagerSkin<CustomerVo, CompanyCustomerViewModel, CompanyCustomerManagerSkin, CompanyCustomerManagerWindowController> {
@Setter
private CompanyService companyService;
public CompanyCustomerManagerSkin(CompanyCustomerManagerWindowController controller) { public CompanyCustomerManagerSkin(CompanyCustomerManagerWindowController controller) {
super(controller); super(controller);
} }
public CompanyService getCompanyService() { public CompanyService getCompanyService() {
if (companyService == null) { return getBean(CompanyService.class);
companyService = getBean(CompanyService.class);
}
return companyService;
} }
public CompanyCustomerService getCompanyCustomerService() { public CustomerService getCompanyCustomerService() {
return controller.getViewModelService(); return controller.getViewModelService();
} }
@@ -37,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());

View File

@@ -1,11 +1,12 @@
package com.ecep.contract.manager.ds.customer.controller; 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.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.hibernate.Hibernate; 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;
@@ -14,15 +15,13 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ecep.contract.manager.ds.company.model.Company; import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.manager.ds.company.service.CompanyService; import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer; import com.ecep.contract.service.CustomerService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService; import com.ecep.contract.service.CompanyService;
import com.ecep.contract.manager.ds.customer.vo.CompanyCustomerViewModel; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ui.AbstManagerWindowController; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.vm.CompanyCustomerViewModel;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@@ -45,24 +44,27 @@ 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;
@Autowired
private CompanyService companyService;
@Autowired
private CompanyCustomerService companyCustomerService;
@Override @Override
public CompanyCustomerService getViewModelService() { public CustomerService getViewModelService() {
return companyCustomerService; return getCachedBean(CustomerService.class);
}
private CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
} }
@Override @Override
@@ -74,7 +76,6 @@ public class CompanyCustomerManagerWindowController
@Override @Override
protected CompanyCustomerManagerSkin createDefaultSkin() { protected CompanyCustomerManagerSkin createDefaultSkin() {
CompanyCustomerManagerSkin skin = new CompanyCustomerManagerSkin(this); CompanyCustomerManagerSkin skin = new CompanyCustomerManagerSkin(this);
skin.setCompanyService(companyService);
return skin; return skin;
} }
@@ -116,21 +117,18 @@ 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 = companyCustomerService.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 (!Hibernate.isInitialized(company)) {
company = companyService.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() + "> ";
companyCustomerService.reBuildingFiles(companyCustomer, msg -> { getViewModelService().reBuildingFiles(companyCustomer, (level, msg) -> {
Platform.runLater(() -> { Platform.runLater(() -> {
listViewDataSet.add(prefix + msg); listViewDataSet.add(prefix + msg);
listView.scrollTo(listViewDataSet.size() - 1); listView.scrollTo(listViewDataSet.size() - 1);
@@ -164,7 +162,7 @@ public class CompanyCustomerManagerWindowController
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("导出"); fileChooser.setTitle("导出");
fileChooser.setInitialFileName("客户资信台账-" + MyDateTimeUtils.format(LocalDate.now()) + ".xlsx"); fileChooser.setInitialFileName("客户资信台账-" + MyDateTimeUtils.format(LocalDate.now()) + ".xlsx");
fileChooser.setInitialDirectory(companyCustomerService.getBasePath()); fileChooser.setInitialDirectory(getViewModelService().getBasePath());
File destFile = fileChooser.showSaveDialog(table.getScene().getWindow()); File destFile = fileChooser.showSaveDialog(table.getScene().getWindow());
tasker.setDestFile(destFile); tasker.setDestFile(destFile);
UITools.showTaskDialogAndWait("导出Excel", tasker, null); UITools.showTaskDialogAndWait("导出Excel", tasker, null);

View File

@@ -1,45 +1,49 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import com.ecep.contract.manager.ds.company.service.CompanyContactService; import java.io.File;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.customer.vo.CompanyCustomerViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.util.DesktopUtils;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import org.hibernate.Hibernate;
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.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.StringUtils; import org.springframework.util.StringUtils;
import java.io.File; import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.service.CompanyContactService;
import com.ecep.contract.service.CustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.CompanyCustomerViewModel;
import com.ecep.contract.vo.CustomerVo;
import com.ecep.contract.vo.CompanyVo;
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.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
@Lazy @Lazy
@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;
@@ -48,14 +52,6 @@ public class CompanyCustomerWindowController extends AbstEntityController<Compan
public Tab fileTab; public Tab fileTab;
public Tab entityTab; public Tab entityTab;
public Tab satisfactionTab; public Tab satisfactionTab;
@Autowired
CompanyService companyService;
@Autowired
CompanyCustomerService companyCustomerService;
@Autowired
CompanyContactService companyContactService;
public TextField companyField; public TextField companyField;
public TextField contactField; public TextField contactField;
public TextField pathField; public TextField pathField;
@@ -70,33 +66,37 @@ public class CompanyCustomerWindowController extends AbstEntityController<Compan
public Button pathAsNameBtn; public Button pathAsNameBtn;
public Button OpenCustomerPathInExplorerBtn; public Button OpenCustomerPathInExplorerBtn;
@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 (!Hibernate.isInitialized(company)) { CompanyVo company = getCompanyService().findById(companyId);
company = companyService.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 companyCustomerService; return getCachedBean(CustomerService.class);
}
public CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
}
public CompanyContactService getCompanyContactService() {
return getCachedBean(CompanyContactService.class);
} }
@Override @Override
@@ -119,6 +119,4 @@ public class CompanyCustomerWindowController extends AbstEntityController<Compan
DesktopUtils.showInExplorer(file); DesktopUtils.showInExplorer(file);
} }
} }

View File

@@ -0,0 +1,173 @@
package com.ecep.contract.controller.customer;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.springframework.util.StringUtils;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.CompanyContactService;
import com.ecep.contract.service.CustomerService;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.QueryService;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.CompanyContactViewModel;
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.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateStringConverter;
public class CustomerTabSkinBase
extends AbstEntityBasedTabSkin<CompanyCustomerWindowController, CustomerVo, CompanyCustomerViewModel>
implements TabSkin {
public CustomerTabSkinBase(CompanyCustomerWindowController controller) {
super(controller);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
initializeCompanyFieldAutoCompletion(controller.companyField);
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);
controller.developDateField.setConverter(converter);
controller.developDateField.valueProperty().bindBidirectional(viewModel.getDevelopDate());
controller.pathField.textProperty().bind(viewModel.getPath());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.createdField.textProperty().bind(
Bindings.createStringBinding(
() -> localDateTimeFormatter(viewModel.getCreated()),
viewModel.getCreated()));
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());
controller.relativeCompanyBtn.setOnAction(event -> {
Integer companyId = viewModel.getCompany().get();
if (companyId != null) {
CompanyVo company = getCompanyService().findById(companyId);
if (company != null) {
CompanyWindowController.show(company, controller.root.getScene().getWindow());
}
}
});
controller.createPathBtn.setOnAction(this::onCompanyCustomerCreatePathAction);
controller.changePathBtn.setOnAction(this::onCompanyCustomerChangePathAction);
controller.pathAsNameBtn.setOnAction(this::onCompanyCustomerPathSameAsNameAction);
}
protected String localDateTimeFormatter(SimpleObjectProperty<LocalDateTime> property) {
LocalDateTime dateTime = property.get();
if (dateTime == null) {
return "";
}
return MyDateTimeUtils.format(dateTime);
}
private void initializeContactFieldAutoCompletion(TextField textField) {
UITools.autoCompletion(textField, viewModel.getContact(), getCompanyContactService());
}
private void initializeCompanyFieldAutoCompletion(TextField textField) {
UITools.autoCompletion(textField, viewModel.getCompany(), getCompanyService());
}
public void onCompanyCustomerCreatePathAction(ActionEvent event) {
CustomerVo companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get());
if (getCompanyCustomerService().makePathAbsent(companyCustomer)) {
companyCustomer = getCompanyCustomerService().save(companyCustomer);
viewModel.update(companyCustomer);
} else {
setStatus("目录存在或创建失败");
}
}
public void onCompanyCustomerChangePathAction(ActionEvent event) {
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) {
setStatus("未实现");
}
public CustomerService getCompanyCustomerService() {
return controller.getCachedBean(CustomerService.class);
}
public CompanyContactService getCompanyContactService() {
return controller.getCachedBean(CompanyContactService.class);
}
public CompanyService getCompanyService() {
return controller.getCachedBean(CompanyService.class);
}
}

View File

@@ -1,38 +1,36 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomerEntity;
import com.ecep.contract.manager.ds.customer.model.CustomerCatalog;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerEntityService;
import com.ecep.contract.manager.ds.customer.vo.CustomerEntityViewModel;
import com.ecep.contract.manager.ds.other.EmployeeStringConverter;
import com.ecep.contract.manager.ds.other.EntityStringConverter;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.manager.util.SpecificationUtils;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
import org.springframework.data.jpa.domain.Specification;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
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);
} }
@@ -52,7 +47,6 @@ public class CustomerTabSkinEntity
return controller.entityTab; return controller.entityTab;
} }
@Override @Override
public void initializeTab() { public void initializeTab() {
super.initializeTab(); super.initializeTab();
@@ -61,34 +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 -> getCompanyCustomerService().findCatalogById(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,11 +84,15 @@ public class CustomerTabSkinEntity
return getCompanyCustomerEntityService(); return getCompanyCustomerEntityService();
} }
CustomerCatalogService getCustomerCatalogService() {
return getCachedBean(CustomerCatalogService.class);
}
@Override @Override
public Specification<CompanyCustomerEntity> getSpecification(CompanyCustomer parent) { public ParamUtils.Builder getSpecification(CustomerVo parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> { ParamUtils.Builder params = getSpecification();
return builder.equal(root.get("customer"), parent); params.equals("customer", parent.getId());
}); return params;
} }
} }

View File

@@ -0,0 +1,323 @@
package com.ecep.contract.controller.customer;
import java.io.File;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.util.StringUtils;
import com.ecep.contract.CustomerFileType;
import com.ecep.contract.DesktopUtils;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.SpringApp;
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.cell.CompanyCustomerFileTableTypeTableCell;
import com.ecep.contract.controller.table.cell.FilePathTableCell;
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
import com.ecep.contract.service.CompanyCustomerFileService;
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.FxmlPath;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.util.UITools;
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.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import lombok.Setter;
@FxmlPath("/ui/company/customer/customer-tab-file.fxml")
public class CustomerTabSkinFile
extends AbstCompanyCustomerTableTabSkin<CustomerFileVo, CustomerFileViewModel>
implements EditableEntityTableTabSkin<CustomerFileVo, CustomerFileViewModel> {
@Setter
private CompanyCustomerFileService companyCustomerFileService;
public TableColumn<CustomerFileViewModel, Number> fileTable_idColumn;
public TableColumn<CustomerFileViewModel, CustomerFileType> fileTable_typeColumn;
public TableColumn<CustomerFileViewModel, String> fileTable_filePathColumn;
public TableColumn<CustomerFileViewModel, String> fileTable_editFilePathColumn;
public TableColumn<CustomerFileViewModel, LocalDate> fileTable_signDateColumn;
public TableColumn<CustomerFileViewModel, Boolean> fileTable_validColumn;
public TableColumn<CustomerFileViewModel, String> fileTable_descriptionColumn;
public Button fileTable_reBuildBtn;
public Button fileTable_updateEvaluationFormBuildBtn;
public Button fileTable_calcNextSignDateBtn;
public MenuItem fileTable_menu_refresh;
public MenuItem fileTable_menu_add;
public MenuItem fileTable_menu_del;
public CustomerTabSkinFile(CompanyCustomerWindowController controller) {
super(controller);
setDragAndDrop(true);
setDragAndDropFileHandler(this::moveFileToCustomer);
}
// 文件 tab
@Override
public Tab getTab() {
return controller.fileTab;
}
@Override
protected CompanyCustomerFileService getViewModelService() {
return getCompanyCustomerFileService();
}
public CompanyCustomerEvaluationFormFileService getEvaluationFormFileService() {
return getCachedBean(CompanyCustomerEvaluationFormFileService.class);
}
@Override
public ParamUtils.Builder getSpecification(CustomerVo parent) {
ParamUtils.Builder params = getSpecification();
params.equals("customer", parent.getId());
return params;
}
@Override
public void initializeTable() {
super.initializeTable();
TableView<CustomerFileViewModel> table = getTableView();
table.disableProperty().bind(viewModel.getPath().isEmpty());
fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
CompanyCustomerFileTypeService fileTypeService = getCachedBean(CompanyCustomerFileTypeService.class);
fileTable_typeColumn.setCellValueFactory(param -> param.getValue().getType());
fileTable_typeColumn.setCellFactory(CompanyCustomerFileTableTypeTableCell.forTableColumn(fileTypeService));
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath());
fileTable_filePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath());
fileTable_editFilePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate());
fileTable_validColumn.setEditable(true);
fileTable_validColumn.setCellValueFactory(param -> param.getValue().getValid());
fileTable_validColumn.setCellFactory(param -> new CheckBoxTableCell<>());
table.setOnDragOver(event -> {
Dragboard dragboard = event.getDragboard();
if (dragboard.hasFiles()) {
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
});
table.setOnDragDropped(event -> {
Dragboard dragboard = event.getDragboard();
boolean success = false;
if (dragboard.hasFiles()) {
List<File> files = dragboard.getFiles();
CompletableFuture.runAsync(() -> {
try {
moveFileToCustomer(files);
} catch (Exception e) {
UITools.showExceptionAndWait("移动文件出错", e);
}
});
}
event.setDropCompleted(success);
event.consume();
});
fileTable_reBuildBtn.setOnAction(this::onFileReBuildingAction);
fileTable_updateEvaluationFormBuildBtn.setOnAction(this::onUpdateEvaluationFormAction);
fileTable_calcNextSignDateBtn.setOnAction(this::onCalcNextSignDateAction);
fileTable_menu_refresh.setOnAction(this::onTableRefreshAction);
fileTable_menu_add.setOnAction(this::onTableDeleteAction);
fileTable_menu_del.setOnAction(this::onFileTableMoveToCompanyPathAction);
}
@Override
protected void onTableRowDoubleClickedAction(CustomerFileViewModel item) {
CustomerFileType fileType = item.getType().get();
if (fileType == CustomerFileType.EvaluationForm) {
// 文件不是 Excel 文件时打开编辑UI
if (!FileUtils.withExtensions(item.getFilePath().get(), FileUtils.XLS,
FileUtils.XLSX)) {
CompanyCustomerEvaluationFormFileVo evaluationFormFile = getEvaluationFormFileService()
.findByCustomerFile(item.getId().get());
CompanyCustomerEvaluationFormFileWindowController.show(evaluationFormFile,
controller.root.getScene().getWindow());
return;
}
}
File file = new File(item.getFilePath().get());
if (!file.exists()) {
setStatus("文件不存在 " + file.getName());
return;
}
DesktopUtils.showInExplorer(file);
}
private void moveFileToCustomer(List<File> files) {
String path = viewModel.getPath().get();
if (!StringUtils.hasText(path)) {
setStatus("未设置目录");
return;
}
File dir = new File(path);
if (!dir.exists()) {
setStatus("目录错误,不存在");
return;
}
CustomerVo companyCustomer = getParent();
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer,
((level, message) -> setStatus(message)));
if (nextSignDate != null && files.size() == 1) {
File file = files.getFirst();
String fileName = file.getName();
if (fileName.startsWith("S")) {
String destFileName = CompanyCustomerConstant.EVALUATION_FORM_NAME2 + "_"
+ MyDateTimeUtils.format(nextSignDate)
+ "." + StringUtils.getFilenameExtension(fileName);
File dest = new File(dir, destFileName);
if (file.renameTo(dest)) {
CustomerFileVo ccf = new CustomerFileVo();
ccf.setCustomer(companyCustomer.getId());
ccf.setType(CustomerFileType.EvaluationForm);
ccf.setFilePath(dest.getAbsolutePath());
ccf.setSignDate(nextSignDate);
ccf.setValid(false);
CustomerFileVo saved = getCompanyCustomerFileService().save(ccf);
Platform.runLater(() -> {
CustomerFileViewModel model = new CustomerFileViewModel();
model.update(saved);
dataSet.add(model);
CompanyCustomerEvaluationFormFileVo evaluationFormFile = getCachedBean(
CompanyCustomerEvaluationFormFileService.class).findByCustomerFile(saved);
CompanyCustomerEvaluationFormFileWindowController.show(evaluationFormFile,
getTableView().getScene().getWindow());
});
return;
}
}
}
for (File file : files) {
File dest = new File(dir, file.getName());
if (file.renameTo(dest)) {
CustomerFileVo ccf = new CustomerFileVo();
ccf.setCustomer(companyCustomer.getId());
ccf.setType(CustomerFileType.General);
ccf.setFilePath(dest.getAbsolutePath());
ccf.setValid(false);
getCompanyCustomerFileService().save(ccf);
}
}
loadTableDataSet();
}
public void onFileReBuildingAction(ActionEvent event) {
CustomerService customerService = getCompanyCustomerService();
try {
CustomerVo companyCustomer = customerService.findById(viewModel.getId().get());
if (customerService.reBuildingFiles(companyCustomer, (level, message) -> setStatus(message))) {
loadTableDataSet();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected boolean deleteRow(CustomerFileViewModel row) {
String path = row.getFilePath().get();
if (super.deleteRow(row)) {
File file = new File(path);
if (file.exists()) {
UITools.showConfirmation("数据记录已经删除,请确认是否删除物理文件", path)
.thenAccept(buttonType -> {
if (buttonType == ButtonType.OK) {
if (file.delete()) {
setStatus("删除文件 " + path);
}
}
});
}
return true;
}
return false;
}
public void onFileTableMoveToCompanyPathAction(ActionEvent event) {
CustomerFileViewModel selectedItem = getSelectedItem();
if (selectedItem == null) {
return;
}
// 检查公司目录设置
Integer companyId = viewModel.getCompany().get();
CompanyVo company = getCompanyService().findById(companyId);
if (!StringUtils.hasText(company.getPath())) {
setStatus("公司目录未设置");
return;
}
File companyPath = new File(company.getPath());
if (!companyPath.exists()) {
setStatus("公司目录设置异常,无法访问");
return;
}
// 创建并启动任务
CustomerFileMoveTasker task = new CustomerFileMoveTasker();
task.setFileId(selectedItem.getId().get());
UITools.showTaskDialogAndWait("移动文件到公司目录", task, null);
// 刷新表格数据
loadTableDataSet();
}
public void onUpdateEvaluationFormAction(ActionEvent event) {
CustomerEvaluationFormUpdateTask task = new CustomerEvaluationFormUpdateTask();
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get()));
UITools.showTaskDialogAndWait("更新评价表", task, null);
loadTableDataSet();
}
public void onCalcNextSignDateAction(ActionEvent event) {
CustomerNextSignDateTask task = new CustomerNextSignDateTask();
task.setCustomer(getEntity());
UITools.showTaskDialogAndWait("计算客户的下一个评价日期", task, null);
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (companyCustomerFileService == null) {
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return companyCustomerFileService;
}
}

View File

@@ -1,40 +1,41 @@
package com.ecep.contract.manager.ds.customer.controller; package com.ecep.contract.controller.customer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import com.ecep.contract.service.CustomerCatalogService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.project.satisfaction_survey.CustomerSatisfactionSurveyWindowController;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.controller.table.cell.ProjectTableCell;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.vo.CustomerVo;
import com.ecep.contract.vo.CustomerCatalogVo;
import com.ecep.contract.vo.CustomerSatisfactionSurveyVo;
import com.ecep.contract.vo.EmployeeVo;
import com.ecep.contract.vo.ProjectVo;
import com.ecep.contract.service.CustomerSatisfactionSurveyService;
import com.ecep.contract.service.ProjectService;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.CustomerEntityViewModel;
import com.ecep.contract.vm.CustomerSatisfactionSurveyViewModel;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.customer.model.CompanyCustomer;
import com.ecep.contract.manager.ds.customer.model.CustomerCatalog;
import com.ecep.contract.manager.ds.customer.vo.CustomerEntityViewModel;
import com.ecep.contract.manager.ds.other.EntityStringConverter;
import com.ecep.contract.manager.ds.project.service.ProjectService;
import com.ecep.contract.manager.ui.table.cell.EmployeeTableCell;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.project.controller.satisfaction_survey.CustomerSatisfactionSurveyWindowController;
import com.ecep.contract.manager.ds.project.model.CustomerSatisfactionSurvey;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.service.CustomerSatisfactionSurveyService;
import com.ecep.contract.manager.ds.project.vo.CustomerSatisfactionSurveyViewModel;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.table.cell.ProjectTableCell;
import com.ecep.contract.manager.util.SpecificationUtils;
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 lombok.Setter; import lombok.Setter;
import org.springframework.data.jpa.domain.Specification;
import java.time.LocalDate;
import java.time.LocalDateTime;
@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;
@@ -55,19 +56,19 @@ private ProjectService projectService;
return controller.satisfactionTab; return controller.satisfactionTab;
} }
@Override @Override
public void initializeTab() { public void initializeTab() {
super.initializeTab(); super.initializeTab();
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);
@@ -75,13 +76,6 @@ private ProjectService projectService;
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 -> getCompanyCustomerService().findCatalogById(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);
@@ -102,14 +96,10 @@ private ProjectService projectService;
} }
@Override @Override
public Specification<CustomerSatisfactionSurvey> getSpecification(CompanyCustomer parent) { public ParamUtils.Builder getSpecification(CustomerVo parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> { ParamUtils.Builder params = getSpecification();
Company company = parent.getCompany(); params.equals("project.customer", parent.getId());
if (company == null) { return params;
return null;
}
return builder.equal(root.get("project").get("customer"), company);
});
} }
@Override @Override

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -1,27 +1,30 @@
package com.ecep.contract.manager.ds.other.controller.department; package com.ecep.contract.controller.department;
import java.util.List;
import org.springframework.data.domain.Pageable;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
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.model.Employee;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vo.DepartmentVo;
import com.ecep.contract.vm.DepartmentViewModel;
import com.ecep.contract.manager.ds.other.EmployeeStringConverter;
import com.ecep.contract.manager.ds.other.model.Department;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.vo.DepartmentViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.ComboBoxTableCell; import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.control.cell.TextFieldTableCell;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import java.util.List;
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);
@@ -31,10 +34,7 @@ public class DepartmentManagerSkin
public void initializeTable() { public void initializeTable() {
getTableView().setEditable(true); getTableView().setEditable(true);
Specification<Employee> spec = (root, query, cb) -> { // 不再需要获取所有员工列表因为现在使用的是leaderId和leaderName
return cb.equal(root.get("isActive"), true);
};
List<Employee> employees = controller.getEmployeeService().findAll(spec, Pageable.ofSize(30)).getContent();
controller.idColumn.setCellValueFactory(param -> param.getValue().getId()); controller.idColumn.setCellValueFactory(param -> param.getValue().getId());
@@ -47,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);
@@ -56,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());
@@ -69,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);
} }
@@ -88,9 +88,9 @@ public class DepartmentManagerSkin
@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);
} }
} }

View File

@@ -1,31 +1,31 @@
package com.ecep.contract.manager.ds.other.controller.department; package com.ecep.contract.controller.department;
import com.ecep.contract.manager.ds.other.model.Department;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.service.DepartmentService;
import com.ecep.contract.manager.ds.other.vo.DepartmentViewModel;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.ViewModelService;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
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.service.DepartmentService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.DepartmentViewModel;
import com.ecep.contract.vo.DepartmentVo;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@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

View File

@@ -0,0 +1,29 @@
package com.ecep.contract.controller.employee;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.model.Employee;
import com.ecep.contract.service.EmployeeRoleService;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.service.PermissionService;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vo.EmployeeVo;
import lombok.Setter;
public abstract class AbstEmployeeBasedTabSkin
extends AbstEntityBasedTabSkin<EmployeeWindowController, EmployeeVo, EmployeeViewModel>
implements TabSkin {
public AbstEmployeeBasedTabSkin(EmployeeWindowController controller) {
super(controller);
}
protected EmployeeRoleService getEmployeeRoleService() {
return getCachedBean(EmployeeRoleService.class);
}
protected PermissionService getPermissionService() {
return getCachedBean(PermissionService.class);
}
}

View File

@@ -0,0 +1,44 @@
package com.ecep.contract.controller.employee;
import java.util.Map;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.EmployeeBasedViewModel;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vm.IdentityViewModel;
import com.ecep.contract.vo.EmployeeVo;
public abstract class AbstEmployeeTableTabSkin<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends AbstEntityTableTabSkin<EmployeeWindowController, EmployeeVo, EmployeeViewModel, T, TV>
implements TabSkin {
public AbstEmployeeTableTabSkin(EmployeeWindowController controller) {
super(controller);
}
public EmployeeService getEmployeeService() {
return controller.employeeService;
}
@Override
protected TV createNewViewModel() {
TV model = super.createNewViewModel();
if (model instanceof EmployeeBasedViewModel m) {
m.getEmployee().set(getEntity().getId());
}
return model;
}
@Override
public ParamUtils.Builder getSpecification(EmployeeVo parent) {
ParamUtils.Builder params = getSpecification();
params.equals("employee", parent.getId());
return params;
}
}

View File

@@ -1,20 +1,20 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.cell.DepartmentTableCell;
import com.ecep.contract.model.Employee;
import com.ecep.contract.service.DepartmentService;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vo.EmployeeVo;
import com.ecep.contract.manager.ds.other.EntityStringConverter;
import com.ecep.contract.manager.ds.other.model.Department;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.service.DepartmentService;
import com.ecep.contract.manager.ds.other.vo.EmployeeViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import com.ecep.contract.manager.ui.table.cell.DepartmentTableCell;
import com.ecep.contract.manager.util.SpecificationUtils;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.CheckBoxTableCell;
import org.springframework.data.jpa.domain.Specification;
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);
@@ -30,14 +30,12 @@ public class EmployeeManagerSkin
} }
@Override @Override
public Specification<Employee> getSpecification() { public ParamUtils.Builder getSpecification() {
Specification<Employee> spec = super.getSpecification(); ParamUtils.Builder params = super.getSpecification();
if (controller.activeCheckBox.isSelected()) { if (controller.activeCheckBox.isSelected()) {
spec = SpecificationUtils.and(spec, (root, query, builder) -> { params.equals("isActive", true);
return builder.isTrue(root.get("isActive"));
});
} }
return spec; return params;
} }
@Override @Override
@@ -51,7 +49,6 @@ public class EmployeeManagerSkin
controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment()); controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment());
controller.departmentColumn.setCellFactory(param -> new DepartmentTableCell<>(getDepartmentService())); controller.departmentColumn.setCellFactory(param -> new DepartmentTableCell<>(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());
controller.entryDateColumn.setCellValueFactory(param -> param.getValue().getEntryDate()); controller.entryDateColumn.setCellValueFactory(param -> param.getValue().getEntryDate());
@@ -68,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);

View File

@@ -1,39 +1,40 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import java.time.LocalDate;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.model.Department;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.task.EmployeesSyncTask;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vo.EmployeeVo;
import com.ecep.contract.manager.cloud.u8.EmployeesSyncTask;
import com.ecep.contract.manager.cloud.u8.YongYouU8Service;
import com.ecep.contract.manager.ds.other.model.Department;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.other.vo.EmployeeViewModel;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.UITools;
import javafx.concurrent.Task;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@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
@@ -68,8 +69,8 @@ public class EmployeeManagerWindowController
* U8系统 同步员工数据 * U8系统 同步员工数据
*/ */
public void onSyncFromU8Action(ActionEvent event) { public void onSyncFromU8Action(ActionEvent event) {
Task<Object> task = new EmployeesSyncTask(); EmployeesSyncTask task = new EmployeesSyncTask();
UITools.showTaskDialogAndWait("" + YongYouU8Service.NAME + " 同步员工数据", task, null); UITools.showTaskDialogAndWait("" + CloudServiceConstant.U8_NAME + " 同步员工数据", task, null);
} }

View File

@@ -1,31 +1,32 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import com.ecep.contract.manager.ds.other.model.Employee; import com.ecep.contract.Desktop;
import com.ecep.contract.manager.ds.other.model.EmployeeAuthBind; import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.manager.ds.other.service.EmployeeAuthBindService; import com.ecep.contract.model.Employee;
import com.ecep.contract.manager.ds.other.vo.EmployeeAuthBindViewModel; import com.ecep.contract.model.EmployeeAuthBind;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.service.EmployeeAuthBindService;
import com.ecep.contract.manager.ui.table.cell.EmployeeTableCell; import com.ecep.contract.util.FxmlPath;
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;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import lombok.Setter; import lombok.Setter;
import org.springframework.data.domain.Sort;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; 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
@@ -79,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, Sort.unsorted()); 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);
} }
}); });

View File

@@ -1,15 +1,14 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import com.ecep.contract.manager.ds.other.EntityStringConverter;
import com.ecep.contract.manager.ds.other.model.Department;
import com.ecep.contract.manager.ds.other.service.DepartmentService;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.UITools;
import javafx.scene.control.Tab;
import javafx.util.converter.LocalDateStringConverter;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.service.DepartmentService;
import com.ecep.contract.util.UITools;
import javafx.scene.control.Tab;
import javafx.util.converter.LocalDateStringConverter;
public class EmployeeTabSkinBase public class EmployeeTabSkinBase
extends AbstEmployeeBasedTabSkin extends AbstEmployeeBasedTabSkin
implements TabSkin { implements TabSkin {
@@ -24,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());
@@ -48,9 +44,10 @@ public class EmployeeTabSkinBase
controller.isActiveField.selectedProperty().bindBidirectional(viewModel.getIsActive()); controller.isActiveField.selectedProperty().bindBidirectional(viewModel.getIsActive());
// Callback<ListView<EmployeeRole>, ListCell<EmployeeRole>> cellFactory =
// Callback<ListView<EmployeeRole>, ListCell<EmployeeRole>> cellFactory = controller.rolesField.getCellFactory(); // controller.rolesField.getCellFactory();
// StringConverter<EmployeeRole> employeeRoleStringConverter = new EntityStringConverter<>(); // StringConverter<EmployeeRole> employeeRoleStringConverter = new
// EntityStringConverter<>();
// controller.rolesField.setCellFactory(param -> { // controller.rolesField.setCellFactory(param -> {
// ListCell<EmployeeRole> cell = cellFactory.call(param); // ListCell<EmployeeRole> cell = cellFactory.call(param);
// if (cell instanceof CheckBoxListCell<EmployeeRole> list) { // if (cell instanceof CheckBoxListCell<EmployeeRole> list) {
@@ -59,10 +56,11 @@ public class EmployeeTabSkinBase
// return cell; // return cell;
// }); // });
// controller.rolesField.getCheckModel().getCheckedItems().setAll(); // controller.rolesField.getCheckModel().getCheckedItems().setAll();
// Property<IndexedCheckModel<EmployeeRole>> selectedRoles = new SimpleObjectProperty<>(); // Property<IndexedCheckModel<EmployeeRole>> selectedRoles = new
// controller.rolesField.getCheckModel().getCheckedItems().addListener((ListChangeListener<? super EmployeeRole>) changed -> { // SimpleObjectProperty<>();
// controller.rolesField.getCheckModel().getCheckedItems().addListener((ListChangeListener<?
// super EmployeeRole>) changed -> {
// System.out.println("newValue = " + changed); // System.out.println("newValue = " + changed);
// }); // });
} }

View File

@@ -1,20 +1,21 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import java.time.LocalDateTime;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.service.EmployeeLoginHistoryService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vo.EmployeeLoginHistoryVo;
import com.ecep.contract.vm.EmployeeLoginHistoryViewModel;
import com.ecep.contract.manager.ds.other.model.EmployeeLoginHistory;
import com.ecep.contract.manager.ds.other.service.EmployeeLoginHistoryService;
import com.ecep.contract.manager.ds.other.vo.EmployeeLoginHistoryViewModel;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import java.time.LocalDateTime;
@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;

View File

@@ -1,18 +1,18 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import java.util.HashMap;
import java.util.List;
import org.springframework.data.domain.Pageable;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.vo.EmployeeRoleVo;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.model.EmployeeRole;
import com.ecep.contract.manager.ui.tab.TabSkin;
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 org.springframework.data.jpa.domain.Specification;
import java.util.List;
public class EmployeeTabSkinRole public class EmployeeTabSkinRole
extends AbstEmployeeBasedTabSkin extends AbstEmployeeBasedTabSkin
@@ -41,24 +41,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() {
// 非系统内置账户 // 非系统内置账户
Specification<EmployeeRole> spec = null; HashMap<String, Object> params = new HashMap<>();
if (!Desktop.instance.getActiveEmployee().isSystemAdministrator()) { List<EmployeeRoleVo> roles = getEmployeeRoleService().findAll(params, Pageable.ofSize(500)).getContent();
spec = (root, query, cb) -> cb.equal(root.get("systemAdministrator"), false);
}
List<EmployeeRole> roles = getEmployeeRoleService().findAll(spec, 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);
@@ -69,10 +66,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);
} }
@@ -82,9 +79,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();
} }
} }

View File

@@ -1,18 +1,5 @@
package com.ecep.contract.manager.ds.other.controller.employee; package com.ecep.contract.controller.employee;
import com.ecep.contract.manager.ds.other.model.Employee;
import com.ecep.contract.manager.ds.other.model.EmployeeRole;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.other.vo.EmployeeViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.ViewModelService;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
import org.controlsfx.control.ListSelectionView; import org.controlsfx.control.ListSelectionView;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -21,11 +8,29 @@ 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.AbstEntityController;
import com.ecep.contract.service.EmployeeService;
import com.ecep.contract.util.FxmlPath;
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.DatePicker;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import lombok.Getter;
@Lazy @Lazy
@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);
/** /**
@@ -55,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;
@@ -66,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);
} }

View File

@@ -1,16 +1,18 @@
package com.ecep.contract.manager.ds.other.controller.inventory; package com.ecep.contract.controller.inventory;
import java.util.function.Function;
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.LocalDateTimeTableCell;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.service.InventoryCatalogService;
import com.ecep.contract.service.InventoryService;
import com.ecep.contract.vm.InventoryViewModel;
import com.ecep.contract.vo.InventoryCatalogVo;
import com.ecep.contract.vo.InventoryVo;
import com.ecep.contract.manager.ds.other.EntityStringConverter;
import com.ecep.contract.manager.ds.other.model.Inventory;
import com.ecep.contract.manager.ds.other.model.InventoryCatalog;
import com.ecep.contract.manager.ds.other.service.InventoryCatalogService;
import com.ecep.contract.manager.ds.other.service.InventoryService;
import com.ecep.contract.manager.ds.other.vo.InventoryViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import com.ecep.contract.manager.ui.util.LocalDateFieldTableCell;
import com.ecep.contract.manager.ui.table.cell.LocalDateTimeTableCell;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
@@ -19,11 +21,8 @@ import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter; import javafx.util.converter.NumberStringConverter;
import lombok.Setter; import lombok.Setter;
import java.util.function.Function; public class InventoryManagerSkin extends
AbstEntityManagerSkin<InventoryVo, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController> {
public class InventoryManagerSkin
extends AbstEntityManagerSkin<Inventory, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<Inventory, InventoryViewModel> {
@Setter @Setter
private InventoryCatalogService catalogService; private InventoryCatalogService catalogService;
@@ -58,18 +57,13 @@ public class InventoryManagerSkin
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());
controller.specificationColumn.setCellFactory(TextFieldTableCell.forTableColumn()); controller.specificationColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.specificationColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSpecification)); controller.specificationColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSpecification));
controller.unitColumn.setCellValueFactory(param -> param.getValue().getUnit()); controller.unitColumn.setCellValueFactory(param -> param.getValue().getUnit());
controller.unitColumn.setCellFactory(TextFieldTableCell.forTableColumn()); controller.unitColumn.setCellFactory(TextFieldTableCell.forTableColumn());
@@ -77,44 +71,48 @@ public class InventoryManagerSkin
controller.purchaseTaxRateColumn.setCellValueFactory(param -> param.getValue().getPurchaseTaxRate()); controller.purchaseTaxRateColumn.setCellValueFactory(param -> param.getValue().getPurchaseTaxRate());
controller.purchaseTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter())); controller.purchaseTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
controller.purchaseTaxRateColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getPurchaseTaxRate)); controller.purchaseTaxRateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getPurchaseTaxRate));
controller.purchasePriceColumn.setCellValueFactory(param -> param.getValue().getPurchasePrice()); controller.purchasePriceColumn.setCellValueFactory(param -> param.getValue().getPurchasePrice());
controller.purchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter())); controller.purchasePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter()));
// controller.purchasePriceColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getPurchasePrice)); // controller.purchasePriceColumn.setOnEditCommit(event ->
// onColumnEditCommit(event, InventoryViewModel::getPurchasePrice));
controller.saleTaxRateColumn.setCellValueFactory(param -> param.getValue().getSaleTaxRate()); controller.saleTaxRateColumn.setCellValueFactory(param -> param.getValue().getSaleTaxRate());
controller.saleTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter())); controller.saleTaxRateColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
controller.saleTaxRateColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSaleTaxRate)); controller.saleTaxRateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSaleTaxRate));
controller.salePriceColumn.setCellValueFactory(param -> param.getValue().getSalePrice()); controller.salePriceColumn.setCellValueFactory(param -> param.getValue().getSalePrice());
controller.salePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter())); controller.salePriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(new CurrencyStringConverter()));
// controller.salePriceColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getSalePrice)); // controller.salePriceColumn.setOnEditCommit(event -> onColumnEditCommit(event,
// InventoryViewModel::getSalePrice));
controller.createTimeColumn.setCellValueFactory(param -> param.getValue().getCreateTime()); controller.createTimeColumn.setCellValueFactory(param -> param.getValue().getCreateTime());
controller.createTimeColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn()); controller.createTimeColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
controller.createTimeColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCreateTime)); controller.createTimeColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getCreateTime));
controller.updateDateColumn.setCellValueFactory(param -> param.getValue().getUpdateDate()); controller.updateDateColumn.setCellValueFactory(param -> param.getValue().getUpdateDate());
controller.updateDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>()); controller.updateDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
controller.updateDateColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getUpdateDate)); controller.updateDateColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getUpdateDate));
controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription()); controller.descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
controller.descriptionColumn.setCellFactory(TextFieldTableCell.forTableColumn()); controller.descriptionColumn.setCellFactory(TextFieldTableCell.forTableColumn());
controller.descriptionColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getDescription)); controller.descriptionColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getDescription));
} }
private <T> void onColumnEditCommit( private <T> void onColumnEditCommit(
TableColumn.CellEditEvent<InventoryViewModel, T> event, TableColumn.CellEditEvent<InventoryViewModel, T> event,
Function<InventoryViewModel, Property<T>> supplier Function<InventoryViewModel, Property<T>> supplier) {
) {
InventoryViewModel row = event.getRowValue(); InventoryViewModel row = event.getRowValue();
supplier.apply(row).setValue(event.getNewValue()); supplier.apply(row).setValue(event.getNewValue());
saveRowData(row); saveRowData(row);
} }
@Override @Override
protected void onTableRowDoubleClickedAction(InventoryViewModel item) { protected void onTableRowDoubleClickedAction(InventoryViewModel item) {
showInOwner(InventoryWindowController.class, item); showInOwner(InventoryWindowController.class, item);
@@ -122,7 +120,7 @@ public class InventoryManagerSkin
@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);
} }

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager.ds.other.controller.inventory; package com.ecep.contract.controller.inventory;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -8,13 +8,15 @@ 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.manager.ds.other.model.Inventory; import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.manager.ds.other.model.InventoryCatalog; import com.ecep.contract.service.InventoryService;
import com.ecep.contract.manager.ds.other.service.InventoryService; import com.ecep.contract.task.InventoryAllSyncTask;
import com.ecep.contract.manager.ds.other.vo.InventoryViewModel; import com.ecep.contract.task.InventorySyncTask;
import com.ecep.contract.manager.ui.AbstManagerWindowController; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.util.UITools;
import com.ecep.contract.manager.util.UITools; 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;
@@ -26,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;
@@ -34,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
@@ -75,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);
} }

View File

@@ -0,0 +1,180 @@
package com.ecep.contract.controller.inventory;
import java.text.NumberFormat;
import java.time.format.DateTimeFormatter;
import java.util.function.Consumer;
import com.ecep.contract.task.InventorySyncTask;
import org.springframework.util.StringUtils;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.service.InventoryCatalogService;
import com.ecep.contract.service.InventoryService;
import com.ecep.contract.util.UITools;
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.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import javafx.util.converter.NumberStringConverter;
public class InventoryTabSkinBase
extends AbstEntityBasedTabSkin<InventoryWindowController, InventoryVo, InventoryViewModel>
implements TabSkin, EditableEntityTableTabSkin<InventoryVo, InventoryViewModel> {
public InventoryTabSkinBase(InventoryWindowController controller) {
super(controller);
}
InventoryService getService() {
return getCachedBean(InventoryService.class);
}
InventoryCatalogService getCatalogService() {
return getCachedBean(InventoryCatalogService.class);
}
@Override
public Tab getTab() {
return controller.baseInfoTab;
}
@Override
public void initializeTab() {
controller.syncBtn.setOnAction(this::onSyncAction);
NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
numberInstance.setMaximumFractionDigits(2);
numberInstance.setMinimumFractionDigits(2);
CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter(numberInstance);
controller.nameField.textProperty().bindBidirectional(viewModel.getName());
controller.nameLockField.selectedProperty().bindBidirectional(viewModel.getNameLock());
controller.codeField.textProperty().bind(viewModel.getCode());
controller.unitField.textProperty().bindBidirectional(viewModel.getUnit());
controller.specificationField.textProperty().bindBidirectional(viewModel.getSpecification());
controller.specificationLockField.selectedProperty().bindBidirectional(viewModel.getSpecificationLock());
UITools.autoCompletion(controller.catalogField, viewModel.getCatalog(), getCatalogService());
controller.purchaseTaxRateField.textProperty().bindBidirectional(viewModel.getPurchaseTaxRate(),
new NumberStringConverter());
// 采购价
bindPriceField(controller.purchasePriceField, viewModel.getPurchasePrice(), currencyStringConverter,
viewModel::updatePurchasePrice);
bindPriceField(controller.purchaseTaxPriceField, viewModel.getPurchaseTaxPrice(), currencyStringConverter,
viewModel::updatePurchaseTaxPrice);
controller.saleTaxRateField.textProperty().bindBidirectional(viewModel.getSaleTaxRate(),
new NumberStringConverter());
// 销售价
bindPriceField(controller.salePriceField, viewModel.getSalePrice(), currencyStringConverter,
viewModel::updateSalePrice);
bindPriceField(controller.saleTaxPriceField, viewModel.getSaleTaxPrice(), currencyStringConverter,
viewModel::updateSaleTaxPrice);
// 采购价不能大于销售价
Bindings.greaterThan(viewModel.getPurchasePrice(), viewModel.getSalePrice())
.addListener((observable, oldValue, newValue) -> {
if (newValue) {
controller.purchasePriceField.getStyleClass().add("error");
controller.salePriceField.getStyleClass().add("error");
} else {
controller.purchasePriceField.getStyleClass().remove("error");
controller.salePriceField.getStyleClass().remove("error");
}
});
Bindings.greaterThan(viewModel.getPurchaseTaxPrice(), viewModel.getSaleTaxPrice())
.addListener((observable, oldValue, newValue) -> {
if (newValue) {
controller.purchaseTaxPriceField.getStyleClass().add("error");
controller.saleTaxPriceField.getStyleClass().add("error");
} else {
controller.purchaseTaxPriceField.getStyleClass().remove("error");
controller.saleTaxPriceField.getStyleClass().remove("error");
}
});
UITools.autoCompletion(controller.creatorField, viewModel.getCreator(), controller.getEmployeeService());
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATE_FORMAT_PATTERN);
controller.createTimeField.textProperty().bindBidirectional(viewModel.getCreateTime(),
new LocalDateStringConverter(dateFormatter, null));
UITools.autoCompletion(controller.updaterField, viewModel.getUpdater(), controller.getEmployeeService());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
controller.updateDateField.textProperty().bindBidirectional(viewModel.getUpdateDate(),
new LocalDateTimeStringConverter(dateTimeFormatter, null));
controller.weightUnitField.textProperty().bindBidirectional(viewModel.getWeightUnit());
controller.sizeUnitField.textProperty().bindBidirectional(viewModel.getSizeUnit());
controller.volumeUnitField.textProperty().bindBidirectional(viewModel.getVolumeUnit());
controller.weightField.textProperty().bindBidirectional(viewModel.getWeight(), new NumberStringConverter());
controller.packagedWeightField.textProperty().bindBidirectional(viewModel.getPackagedWeight(),
new NumberStringConverter());
controller.sizeLengthField.textProperty().bindBidirectional(viewModel.getSizeLength(),
new NumberStringConverter());
controller.sizeWidthField.textProperty().bindBidirectional(viewModel.getSizeWidth(),
new NumberStringConverter());
controller.sizeHeightField.textProperty().bindBidirectional(viewModel.getSizeHeight(),
new NumberStringConverter());
controller.volumeField.textProperty().bindBidirectional(viewModel.getVolume(), new NumberStringConverter());
controller.packagedSizeLengthField.textProperty().bindBidirectional(viewModel.getPackagedSizeLength(),
new NumberStringConverter());
controller.packagedSizeWidthField.textProperty().bindBidirectional(viewModel.getPackagedSizeWidth(),
new NumberStringConverter());
controller.packagedSizeHeightField.textProperty().bindBidirectional(viewModel.getPackagedSizeHeight(),
new NumberStringConverter());
controller.packagedVolumeField.textProperty().bindBidirectional(viewModel.getPackagedVolume(),
new NumberStringConverter());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
}
private void bindPriceField(TextField textField, SimpleDoubleProperty property,
CurrencyStringConverter stringConverter, Consumer<Double> updater) {
textField.setText(stringConverter.toString(property.get()));
property.addListener((observable, oldValue, newValue) -> {
textField.setText(stringConverter.toString(newValue));
});
textField.setOnKeyTyped(event -> {
Number number = stringConverter.fromString(textField.getText());
updater.accept(number.doubleValue());
});
}
private void onSyncAction(ActionEvent event) {
InventoryVo inventory = getEntity();
InventorySyncTask task = new InventorySyncTask();
task.setInventory(inventory);
UITools.showTaskDialogAndWait("同步数据", task, null);
}
@Override
public void deleteRowData(InventoryVo entity) {
getService().delete(entity);
}
@Override
public InventoryVo loadRowData(InventoryViewModel row) {
return getService().findById(row.getId().get());
}
@Override
public InventoryVo saveRowData(InventoryVo entity) {
return getService().save(entity);
}
}

View File

@@ -1,34 +1,32 @@
package com.ecep.contract.manager.ds.other.controller.inventory; package com.ecep.contract.controller.inventory;
import java.text.NumberFormat;
import java.time.LocalDate;
import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.service.ContractItemService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.InventoryHistoryPriceService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.ContractViewModel;
import com.ecep.contract.vm.InventoryViewModel;
import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.vo.InventoryVo;
import com.ecep.contract.manager.ds.contract.controller.ContractWindowController;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.model.ContractItem;
import com.ecep.contract.manager.ds.contract.service.ContractItemService;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.contract.vo.ContractViewModel;
import com.ecep.contract.manager.ds.other.model.Inventory;
import com.ecep.contract.manager.ds.other.service.InventoryHistoryPriceService;
import com.ecep.contract.manager.ds.other.vo.InventoryViewModel;
import com.ecep.contract.manager.ui.table.AbstEntityTableTabSkin;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.SpecificationUtils;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import javafx.scene.control.ContextMenu; import javafx.scene.control.ContextMenu;
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.util.converter.CurrencyStringConverter; import javafx.util.converter.CurrencyStringConverter;
import lombok.Setter; import lombok.Setter;
import org.springframework.data.jpa.domain.Specification;
import java.text.NumberFormat;
import java.time.LocalDate;
@FxmlPath("/ui/inventory/inventory-contract.fxml") @FxmlPath("/ui/inventory/inventory-contract.fxml")
public class InventoryTabSkinContracts public class InventoryTabSkinContracts
extends AbstEntityTableTabSkin<InventoryWindowController, Inventory, InventoryViewModel, Contract, ContractViewModel> extends
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;
@@ -77,7 +75,6 @@ public class InventoryTabSkinContracts
return controller.contractsTab; return controller.contractsTab;
} }
@Override @Override
public void initializeTable() { public void initializeTable() {
super.initializeTable(); super.initializeTable();
@@ -93,18 +90,10 @@ public class InventoryTabSkinContracts
} }
@Override @Override
public Specification<Contract> getSpecification(Inventory parent) { public ParamUtils.Builder getSpecification(InventoryVo parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> { ParamUtils.Builder params = getSpecification();
// 创建ContractItem的子查询 params.equals("inventory", parent.getId());
Subquery<Integer> subquery = query.subquery(Integer.class); return params;
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);
});
} }
@Override @Override

View File

@@ -1,28 +1,4 @@
package com.ecep.contract.manager.ds.other.controller.inventory; package com.ecep.contract.controller.inventory;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.model.ContractItem;
import com.ecep.contract.manager.ds.contract.service.ContractItemService;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.other.model.HistoryPrice;
import com.ecep.contract.manager.ds.other.model.Inventory;
import com.ecep.contract.manager.ds.other.model.InventoryHistoryPrice;
import com.ecep.contract.manager.ds.other.service.InventoryHistoryPriceService;
import com.ecep.contract.manager.ds.other.vo.InventoryHistoryPriceViewModel;
import com.ecep.contract.manager.ds.other.vo.InventoryViewModel;
import com.ecep.contract.manager.ui.table.AbstEntityTableTabSkin;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.SpecificationUtils;
import javafx.event.ActionEvent;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
import org.hibernate.Hibernate;
import org.springframework.data.jpa.domain.Specification;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.time.MonthDay; import java.time.MonthDay;
@@ -32,9 +8,36 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.HistoryPrice;
import com.ecep.contract.service.ContractItemService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.InventoryHistoryPriceService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.InventoryHistoryPriceViewModel;
import com.ecep.contract.vm.InventoryViewModel;
import com.ecep.contract.vo.ContractItemVo;
import com.ecep.contract.vo.ContractVo;
import com.ecep.contract.vo.InventoryHistoryPriceVo;
import com.ecep.contract.vo.InventoryVo;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
@FxmlPath("/ui/inventory/inventory-history-price.fxml") @FxmlPath("/ui/inventory/inventory-history-price.fxml")
public class InventoryTabSkinHistoryPrice public class InventoryTabSkinHistoryPrice
extends AbstEntityTableTabSkin<InventoryWindowController, Inventory, InventoryViewModel, InventoryHistoryPrice, InventoryHistoryPriceViewModel> extends
AbstEntityTableTabSkin<InventoryWindowController, InventoryVo, InventoryViewModel, InventoryHistoryPriceVo, InventoryHistoryPriceViewModel>
implements TabSkin { implements TabSkin {
public Button refreshBtn; public Button refreshBtn;
public TableColumn<InventoryHistoryPriceViewModel, Number> idColumn; public TableColumn<InventoryHistoryPriceViewModel, Number> idColumn;
@@ -66,42 +69,25 @@ public class InventoryTabSkinHistoryPrice
public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchaseTaxPriceColumn; public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchaseTaxPriceColumn;
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> miniPurchasePriceDateColumn; public TableColumn<InventoryHistoryPriceViewModel, MonthDay> miniPurchasePriceDateColumn;
@Setter
InventoryHistoryPriceService service;
@Setter
ContractService contractService;
@Setter
ContractItemService contractItemService;
public InventoryTabSkinHistoryPrice(InventoryWindowController controller) { public InventoryTabSkinHistoryPrice(InventoryWindowController controller) {
super(controller); super(controller);
} }
@Override @Override
protected InventoryHistoryPriceService getViewModelService() { protected InventoryHistoryPriceService getViewModelService() {
return getService(); return getHistoryPriceService();
} }
InventoryHistoryPriceService getService() { InventoryHistoryPriceService getHistoryPriceService() {
if (service == null) { return getCachedBean(InventoryHistoryPriceService.class);
service = getBean(InventoryHistoryPriceService.class);
}
return service;
} }
ContractItemService getContractItemService() { ContractItemService getContractItemService() {
if (contractItemService == null) { return getCachedBean(ContractItemService.class);
contractItemService = getBean(ContractItemService.class);
}
return contractItemService;
} }
ContractService getContractService() { ContractService getContractService() {
if (contractService == null) { return getCachedBean(ContractService.class);
contractService = getBean(ContractService.class);
}
return contractService;
} }
@Override @Override
@@ -109,7 +95,6 @@ public class InventoryTabSkinHistoryPrice
return controller.historyPriceTab; return controller.historyPriceTab;
} }
@Override @Override
public void initializeTable() { public void initializeTable() {
super.initializeTable(); super.initializeTable();
@@ -176,67 +161,70 @@ public class InventoryTabSkinHistoryPrice
private void onRefreshAction(ActionEvent event) { private void onRefreshAction(ActionEvent event) {
runAsync(() -> { runAsync(() -> {
HashMap<Integer, InventoryHistoryPrice> historyPriceMap = new HashMap<>(); HashMap<Integer, InventoryHistoryPriceVo> historyPriceMap = new HashMap<>();
for (InventoryHistoryPrice historyPrice : getService().findAllByInventory(getParent())) { for (InventoryHistoryPriceVo historyPrice : getHistoryPriceService().findAllByInventory(getParent())) {
InventoryHistoryPrice oldValue = historyPriceMap.put(historyPrice.getYear().getValue(), historyPrice); InventoryHistoryPriceVo oldValue = historyPriceMap.put(historyPrice.getYear().getValue(), historyPrice);
if (oldValue != null) { if (oldValue != null) {
getService().delete(oldValue); getHistoryPriceService().delete(oldValue);
} }
} }
List<ContractItemVo> items = getContractItemService().findAllByInventory(getParent());
List<ContractItem> items = getContractItemService().findAllByInventory(getParent());
items.stream().collect(Collectors.groupingBy(v -> { items.stream().collect(Collectors.groupingBy(v -> {
Contract contract = v.getContract(); Integer contractId = v.getContractId();
if (!Hibernate.isInitialized(contract)) { ContractVo contract = getContractService().findById(contractId);
contract = getContractService().findById(contract.getId());
v.setContract(contract);
}
return contract.getSetupDate().getYear(); return contract.getSetupDate().getYear();
})).forEach((year, list) -> { })).forEach((year, list) -> {
InventoryHistoryPrice historyPrice = historyPriceMap.computeIfAbsent(year, k -> { InventoryHistoryPriceVo historyPrice = historyPriceMap.computeIfAbsent(year, k -> {
InventoryHistoryPrice price = new InventoryHistoryPrice(); InventoryHistoryPriceVo price = new InventoryHistoryPriceVo();
price.setInventory(getParent()); price.setInventoryId(getParent().getId());
price.setYear(year); price.setYear(Year.of(year));
return price; return price;
}); });
list.stream().collect(Collectors.groupingBy(v -> { list.stream().collect(Collectors.groupingBy(v -> {
Contract contract = v.getContract(); Integer contractId = v.getContractId();
ContractVo contract = getContractService().findById(contractId);
return contract.getPayWay(); return contract.getPayWay();
})).forEach((payWay, contractItems) -> { })).forEach((payWay, contractItems) -> {
if (ContractPayWay.RECEIVE.equals(payWay)) { if (ContractPayWay.RECEIVE.equals(payWay)) {
// 销售 // 销售
list.stream().max(Comparator.comparing(v -> { list.stream().max(Comparator.comparing(v -> {
Contract contract = v.getContract(); Integer contractId = v.getContractId();
ContractVo contract = getContractService().findById(contractId);
return contract.getSetupDate(); return contract.getSetupDate();
})).ifPresent(v -> update(historyPrice.getLatestSalePrice(), v)); })).ifPresent(v -> update(historyPrice.getLatestSalePrice(), v));
list.stream().max(Comparator.comparing(ContractItem::getTaxPrice)).ifPresent(v -> update(historyPrice.getMaxSalePrice(), v)); list.stream().max(Comparator.comparing(ContractItemVo::getTaxPrice))
list.stream().min(Comparator.comparing(ContractItem::getTaxPrice)).ifPresent(v -> update(historyPrice.getMiniSalePrice(), v)); .ifPresent(v -> update(historyPrice.getMaxSalePrice(), v));
list.stream().min(Comparator.comparing(ContractItemVo::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMiniSalePrice(), v));
} else if (ContractPayWay.PAY.equals(payWay)) { } else if (ContractPayWay.PAY.equals(payWay)) {
// 采购 // 采购
list.stream().max(Comparator.comparing(v -> { list.stream().max(Comparator.comparing(v -> {
Contract contract = v.getContract(); Integer contractId = v.getContractId();
ContractVo contract = getContractService().findById(contractId);
return contract.getSetupDate(); return contract.getSetupDate();
})).ifPresent(v -> update(historyPrice.getLatestPurchasePrice(), v)); })).ifPresent(v -> update(historyPrice.getLatestPurchasePrice(), v));
list.stream().max(Comparator.comparing(ContractItem::getTaxPrice)).ifPresent(v -> update(historyPrice.getMaxPurchasePrice(), v)); list.stream().max(Comparator.comparing(ContractItemVo::getTaxPrice))
list.stream().min(Comparator.comparing(ContractItem::getTaxPrice)).ifPresent(v -> update(historyPrice.getMiniPurchasePrice(), v)); .ifPresent(v -> update(historyPrice.getMaxPurchasePrice(), v));
list.stream().min(Comparator.comparing(ContractItemVo::getTaxPrice))
.ifPresent(v -> update(historyPrice.getMiniPurchasePrice(), v));
} else { } else {
} }
}); });
getService().save(historyPrice); getHistoryPriceService().save(historyPrice);
}); });
loadTableDataSet(); loadTableDataSet();
}); });
} }
void update(HistoryPrice historyPrice, ContractItem item) { void update(HistoryPrice historyPrice, ContractItemVo item) {
if (item == null) { if (item == null) {
historyPrice.setTaxRate(0); historyPrice.setTaxRate(0);
historyPrice.setPreTaxPrice(0); historyPrice.setPreTaxPrice(0);
@@ -245,22 +233,19 @@ public class InventoryTabSkinHistoryPrice
return; return;
} }
getContractService(); getContractService();
Contract contract = item.getContract(); Integer contractId = item.getContractId();
if (!Hibernate.isInitialized(contract)) { ContractVo contract = getContractService().findById(contractId);
contract = getContractService().findById(contract.getId()); historyPrice.setTaxRate(item.getTaxRate().floatValue());
item.setContract(contract);
}
historyPrice.setTaxRate((float) item.getTaxRate());
historyPrice.setPreTaxPrice(item.getExclusiveTaxPrice()); historyPrice.setPreTaxPrice(item.getExclusiveTaxPrice());
historyPrice.setPostTaxPrice(item.getTaxPrice()); historyPrice.setPostTaxPrice(item.getTaxPrice());
historyPrice.setMonthDay(MonthDay.from(contract.getSetupDate())); historyPrice.setMonthDay(MonthDay.from(contract.getSetupDate()));
} }
@Override @Override
public Specification<InventoryHistoryPrice> getSpecification(Inventory parent) { public ParamUtils.Builder getSpecification(InventoryVo parent) {
return SpecificationUtils.and(getSpecification(), (root, query, builder) -> { ParamUtils.Builder params = getSpecification();
return builder.equal(root.get("inventory"), parent); params.equals("inventory", parent.getId());
}); return params;
} }
@Override @Override

View File

@@ -1,25 +1,46 @@
package com.ecep.contract.manager.ds.other.controller.inventory; package com.ecep.contract.controller.inventory;
import com.ecep.contract.manager.ds.other.model.Inventory;
import com.ecep.contract.manager.ds.other.service.InventoryService;
import com.ecep.contract.manager.ds.other.vo.InventoryViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
import com.ecep.contract.manager.ui.FxmlPath;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.WindowEvent;
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.AbstEntityController;
import com.ecep.contract.controller.BaseController;
import com.ecep.contract.service.InventoryService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.InventoryViewModel;
import com.ecep.contract.vo.InventoryVo;
import javafx.stage.Window;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
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.WindowEvent;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@Component @Component
@FxmlPath("/ui/inventory/inventory.fxml") @FxmlPath("/ui/inventory/inventory.fxml")
public class InventoryWindowController extends AbstEntityController<Inventory, InventoryViewModel> { public class InventoryWindowController extends AbstEntityController<InventoryVo, InventoryViewModel> {
/**
* 显示界面
*/
public static void show(InventoryViewModel viewModel, Window window) {
BaseController.show(InventoryWindowController.class, viewModel, window);
}
/**
* 显示界面
*/
public static void show(InventoryVo inventory, Window owner) {
show(InventoryViewModel.from(inventory), owner);
}
@FXML @FXML
public BorderPane root; public BorderPane root;
@FXML @FXML
@@ -31,7 +52,6 @@ public class InventoryWindowController extends AbstEntityController<Inventory, I
@FXML @FXML
public Tab contractsTab; public Tab contractsTab;
@FXML @FXML
public TextField nameField; public TextField nameField;
@FXML @FXML
@@ -97,27 +117,16 @@ public class InventoryWindowController extends AbstEntityController<Inventory, I
@FXML @FXML
public TextField packagedVolumeField; public TextField packagedVolumeField;
@Autowired
private InventoryService service;
@Override @Override
public void onShown(WindowEvent windowEvent) { public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent); super.onShown(windowEvent);
getTitle().bind(Bindings.createStringBinding(() -> "[" + viewModel.getId().get() + "] 存货详情 " + viewModel.getName().getValue() + " " + viewModel.getSpecification().get(), viewModel.getName(), viewModel.getCode(), viewModel.getSpecification())); getTitle().bind(Bindings.createStringBinding(
() -> "[" + viewModel.getId().get() + "] 存货详情 " + viewModel.getName().getValue() + " "
+ viewModel.getSpecification().get(),
viewModel.getName(), viewModel.getCode(), viewModel.getSpecification()));
root.getScene().getStylesheets().add("/ui/inventory/inventory.css"); root.getScene().getStylesheets().add("/ui/inventory/inventory.css");
} }
@Override
protected Inventory loadEntity() {
return service.findById(viewModel.getId().get());
}
@Override
protected Inventory saveEntity(Inventory entity) {
return service.save(entity);
}
@Override @Override
protected void registerTabSkins() { protected void registerTabSkins() {
registerTabSkin(baseInfoTab, tab -> new InventoryTabSkinBase(this)); registerTabSkin(baseInfoTab, tab -> new InventoryTabSkinBase(this));
@@ -127,6 +136,6 @@ public class InventoryWindowController extends AbstEntityController<Inventory, I
@Override @Override
public InventoryService getViewModelService() { public InventoryService getViewModelService() {
return service; return getCachedBean(InventoryService.class);
} }
} }

View File

@@ -0,0 +1,41 @@
package com.ecep.contract.controller.list.cell;
import com.ecep.contract.ContractFileType;
import com.ecep.contract.Desktop;
import com.ecep.contract.service.ContractFileTypeService;
import com.ecep.contract.vo.ContractFileTypeLocalVo;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
/**
* @param <T>
*/
public class ContractFileTypeListCell<T> extends ListCell<ContractFileType> {
public static Callback<ListView<ContractFileType>, ListCell<ContractFileType>> forListView(ContractFileTypeService typeService) {
return listView -> new ContractFileTypeListCell<>(typeService);
}
private final ContractFileTypeService service;
public ContractFileTypeListCell(ContractFileTypeService service) {
this.service = service;
}
@Override
protected void updateItem(ContractFileType item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
return;
}
ContractFileTypeLocalVo local = service.findByType(Desktop.instance.getActiveEmployee().localeProperty().get(), item);
if (local == null) {
setText(item.name());
} else {
setText(local.getValue());
}
}
}

View File

@@ -0,0 +1,30 @@
package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.service.EmployeeRoleService;
import com.ecep.contract.service.FunctionService;
import com.ecep.contract.service.PermissionService;
import com.ecep.contract.vm.EmployeeRoleViewModel;
import com.ecep.contract.vo.EmployeeRoleVo;
public abstract class AbstEmployeeRoleBasedTabSkin
extends AbstEntityBasedTabSkin<EmployeeRoleWindowController, EmployeeRoleVo, EmployeeRoleViewModel> {
public AbstEmployeeRoleBasedTabSkin(EmployeeRoleWindowController controller) {
super(controller);
viewModel = controller.getViewModel();
}
EmployeeRoleService getRoleService() {
return getCachedBean(EmployeeRoleService.class);
}
FunctionService getFunctionService() {
return getCachedBean(FunctionService.class);
}
public PermissionService getPermissionService() {
return controller.permissionService;
}
}

View File

@@ -1,24 +1,23 @@
package com.ecep.contract.manager.ds.other.controller.permission; package com.ecep.contract.controller.permission;
import com.ecep.contract.manager.ds.other.model.Function;
import com.ecep.contract.manager.ds.other.service.FunctionService;
import com.ecep.contract.manager.ds.other.service.PermissionService;
import com.ecep.contract.manager.ds.other.vo.FunctionViewModel;
import com.ecep.contract.manager.ds.other.vo.PermissionViewModel;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.ViewModelService;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Description; import org.springframework.context.annotation.Description;
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.service.FunctionService;
import com.ecep.contract.service.PermissionService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.FunctionViewModel;
import com.ecep.contract.vo.FunctionVo;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy @Lazy
@Scope("prototype") @Scope("prototype")
@@ -26,7 +25,7 @@ import org.springframework.stereotype.Component;
@FxmlPath("/ui/employee/functions-manager.fxml") @FxmlPath("/ui/employee/functions-manager.fxml")
@Component @Component
public class EmployeeFunctionsManagerWindowController public class EmployeeFunctionsManagerWindowController
extends AbstManagerWindowController<Function, FunctionViewModel, FunctionManagerSkin> { extends AbstManagerWindowController<FunctionVo, FunctionViewModel, FunctionManagerSkin> {
@Autowired @Autowired
PermissionService permissionService; PermissionService permissionService;

View File

@@ -1,17 +1,19 @@
package com.ecep.contract.manager.ds.other.controller.permission; package com.ecep.contract.controller.permission;
import com.ecep.contract.controller.AbstEntityManagerSkin;
import com.ecep.contract.controller.ManagerSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.vo.EmployeeRoleVo;
import com.ecep.contract.service.PermissionService;
import com.ecep.contract.vm.EmployeeRoleViewModel;
import com.ecep.contract.manager.ds.other.model.EmployeeRole;
import com.ecep.contract.manager.ds.other.service.PermissionService;
import com.ecep.contract.manager.ds.other.vo.EmployeeRoleViewModel;
import com.ecep.contract.manager.ui.AbstEntityManagerSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.ManagerSkin;
import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.CheckBoxTableCell;
import lombok.Setter; import lombok.Setter;
public class EmployeeRoleManagerSkin public class EmployeeRoleManagerSkin
extends AbstEntityManagerSkin<EmployeeRole, EmployeeRoleViewModel, EmployeeRoleManagerSkin, EmployeeRoleManagerWindowController> extends
implements ManagerSkin, EditableEntityTableTabSkin<EmployeeRole, EmployeeRoleViewModel> { AbstEntityManagerSkin<EmployeeRoleVo, EmployeeRoleViewModel, EmployeeRoleManagerSkin, EmployeeRoleManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<EmployeeRoleVo, EmployeeRoleViewModel> {
@Setter @Setter
private PermissionService permissionService; private PermissionService permissionService;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.manager.ds.other.controller.permission; package com.ecep.contract.controller.permission;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -7,15 +7,14 @@ 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.manager.ds.other.model.EmployeeRole; import com.ecep.contract.controller.AbstManagerWindowController;
import com.ecep.contract.manager.ds.other.service.EmployeeRoleService; import com.ecep.contract.service.EmployeeRoleService;
import com.ecep.contract.manager.ds.other.service.PermissionService; import com.ecep.contract.service.PermissionService;
import com.ecep.contract.manager.ds.other.vo.EmployeeRoleViewModel; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.manager.ui.AbstManagerWindowController; import com.ecep.contract.vm.EmployeeRoleViewModel;
import com.ecep.contract.manager.ui.FxmlPath; import com.ecep.contract.vo.EmployeeRoleVo;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -24,7 +23,7 @@ import javafx.stage.Stage;
@Component @Component
@FxmlPath("/ui/employee/roles-manager.fxml") @FxmlPath("/ui/employee/roles-manager.fxml")
public class EmployeeRoleManagerWindowController public class EmployeeRoleManagerWindowController
extends AbstManagerWindowController<EmployeeRole, EmployeeRoleViewModel, EmployeeRoleManagerSkin> { extends AbstManagerWindowController<EmployeeRoleVo, EmployeeRoleViewModel, EmployeeRoleManagerSkin> {
private static final Logger logger = LoggerFactory.getLogger(EmployeeRoleManagerWindowController.class); private static final Logger logger = LoggerFactory.getLogger(EmployeeRoleManagerWindowController.class);
@Autowired @Autowired

View File

@@ -1,7 +1,8 @@
package com.ecep.contract.manager.ds.other.controller.permission; package com.ecep.contract.controller.permission;
import com.ecep.contract.Desktop;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.ui.tab.TabSkin;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
public class EmployeeRoleTabSkinBase extends AbstEmployeeRoleBasedTabSkin implements TabSkin { public class EmployeeRoleTabSkinBase extends AbstEmployeeRoleBasedTabSkin implements TabSkin {

View File

@@ -1,13 +1,13 @@
package com.ecep.contract.manager.ds.other.controller.permission; package com.ecep.contract.controller.permission;
import java.util.List; import java.util.List;
import org.controlsfx.control.ListSelectionView; import org.controlsfx.control.ListSelectionView;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.manager.ds.other.model.EmployeeRole; import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.manager.ds.other.model.Function; import com.ecep.contract.vo.EmployeeRoleVo;
import com.ecep.contract.vo.FunctionVo;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
@@ -20,7 +20,7 @@ import javafx.scene.control.Tab;
public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin { public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false); private final SimpleBooleanProperty changed = new SimpleBooleanProperty(false);
private ListSelectionView<Function> functionsField; private ListSelectionView<FunctionVo> functionsField;
public EmployeeRoleTabSkinFunctions(EmployeeRoleWindowController controller) { public EmployeeRoleTabSkinFunctions(EmployeeRoleWindowController controller) {
super(controller); super(controller);
@@ -50,7 +50,7 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
} }
private void loadSelectedRoles() { private void loadSelectedRoles() {
List<Function> selectedRoles = getRoleService().getFunctionsByRoleId(viewModel.getId().get()); List<FunctionVo> selectedRoles = getRoleService().getFunctionsByRoleId(viewModel.getId().get());
if (selectedRoles != null) { if (selectedRoles != null) {
functionsField.getTargetItems().setAll(selectedRoles); functionsField.getTargetItems().setAll(selectedRoles);
} }
@@ -59,14 +59,14 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
private void initializeListView() { private void initializeListView() {
// 非系统内置账户 // 非系统内置账户
Specification<Function> spec = (root, query, cb) -> cb.equal(root.get("active"), true); List<FunctionVo> roles = getFunctionService()
List<Function> roles = getFunctionService().findAll(spec, Pageable.ofSize(500)).getContent(); .findAll(ParamUtils.builder().equals("active", true).build(), Pageable.ofSize(500)).getContent();
functionsField.getSourceItems().setAll(roles); functionsField.getSourceItems().setAll(roles);
functionsField.setCellFactory(param -> { functionsField.setCellFactory(param -> {
return new ListCell<>() { return new ListCell<FunctionVo>() {
@Override @Override
protected void updateItem(Function item, boolean empty) { protected void updateItem(FunctionVo item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item == null || empty) { if (item == null || empty) {
setText(null); setText(null);
@@ -77,10 +77,10 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
}; };
}); });
functionsField.getTargetItems().addListener((ListChangeListener<Function>) change -> { functionsField.getTargetItems().addListener((ListChangeListener<FunctionVo>) change -> {
while (change.next()) { while (change.next()) {
List<? extends Function> added = change.getAddedSubList(); List<? extends FunctionVo> added = change.getAddedSubList();
List<? extends Function> removed = change.getRemoved(); List<? extends FunctionVo> removed = change.getRemoved();
if (!added.isEmpty() || !removed.isEmpty()) { if (!added.isEmpty() || !removed.isEmpty()) {
changed.set(true); changed.set(true);
} }
@@ -99,7 +99,7 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin {
} }
private void saveRoles(ActionEvent event) { private void saveRoles(ActionEvent event) {
EmployeeRole entity = getEntity(); EmployeeRoleVo entity = getEntity();
entity.setFunctions(functionsField.getTargetItems()); entity.setFunctions(functionsField.getTargetItems());
save(entity); save(entity);
loadSelectedRoles(); loadSelectedRoles();

Some files were not shown because too many files have changed in this diff Show More