Compare commits

..

14 Commits

Author SHA1 Message Date
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
70 changed files with 2741 additions and 1097 deletions

View File

@@ -0,0 +1,53 @@
-- 项目资金计划表结构
CREATE TABLE `PROJECT_FUND_PLAN` (
`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`PROJECT_ID` int DEFAULT NULL COMMENT '项目ID',
`PAY_DATE` date DEFAULT NULL COMMENT '付款日期',
`PAY_WAY` int DEFAULT NULL COMMENT '付款方式',
`PAY_RATIO` float DEFAULT NULL COMMENT '付款比例',
`PAY_CURRENCY` double DEFAULT NULL COMMENT '付款金额',
`PAY_TERM` varchar(255) DEFAULT NULL COMMENT '付款条件',
`CONTRACT_PAY_PLAN_ID` int DEFAULT NULL COMMENT '合同付款计划ID',
`UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新日期',
PRIMARY KEY (`ID`),
KEY `FK_PROJECT_FUND_PLAN_PROJECT` (`PROJECT_ID`),
KEY `FK_PROJECT_FUND_PLAN_CONTRACT_PAY_PLAN` (`CONTRACT_PAY_PLAN_ID`),
CONSTRAINT `FK_PROJECT_FUND_PLAN_PROJECT` FOREIGN KEY (`PROJECT_ID`) REFERENCES `PROJECT` (`ID`),
CONSTRAINT `FK_PROJECT_FUND_PLAN_CONTRACT_PAY_PLAN` FOREIGN KEY (`CONTRACT_PAY_PLAN_ID`) REFERENCES `CONTRACT_PAY_PLAN` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='项目资金计划';
/** Alert 增加 PAY_WAY **/
ALTER TABLE `PROJECT_FUND_PLAN`
ADD COLUMN `PAY_WAY` int DEFAULT NULL COMMENT '付款方式';
-- 项目投标表结构 (参考)
CREATE TABLE `PROJECT_BID` (
`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`PROJECT_ID` int DEFAULT NULL COMMENT '项目ID',
`LEVEL` int NOT NULL COMMENT '客户资信等级',
`AMOUNT` double DEFAULT NULL COMMENT '报价金额',
`CUS_EVAL_FILE_ID` int DEFAULT NULL COMMENT '评价表单文件ID',
`COST_ID` int DEFAULT NULL COMMENT '成本ID',
`STANDARD_PAY_WAY` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否标准付款方式',
`NO_STANDARD_PAY_WAY_TEXT` varchar(255) DEFAULT NULL COMMENT '非标准付款方式文本',
`STANDARD_CONTRACT_TEXT` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否标准合同文本',
`NO_STANDARD_CONTRACT_TEXT` varchar(255) DEFAULT NULL COMMENT '非标准合同文本',
`AUTHORIZER_FILE` varchar(255) DEFAULT NULL COMMENT '审核文件',
`BID_ACCEPTANCE_LETTER_FILE` varchar(255) DEFAULT NULL COMMENT '中标通知书文件',
`APPLICANT_ID` int DEFAULT NULL COMMENT '申请人ID',
`APPLY_DATE` datetime DEFAULT NULL COMMENT '申请日期',
`AUTHORIZER_ID` int DEFAULT NULL COMMENT '审核人ID',
`AUTHORIZER_DATE` datetime DEFAULT NULL COMMENT '审核时间',
`DESCRIPTION` text COMMENT '说明',
PRIMARY KEY (`ID`),
KEY `FK_PROJECT_BID_PROJECT` (`PROJECT_ID`),
KEY `FK_PROJECT_BID_CUS_EVAL_FILE` (`CUS_EVAL_FILE_ID`),
KEY `FK_PROJECT_BID_COST` (`COST_ID`),
KEY `FK_PROJECT_BID_APPLICANT` (`APPLICANT_ID`),
KEY `FK_PROJECT_BID_AUTHORIZER` (`AUTHORIZER_ID`),
CONSTRAINT `FK_PROJECT_BID_PROJECT` FOREIGN KEY (`PROJECT_ID`) REFERENCES `PROJECT` (`ID`),
CONSTRAINT `FK_PROJECT_BID_CUS_EVAL_FILE` FOREIGN KEY (`CUS_EVAL_FILE_ID`) REFERENCES `COMPANY_CUSTOMER_EVALUATION_FORM_FILE` (`ID`),
CONSTRAINT `FK_PROJECT_BID_COST` FOREIGN KEY (`COST_ID`) REFERENCES `PROJECT_COST` (`ID`),
CONSTRAINT `FK_PROJECT_BID_APPLICANT` FOREIGN KEY (`APPLICANT_ID`) REFERENCES `EMPLOYEE` (`ID`),
CONSTRAINT `FK_PROJECT_BID_AUTHORIZER` FOREIGN KEY (`AUTHORIZER_ID`) REFERENCES `EMPLOYEE` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='项目投标';

View File

@@ -10,7 +10,7 @@
</parent>
<groupId>com.ecep.contract</groupId>
<artifactId>Contract-Manager</artifactId>
<version>0.0.45-SNAPSHOT</version>
<version>0.0.47-SNAPSHOT</version>
<name>Contract-Manager</name>
<description>Contract-Manager</description>
<url/>

View File

@@ -1,5 +1,6 @@
package com.ecep.contract.manager;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
@@ -8,22 +9,28 @@ import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
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.util.MyDateTimeUtils;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.ToString;
/**
* 当前登录用户
*/
@ToString(of = {"id", "name", "email", "phone", "roles"})
@ToString(callSuper = false, exclude = "roles")
public class CurrentEmployee extends EmployeeViewModel {
private static final Logger logger = LoggerFactory.getLogger(CurrentEmployee.class);
/**
@@ -34,24 +41,89 @@ public class CurrentEmployee extends EmployeeViewModel {
* 角色
*/
private SimpleListProperty<EmployeeRole> roles = new SimpleListProperty<>(FXCollections.observableArrayList());
private DateTimeFormatter dateFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_DATE_FORMAT_PATTERN);
private DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
private DateTimeFormatter timeFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_TIME_FORMAT_PATTERN);
private LocalDateStringConverter localDateStringConverter = new LocalDateStringConverter(
dateFormatter, null);
private LocalDateTimeStringConverter localDateTimeStringConverter = new LocalDateTimeStringConverter(
dateTimeFormatter, null);
private NumberStringConverter numberStringConverter = new NumberStringConverter("#,##0.00");
private CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter("#,##0.00");
/**
* 是否系统管理员
*/
public boolean isSystemAdministrator() {
return roles.stream().anyMatch(EmployeeRole::isSystemAdministrator);
}
/**
* 语言环境属性
*/
public SimpleObjectProperty<Locale> localeProperty() {
return locale;
}
/**
* 角色属性
*/
public SimpleListProperty<EmployeeRole> rolesProperty() {
return roles;
}
@Override
protected void updateFrom(Employee v) {
super.updateFrom(v);
// 根据用户的个人配置修改
if (StringUtils.hasText(v.getLocale())) {
try {
localeProperty().set(Locale.forLanguageTag(v.getLocale()));
} catch (Exception e) {
handleException("解析设置用户区域设置失败," + v.getLocale(), e);
}
}
if (StringUtils.hasText(v.getDateFormatter())) {
try {
dateFormatter = DateTimeFormatter.ofPattern(v.getDateFormatter());
localDateStringConverter = new LocalDateStringConverter(dateFormatter, null);
} catch (Exception e) {
handleException("解析设置用户日期格式失败," + v.getDateFormatter(), e);
}
}
if (StringUtils.hasText(v.getDateTimeFormatter())) {
try {
dateTimeFormatter = DateTimeFormatter.ofPattern(v.getDateTimeFormatter());
localDateTimeStringConverter = new LocalDateTimeStringConverter(dateTimeFormatter, null);
} catch (Exception e) {
handleException("解析设置用户日期时间格式失败," + v.getDateTimeFormatter(), e);
}
}
if (StringUtils.hasText(v.getNumberFormatter())) {
try {
numberStringConverter = new NumberStringConverter(localeProperty().get(), v.getNumberFormatter());
} catch (Exception e) {
handleException("解析设置用户数字格式失败," + v.getNumberFormatter(), e);
}
}
if (StringUtils.hasText(v.getCurrencyFormatter())) {
try {
currencyStringConverter = new CurrencyStringConverter(localeProperty().get(), v.getCurrencyFormatter());
} catch (Exception e) {
handleException("解析设置用户货币格式失败," + v.getCurrencyFormatter(), e);
}
}
}
/**
* 初始化
*/
@@ -92,4 +164,24 @@ public class CurrentEmployee extends EmployeeViewModel {
}, 10, 10, TimeUnit.SECONDS);
return future;
}
private void handleException(String msg, Exception e) {
logger.warn(msg, e);
}
public LocalDateStringConverter getLocalDateStringConverter() {
return localDateStringConverter;
}
public LocalDateTimeStringConverter getLocalDateTimeStringConverter() {
return localDateTimeStringConverter;
}
public NumberStringConverter getNumberStringConverter() {
return numberStringConverter;
}
public CurrencyStringConverter getCurrencyStringConverter() {
return currencyStringConverter;
}
}

View File

@@ -1,10 +1,28 @@
package com.ecep.contract.manager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
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;
@@ -19,18 +37,6 @@ 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.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.*;
import java.util.logging.Level;
/**
* JavaFx 应用程序
@@ -122,6 +128,10 @@ 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) {
System.out.println("Desktop.startSpringApp");
// 更新窗口标题
@@ -158,7 +168,7 @@ public class Desktop extends Application {
holder.info("启动中,请稍后...");
CompletableFuture.runAsync(() -> {
runAsync(() -> {
try {
//
holder.info("读取配置文件...");
@@ -176,7 +186,7 @@ public class Desktop extends Application {
}
}
CompletableFuture.runAsync(() -> {
runAsync(() -> {
SpringApp.launch(properties, holder);
ConfigurableListableBeanFactory beanFactory = SpringApp.context.getBeanFactory();

View File

@@ -108,7 +108,7 @@ public class SpringApp {
CompletableFuture.runAsync(() -> {
// 在这里调用 startup 性能分析
analyzeStartupPerformance(startup);
});
}, Desktop.instance.getExecutorService());
}
/**

View File

@@ -12,6 +12,7 @@ import com.ecep.contract.manager.ds.company.repository.CompanyContactRepository;
import com.ecep.contract.manager.ds.company.repository.CompanyOldNameRepository;
import com.ecep.contract.manager.ds.contract.ContractFileType;
import com.ecep.contract.manager.ds.other.repository.SysConfRepository;
import com.ecep.contract.manager.ds.other.service.SysConfService;
import com.ecep.contract.manager.ds.company.repository.CompanyFileRepository;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.model.ContractFile;
@@ -73,7 +74,7 @@ public class OldVersionService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private SysConfRepository confRepository;
private SysConfService confService;
@Lazy
@Autowired
private CompanyService companyService;
@@ -215,7 +216,7 @@ public class OldVersionService {
logger.debug("createSyncTask");
}
boolean autoSyncEnable = confRepository.get("cloud.old.auto_sync.enable", false);
boolean autoSyncEnable = confService.getBoolean("cloud.old.auto_sync.enable");
if (!autoSyncEnable) {
return;
}

View File

@@ -370,7 +370,7 @@ public class CloudRkService implements ViewModelService<CloudRk, CloudRkInfoView
executorService.schedule(() -> {
// 定时 30分钟 运行一次
executorService.scheduleAtFixedRate(() -> {
Desktop.instance.getTaskMonitorCenter().registerTask(new CloudRkSyncTask()).submit();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(new CloudRkSyncTask());
}, 0, 30, TimeUnit.MINUTES);
}, 1, TimeUnit.MINUTES);
}

View File

@@ -1,14 +1,10 @@
package com.ecep.contract.manager.cloud.tyc;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.other.repository.SysConfRepository;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.util.MyStringUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,10 +16,16 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.other.service.SysConfService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.util.MyStringUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform;
@Lazy
@Service
@@ -53,7 +55,6 @@ public class CloudTycService implements ViewModelService<CloudTyc, CloudTycInfoV
@Autowired
private CloudTycRepository cloudTycRepository;
public CloudTyc getOrCreateCloudTyc(CloudInfo info) {
Optional<CloudTyc> optional = cloudTycRepository.findById(info.getId());
return optional.orElseGet(() -> getOrCreateCloudTyc(info.getCompany()));
@@ -98,7 +99,6 @@ public class CloudTycService implements ViewModelService<CloudTyc, CloudTycInfoV
return rk;
}
// 查询有 Score 的记录
List<CloudTyc> hasLatestList = hasCouldIdList.stream().filter(v -> {
return v.getScore() != null && v.getScore() > 0;
@@ -148,7 +148,7 @@ public class CloudTycService implements ViewModelService<CloudTyc, CloudTycInfoV
}
public void showInBrowse(String cloudId) {
String baseUrl = SpringApp.getBean(SysConfRepository.class).get("cloud.tyc.company");
String baseUrl = SpringApp.getBean(SysConfService.class).getString("cloud.tyc.company");
if (!StringUtils.hasText(baseUrl)) {
UITools.showAlertAndWait("系统参数 cloud.tyc.company 未配置");
return;
@@ -162,7 +162,6 @@ public class CloudTycService implements ViewModelService<CloudTyc, CloudTycInfoV
}
public void deleteByCompany(Company company) {
int deleted = cloudTycRepository.deleteAllByCompany(company);
if (deleted > 0) {
@@ -192,7 +191,6 @@ public class CloudTycService implements ViewModelService<CloudTyc, CloudTycInfoV
return cloudTycRepository.findAll(spec, pageable);
}
@Override
public Specification<CloudTyc> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {

View File

@@ -4,7 +4,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -12,14 +12,15 @@ import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.model.ContractGroup;
import com.ecep.contract.manager.ds.contract.service.ContractGroupService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import javafx.concurrent.Task;
import lombok.Setter;
/**
* 同步合同分组
*/
public class ContractGroupSyncTask extends Task<Object> {
public class ContractGroupSyncTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(ContractGroupSyncTask.class);
@Setter
private ContractGroupService contractGroupService;
@@ -36,29 +37,29 @@ public class ContractGroupSyncTask extends Task<Object> {
}
@Override
protected Object call() throws Exception {
YongYouU8Repository repository = null;
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Service service = null;
try {
repository = SpringApp.getBean(YongYouU8Repository.class);
service = SpringApp.getBean(YongYouU8Service.class);
} catch (BeansException e) {
logger.error("can't get bean of YongYouU8Repository", e);
logger.error("can't get bean of YongYouU8Service", e);
holder.error("can't get bean of YongYouU8Service");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
updateMessage("读取 U8 系统 CM_Group 数据表...");
List<Map<String, Object>> list = repository.queryAllContractGroup();
logger.info("读取 U8 系统 CM_Group 数据表...");
List<Map<String, Object>> list = service.queryAllContractGroup();
int size = list.size();
updateMessage("总共读取 CM_Group 数据 " + size + "");
holder.debug("总共读取 CM_Group 数据 " + size + "");
for (Map<String, Object> map : list) {
if (isCancelled()) {
updateMessage("Cancelled");
holder.info("Cancelled");
return null;
}
sync(map, msg -> {
updateMessage(counter.get() + "/" + size + ">" + msg);
});
MessageHolder sub = holder.sub(counter.get() + "/" + size + ">" );
sync(map, sub);
// 更新进度
updateProgress(counter.incrementAndGet(), size);
}
@@ -66,7 +67,7 @@ public class ContractGroupSyncTask extends Task<Object> {
return null;
}
private void sync(Map<String, Object> map, Consumer<String> consumer) {
private void sync(Map<String, Object> map, MessageHolder holder) {
boolean modified = false;
String groupCode = (String) map.get("cGroupID");
@@ -77,26 +78,28 @@ public class ContractGroupSyncTask extends Task<Object> {
ContractGroup contractGroup = service.findByCode(groupCode);
if (contractGroup == null) {
contractGroup = service.newContractGroup();
consumer.accept("新建合同分组:" + groupCode);
holder.info("新建合同分组:" + groupCode);
modified = true;
}
if (!Objects.equals(contractGroup.getCode(), groupCode)) {
contractGroup.setCode(groupCode);
holder.info("合同分组代码:" + contractGroup.getCode() + " -> " + groupCode);
modified = true;
}
if (!Objects.equals(contractGroup.getName(), groupName)) {
contractGroup.setName(groupName);
holder.info("合同分组名称:" + contractGroup.getName() + " -> " + groupName);
modified = true;
}
if (!Objects.equals(contractGroup.getTitle(), groupTitle)) {
contractGroup.setTitle(groupTitle);
holder.info("合同分组标题:" + contractGroup.getTitle() + " -> " + groupTitle);
modified = true;
}
if (modified) {
service.save(contractGroup);
consumer.accept("更新" + contractGroup.getName() + " 信息");
}
}
}

View File

@@ -1,25 +1,26 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.model.ContractKind;
import com.ecep.contract.manager.ds.contract.service.ContractKindService;
import javafx.concurrent.Task;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.model.ContractKind;
import com.ecep.contract.manager.ds.contract.service.ContractKindService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import lombok.Setter;
/**
* 同步合同分类
*/
public class ContractKindSyncTask extends Task<Object> {
public class ContractKindSyncTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(ContractKindSyncTask.class);
@Setter
private ContractKindService contractKindService;
@@ -36,29 +37,29 @@ public class ContractKindSyncTask extends Task<Object> {
}
@Override
protected Object call() throws Exception {
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Repository repository = null;
try {
repository = SpringApp.getBean(YongYouU8Repository.class);
} catch (BeansException e) {
logger.error("can't get bean of YongYouU8Repository", e);
holder.error("can't get bean of YongYouU8Repository");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
updateMessage("读取 U8 系统 CM_Kind 数据表...");
logger.info("读取 U8 系统 CM_Kind 数据表...");
List<Map<String, Object>> list = repository.queryAllContractKind();
int size = list.size();
updateMessage("总共读取 CM_Kind 数据 " + size + "");
holder.debug("总共读取 CM_Kind 数据 " + size + "");
for (Map<String, Object> map : list) {
if (isCancelled()) {
updateMessage("Cancelled");
holder.info("Cancelled");
return null;
}
sync(map, msg -> {
updateMessage(counter.get() + "/" + size + ">" + msg);
});
MessageHolder sub = holder.sub(counter.get() + "/" + size + ">");
sync(map, sub);
// 更新进度
updateProgress(counter.incrementAndGet(), size);
}
@@ -66,7 +67,7 @@ public class ContractKindSyncTask extends Task<Object> {
return null;
}
private void sync(Map<String, Object> map, Consumer<String> consumer) {
private void sync(Map<String, Object> map, MessageHolder holder) {
boolean modified = false;
String typeCode = (String) map.get("KindID");
@@ -77,26 +78,29 @@ public class ContractKindSyncTask extends Task<Object> {
ContractKind contractKind = kindService.findByCode(typeCode);
if (contractKind == null) {
contractKind = new ContractKind();
consumer.accept("新建合同分类:" + typeCode);
holder.info("新建合同分类:" + typeCode);
modified = true;
}
if (!Objects.equals(contractKind.getCode(), typeCode)) {
contractKind.setCode(typeCode);
holder.info("合同分类代码:" + contractKind.getCode() + " -> " + typeCode);
modified = true;
}
if (!Objects.equals(contractKind.getName(), typeName)) {
contractKind.setName(typeName);
holder.info("合同分类名称:" + contractKind.getName() + " -> " + typeName);
modified = true;
}
if (!Objects.equals(contractKind.getTitle(), typeTitle)) {
contractKind.setTitle(typeTitle);
holder.info("合同分类描述:" + contractKind.getTitle() + " -> " + typeTitle);
modified = true;
}
if (modified) {
kindService.save(contractKind);
consumer.accept("更新" + contractKind.getName() + " 信息");
}
}
}

View File

@@ -1,14 +1,5 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.contract.tasker.AbstContractRepairTasker;
import com.ecep.contract.manager.ui.MessageHolder;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Map;
@@ -16,21 +7,45 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.util.StringUtils;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.manager.ds.contract.tasker.AbstContractRepairTasker;
import com.ecep.contract.manager.ui.MessageHolder;
/**
* 合同同步任务
*/
public class ContractSyncTask extends AbstContractRepairTasker {
private static final Logger logger = LoggerFactory.getLogger(ContractSyncTask.class);
@Setter
private boolean useLatestId = true;
private YongYouU8Repository repository;
public ContractSyncTask() {
updateTitle("用友U8系统-同步合同");
}
@Override
protected void repair(MessageHolder holder) {
updateTitle("用友U8系统-同步合同");
try {
repository = SpringApp.getBean(YongYouU8Repository.class);
} catch (BeansException e) {
holder.error("无法获取 YongYouU8Repository " + e.getMessage());
return;
}
if (getConfService().getBoolean(ContractCtx.KEY_SYNC_USE_LATEST_ID)) {
syncByLatestId(holder);
} else {
syncByLatestDate(holder);
}
}
private void syncByLatestId(MessageHolder holder) {
int latestId = getConfService().getInt(ContractService.CONTRACT_LATEST_ID);
int latestId = getConfService().getInt(ContractCtx.KEY_SYNC_BY_LATEST_ID);
updateTitle("用友U8系统-同步合同,从 " + latestId + " 开始");
Long total = repository.countAllContracts(latestId);
@@ -66,11 +81,11 @@ public class ContractSyncTask extends AbstContractRepairTasker {
updateProgress(counter.incrementAndGet(), total);
});
}
getConfService().set(ContractService.CONTRACT_LATEST_ID, String.valueOf(reference.get()));
getConfService().set(ContractCtx.KEY_SYNC_BY_LATEST_ID, String.valueOf(reference.get()));
}
private void syncByLatestDate(MessageHolder holder) {
String strDateTime = getConfService().getString(ContractService.CONTRACT_LATEST_DATE);
String strDateTime = getConfService().getString(ContractCtx.KEY_SYNC_BY_LATEST_DATE);
LocalDateTime latestDateTime = null;
if (StringUtils.hasText(strDateTime)) {
try {
@@ -120,17 +135,7 @@ public class ContractSyncTask extends AbstContractRepairTasker {
updateProgress(counter.incrementAndGet(), total);
});
}
getConfService().set(ContractService.CONTRACT_LATEST_DATE, String.valueOf(reference.get()));
getConfService().set(ContractCtx.KEY_SYNC_BY_LATEST_DATE, String.valueOf(reference.get()));
}
@Override
protected void repair(MessageHolder holder) {
updateTitle("用友U8系统-同步合同");
repository = SpringApp.getBean(YongYouU8Repository.class);
if (useLatestId) {
syncByLatestId(holder);
} else {
syncByLatestDate(holder);
}
}
}

View File

@@ -1,25 +1,26 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.model.ContractType;
import com.ecep.contract.manager.ds.contract.service.ContractTypeService;
import javafx.concurrent.Task;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.contract.model.ContractType;
import com.ecep.contract.manager.ds.contract.service.ContractTypeService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import lombok.Setter;
/**
* 同步合同类型
*/
public class ContractTypeSyncTask extends Task<Object> {
public class ContractTypeSyncTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(ContractTypeSyncTask.class);
@Setter
private ContractTypeService contractTypeService;
@@ -36,29 +37,29 @@ public class ContractTypeSyncTask extends Task<Object> {
}
@Override
protected Object call() throws Exception {
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Repository repository = null;
try {
repository = SpringApp.getBean(YongYouU8Repository.class);
} catch (BeansException e) {
logger.error("can't get bean of YongYouU8Repository", e);
logger.error("can't get bean of YongYouU8Service", e);
holder.error("can't get bean of YongYouU8Service");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
updateMessage("读取 U8 系统 CM_Type,CM_TypeClass 数据表...");
logger.info("读取 U8 系统 CM_Type,CM_TypeClass 数据表...");
List<Map<String, Object>> list = repository.queryAllContractType();
int size = list.size();
updateMessage("总共读取 CM_Type,CM_TypeClass 数据 " + size + "");
holder.debug("总共读取 CM_Type,CM_TypeClass 数据 " + size + "");
for (Map<String, Object> map : list) {
if (isCancelled()) {
updateMessage("Cancelled");
holder.info("Cancelled");
return null;
}
sync(map, msg -> {
updateMessage(counter.get() + "/" + size + ">" + msg);
});
MessageHolder sub = holder.sub(counter.get() + "/" + size + ">" );
sync(map, sub);
// 更新进度
updateProgress(counter.incrementAndGet(), size);
}
@@ -66,7 +67,7 @@ public class ContractTypeSyncTask extends Task<Object> {
return null;
}
private void sync(Map<String, Object> map, Consumer<String> consumer) {
private void sync(Map<String, Object> map, MessageHolder holder) {
boolean modified = false;
String typeCode = (String) map.get("cTypeCode");
@@ -79,34 +80,39 @@ public class ContractTypeSyncTask extends Task<Object> {
ContractType contractType = typeService.findByCode(typeCode);
if (contractType == null) {
contractType = new ContractType();
consumer.accept("新建合同类型:" + typeCode);
holder.info("新建合同类型:" + typeCode);
modified = true;
}
if (!Objects.equals(contractType.getCode(), typeCode)) {
contractType.setCode(typeCode);
holder.info("合同类型代码:" + contractType.getCode() + " -> " + typeCode);
modified = true;
}
if (!Objects.equals(contractType.getName(), typeName)) {
contractType.setName(typeName);
holder.info("合同类型名称:" + contractType.getName() + " -> " + typeName);
modified = true;
}
if (!Objects.equals(contractType.getTitle(), typeTitle)) {
contractType.setTitle(typeTitle);
holder.info("合同类型标题:" + contractType.getTitle() + " -> " + typeTitle);
modified = true;
}
if (!Objects.equals(contractType.getCatalog(), typeCatalog)) {
contractType.setTitle(typeTitle);
contractType.setCatalog(typeCatalog);
holder.info("合同类型分类:" + contractType.getCatalog() + " -> " + typeCatalog);
modified = true;
}
if (!Objects.equals(contractType.getDirection(), typeDirection)) {
contractType.setTitle(typeTitle);
contractType.setDirection(typeDirection);
holder.info("合同类型方向:" + contractType.getDirection() + " -> " + typeDirection);
modified = true;
}
if (modified) {
typeService.save(contractType);
consumer.accept("更新" + contractType.getName() + " 信息");
}
}
}

View File

@@ -1,70 +1,99 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.customer.model.CustomerCatalog;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import javafx.concurrent.Task;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.customer.model.CustomerCatalog;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import lombok.Setter;
/**
* 同步客户分类
*/
public class CustomerClassSyncTask extends Task<Object> {
public class CustomerClassSyncTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(CustomerClassSyncTask.class);
@Setter
private CompanyCustomerService companyCustomerService;
public CustomerClassSyncTask() {
updateTitle("用友U8系统-同步客户分类信息");
}
CompanyCustomerService getCompanyCustomerService() {
if (companyCustomerService == null) {
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
}
return companyCustomerService;
}
@Override
protected Object call() throws Exception {
YongYouU8Service service = SpringApp.getBean(YongYouU8Service.class);
CompanyCustomerService customerService = SpringApp.getBean(CompanyCustomerService.class);
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Repository repository = null;
try {
repository = SpringApp.getBean(YongYouU8Repository.class);
} catch (BeansException e) {
logger.error("can't get bean of YongYouU8Repository", e);
holder.error("can't get bean of YongYouU8Repository");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
updateMessage("读取 U8 系统 CustomerClass 数据表...");
List<Map<String, Object>> list = service.queryAllCustomerClass();
logger.info("读取 U8 系统 CustomerClass 数据表...");
List<Map<String, Object>> list = repository.queryAllCustomerClass();
int size = list.size();
updateMessage("总共读取 CustomerClass 数据 " + size + "");
holder.debug("总共读取 CustomerClass 数据 " + size + "");
for (Map<String, Object> map : list) {
if (isCancelled()) {
updateMessage("Cancelled");
holder.info("Cancelled");
return null;
}
boolean modified = false;
String code = (String) map.get("cCCCode");
String name = (String) map.get("cCCName");
CustomerCatalog customerCatalog = customerService.findCatalogByCode(code);
if (customerCatalog == null) {
customerCatalog = new CustomerCatalog();
updateMessage("新建客户分类:" + code);
modified = true;
}
if (!Objects.equals(customerCatalog.getCode(), code)) {
customerCatalog.setCode(code);
modified = true;
}
if (!Objects.equals(customerCatalog.getName(), name)) {
customerCatalog.setName(name);
modified = true;
}
if (modified) {
customerService.save(customerCatalog);
updateMessage("更新" + customerCatalog.getName() + " 信息");
}
MessageHolder sub = holder.sub(counter.get() + "/" + size + ">" );
sync(map, sub);
// 更新进度
updateProgress(counter.incrementAndGet(), size);
}
return null;
}
private void sync(Map<String, Object> map, MessageHolder holder) {
boolean modified = false;
String code = (String) map.get("cCCCode");
String name = (String) map.get("cCCName");
CompanyCustomerService customerService = getCompanyCustomerService();
CustomerCatalog customerCatalog = customerService.findCatalogByCode(code);
if (customerCatalog == null) {
customerCatalog = new CustomerCatalog();
holder.info("新建客户分类:" + code);
modified = true;
}
if (!Objects.equals(customerCatalog.getCode(), code)) {
customerCatalog.setCode(code);
holder.info("客户分类代码:" + customerCatalog.getCode() + " -> " + code);
modified = true;
}
if (!Objects.equals(customerCatalog.getName(), name)) {
customerCatalog.setName(name);
holder.info("客户分类名称:" + customerCatalog.getName() + " -> " + name);
modified = true;
}
if (modified) {
customerService.save(customerCatalog);
}
}
}

View File

@@ -0,0 +1,113 @@
package com.ecep.contract.manager.cloud.u8;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.CloudBaseInfo;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import lombok.Setter;
/**
* 数据迁移任务从CloudInfo迁移到CloudYu
*/
public class DateTransferTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(DateTransferTask.class);
@Setter
private YongYouU8Service yongYouU8Service;
public DateTransferTask() {
updateTitle("用友U8系统-数据迁移任务");
}
YongYouU8Service getYongYouU8Service() {
if (yongYouU8Service == null) {
yongYouU8Service = SpringApp.getBean(YongYouU8Service.class);
}
return yongYouU8Service;
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Service u8Service = null;
try {
u8Service = getYongYouU8Service();
} catch (BeansException e) {
logger.error("获取用友U8服务失败", e);
holder.error("获取用友U8服务失败");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
logger.info("开始数据迁移任务...");
Iterable<CloudInfo> cloudInfos = u8Service.findAllCloudYu();
// 计算总数
int total = 0;
for (@SuppressWarnings("unused")
CloudInfo info : cloudInfos) {
total++;
}
holder.debug("总共需要迁移的数据量: " + total + "");
// 重新获取Iterable以进行遍历
cloudInfos = u8Service.findAllCloudYu();
for (CloudInfo v : cloudInfos) {
if (isCancelled()) {
holder.info("迁移任务已取消");
return null;
}
MessageHolder sub = holder.sub(counter.get() + "/" + total + ">");
try {
sync(v, sub);
} catch (Exception e) {
sub.error("迁移失败: " + e.getMessage());
logger.error("迁移数据失败", e);
}
// 更新进度
updateProgress(counter.incrementAndGet(), total);
}
holder.info("数据迁移任务完成");
return null;
}
private void sync(CloudInfo v, MessageHolder holder) {
YongYouU8Service u8Service = getYongYouU8Service();
CloudYu cloudYu = u8Service.getOrCreateCloudYu(v);
if (copyTo(v, cloudYu)) {
u8Service.save(cloudYu);
holder.info("成功迁移数据: " + v.getCloudId());
} else {
holder.debug("数据未变更,无需迁移: " + v.getCloudId());
}
}
boolean copyTo(CloudInfo v, CloudBaseInfo cloudRk) {
boolean modified = false;
if (!Objects.equals(cloudRk.getLatestUpdate(), v.getLatestUpdate())) {
cloudRk.setLatestUpdate(v.getLatestUpdate());
modified = true;
}
if (!Objects.equals(cloudRk.getCloudId(), v.getCloudId())) {
cloudRk.setCloudId(v.getCloudId());
modified = true;
}
if (!Objects.equals(cloudRk.getCompany(), v.getCompany())) {
cloudRk.setCompany(v.getCompany());
modified = true;
}
return modified;
}
}

View File

@@ -1,70 +1,99 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.vendor.model.VendorCatalog;
import com.ecep.contract.manager.ds.vendor.repository.VendorClassRepository;
import javafx.concurrent.Task;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.vendor.model.VendorCatalog;
import com.ecep.contract.manager.ds.vendor.repository.VendorClassRepository;
import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.ui.Tasker;
import lombok.Setter;
/**
* 同步供应商分类
*/
public class VendorClassSyncTask extends Task<Object> {
public class VendorClassSyncTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(VendorClassSyncTask.class);
@Setter
private VendorClassRepository vendorClassRepository;
public VendorClassSyncTask() {
updateTitle("用友U8系统-同步供应商分类信息");
}
VendorClassRepository getVendorClassRepository() {
if (vendorClassRepository == null) {
vendorClassRepository = SpringApp.getBean(VendorClassRepository.class);
}
return vendorClassRepository;
}
@Override
protected Object call() throws Exception {
YongYouU8Service service = SpringApp.getBean(YongYouU8Service.class);
VendorClassRepository groupRepository = SpringApp.getBean(VendorClassRepository.class);
protected Object execute(MessageHolder holder) throws Exception {
YongYouU8Service service = null;
try {
service = SpringApp.getBean(YongYouU8Service.class);
} catch (BeansException e) {
logger.error("can't get bean of YongYouU8Service", e);
holder.error("can't get bean of YongYouU8Service");
return null;
}
AtomicInteger counter = new AtomicInteger(0);
updateMessage("读取 U8 系统 VendorClass 数据表...");
logger.info("读取 U8 系统 VendorClass 数据表...");
List<Map<String, Object>> list = service.queryAllVendorClass();
int size = list.size();
updateMessage("总共读取 VendorClass 数据 " + size + "");
holder.debug("总共读取 VendorClass 数据 " + size + "");
for (Map<String, Object> map : list) {
if (isCancelled()) {
updateMessage("Cancelled");
holder.info("Cancelled");
return null;
}
boolean modified = false;
String code = (String) map.get("cVCCode");
String name = (String) map.get("cVCName");
VendorCatalog vendorCatalog = groupRepository.findByCode(code).orElse(null);
if (vendorCatalog == null) {
vendorCatalog = new VendorCatalog();
updateMessage("新建供应商分类:" + code);
modified = true;
}
if (!Objects.equals(vendorCatalog.getCode(), code)) {
vendorCatalog.setCode(code);
modified = true;
}
if (!Objects.equals(vendorCatalog.getName(), name)) {
vendorCatalog.setName(name);
modified = true;
}
if (modified) {
groupRepository.save(vendorCatalog);
updateMessage("更新" + vendorCatalog.getName() + " 信息");
}
MessageHolder sub = holder.sub(counter.get() + "/" + size + ">" );
sync(map, sub);
// 更新进度
updateProgress(counter.incrementAndGet(), size);
}
return null;
}
private void sync(Map<String, Object> map, MessageHolder holder) {
boolean modified = false;
String code = (String) map.get("cVCCode");
String name = (String) map.get("cVCName");
VendorClassRepository repository = getVendorClassRepository();
VendorCatalog vendorCatalog = repository.findByCode(code).orElse(null);
if (vendorCatalog == null) {
vendorCatalog = new VendorCatalog();
holder.info("新建供应商分类:" + code);
modified = true;
}
if (!Objects.equals(vendorCatalog.getCode(), code)) {
vendorCatalog.setCode(code);
holder.info("供应商分类代码:" + vendorCatalog.getCode() + " -> " + code);
modified = true;
}
if (!Objects.equals(vendorCatalog.getName(), name)) {
vendorCatalog.setName(name);
holder.info("供应商分类名称:" + vendorCatalog.getName() + " -> " + name);
modified = true;
}
if (modified) {
repository.save(vendorCatalog);
}
}
}

View File

@@ -0,0 +1,71 @@
package com.ecep.contract.manager.cloud.u8;
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.manager.cloud.u8.ctx.CompanyCtx;
import com.ecep.contract.manager.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.manager.ds.other.BooleanConfig;
import com.ecep.contract.manager.ds.other.LocalDateConfig;
import com.ecep.contract.manager.ds.other.LocalDateTimeConfig;
import com.ecep.contract.manager.ds.other.StringConfig;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.FxmlPath;
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(CompanyCtx.AUTO_CREATE_COMPANY_AFTER);
LocalDateTimeConfig config2 = new LocalDateTimeConfig(ContractCtx.KEY_SYNC_BY_LATEST_DATE);
StringConfig config3 = new StringConfig(ContractCtx.KEY_SYNC_BY_LATEST_ID);
StringConfig config4 = new StringConfig(ContractCtx.KEY_SYNC_ELAPSE);
BooleanConfig config5 = new BooleanConfig(ContractCtx.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

@@ -1,30 +1,23 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.CloudBaseInfo;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.Message;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.util.UITools;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ui.AbstManagerWindowController;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.UITools;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
@Lazy
@Scope("prototype")
@@ -38,9 +31,6 @@ public class YongYouU8ManagerWindowController
show(YongYouU8ManagerWindowController.class, null);
}
@Autowired
private ScheduledExecutorService scheduledExecutorService;
public TableColumn<CloudYuInfoViewModel, Number> idColumn;
public TableColumn<CloudYuInfoViewModel, LocalDateTime> latestUpdateColumn;
public TableColumn<CloudYuInfoViewModel, Company> companyColumn;
@@ -58,73 +48,25 @@ public class YongYouU8ManagerWindowController
return new YongYouU8ManagerSkin(this);
}
@Override
public void show(Stage stage) {
super.show(stage);
getTitle().set("数据源:用友U8");
getTitle().set("用友U8");
}
/**
* 打开配置窗口
*/
public void onConfigAction(ActionEvent event) {
BaseController.show(YongYouU8ConfigWindowController.class, null);
}
/**
* 数据迁移,从 CloudInfo 迁移到 CloudRk
*/
public void onDateTransferAction(ActionEvent event) {
CompletableFuture.runAsync(() -> {
com.ecep.contract.manager.cloud.CloudInfoRepository cloudInfoRepository = SpringApp.getBean(com.ecep.contract.manager.cloud.CloudInfoRepository.class);
YongYouU8Service u8Service = SpringApp.getBean(YongYouU8Service.class);
cloudInfoRepository.findAll().forEach(v -> {
try {
CloudYu cloudYu = u8Service.getOrCreateCloudYu(v);
if (copyTo(v, cloudYu)) {
u8Service.save(cloudYu);
}
} catch (Exception e) {
setStatus(e.getMessage());
e.printStackTrace();
}
});
});
}
boolean copyTo(CloudInfo v, CloudBaseInfo cloudRk) {
boolean modified = false;
if (!Objects.equals(cloudRk.getLatestUpdate(), v.getLatestUpdate())) {
cloudRk.setLatestUpdate(v.getLatestUpdate());
modified = true;
}
if (!Objects.equals(cloudRk.getCloudId(), v.getCloudId())) {
cloudRk.setCloudId(v.getCloudId());
modified = true;
}
if (!Objects.equals(cloudRk.getCompany(), v.getCompany())) {
cloudRk.setCompany(v.getCompany());
modified = true;
}
return modified;
}
private <T extends Task<Object>> void initializeTask(T task, String prefix, Consumer<String> consumer) {
task.setOnScheduled(e -> {
consumer.accept("正在从用友U8同步" + prefix + ",请稍后...");
});
task.setOnRunning(e -> {
consumer.accept("开始同步" + prefix + "...");
});
task.setOnSucceeded(e -> {
consumer.accept(prefix + "同步完成...");
});
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
consumer.accept(newValue.getMessage());
logger.error("同步{}发生异常", prefix, newValue);
});
scheduledExecutorService.submit(task);
consumer.accept("同步任务已创建...");
DateTransferTask task = new DateTransferTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onPersonSyncAction(ActionEvent event) {
@@ -153,39 +95,28 @@ public class YongYouU8ManagerWindowController
}
public void onContractGroupSyncAction(ActionEvent event) {
Task<Object> task = new ContractGroupSyncTask();
UITools.showTaskDialogAndWait("合同分组数据同步", task, consumer -> {
initializeTask(task, "合同分组数据", msg -> consumer.accept(Message.info(msg)));
});
ContractGroupSyncTask task = new ContractGroupSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractTypeSyncAction(ActionEvent event) {
Task<Object> task = new ContractTypeSyncTask();
UITools.showTaskDialogAndWait("合同分类数据同步", task, consumer -> {
initializeTask(task, "合同分类数据", msg -> consumer.accept(Message.info(msg)));
});
ContractTypeSyncTask task = new ContractTypeSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onContractKindSyncAction(ActionEvent event) {
Task<Object> task = new ContractKindSyncTask();
UITools.showTaskDialogAndWait("合同类型数据同步", task, consumer -> {
initializeTask(task, "合同类型数据", msg -> consumer.accept(Message.info(msg)));
});
ContractKindSyncTask task = new ContractKindSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onVendorClassSyncAction(ActionEvent event) {
Task<Object> task = new VendorClassSyncTask();
UITools.showTaskDialogAndWait("供应商分类数据同步", task, consumer -> {
initializeTask(task, "供应商分类数据", msg -> consumer.accept(Message.info(msg)));
});
VendorClassSyncTask task = new VendorClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
public void onCustomerClassSyncAction(ActionEvent event) {
Task<Object> task = new CustomerClassSyncTask();
UITools.showTaskDialogAndWait("客户分类数据同步", task, consumer -> {
initializeTask(task, "客户分类数据", msg -> consumer.accept(Message.info(msg)));
});
CustomerClassSyncTask task = new CustomerClassSyncTask();
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
}
}

View File

@@ -1,18 +1,12 @@
package com.ecep.contract.manager.cloud.u8;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.cloud.u8.ctx.AbstractYongYouU8Ctx;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.ui.task.MonitoredTask;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javafx.application.Platform;
import javafx.concurrent.Task;
import org.controlsfx.control.TaskProgressView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,12 +19,19 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.cloud.CloudInfo;
import com.ecep.contract.manager.cloud.CloudInfoRepository;
import com.ecep.contract.manager.cloud.u8.ctx.AbstractYongYouU8Ctx;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.other.service.EmployeeService;
import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ui.ViewModelService;
import com.ecep.contract.manager.ui.task.MonitoredTask;
import javafx.concurrent.Task;
@Lazy
@Service
@@ -39,9 +40,17 @@ public class YongYouU8Service implements ViewModelService<CloudYu, CloudYuInfoVi
private static final Logger logger = LoggerFactory.getLogger(YongYouU8Service.class);
public static final String NAME = "用友U8";
public static final String KEY_HOST_IP = "u8.db.server.ip";
public static final String KEY_DATABASE = "u8.db.database";
public static final String KEY_USER_NAME = "u8.db.server.name";
public static final String KEY_PASSWORD = "u8.db.server.password";
@Lazy
@Autowired
private YongYouU8Repository repository;
@Lazy
@Autowired
private CloudInfoRepository cloudInfoRepository;
@Lazy
@Autowired
private CompanyService companyService;
@@ -230,4 +239,8 @@ public class YongYouU8Service implements ViewModelService<CloudYu, CloudYuInfoVi
ctx.setCompanyVendorService(companyVendorService);
ctx.setCompanyCustomerService(companyCustomerService);
}
public Iterable<CloudInfo> findAllCloudYu() {
return cloudInfoRepository.findAll();
}
}

View File

@@ -1,23 +1,26 @@
package com.ecep.contract.manager.cloud.u8.ctx;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.cloud.u8.YongYouU8Service;
import com.ecep.contract.manager.ds.company.model.Company;
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.other.service.SysConfService;
import com.ecep.contract.manager.ui.MessageHolder;
import lombok.Setter;
import org.springframework.util.StringUtils;
import static com.ecep.contract.manager.SpringApp.getBean;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import static com.ecep.contract.manager.SpringApp.getBean;
import org.springframework.util.StringUtils;
import com.ecep.contract.manager.cloud.u8.YongYouU8Service;
import com.ecep.contract.manager.ds.company.model.Company;
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.ui.MessageHolder;
import lombok.Setter;
public class CompanyCtx extends AbstractYongYouU8Ctx {
/**
* 自动创建公司的时间
*/
public static final String AUTO_CREATE_COMPANY_AFTER = "cloud.u8.auto-create-company-after";
@Setter
@@ -111,7 +114,7 @@ public class CompanyCtx extends AbstractYongYouU8Ctx {
return null;
}
CompanyService companyService = getCompanyService();
String autoCreateAfter = SpringApp.getBean(SysConfService.class).getString(AUTO_CREATE_COMPANY_AFTER);
String autoCreateAfter = getConfService().getString(AUTO_CREATE_COMPANY_AFTER);
// 当配置存在,且开发时间小于指定时间,不创建
if (StringUtils.hasText(autoCreateAfter)) {
LocalDate miniDate = LocalDate.parse(autoCreateAfter);

View File

@@ -1,11 +1,42 @@
package com.ecep.contract.manager.cloud.u8.ctx;
import static com.ecep.contract.manager.SpringApp.getBean;
import java.io.File;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import com.ecep.contract.manager.cloud.u8.YongYouU8Service;
import com.ecep.contract.manager.ds.company.CompanyFileUtils;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.contract.ContractFileType;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.model.*;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.model.ContractCatalog;
import com.ecep.contract.manager.ds.contract.model.ContractFile;
import com.ecep.contract.manager.ds.contract.model.ContractFileTypeLocal;
import com.ecep.contract.manager.ds.contract.model.ContractGroup;
import com.ecep.contract.manager.ds.contract.model.ContractItem;
import com.ecep.contract.manager.ds.contract.model.ContractKind;
import com.ecep.contract.manager.ds.contract.model.ContractPayPlan;
import com.ecep.contract.manager.ds.contract.model.ContractType;
import com.ecep.contract.manager.ds.contract.service.ContractFileService;
import com.ecep.contract.manager.ds.contract.service.ContractItemService;
import com.ecep.contract.manager.ds.contract.service.ContractPayPlanService;
@@ -27,24 +58,31 @@ import com.ecep.contract.manager.ui.MessageHolder;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.NumberUtils;
import com.ecep.contract.manager.util.TaxRateUtils;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.ecep.contract.manager.SpringApp.getBean;
/**
* 合同上下文
*/
public class ContractCtx extends AbstractYongYouU8Ctx {
public static final String KEY_PREFIX = "cloud.u8.contract.";
/**
* 合同同步后的最后一个日期
*/
public static final String KEY_SYNC_BY_LATEST_DATE = KEY_PREFIX + "latestDate";
/**
* 合同同步后的最后一个合同ID
*/
public static final String KEY_SYNC_BY_LATEST_ID = KEY_PREFIX + "latestId";
/**
* 合同同步时是否使用最后更新的Id来判断更新范围否则使用最后更新的合同日期来判断更新范围
*/
public static final String KEY_SYNC_USE_LATEST_ID = KEY_PREFIX + "sync.use-latest-id";
/**
* 合同同步时的时间间隔,单位:秒
*/
public static final String KEY_SYNC_ELAPSE = KEY_PREFIX + "sync.elapse";
@Setter
private ContractService contractService;
@@ -66,21 +104,18 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
private InventoryCtx inventoryCtx;
@Setter
private ProjectCtx projectCtx;
@Getter
@Setter
private int vendorEntityUpdateDelayDays = 1;
@Getter
@Setter
private int customerEntityUpdateDelayDays = 1;
public ContractService getContractService() {
if (contractService == null) {
contractService = getBean(ContractService.class);
}
return contractService;
}
ContractItemService getContractItemService() {
if (contractItemService == null) {
contractItemService = getBean(ContractItemService.class);
@@ -656,7 +691,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
contract.setGuid(guid);
contract.setCode(contractId);
contract = service.save(contract);
holder.info("新建合同:" + contractId+", GUID: "+guid);
holder.info("新建合同:" + contractId + ", GUID: " + guid);
}
}
return contract;

View File

@@ -78,7 +78,11 @@ public enum ContractFileType {
/**
* 重大项目决策记录单
*/
CriticalProjectDecisionRecord(true, true)
CriticalProjectDecisionRecord(true, true),
/**
* 顾客满意度调查表
*/
CustomerSatisfactionSurvey(true, false),
;
final boolean supportCustomer;

View File

@@ -1,5 +1,18 @@
package com.ecep.contract.manager.ds.contract.controller;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import org.controlsfx.control.textfield.TextFields;
import org.controlsfx.glyphfont.Glyph;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.company.CompanyFileUtils;
import com.ecep.contract.manager.ds.company.CompanyStringConverter;
@@ -26,8 +39,8 @@ import com.ecep.contract.manager.ds.vendor.model.CompanyVendor;
import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ds.vendor.service.VendorGroupService;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
@@ -36,34 +49,13 @@ import javafx.scene.control.TextInputDialog;
import javafx.stage.DirectoryChooser;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
import org.controlsfx.control.textfield.TextFields;
import org.controlsfx.glyphfont.Glyph;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
*
* 合同基础信息标签页
*/
public class ContractTabSkinBase
extends AbstContractBasedTabSkin
implements TabSkin {
@Setter
private CompanyCustomerService companyCustomerService;
@Setter
private SaleTypeService saleTypeService;
@Setter
private ExtendVendorInfoService extendVendorInfoService;
@Setter
private VendorGroupService vendorGroupService;
public class ContractTabSkinBase extends AbstContractBasedTabSkin {
public ContractTabSkinBase(ContractWindowController controller) {
super(controller);
@@ -88,7 +80,8 @@ public class ContractTabSkinBase
return v == null ? "关联" : "打开";
}));
Glyph linkContractProjectBtnIcon = new Glyph("FontAwesome", viewModel.getProject().get() == null ? "UNLINK" : "LINK");
Glyph linkContractProjectBtnIcon = new Glyph("FontAwesome",
viewModel.getProject().get() == null ? "UNLINK" : "LINK");
controller.linkContractProjectBtn.graphicProperty().set(linkContractProjectBtnIcon);
linkContractProjectBtnIcon.iconProperty().bind(viewModel.getProject().map(v -> {
return v == null ? "UNLINK" : "LINK";
@@ -124,7 +117,8 @@ public class ContractTabSkinBase
if (!Hibernate.isInitialized(type)) {
type = getContractService().findTypeById(type.getId());
}
return type.getCode() + " " + type.getCatalog() + " " + type.getName() + " " + type.getTitle() + "(" + type.getDirection() + ")";
return type.getCode() + " " + type.getCatalog() + " " + type.getName() + " " + type.getTitle() + "("
+ type.getDirection() + ")";
}));
controller.kindField.textProperty().bind(viewModel.getKind().map(kind -> {
@@ -150,8 +144,10 @@ public class ContractTabSkinBase
}
return !Objects.equals(type.getDirection(), "");
}, viewModel.getType()));
controller.openRelativeCompanyCustomerBtn.managedProperty().bind(controller.openRelativeCompanyCustomerBtn.disabledProperty().not());
controller.openRelativeCompanyCustomerBtn.visibleProperty().bind(controller.openRelativeCompanyCustomerBtn.managedProperty());
controller.openRelativeCompanyCustomerBtn.managedProperty()
.bind(controller.openRelativeCompanyCustomerBtn.disabledProperty().not());
controller.openRelativeCompanyCustomerBtn.visibleProperty()
.bind(controller.openRelativeCompanyCustomerBtn.managedProperty());
// as vendor
controller.openRelativeCompanyVendorBtn.setOnAction(this::onContractOpenRelativeCompanyVendorAction);
@@ -169,10 +165,13 @@ public class ContractTabSkinBase
}
return !Objects.equals(type.getDirection(), "");
}, viewModel.getType()));
controller.openRelativeCompanyVendorBtn.managedProperty().bind(controller.openRelativeCompanyVendorBtn.disabledProperty().not());
controller.openRelativeCompanyVendorBtn.visibleProperty().bind(controller.openRelativeCompanyVendorBtn.managedProperty());
controller.openRelativeCompanyVendorBtn.managedProperty()
.bind(controller.openRelativeCompanyVendorBtn.disabledProperty().not());
controller.openRelativeCompanyVendorBtn.visibleProperty()
.bind(controller.openRelativeCompanyVendorBtn.managedProperty());
LocalDateStringConverter localDateStringConverter = controller.localDateStringConverter;
LocalDateStringConverter localDateStringConverter = controller.getCurrentEmployee()
.getLocalDateStringConverter();
controller.setupDateField.setConverter(localDateStringConverter);
controller.setupDateField.valueProperty().bindBidirectional(viewModel.getSetupDate());
@@ -202,21 +201,23 @@ public class ContractTabSkinBase
initializeBaseTabProjectFieldAutoCompletion(controller.projectField);
controller.pathField.textProperty().bind(viewModel.getPath());
// controller.createdField.textProperty().bind(viewModel.getCreated().map(MyDateTimeUtils::format));
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
controller.createdField.textProperty().bindBidirectional(viewModel.getCreated(), new LocalDateTimeStringConverter(dateTimeFormatter, null));
controller.createdField.textProperty().bindBidirectional(viewModel.getCreated(),
controller.getCurrentEmployee().getLocalDateTimeStringConverter());
controller.guidField.textProperty().bind(viewModel.getGuid());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
NumberStringConverter numberStringConverter = new NumberStringConverter(controller.getLocale(), "#,##0.00");
NumberStringConverter numberStringConverter = controller.getCurrentEmployee().getNumberStringConverter();
controller.amountField.textProperty().bindBidirectional(viewModel.getAmount(), numberStringConverter);
controller.totalQuantityField.textProperty().bindBidirectional(viewModel.getTotalQuantity(), numberStringConverter);
controller.totalQuantityField.textProperty().bindBidirectional(viewModel.getTotalQuantity(),
numberStringConverter);
controller.totalAmountField.textProperty().bindBidirectional(viewModel.getTotalAmount(), numberStringConverter);
controller.totalUnTaxAmountField.textProperty().bindBidirectional(viewModel.getTotalUnTaxAmount(), numberStringConverter);
controller.execQuantityField.textProperty().bindBidirectional(viewModel.getExecQuantity(), numberStringConverter);
controller.totalUnTaxAmountField.textProperty().bindBidirectional(viewModel.getTotalUnTaxAmount(),
numberStringConverter);
controller.execQuantityField.textProperty().bindBidirectional(viewModel.getExecQuantity(),
numberStringConverter);
controller.execAmountField.textProperty().bindBidirectional(viewModel.getExecAmount(), numberStringConverter);
controller.execUnTaxAmountField.textProperty().bindBidirectional(viewModel.getExecUnTaxAmount(), numberStringConverter);
controller.execUnTaxAmountField.textProperty().bindBidirectional(viewModel.getExecUnTaxAmount(),
numberStringConverter);
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
}
@@ -264,7 +265,6 @@ public class ContractTabSkinBase
return;
}
project = new Project();
String name = viewModel.getName().get();
@@ -327,7 +327,6 @@ public class ContractTabSkinBase
ContractWindowController.show(parent, controller.root.getScene().getWindow());
}
/**
* 创建合同存储目录
*/
@@ -470,7 +469,6 @@ public class ContractTabSkinBase
CompanyVendorWindowController.show(companyVendor, null);
}
private void initializeBaseTabCompanyFieldAutoCompletion(TextField textField) {
CompanyStringConverter converter = SpringApp.getBean(CompanyStringConverter.class);
UITools.autoCompletion(textField, viewModel.getCompany(), converter);
@@ -498,12 +496,8 @@ public class ContractTabSkinBase
UITools.autoCompletion(textField, viewModel.getProject(), converter::suggest, stringConverter);
}
public CompanyCustomerService getCompanyCustomerService() {
if (companyCustomerService == null) {
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
}
return companyCustomerService;
return controller.getCachedBean(CompanyCustomerService.class);
}
public ProjectService getProjectService() {
@@ -511,23 +505,14 @@ public class ContractTabSkinBase
}
public SaleTypeService getSaleTypeService() {
if (saleTypeService == null) {
saleTypeService = SpringApp.getBean(SaleTypeService.class);
}
return saleTypeService;
return controller.getCachedBean(SaleTypeService.class);
}
public ExtendVendorInfoService getExtendVendorInfoService() {
if (extendVendorInfoService == null) {
extendVendorInfoService = SpringApp.getBean(ExtendVendorInfoService.class);
}
return extendVendorInfoService;
return controller.getCachedBean(ExtendVendorInfoService.class);
}
public VendorGroupService getVendorGroupService() {
if (vendorGroupService == null) {
vendorGroupService = SpringApp.getBean(VendorGroupService.class);
}
return vendorGroupService;
return controller.getCachedBean(VendorGroupService.class);
}
}

View File

@@ -1,5 +1,13 @@
package com.ecep.contract.manager.ds.contract.controller;
import java.io.File;
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 org.springframework.util.StringUtils;
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;
@@ -13,21 +21,19 @@ 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.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;
import javafx.util.converter.LocalDateStringConverter;
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 org.springframework.util.StringUtils;
import java.io.File;
import java.time.format.DateTimeFormatter;
@Lazy
@Scope("prototype")
@@ -70,9 +76,6 @@ public class ContractWindowController
@Autowired
ProjectService projectService;
LocalDateStringConverter localDateStringConverter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
public TextField nameField;
public TextField guidField;
public TextField codeField;

View File

@@ -19,6 +19,9 @@ import org.springframework.util.StringUtils;
import java.util.List;
/**
* 合同支付计划服务
*/
@Lazy
@Service
@CacheConfig(cacheNames = "contract-pay-plan")

View File

@@ -9,6 +9,7 @@ import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.ecep.contract.manager.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.repository.ContractRepository;
@@ -52,9 +53,6 @@ import com.ecep.contract.manager.ui.ViewModelService;
public class ContractService implements ViewModelService<Contract, ContractViewModel> {
private static final Logger logger = LoggerFactory.getLogger(ContractService.class);
public static final String CONTRACT_BASE_PATH = "contract.base.path";
public static final String CONTRACT_LATEST_ID = "cloud.u8.contract.latestId";
public static final String CONTRACT_LATEST_DATE = "cloud.u8.contract.latestDate";
@Lazy
@Autowired
private ContractCatalogService contractCatalogService;
@@ -147,18 +145,6 @@ public class ContractService implements ViewModelService<Contract, ContractViewM
return contractRepository.findByProjectIdAndPayWay(project.getId(), ContractPayWay.RECEIVE).orElse(null);
}
/**
* 更新合同最新ID和日期
*/
public void updateLatestIdAndDate(Integer latestId, LocalDateTime latestDate) {
if (latestId != null) {
confService.set(CONTRACT_LATEST_ID, String.valueOf(latestId));
}
if (latestDate != null) {
confService.set(CONTRACT_LATEST_DATE, latestDate.toString());
}
}
public File getBasePath() {
return new File(confService.getString(CONTRACT_BASE_PATH));
}

View File

@@ -42,13 +42,15 @@ import java.util.stream.Collectors;
@Lazy
@Service
@CacheConfig(cacheNames = "company-customer")
public class CompanyCustomerService extends CompanyBasicService implements ViewModelService<CompanyCustomer, CompanyCustomerViewModel> {
public class CompanyCustomerService extends CompanyBasicService
implements ViewModelService<CompanyCustomer, CompanyCustomerViewModel> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerService.class);
private static final String EVALUATION_FORM_NAME1 = "评定表";
public static final String EVALUATION_FORM_NAME2 = "评估表";
public static final String KEY_BASE_PATH = "customer.base.path";
public static final String KEY_SALEBOOK_PATH = "customer.salebook.path";
@Lazy
@Autowired
@@ -78,20 +80,16 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
return companyCustomerRepository.findById(id).orElse(null);
}
@Caching(
evict = {
@CacheEvict(key = "#p0.id")
}
)
@Caching(evict = {
@CacheEvict(key = "#p0.id")
})
public CompanyCustomer save(CompanyCustomer companyCustomer) {
return companyCustomerRepository.save(companyCustomer);
}
@Caching(
evict = {
@CacheEvict(key = "#p0")
}
)
@Caching(evict = {
@CacheEvict(key = "#p0")
})
@Override
public void delete(CompanyCustomer entity) {
companyCustomerRepository.delete(entity);
@@ -164,7 +162,7 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
List<CompanyCustomerFile> retrieveFiles = new ArrayList<>();
List<File> needMoveToCompanyPath = new ArrayList<>();
//TODO 客户有曾用名,可能存在多个目录
// TODO 客户有曾用名,可能存在多个目录
fetchFiles(companyCustomer.getPath(), needMoveToCompanyPath, retrieveFiles, map, status);
// 移动文件到公司目录下 to company path
@@ -173,7 +171,7 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
status.accept("导入 " + retrieveFiles.size() + " 个文件");
if (!retrieveFiles.isEmpty()) {
//update db
// update db
retrieveFiles.forEach(v -> v.setCustomer(companyCustomer));
companyCustomerFileService.saveAll(retrieveFiles);
modified = true;
@@ -182,9 +180,9 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
return modified;
}
@Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file, Consumer<String> status) {
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file,
Consumer<String> status) {
dbFile.setType((T) CompanyCustomerFileType.General);
fillFile(dbFile, file, null, status);
companyCustomerFileService.save((CompanyCustomerFile) dbFile);
@@ -192,7 +190,8 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
}
@Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file, List<File> fileList, Consumer<String> status) {
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file,
List<File> fileList, Consumer<String> status) {
boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status);
if (fileList != null) {
@@ -225,7 +224,8 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
return modified;
}
private <T, F extends CompanyBasicFile<T>> void updateEvaluationFileByJsonFile(F customerFile, File jsonFile, Consumer<String> status) throws IOException {
private <T, F extends CompanyBasicFile<T>> void updateEvaluationFileByJsonFile(F customerFile, File jsonFile,
Consumer<String> status) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(jsonFile);
if (!root.isObject()) {
@@ -237,19 +237,21 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
data.remove("valid");
JsonNode type = data.remove("type");
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService.findCustomerEvaluationFormFileByCustomerFile((CompanyCustomerFile) customerFile);
CompanyCustomerEvaluationFormFile formFile = companyCustomerFileService
.findCustomerEvaluationFormFileByCustomerFile((CompanyCustomerFile) customerFile);
objectMapper.updateValue(formFile, data);
logger.info("load json data from {}", jsonFile.getName());
formFile.setCatalog(type.asText());
companyCustomerEvaluationFormFileRepository.save(formFile);
// companyCustomerEvaluationFormFileRepository.save(formFile);
// companyCustomerEvaluationFormFileRepository.save(formFile);
if (jsonFile.delete()) {
logger.info("delete json file {}", jsonFile.getName());
}
}
@Override
protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList, Consumer<String> status) {
protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList,
Consumer<String> status) {
CompanyCustomerFile customerFile = new CompanyCustomerFile();
customerFile.setType(CompanyCustomerFileType.General);
if (fillFile(customerFile, file, fileList, status)) {
@@ -271,7 +273,8 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
protected boolean isEvaluationFile(String fileName) {
return (fileName.contains(EVALUATION_FORM_NAME1)
|| fileName.contains(EVALUATION_FORM_NAME2))
&& (CompanyFileUtils.withExtensions(fileName, CompanyFileUtils.JPG, CompanyFileUtils.JPEG, CompanyFileUtils.PDF));
&& (CompanyFileUtils.withExtensions(fileName, CompanyFileUtils.JPG, CompanyFileUtils.JPEG,
CompanyFileUtils.PDF));
}
public boolean makePathAbsent(CompanyCustomer companyCustomer) {
@@ -302,7 +305,8 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
}
String companyName = company.getName();
String fileName = formatCompanyVendorId(companyCustomer.getId()) + "-" + CompanyFileUtils.escapeFileName(companyName);
String fileName = formatCompanyVendorId(companyCustomer.getId()) + "-"
+ CompanyFileUtils.escapeFileName(companyName);
File dir = new File(basePath, fileName);
if (!dir.exists()) {
@@ -313,7 +317,6 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
return dir;
}
public Page<CompanyCustomer> findAll(Specification<CompanyCustomer> spec, Pageable pageable) {
return companyCustomerRepository.findAll(spec, pageable);
}
@@ -347,8 +350,9 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
/***
* 合并两个CompanyCustomer对象并将fromCustomer的信息合并到toCustomer中并保存到数据库中。
*
* @param from 源客户对象
* @param to 目标客户对象
* @param to 目标客户对象
*/
public void resetTo(CompanyCustomer from, CompanyCustomer to) {
// file
@@ -359,7 +363,6 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
companyCustomerRepository.delete(from);
}
/**
* 删除 company 的 客户
*/
@@ -386,12 +389,10 @@ public class CompanyCustomerService extends CompanyBasicService implements ViewM
return customerCatalogRepository.findByCode(code).orElse(null);
}
@Caching(
evict = {
@CacheEvict(key = "'catalog-'+#p0.id"),
@CacheEvict(key = "'catalog-code-'+#p0.code")
}
)
@Caching(evict = {
@CacheEvict(key = "'catalog-'+#p0.id"),
@CacheEvict(key = "'catalog-code-'+#p0.code")
})
public CustomerCatalog save(CustomerCatalog catalog) {
return customerCatalogRepository.save(catalog);
}

View File

@@ -0,0 +1,128 @@
package com.ecep.contract.manager.ds.other;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.other.model.SysConf;
import com.ecep.contract.manager.ds.other.service.SysConfService;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Control;
import lombok.Getter;
import lombok.Setter;
public abstract class AbstractConfigBounder<T> implements ConfigBounder {
@Setter
private SysConfService confService;
String key;
SysConf conf;
@Getter
@Setter
Control control;
@Getter
@Setter
javafx.util.StringConverter<T> converter;
@Setter
Property<T> property;
ObjectProperty<LocalDateTime> modified = new SimpleObjectProperty<>(this, "modified");
ObjectProperty<LocalDateTime> created = new SimpleObjectProperty<>(this, "created");
public AbstractConfigBounder(String key) {
this.key = key;
}
@Override
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, Desktop.instance.getExecutorService()).exceptionally(ex -> {
ex.printStackTrace();
return null;
});
}
@Override
public SysConfService getConfService() {
if (confService == null) {
confService = SpringApp.getBean(SysConfService.class);
}
return confService;
}
@Override
public void initialize() {
getControl().setDisable(true);
runAsync(this::asyncHandle);
}
protected void asyncHandle() {
conf = getConfService().findById(key);
if (conf == null) {
conf = new SysConf();
conf.setId(key);
conf.setCreated(LocalDateTime.now());
conf.setModified(LocalDateTime.now());
conf = getConfService().save(conf);
}
Property<T> property = getProperty();
property.addListener(this::propertyChanged);
bindBidirectional(property);
modified.set(conf.getModified());
created.set(conf.getCreated());
getControl().setDisable(false);
}
public Property<T> getProperty() {
if (property == null) {
T value = getConverter().fromString(conf.getValue());
property = createProperty(value);
setProperty(property);
}
return property;
}
public ObjectProperty<LocalDateTime> getModifiedProperty() {
return modified;
}
public ObjectProperty<LocalDateTime> getCreatedProperty() {
return created;
}
@SuppressWarnings("unchecked")
protected Property<T> createProperty(T value) {
if (value instanceof Boolean b) {
return (Property<T>) new SimpleBooleanProperty(b);
} else if (value instanceof Integer i) {
return (Property<T>) new SimpleIntegerProperty(i);
} else if (value instanceof Double d) {
return (Property<T>) new SimpleDoubleProperty(d);
} else if (value instanceof String s) {
return (Property<T>) new SimpleStringProperty(s);
}
return new SimpleObjectProperty<>(value);
}
abstract void bindBidirectional(Property<T> property);
void propertyChanged(ObservableValue<? extends T> observable, T oldValue,
T newValue) {
conf.setValue(getConverter().toString(newValue));
conf.setModified(LocalDateTime.now());
save();
}
public void save() {
SysConf saved = getConfService().save(conf);
modified.set(saved.getModified());
created.set(saved.getCreated());
}
}

View File

@@ -22,6 +22,7 @@ public class BankStringConverter extends EntityStringConverter<Bank> {
@PostConstruct
private void init() {
// #2
setInitialized(project -> service.findById(project.getId()));
setSuggestion(service::search);
setFromString(service::findByName);

View File

@@ -0,0 +1,40 @@
package com.ecep.contract.manager.ds.other;
import org.controlsfx.control.ToggleSwitch;
import javafx.beans.property.Property;
import javafx.util.StringConverter;
import javafx.util.converter.BooleanStringConverter;
public class BooleanConfig extends AbstractConfigBounder<Boolean> {
public BooleanConfig(String key) {
super(key);
}
@Override
public ToggleSwitch getControl() {
return (ToggleSwitch) super.getControl();
}
@Override
public StringConverter<Boolean> getConverter() {
StringConverter<Boolean> converter = super.getConverter();
if (converter == null) {
converter = new BooleanStringConverter();
setConverter(converter);
}
return converter;
}
@Override
protected Property<Boolean> createProperty(Boolean value) {
// fixbug when value is null
value = value == null ? false : value;
return super.createProperty(value);
}
@Override
void bindBidirectional(Property<Boolean> property) {
getControl().selectedProperty().bindBidirectional(property);
}
}

View File

@@ -0,0 +1,17 @@
package com.ecep.contract.manager.ds.other;
import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.ds.other.service.SysConfService;
public interface ConfigBounder {
void setConfService(SysConfService confService);
void initialize();
CompletableFuture<Void> runAsync(Runnable runnable);
SysConfService getConfService();
}

View File

@@ -0,0 +1,37 @@
package com.ecep.contract.manager.ds.other;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javafx.beans.property.Property;
import javafx.scene.control.DatePicker;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateStringConverter;
public class LocalDateConfig extends AbstractConfigBounder<LocalDate> {
public LocalDateConfig(String string) {
super(string);
}
@Override
public DatePicker getControl() {
return (DatePicker) super.getControl();
}
@Override
public StringConverter<LocalDate> getConverter() {
StringConverter<LocalDate> converter = super.getConverter();
if (converter == null) {
converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
setConverter(converter);
}
return converter;
}
@Override
void bindBidirectional(Property<LocalDate> property) {
getControl().valueProperty().bindBidirectional(property);
}
}

View File

@@ -0,0 +1,45 @@
package com.ecep.contract.manager.ds.other;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javafx.beans.property.Property;
import javafx.scene.control.TextField;
import javafx.util.StringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import lombok.Getter;
import lombok.Setter;
public class LocalDateTimeConfig extends AbstractConfigBounder<LocalDateTime> {
@Getter
@Setter
private javafx.util.StringConverter<LocalDateTime> controlConverter;
public LocalDateTimeConfig(String string) {
super(string);
}
@Override
public TextField getControl() {
return (TextField) super.getControl();
}
@Override
public javafx.util.StringConverter<LocalDateTime> getConverter() {
javafx.util.StringConverter<LocalDateTime> converter = super.getConverter();
if (converter == null) {
converter = new LocalDateTimeStringConverter(DateTimeFormatter.ISO_LOCAL_DATE_TIME, null);
setConverter(converter);
}
return converter;
}
@Override
void bindBidirectional(Property<LocalDateTime> property) {
StringConverter<LocalDateTime> converter = getControlConverter();
if (converter == null) {
converter = getConverter();
}
getControl().textProperty().bindBidirectional(property, converter);
}
}

View File

@@ -0,0 +1,52 @@
package com.ecep.contract.manager.ds.other;
import javafx.beans.property.Property;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.util.StringConverter;
public class StringConfig extends AbstractConfigBounder<String> {
public StringConfig(String string) {
super(string);
}
// @Override
// public TextField getControl() {
// return (TextField) super.getControl();
// }
@Override
public StringConverter<String> getConverter() {
StringConverter<String> converter = super.getConverter();
if (converter == null) {
converter = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
setConverter(converter);
}
return converter;
}
@Override
void bindBidirectional(Property<String> property) {
Control ctrl = getControl();
if (ctrl == null) {
return;
}
if (ctrl instanceof TextField textField) {
textField.textProperty().bindBidirectional(property);
} else if (ctrl instanceof Label label) {
label.textProperty().bindBidirectional(property);
}
}
}

View File

@@ -220,13 +220,4 @@ public class HomeWindowController extends BaseController {
public void onShowTaskMonitorWindowAction(ActionEvent event) {
showInOwner(TaskMonitorViewController.class);
}
/**
* 运行任务监控演示
*/
public void onRunTaskMonitorDemo(ActionEvent event) {
com.ecep.contract.manager.ui.task.DemoTask.runDemo();
// 自动打开任务监控窗口
showInOwner(TaskMonitorViewController.class);
}
}

View File

@@ -1,11 +1,21 @@
package com.ecep.contract.manager.ds.other.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.manager.cloud.u8.YongYouU8Service;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.other.StringConfig;
import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.other.repository.SysConfRepository;
import com.ecep.contract.manager.ui.BaseController;
import jakarta.annotation.PreDestroy;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
@@ -14,14 +24,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.io.File;
@Lazy
@Scope("prototype")
@Component
@@ -42,90 +45,76 @@ public class SysConfWindowController extends BaseController {
public Label customerEvaluationFormTemplateLabel;
public Label customerSaleBookPathLabel;
StringConfig contractPathConfig = new StringConfig(ContractService.CONTRACT_BASE_PATH);
StringConfig vendorPathConfig = new StringConfig(CompanyVendorService.KEY_BASE_PATH);
StringConfig vendorEvaluationFormTemplateConfig = new StringConfig(
CompanyVendorService.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerPathConfig = new StringConfig(CompanyCustomerService.KEY_BASE_PATH);
StringConfig customerEvaluationFormTemplateConfig = new StringConfig(
CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE);
StringConfig customerSaleBookPathConfig = new StringConfig(CompanyCustomerService.KEY_SALEBOOK_PATH);
@Autowired
private SysConfRepository confRepository;
private final SimpleStringProperty companyContractPath = new SimpleStringProperty();
private final SimpleStringProperty u8DataBaseServerHost = new SimpleStringProperty();
private final SimpleStringProperty u8DataBaseCatalog = new SimpleStringProperty();
private final SimpleStringProperty u8DataBaseUserName = new SimpleStringProperty();
private final SimpleStringProperty u8DataBasePassword = new SimpleStringProperty();
private final SimpleStringProperty vendorPath = new SimpleStringProperty();
private final SimpleStringProperty vendorEvaluationFormTemplate = new SimpleStringProperty();
private final SimpleStringProperty customerPath = new SimpleStringProperty();
private final SimpleStringProperty customerEvaluationFormTemplate = new SimpleStringProperty();
private final SimpleStringProperty customerSaleBookPath = new SimpleStringProperty();
StringConfig u8DataBaseServerHostConfig = new StringConfig(YongYouU8Service.KEY_HOST_IP);
StringConfig u8DataBaseCatalogConfig = new StringConfig(YongYouU8Service.KEY_DATABASE);
StringConfig u8DataBaseUserNameConfig = new StringConfig(YongYouU8Service.KEY_USER_NAME);
StringConfig u8DataBasePasswordConfig = new StringConfig(YongYouU8Service.KEY_PASSWORD);
public void initialize() {
contractPathConfig.setControl(companyContractPathLabel);
contractPathConfig.initialize();
initializeDirectory(companyContractPathLabel, companyContractPath, ContractService.CONTRACT_BASE_PATH);
vendorPathConfig.setControl(vendorPathLabel);
vendorPathConfig.initialize();
vendorEvaluationFormTemplateConfig.setControl(vendorEvaluationFormTemplateLabel);
vendorEvaluationFormTemplateConfig.initialize();
initializeDirectory(vendorPathLabel, vendorPath, CompanyVendorService.KEY_BASE_PATH);
initializeFile(vendorEvaluationFormTemplateLabel, vendorEvaluationFormTemplate, CompanyVendorService.KEY_EVALUATION_FORM_TEMPLATE);
customerPathConfig.setControl(customerPathLabel);
customerPathConfig.initialize();
customerEvaluationFormTemplateConfig.setControl(customerEvaluationFormTemplateLabel);
customerEvaluationFormTemplateConfig.initialize();
customerSaleBookPathConfig.setControl(customerSaleBookPathLabel);
customerSaleBookPathConfig.initialize();
initializeDirectory(customerPathLabel, customerPath, CompanyCustomerService.KEY_BASE_PATH);
initializeFile(customerEvaluationFormTemplateLabel, customerEvaluationFormTemplate, CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE);
initializeDirectory(customerSaleBookPathLabel, customerSaleBookPath, "customer.salebook.path");
initializeField(u8DataBaseServerHostField, u8DataBaseServerHost, "u8.db.server.ip");
initializeField(u8DataBaseCatalogField, u8DataBaseCatalog, "u8.db.database");
initializeField(u8DataBaseUserNameField, u8DataBaseUserName, "u8.db.server.name");
initializeField(u8DataBasePasswordField, u8DataBasePassword, "u8.db.server.password");
u8DataBaseServerHostConfig.setControl(u8DataBaseServerHostField);
u8DataBaseServerHostConfig.initialize();
u8DataBaseCatalogConfig.setControl(u8DataBaseCatalogField);
u8DataBaseCatalogConfig.initialize();
u8DataBaseUserNameConfig.setControl(u8DataBaseUserNameField);
u8DataBaseUserNameConfig.initialize();
u8DataBasePasswordConfig.setControl(u8DataBasePasswordField);
u8DataBasePasswordConfig.initialize();
logger.debug("#initialize()");
}
private void initializeFile(Label label, SimpleStringProperty property, String key) {
label.textProperty().bind(property);
property.set(confRepository.get(key, ""));
}
private void initializeDirectory(Label label, SimpleStringProperty property, String key) {
label.textProperty().bind(property);
property.set(confRepository.get(key, ""));
}
private void initializeField(TextField field, SimpleStringProperty property, String key) {
field.textProperty().bindBidirectional(property);
property.set(confRepository.get(key, ""));
property.addListener((observable, oldValue, newValue) -> {
confRepository.set(key, newValue);
});
}
private void directoryChoose(SimpleStringProperty property, String title, String key, ActionEvent event) {
private void directoryChoose(StringConfig config, String title, String key, ActionEvent event) {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(title);
File value = new File(property.get());
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) {
property.set(selected.getAbsolutePath());
confRepository.set(key, property.get());
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCompanyContractPath(ActionEvent actionEvent) {
directoryChoose(companyContractPath, "请选择合同目录", ContractService.CONTRACT_BASE_PATH, actionEvent);
directoryChoose(contractPathConfig, "请选择合同目录", ContractService.CONTRACT_BASE_PATH, actionEvent);
}
public void changeVendorPath(ActionEvent actionEvent) {
directoryChoose(vendorPath, "请选择供应商目录", CompanyVendorService.KEY_BASE_PATH, actionEvent);
directoryChoose(vendorPathConfig, "请选择供应商目录", CompanyVendorService.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerPath(ActionEvent actionEvent) {
directoryChoose(customerPath, "请选择客户目录", CompanyCustomerService.KEY_BASE_PATH, actionEvent);
directoryChoose(customerPathConfig, "请选择客户目录", CompanyCustomerService.KEY_BASE_PATH, actionEvent);
}
public void changeCustomerSaleBookPath(ActionEvent actionEvent) {
directoryChoose(customerSaleBookPath, "请选择销售台账目录", "customer.salebook.path", actionEvent);
directoryChoose(customerSaleBookPathConfig, "请选择销售台账目录", "customer.salebook.path", actionEvent);
}
// 模拟销毁方法
@@ -134,28 +123,29 @@ public class SysConfWindowController extends BaseController {
}
private void fileChoose(SimpleStringProperty property, String title, String key, ActionEvent event) {
private void fileChoose(StringConfig config, String title, String key, ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.setTitle(title);
File value = new File(property.get());
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) {
property.set(selected.getAbsolutePath());
confRepository.set(key, property.get());
config.getProperty().setValue(selected.getAbsolutePath());
config.save();
}
}
public void changeCustomerEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(customerEvaluationFormTemplate, "请选择客户资信评估表模板", CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE, actionEvent);
fileChoose(customerEvaluationFormTemplateConfig, "请选择客户资信评估表模板",
CompanyCustomerFileService.KEY_EVALUATION_FORM_TEMPLATE, actionEvent);
}
public void changeVendorEvaluationFormTemplate(ActionEvent actionEvent) {
fileChoose(vendorEvaluationFormTemplate, "请选择供方调查评价表模板", CompanyVendorService.KEY_EVALUATION_FORM_TEMPLATE, actionEvent);
fileChoose(vendorEvaluationFormTemplateConfig, "请选择供方调查评价表模板",
CompanyVendorService.KEY_EVALUATION_FORM_TEMPLATE,
actionEvent);
}
}

View File

@@ -1,5 +1,7 @@
package com.ecep.contract.manager.ds.other.controller.inventory;
import java.util.function.Function;
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;
@@ -7,10 +9,9 @@ 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 com.ecep.contract.manager.ui.util.LocalDateFieldTableCell;
import javafx.beans.property.Property;
import javafx.event.ActionEvent;
import javafx.scene.control.TableColumn;
@@ -19,11 +20,8 @@ import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import lombok.Setter;
import java.util.function.Function;
public class InventoryManagerSkin
extends AbstEntityManagerSkin<Inventory, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController>
implements ManagerSkin, EditableEntityTableTabSkin<Inventory, InventoryViewModel> {
public class InventoryManagerSkin extends
AbstEntityManagerSkin<Inventory, InventoryViewModel, InventoryManagerSkin, InventoryManagerWindowController> {
@Setter
private InventoryCatalogService catalogService;
@@ -69,7 +67,8 @@ public class InventoryManagerSkin
controller.specificationColumn.setCellValueFactory(param -> param.getValue().getSpecification());
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.setCellFactory(TextFieldTableCell.forTableColumn());
@@ -77,44 +76,48 @@ public class InventoryManagerSkin
controller.purchaseTaxRateColumn.setCellValueFactory(param -> param.getValue().getPurchaseTaxRate());
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.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.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.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.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.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.setCellFactory(TextFieldTableCell.forTableColumn());
controller.descriptionColumn.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getDescription));
controller.descriptionColumn
.setOnEditCommit(event -> onColumnEditCommit(event, InventoryViewModel::getDescription));
}
private <T> void onColumnEditCommit(
TableColumn.CellEditEvent<InventoryViewModel, T> event,
Function<InventoryViewModel, Property<T>> supplier
) {
Function<InventoryViewModel, Property<T>> supplier) {
InventoryViewModel row = event.getRowValue();
supplier.apply(row).setValue(event.getNewValue());
saveRowData(row);
}
@Override
protected void onTableRowDoubleClickedAction(InventoryViewModel item) {
showInOwner(InventoryWindowController.class, item);

View File

@@ -56,19 +56,36 @@ public class Employee implements Entity, IdentityEntity, NamedEntity {
@Column(name = "LEAVE_DATE")
private LocalDate leaveDate;
@Column(name = "LOCALE")
private String locale;
@Column(name = "DATE_FORMATTER")
private String dateFormatter;
@Column(name = "DATETIME_FORMATTER")
private String dateTimeFormatter;
@Column(name = "TIME_FORMATTER")
private String timeFormatter;
@Column(name = "TIME_ZONE")
private String timeZone;
@Column(name = "NUMBER_FORMATTER")
private String numberFormatter;
@Column(name = "CURRENCY_FORMATTER")
private String currencyFormatter;
/**
* 员工是否活跃的状态
*/
@Column(name = "IS_ACTIVE", nullable = false)
private boolean isActive;
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinTable(
name = "EMPLOYEE_ROLES",
joinColumns = @JoinColumn(name = "EMPLOYEE_ID"),
inverseJoinColumns = @JoinColumn(name = "ROLE_ID")
)
private java.util.List<EmployeeRole> roles = new java.util.ArrayList<>();
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinTable(name = "EMPLOYEE_ROLES", joinColumns = @JoinColumn(name = "EMPLOYEE_ID"), inverseJoinColumns = @JoinColumn(name = "ROLE_ID"))
private java.util.List<EmployeeRole> roles = new java.util.ArrayList<>();
@Override
public String toPrettyString() {
@@ -80,17 +97,26 @@ public class Employee implements Entity, IdentityEntity, NamedEntity {
@Override
public final boolean equals(Object object) {
if (this == object) return true;
if (object == null) return false;
Class<?> oEffectiveClass = object instanceof HibernateProxy ? ((HibernateProxy) object).getHibernateLazyInitializer().getPersistentClass() : object.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
if (thisEffectiveClass != oEffectiveClass) return false;
if (this == object)
return true;
if (object == null)
return false;
Class<?> oEffectiveClass = object instanceof HibernateProxy
? ((HibernateProxy) object).getHibernateLazyInitializer().getPersistentClass()
: object.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy
? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass()
: this.getClass();
if (thisEffectiveClass != oEffectiveClass)
return false;
Employee employee = (Employee) object;
return getId() != null && Objects.equals(getId(), employee.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
return this instanceof HibernateProxy
? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode()
: getClass().hashCode();
}
}

View File

@@ -1,14 +1,15 @@
package com.ecep.contract.manager.ds.other.model;
import java.time.LocalDateTime;
import org.hibernate.annotations.ColumnDefault;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import java.time.Instant;
@Getter
@Setter
@@ -24,9 +25,9 @@ public class SysConf {
@ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "MODIFIED")
private Instant modified;
private LocalDateTime modified;
@Column(name = "CREATED")
private Instant created;
private LocalDateTime created;
}

View File

@@ -1,85 +1,11 @@
package com.ecep.contract.manager.ds.other.repository;
import com.ecep.contract.manager.ds.other.model.SysConf;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.ds.MyRepository;
import com.ecep.contract.manager.ds.other.model.SysConf;
@Repository
public interface SysConfRepository extends CrudRepository<SysConf, String>, PagingAndSortingRepository<SysConf, String> {
default String get(String key) {
return get(key, null);
}
public interface SysConfRepository extends MyRepository<SysConf, String> {
/**
* 读取配置值
*
* @param key 配置项
* @param defaultValue 没有配置时的默认值
* @return 配置值
*/
default String get(String key, String defaultValue) {
Optional<SysConf> optional = findById(key);
if (optional.isPresent()) {
return optional.get().getValue();
} else {
return defaultValue;
}
}
default long get(String key, long defaultValue) {
Optional<SysConf> optional = findById(key);
return optional.map(sysConf -> Long.parseLong(sysConf.getValue())).orElse(defaultValue);
}
default int get(String key, int defaultValue) {
Optional<SysConf> optional = findById(key);
return optional.map(sysConf -> Integer.parseInt(sysConf.getValue())).orElse(defaultValue);
}
default boolean get(String key, boolean defaultValue) {
Optional<SysConf> optional = findById(key);
return optional.map(sysConf -> {
String value = sysConf.getValue();
if (Boolean.parseBoolean(value)) {
return true;
}
return "1".equalsIgnoreCase(value);
}).orElse(defaultValue);
}
/**
* 保存配置值
*
* @param key 配置项
* @param value 配置值
* @return 配置值对象
*/
default SysConf set(String key, String value) {
SysConf conf;
Optional<SysConf> optional = findById(key);
if (optional.isPresent()) {
conf = optional.get();
} else {
conf = new SysConf();
conf.setId(key);
conf.setCreated(Instant.now());
}
conf.setValue(value);
conf.setModified(Instant.now());
save(conf);
return conf;
}
default CompletableFuture<Optional<SysConf>> value(String key) {
return CompletableFuture.supplyAsync(() -> findById(key));
}
default CompletableFuture<SysConf> value(String key, String value) {
return CompletableFuture.supplyAsync(() -> set(key, value));
}
}

View File

@@ -1,7 +1,7 @@
package com.ecep.contract.manager.ds.other.service;
import com.ecep.contract.manager.ds.other.model.SysConf;
import com.ecep.contract.manager.ds.other.repository.SysConfRepository;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
@@ -9,12 +9,14 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.time.Instant;
import com.ecep.contract.manager.ds.other.model.SysConf;
import com.ecep.contract.manager.ds.other.repository.SysConfRepository;
@Lazy
@Service
@CacheConfig(cacheNames = "sys_conf")
public class SysConfService {
@Lazy
@Autowired
private SysConfRepository repository;
@@ -22,16 +24,21 @@ public class SysConfService {
return repository.findById(key).orElse(null);
}
@CacheEvict(key = "#p0.id")
public SysConf save(SysConf conf) {
return repository.save(conf);
}
@CacheEvict(key = "#p0")
public SysConf set(String key, String value) {
SysConf conf = findById(key);
if (conf == null) {
conf = new SysConf();
conf.setId(key);
conf.setCreated(Instant.now());
conf.setCreated(LocalDateTime.now());
}
conf.setValue(value);
conf.setModified(Instant.now());
conf.setModified(LocalDateTime.now());
repository.save(conf);
return conf;
}
@@ -94,6 +101,4 @@ public class SysConfService {
return "1".equalsIgnoreCase(conf.getValue());
}
}
}

View File

@@ -1,41 +0,0 @@
package com.ecep.contract.manager.ds.project.controller;
import com.ecep.contract.manager.SpringApp;
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.CompanyCustomerFileService;
import com.ecep.contract.manager.ds.project.vo.ProjectBidViewModel;
import javafx.scene.control.TableCell;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
class EvaluationFileTableCell extends TableCell<ProjectBidViewModel, CompanyCustomerEvaluationFormFile> {
private CompanyCustomerFileService fileService;
public CompanyCustomerFileService getFileService() {
if (fileService == null) {
fileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return fileService;
}
@Override
protected void updateItem(CompanyCustomerEvaluationFormFile item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
if (!Hibernate.isInitialized(item)) {
item = getFileService().findCustomerEvaluationFormFileById(item.getId());
}
CompanyCustomerFile customerFile = item.getCustomerFile();
String path = customerFile.getFilePath();
if (StringUtils.hasText(path)) {
File file = new File(path);
setText(file.getName());
}
}
}

View File

@@ -1,35 +1,33 @@
package com.ecep.contract.manager.ds.project.controller;
import com.ecep.contract.manager.SpringApp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import com.ecep.contract.manager.ds.company.CompanyStringConverter;
import com.ecep.contract.manager.ds.company.service.CompanyService;
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.CompanyCustomerFileService;
import com.ecep.contract.manager.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.manager.ds.other.EmployeeStringConverter;
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.bid.ProjectBidWindowController;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectBid;
import com.ecep.contract.manager.ds.project.service.ProjectBidService;
import com.ecep.contract.manager.ds.project.vo.ProjectBidViewModel;
import com.ecep.contract.manager.ui.*;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.RefreshableSkin;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import javafx.scene.control.*;
import com.ecep.contract.manager.ui.table.cell.EmployeeTableCell;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.converter.LocalDateStringConverter;
import lombok.Setter;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;
@FxmlPath("/ui/project/project-tab-bid.fxml")
public class ProjectTabSkinBid

View File

@@ -0,0 +1,255 @@
package com.ecep.contract.manager.ds.project.controller;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ecep.contract.manager.ds.contract.model.Contract;
import com.ecep.contract.manager.ds.contract.model.ContractPayPlan;
import com.ecep.contract.manager.ds.contract.service.ContractPayPlanService;
import com.ecep.contract.manager.ds.contract.service.ContractService;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectFundPlan;
import com.ecep.contract.manager.ds.project.service.ProjectFundPlanService;
import com.ecep.contract.manager.ds.project.vo.ProjectFundPlanViewModel;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.ui.RefreshableSkin;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.util.NumberUtils;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
import lombok.Setter;
/**
* 项目资金计划
* 工单 #2
* fxml 文件在 /src/main/resources/ui/ 目录
*/
@FxmlPath("/ui/project/project-tab-fund-plan.fxml")
public class ProjectTabSkinFundPlan
extends AbstProjectTableTabSkin<ProjectFundPlan, ProjectFundPlanViewModel>
implements TabSkin, EditableEntityTableTabSkin<ProjectFundPlan, ProjectFundPlanViewModel>, RefreshableSkin {
private static final Logger logger = LoggerFactory.getLogger(ProjectTabSkinFundPlan.class);
public TableColumn<ProjectFundPlanViewModel, Number> idColumn;
public TableColumn<ProjectFundPlanViewModel, Number> versionColumn;
public TableColumn<ProjectFundPlanViewModel, LocalDate> payDateColumn;
public TableColumn<ProjectFundPlanViewModel, Number> payRatioColumn;
public TableColumn<ProjectFundPlanViewModel, Number> payCurrencyColumn;
public TableColumn<ProjectFundPlanViewModel, String> payTermColumn;
public TableColumn<ProjectFundPlanViewModel, LocalDateTime> updateDateColumn;
public TableColumn<ProjectFundPlanViewModel, String> payWayColumn;
public Button updatePlanBtn;
@Setter
private ProjectFundPlanService projectFundPlanService;
public ProjectTabSkinFundPlan(ProjectWindowController controller) {
super(controller);
}
@Override
protected ProjectFundPlanService getViewModelService() {
if (projectFundPlanService == null) {
projectFundPlanService = getBean(ProjectFundPlanService.class);
}
return projectFundPlanService;
}
public ContractPayPlanService getContractPayPlanService() {
return controller.getCachedBean(ContractPayPlanService.class);
}
private ContractService getContractService() {
return controller.getCachedBean(ContractService.class);
}
@Override
public Tab getTab() {
return controller.fundPlanTab;
}
@Override
public void initializeTable() {
super.initializeTable();
// 关闭所有的排序功能
idColumn.setSortable(false);
versionColumn.setSortable(false);
payDateColumn.setSortable(false);
payRatioColumn.setSortable(false);
payCurrencyColumn.setSortable(false);
payTermColumn.setSortable(false);
updateDateColumn.setSortable(false);
payWayColumn.setSortable(false);
bindNumberColumn(idColumn, ProjectFundPlanViewModel::getId);
bindLocalDateColumn(payDateColumn, ProjectFundPlanViewModel::getPayDate);
bindColumn(payWayColumn, model -> {
javafx.beans.property.SimpleStringProperty property = new javafx.beans.property.SimpleStringProperty();
property.set(model.getPayWay().get() != null ? model.getPayWay().get().getText() : "");
return property;
});
bindNumberColumn(payRatioColumn, ProjectFundPlanViewModel::getPayRatio);
bindNumberColumn(payCurrencyColumn, ProjectFundPlanViewModel::getPayCurrency);
bindColumn(payTermColumn, ProjectFundPlanViewModel::getPayTerm);
bindLocalDateTimeColumn(updateDateColumn, ProjectFundPlanViewModel::getUpdateDate);
updatePlanBtn.setOnAction(this::onUpdatePlanAction);
}
@Override
public List<ProjectFundPlanViewModel> loadTableData(Project parent) {
List<ProjectFundPlanViewModel> rows = super.loadTableData(parent);
if (rows == null) {
return null;
}
// TODO 按付款日期排序
// rows.stream().sorted(Comparator.comparing(ProjectFundPlanViewModel::getPayDate)).collect(Collectors.toList());
return rows;
}
/**
* 从合同的付款计划中更新项目的资金计划
* 1. 从项目相关的合同中获取付款计划
* 2. 更新项目的资金计划
*/
private void onUpdatePlanAction(ActionEvent event) {
try {
// 获取当前项目
Project project = getParent();
if (project == null || project.getId() == null) {
setStatus("提示, 无法获取项目信息");
return;
}
List<ProjectFundPlan> fundPlans = getViewModelService().findAllByProject(project);
// 将现有的项目资金计划转为以 ContractPayPlan 的id为key的map
java.util.Map<Number, ProjectFundPlan> fundPlansMap = fundPlans.stream()
.filter(plan -> plan.getContractPayPlan() != null && plan.getContractPayPlan().getId() != null)
.collect(Collectors.toMap(
plan -> plan.getContractPayPlan().getId(),
plan -> plan));
// 获取项目关联的所有合同
List<Contract> contracts = getContractService().findAllByProject(project);
if (contracts == null || contracts.isEmpty()) {
setStatus("提示, 未找到与项目关联的合同");
return;
}
// 遍历所有合同
for (Contract contract : contracts) {
// 获取合同的付款计划
List<ContractPayPlan> payPlans = getContractPayPlanService().findAllByContract(contract);
// 遍历每个付款计划
for (ContractPayPlan payPlan : payPlans) {
// 检查是否已存在相同的合同付款计划
if (fundPlansMap.containsKey(payPlan.getId())) {
// 更新
ProjectFundPlan fundPlan = fundPlansMap.remove(payPlan.getId());
// 检查是否需要更新
boolean needsUpdate = false;
if (!Objects.equals(fundPlan.getPayWay(), contract.getPayWay())) {
fundPlan.setPayWay(contract.getPayWay());
needsUpdate = true;
}
if (!Objects.equals(fundPlan.getPayDate(), payPlan.getPayDate())) {
fundPlan.setPayDate(payPlan.getPayDate());
needsUpdate = true;
}
if (!NumberUtils.equals(fundPlan.getPayRatio(), payPlan.getPayRatio())) {
fundPlan.setPayRatio(payPlan.getPayRatio());
needsUpdate = true;
}
if (!Objects.equals(fundPlan.getPayTerm(), payPlan.getPayTerm())) {
fundPlan.setPayTerm(payPlan.getPayTerm());
needsUpdate = true;
}
if (!NumberUtils.equals(fundPlan.getPayCurrency(), payPlan.getPayCurrency())) {
fundPlan.setPayCurrency(payPlan.getPayCurrency());
needsUpdate = true;
}
if (needsUpdate) {
fundPlan.setContractPayPlan(payPlan);
fundPlan.setUpdateDate(LocalDateTime.now());
saveRowData(fundPlan);
}
continue;
}
// 创建新的项目资金计划
ProjectFundPlan fundPlan = getViewModelService().newInstanceByProject(project);
// 设置资金计划的属性
fundPlan.setPayDate(payPlan.getPayDate());
fundPlan.setPayWay(contract.getPayWay());
fundPlan.setPayRatio(payPlan.getPayRatio());
fundPlan.setPayTerm(payPlan.getPayTerm());
fundPlan.setPayCurrency(payPlan.getPayCurrency());
fundPlan.setContractPayPlan(payPlan);
fundPlan.setUpdateDate(LocalDateTime.now());
saveRowData(fundPlan);
}
}
// 删除
if (!fundPlansMap.isEmpty()) {
for (ProjectFundPlan fundPlan : fundPlansMap.values()) {
getViewModelService().delete(fundPlan);
}
}
// 刷新表格数据
refresh();
} catch (Exception e) {
logger.error("更新项目资金计划失败", e);
}
}
@Override
protected ProjectFundPlanViewModel createNewViewModel() {
ProjectFundPlanViewModel model = new ProjectFundPlanViewModel();
Project project = getParent();
model.getProject().set(project);
model.getUpdateDate().set(LocalDateTime.now());
saveRow(model);
return model;
}
@Override
protected void onTableRowDoubleClickedAction(ProjectFundPlanViewModel item) {
// 可以在这里实现双击打开详情窗口的逻辑
// 目前暂不实现具体的窗口
}
private ProjectFundPlanService getProjectFundPlanService() {
if (projectFundPlanService == null) {
projectFundPlanService = getBean(ProjectFundPlanService.class);
}
return projectFundPlanService;
}
@Override
public void deleteRowData(ProjectFundPlan entity) {
getProjectFundPlanService().delete(entity);
}
@Override
public ProjectFundPlan loadRowData(ProjectFundPlanViewModel row) {
return getProjectFundPlanService().findById(row.getId().get());
}
@Override
public ProjectFundPlan saveRowData(ProjectFundPlan entity) {
return getProjectFundPlanService().save(entity);
}
}

View File

@@ -108,6 +108,13 @@ public class ProjectWindowController extends AbstEntityController<Project, Proje
* 投标 Bid Tab
*/
public Tab bidTab;
/**
* 资金计划 Tab
*/
public Tab fundPlanTab;
/**
* 顾客满意度 Tab
*/
public Tab satisfactionTab;
@Autowired
@@ -144,6 +151,7 @@ public class ProjectWindowController extends AbstEntityController<Project, Proje
registerTabSkin(costTab, tab -> new ProjectTabSkinCost(this));
registerTabSkin(quotationApprovalTab, tab -> new ProjectTabSkinQuotation(this));
registerTabSkin(bidTab, tab -> new ProjectTabSkinBid(this));
registerTabSkin(fundPlanTab, tab -> new ProjectTabSkinFundPlan(this));
// registerTabSkin(costItemTab, this::createCostItemTabSkin);
registerTabSkin(satisfactionTab, tab -> new ProjectTabSkinCustomerSatisfactionSurvey(this));
}

View File

@@ -0,0 +1,81 @@
package com.ecep.contract.manager.ds.project.model;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.model.ContractPayPlan;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 项目投标
* <p>
* Approval 审批
*/
@Getter
@Setter
@Entity
@Table(name = "PROJECT_FUND_PLAN")
@ToString
public class ProjectFundPlan implements IdentityEntity, ProjectBasedEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", nullable = false)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PROJECT_ID")
private Project project;
/**
* 付款日期
*/
@Column(name = "PAY_DATE")
private LocalDate payDate;
/**
* 付款方式
*/
@Column(name = "PAY_WAY")
@Enumerated(EnumType.ORDINAL)
private ContractPayWay payWay;
/**
* 付款比例
*/
@Column(name = "PAY_RATIO")
private float payRatio;
/**
* 付款金额
*/
@Column(name = "PAY_CURRENCY")
private double payCurrency;
/**
* 付款条件
*/
@Column(name = "PAY_TERM")
private String payTerm;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CONTRACT_PAY_PLAN_ID")
private ContractPayPlan contractPayPlan;
/**
* 更新日期,从销售合同和采购合同中更新
*/
@Column(name = "UPDATE_TIME")
private LocalDateTime updateDate;
}

View File

@@ -1,16 +1,15 @@
package com.ecep.contract.manager.ds.project.repository;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectBid;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.ecep.contract.manager.ds.MyRepository;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectBid;
@Repository
public interface ProjectBidRepository
extends JpaRepository<ProjectBid, Integer>, JpaSpecificationExecutor<ProjectBid> {
public interface ProjectBidRepository extends MyRepository<ProjectBid, Integer> {
/**
* 根据项目查询

View File

@@ -0,0 +1,20 @@
package com.ecep.contract.manager.ds.project.repository;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.ecep.contract.manager.ds.MyRepository;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectFundPlan;
@Repository
public interface ProjectFundPlanRepository extends MyRepository<ProjectFundPlan, Integer> {
/**
* 根据项目查询
*
* @param project 项目
* @return ProjectFundPlan list
*/
List<ProjectFundPlan> findAllByProject(Project project);
}

View File

@@ -0,0 +1,72 @@
package com.ecep.contract.manager.ds.project.service;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectFundPlan;
import com.ecep.contract.manager.ds.project.repository.ProjectFundPlanRepository;
import com.ecep.contract.manager.ds.project.vo.ProjectFundPlanViewModel;
import com.ecep.contract.manager.ui.ViewModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Lazy
@Service
public class ProjectFundPlanService implements ViewModelService<ProjectFundPlan, ProjectFundPlanViewModel> {
private static final Logger logger = LoggerFactory.getLogger(ProjectFundPlanService.class);
@Lazy
@Autowired
private ProjectFundPlanRepository repository;
public ProjectFundPlan findById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
public Page<ProjectFundPlan> findAll(Specification<ProjectFundPlan> spec, Pageable pageable) {
return repository.findAll(spec, pageable);
}
public List<ProjectFundPlan> findAllByProject(Project project) {
return repository.findAllByProject(project);
}
public ProjectFundPlan save(ProjectFundPlan fundPlan) {
return repository.save(fundPlan);
}
@Override
public Specification<ProjectFundPlan> getSpecification(String searchText) {
return (root, query, builder) -> {
return builder.or(
builder.like(root.get("payTerm"), "%" + searchText + "%"),
builder.like(builder.toString(root.get("payRatio")), "%" + searchText + "%"),
builder.like(builder.toString(root.get("payCurrency")), "%" + searchText + "%")
);
};
}
public void delete(ProjectFundPlan fundPlan) {
repository.delete(fundPlan);
}
public ProjectFundPlan newInstanceByProject(Project project) {
ProjectFundPlan fundPlan = new ProjectFundPlan();
fundPlan.setProject(project);
fundPlan.setUpdateDate(LocalDateTime.now());
return fundPlan;
}
public List<ProjectFundPlan> findAll(Specification<ProjectFundPlan> spec, Sort sort) {
return repository.findAll(spec, sort);
}
}

View File

@@ -0,0 +1,154 @@
package com.ecep.contract.manager.ds.project.vo;
import com.ecep.contract.manager.ds.contract.ContractPayWay;
import com.ecep.contract.manager.ds.contract.model.ContractPayPlan;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.manager.ds.project.model.Project;
import com.ecep.contract.manager.ds.project.model.ProjectFundPlan;
import com.ecep.contract.manager.util.NumberUtils;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleFloatProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 项目资金计划视图模型
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ProjectFundPlanViewModel extends IdentityViewModel<ProjectFundPlan> implements ProjectBasedViewModel {
/**
* 关联的项目
*/
private SimpleObjectProperty<Project> project = new SimpleObjectProperty<>();
/**
* 付款日期
*/
private SimpleObjectProperty<LocalDate> payDate = new SimpleObjectProperty<>();
/**
* 付款比例
*/
private SimpleFloatProperty payRatio = new SimpleFloatProperty();
/**
* 付款金额
*/
private SimpleDoubleProperty payCurrency = new SimpleDoubleProperty();
/**
* 余额
*/
private SimpleDoubleProperty balance = new SimpleDoubleProperty();
/**
* 付款条件
*/
private SimpleStringProperty payTerm = new SimpleStringProperty();
/**
* 合同付款计划
*/
private SimpleObjectProperty<ContractPayPlan> contractPayPlan = new SimpleObjectProperty<>();
/**
* 更新日期
*/
private SimpleObjectProperty<LocalDateTime> updateDate = new SimpleObjectProperty<>();
/**
* 付款方式
*/
private SimpleObjectProperty<ContractPayWay> payWay = new SimpleObjectProperty<>();
/**
* 从实体对象创建视图模型
*/
public static ProjectFundPlanViewModel from(ProjectFundPlan entity) {
ProjectFundPlanViewModel model = new ProjectFundPlanViewModel();
model.update(entity);
return model;
}
@Override
protected void updateFrom(ProjectFundPlan entity) {
super.updateFrom(entity);
// 设置项目关联
getProject().set(entity.getProject());
// 设置付款相关信息
getPayDate().set(entity.getPayDate());
getPayRatio().set(entity.getPayRatio());
getPayCurrency().set(entity.getPayCurrency());
getPayTerm().set(entity.getPayTerm());
// 设置合同付款计划关联
getContractPayPlan().set(entity.getContractPayPlan());
// 设置更新日期
getUpdateDate().set(entity.getUpdateDate());
// 设置付款方式
getPayWay().set(entity.getPayWay());
}
@Override
public boolean copyTo(ProjectFundPlan entity) {
boolean modified = super.copyTo(entity);
// 复制项目关联
if (!Objects.equals(getProject().get(), entity.getProject())) {
entity.setProject(getProject().get());
modified = true;
}
// 复制付款相关信息
if (!Objects.equals(getPayDate().get(), entity.getPayDate())) {
entity.setPayDate(getPayDate().get());
modified = true;
}
if (!NumberUtils.equals(getPayRatio().get(), entity.getPayRatio())) {
entity.setPayRatio(getPayRatio().get());
modified = true;
}
if (!NumberUtils.equals(getPayCurrency().get(), entity.getPayCurrency())) {
entity.setPayCurrency(getPayCurrency().get());
modified = true;
}
if (!Objects.equals(getPayTerm().get(), entity.getPayTerm())) {
entity.setPayTerm(getPayTerm().get());
modified = true;
}
// 复制合同付款计划关联
if (!Objects.equals(getContractPayPlan().get(), entity.getContractPayPlan())) {
entity.setContractPayPlan(getContractPayPlan().get());
modified = true;
}
// 复制更新日期
if (!Objects.equals(getUpdateDate().get(), entity.getUpdateDate())) {
entity.setUpdateDate(getUpdateDate().get());
modified = true;
}
// 复制付款方式
if (!Objects.equals(getPayWay().get(), entity.getPayWay())) {
entity.setPayWay(getPayWay().get());
modified = true;
}
return modified;
}
}

View File

@@ -14,11 +14,13 @@ import com.ecep.contract.manager.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.manager.ds.vendor.vo.CompanyVendorViewModel;
import com.ecep.contract.manager.ui.tab.AbstEntityBasedTabSkin;
import com.ecep.contract.manager.ui.ComboBoxUtils;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.event.ActionEvent;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.util.converter.LocalDateStringConverter;
import javafx.util.converter.LocalDateTimeStringConverter;
import java.time.format.DateTimeFormatter;
import java.util.List;
@@ -36,7 +38,7 @@ public class CompanyVendorTabSkinBase
return controller.baseInfoTab;
}
CompanyContactService getCompanyContactService(){
CompanyContactService getCompanyContactService() {
if (companyContactService == null) {
companyContactService = SpringApp.getBean(CompanyContactService.class);
}
@@ -45,14 +47,15 @@ public class CompanyVendorTabSkinBase
@Override
public void initializeTab() {
// initializeBaseTabCompanyFieldAutoCompletion(controller.companyField);
// initializeBaseTabCompanyFieldAutoCompletion(controller.companyField);
LocalDateStringConverter converter = new LocalDateStringConverter(DateTimeFormatter.ISO_LOCAL_DATE, null);
controller.developDateField.setConverter(converter);
controller.developDateField.valueProperty().bindBidirectional(viewModel.getDevelopDate());
List<VendorTypeLocal> vendorTypeLocals = getCompanyVendorService().findAllTypes(controller.getLocale().toLanguageTag());
List<VendorTypeLocal> vendorTypeLocals = getCompanyVendorService()
.findAllTypes(controller.getLocale().toLanguageTag());
ComboBoxUtils.initialComboBox(controller.catalogField, vendorTypeLocals, true);
ComboBoxUtils.bindComboBox(controller.catalogField, viewModel.getType(), vendorTypeLocals);
@@ -62,7 +65,10 @@ public class CompanyVendorTabSkinBase
controller.pathField.textProperty().bind(viewModel.getPath());
controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription());
controller.createdField.textProperty().bind(createStringBinding(viewModel.getCreated()));
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(MyDateTimeUtils.DEFAULT_DATETIME_FORMAT_PATTERN);
controller.createdField.textProperty().bindBidirectional(viewModel.getCreated(),
new LocalDateTimeStringConverter(dateTimeFormatter, null));
controller.versionLabel.textProperty().bind(viewModel.getVersion().asString());
controller.relativeCompanyBtn.disableProperty().bind(viewModel.getCompany().isNull());

View File

@@ -8,8 +8,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.table.TableTabSkin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
@@ -20,6 +18,8 @@ import org.springframework.data.jpa.domain.Specification;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.manager.ui.table.EditableEntityTableTabSkin;
import com.ecep.contract.manager.ui.table.TableTabSkin;
import com.ecep.contract.manager.util.TableViewUtils;
import com.ecep.contract.manager.util.UITools;
@@ -43,12 +43,15 @@ import javafx.scene.input.KeyEvent;
import javafx.util.converter.NumberStringConverter;
/**
* 实体管理器皮肤
* 提供了实体管理器的基本功能,如查询、新增、删除、修改、分页等
*
* @param <T> Entity 的类型
* @param <TV> Entity 对应的ViewModel
* @param <Skin> Skin 的类型
* @param <SKIN> Skin 的类型
* @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> {
private static final Logger logger = LoggerFactory.getLogger(AbstEntityManagerSkin.class);
/**
@@ -61,6 +64,10 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
protected PageRequest currentPageable = PageRequest.ofSize(25);
protected final SimpleIntegerProperty currentPageNumber = new SimpleIntegerProperty();
// 是否允许调整表格高度
private boolean allowResize = true;
// 记录延时任务信息
private ScheduledFuture<?> loadTableDataSetFuture;
public AbstEntityManagerSkin(C controller) {
this.controller = controller;
@@ -199,6 +206,9 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
* 根据表格高度重新计算分页的页大小
*/
private void resizeTable(Object observable, Bounds old, Bounds newBounds) {
if (!allowResize) {
return;
}
double tableHeight = newBounds.getHeight();
if (tableHeight <= 0) {
return;
@@ -208,8 +218,13 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
if (lookup != null) {
double rowHeight = lookup.prefHeight(-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) {
return;
}
@@ -259,9 +274,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();
Property<K> property = function.apply(row);
Property<K> property = propGetter.apply(row);
property.setValue(event.getNewValue());
try {
saveRowData(row);
@@ -317,21 +340,47 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
row.saveInFxApplicationThread(service);
}
/**
* 加载行数据
*
* @param row
* @return
*/
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) {
if (entity == null) {
return;
}
ViewModelService<T, TV> service = getViewModelService();
getViewModelService().delete(entity);
}
/**
* 保存行数据
*
* @param entity
* @return
*/
public T saveRowData(T entity) {
if (entity == null) {
return null;
}
ViewModelService<T, TV> service = getViewModelService();
return getViewModelService().save(entity);
}
// 记录延时任务信息
private ScheduledFuture<?> loadTableDataSetFuture;
@Override
public void loadTableDataSet() {
loadTableDataSet(false);
@@ -359,6 +408,7 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
private CompletableFuture<Void> _reloadTableData() {
CompletableFuture<Void> future = new CompletableFuture<>();
Platform.runLater(() -> {
allowResize = false; // 禁用调整
dataSet.clear();
runAsync(() -> {
controller.setStatus("载入中...");
@@ -366,8 +416,10 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
Platform.runLater(() -> {
try {
updateTableDataSet(models);
allowResize = true; // 恢复调整
future.complete(null);
} catch (Exception e) {
allowResize = true; // 恢复调整
future.completeExceptionally(e);
}
});
@@ -379,15 +431,41 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return future;
}
/**
* 更新表格数据
*
* @param models
*/
protected void updateTableDataSet(List<TV> models) {
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;
if (logger.isDebugEnabled()) {
logger.debug("update table dataSet cost: {} ms", timeCost);
}
}
/**
* 加载表格数据
*
* @return
*/
protected List<TV> loadTableData() {
Specification<T> spec = getSpecification();
ViewModelService<T, TV> service = getViewModelService();
@@ -404,16 +482,24 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return page.map(service::from).toList();
}
/**
* 获取ViewModelService
*
* @return
*/
protected ViewModelService<T, TV> getViewModelService() {
ViewModelService<T, TV> service = controller.getViewModelService();
if (service == null) {
if (logger.isWarnEnabled()) {
logger.warn("ViewModelService is null");
}
throw new IllegalArgumentException("ViewModelService is null");
}
return service;
}
/**
* 获取查询条件
*
* @return
*/
public Specification<T> getSpecification() {
TextField field = controller.searchKeyField;
if (field != null) {
@@ -422,8 +508,14 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
return null;
}
/**
* 获取查询条件
*
* @param searchText
* @return
*/
protected Specification<T> getSpecification(String searchText) {
return controller.getViewModelService().getSpecification(searchText);
return getViewModelService().getSpecification(searchText);
}
/**
@@ -434,6 +526,11 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
protected void onTableRowDoubleClickedAction(TV item) {
}
/**
* 更新页脚
*
* @param page
*/
protected void updateFooter(Page<T> page) {
Platform.runLater(() -> {
controller.previousPageBtn.setDisable(!page.hasPrevious());
@@ -444,19 +541,44 @@ public abstract class AbstEntityManagerSkin<T extends IdentityEntity, TV extends
});
}
/**
* 获取表格排序
*
* @return
*/
public List<Sort.Order> getTableOrders() {
return TableViewUtils.getOrders(getTableView());
}
/**
* 获取表格排序
*
* @return
*/
public Sort getSortByTable() {
if (getTableView() == null) {
return Sort.unsorted();
}
return Sort.by(getTableOrders());
}
/**
* 获取分页参数
*
* @return
*/
public Pageable getPageable() {
Sort sort = getSortByTable();
return currentPageable.withSort(sort);
}
/**
* 显示在当前窗口为父窗口的新窗口
*
* @param <Controller> 控制器类型
* @param clz 控制器类
* @param model 数据
*/
protected <Controller extends AbstEntityController<T, TV>> void showInOwner(Class<Controller> clz, TV model) {
BaseController.show(clz, model, getTableView().getScene().getWindow());
}

View File

@@ -1,11 +1,13 @@
package com.ecep.contract.manager.ui;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.manager.ui.tab.TabSkin;
import com.ecep.contract.manager.util.UITools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
public abstract class AbstGenericTabSkin<C extends BaseController> implements TabSkin {
private static final Logger logger = LoggerFactory.getLogger(AbstGenericTabSkin.class);
@@ -18,6 +20,10 @@ public abstract class AbstGenericTabSkin<C extends BaseController> implements Ta
this.controller = controller;
}
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
return controller.getCachedBean(requiredType);
}
protected void setStatus(String status) {
controller.setStatus(status);
}
@@ -38,4 +44,5 @@ public abstract class AbstGenericTabSkin<C extends BaseController> implements Ta
}
UITools.showExceptionAndWait(message, ex);
}
}

View File

@@ -1,5 +1,21 @@
package com.ecep.contract.manager.ui;
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 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.manager.CurrentEmployee;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.SpringApp;
import com.ecep.contract.manager.ds.other.model.Employee;
@@ -9,6 +25,7 @@ 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.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@@ -21,21 +38,6 @@ import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
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;
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);
@@ -45,7 +47,8 @@ public class BaseController {
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();
if (toFront(key)) {
return null;
@@ -65,8 +68,8 @@ public class BaseController {
}
@SuppressWarnings("unchecked")
public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController>
void show(Class<T> clz, M viewModel, Window owner) {
public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController> void show(
Class<T> clz, M viewModel, Window owner) {
String key = getKey(clz, viewModel);
if (toFront(key)) {
return;
@@ -76,7 +79,6 @@ public class BaseController {
throw new RuntimeException("@FxmlPath is required");
}
FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
T controller = loader.getController();
if (controller instanceof AbstEntityController<?, ?>) {
@@ -114,7 +116,6 @@ public class BaseController {
});
}
public static boolean toFront(String key) {
Stage stage = stages.get(key);
if (stage != null) {
@@ -161,6 +162,11 @@ public class BaseController {
return future;
}
public CompletableFuture<Void> runAsync(Runnable runnable) {
return CompletableFuture.runAsync(runnable, Desktop.instance.getExecutorService())
.exceptionally(this::handleException);
}
/**
* 窗口标题
*/
@@ -177,33 +183,30 @@ public class BaseController {
private String stageKey;
public Label leftStatusLabel;
public Label rightStatusLabel;
@Getter
@Setter
private Locale locale = Locale.getDefault();
@Setter
private SysConfService confService;
@Setter
private EmployeeService employeeService;
private Employee currentUser;
private HashMap<Class<?>, Object> cachedBeans = new HashMap<>();
protected <T> T getBean(Class<T> requiredType) throws BeansException {
return SpringApp.getBean(requiredType);
}
public SysConfService getConfService() {
if (confService == null) {
confService = getBean(SysConfService.class);
@SuppressWarnings("unchecked")
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
Object object = cachedBeans.get(requiredType);
if (object == null) {
object = getBean(requiredType);
cachedBeans.put(requiredType, object);
}
return confService;
return (T) object;
}
public SysConfService getConfService() {
return getCachedBean(SysConfService.class);
}
public EmployeeService getEmployeeService() {
if (employeeService == null) {
employeeService = getBean(EmployeeService.class);
}
return employeeService;
return getCachedBean(EmployeeService.class);
}
public Employee getCurrentUser() {
@@ -213,6 +216,18 @@ public class BaseController {
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) {
return SpringApp.getMessage(code, args, getLocale());
}

View File

@@ -6,6 +6,13 @@ import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.service.IEntityService;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
/**
* 视图模型服务接口
*
* @param <T> 实体类型
* @param <TV> 视图模型类型
* @author 2025-08-02
*/
public interface ViewModelService<T extends IdentityEntity, TV extends IdentityViewModel<T>>
extends IEntityService<T> {

View File

@@ -1,5 +1,13 @@
package com.ecep.contract.manager.ui.tab;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
import com.ecep.contract.manager.ui.AbstEntityController;
@@ -7,26 +15,15 @@ import com.ecep.contract.manager.ui.AbstGenericTabSkin;
import com.ecep.contract.manager.ui.BaseController;
import com.ecep.contract.manager.ui.FxmlPath;
import com.ecep.contract.manager.util.FxmlUtils;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import com.ecep.contract.manager.util.UITools;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
public abstract class AbstEntityBasedTabSkin<C extends AbstEntityController<T, V>, T extends IdentityEntity, V extends IdentityViewModel<T>>
extends AbstGenericTabSkin<C> {
@@ -184,20 +181,4 @@ public abstract class AbstEntityBasedTabSkin<C extends AbstEntityController<T, V
}
}
}
protected String localDateTimeFormatter(Property<LocalDateTime> property) {
LocalDateTime dateTime = property.getValue();
if (dateTime == null) {
return "";
}
return MyDateTimeUtils.format(dateTime);
}
protected StringBinding createStringBinding(Property<LocalDateTime> property) {
return Bindings.createStringBinding(
() -> localDateTimeFormatter(property),
property);
}
}

View File

@@ -3,6 +3,8 @@ package com.ecep.contract.manager.ui.table.cell;
import java.util.concurrent.Future;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
@@ -18,6 +20,8 @@ import javafx.application.Platform;
* @param <T>
*/
public class AsyncUpdateTableCell<V, T extends IdentityEntity> extends javafx.scene.control.TableCell<V, T> {
private static final Logger logger = LoggerFactory.getLogger(AsyncUpdateTableCell.class);
/**
* 转换为文本
*/
@@ -32,6 +36,20 @@ public class AsyncUpdateTableCell<V, T extends IdentityEntity> extends javafx.sc
}
private IEntityService<T> service;
Future<?> syncFuture;
/**
* 通过 #setService 设置服务类,或者子类实现, 提供服务类
*
* @return
*/
protected IEntityService<T> getServiceBean() {
return null;
}
public String format(T entity) {
return toString(entity);
}
public void setService(IEntityService<T> service) {
this.service = service;
@@ -44,13 +62,15 @@ public class AsyncUpdateTableCell<V, T extends IdentityEntity> extends javafx.sc
return service;
}
protected IEntityService<T> getServiceBean() {
return null;
}
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
// 取消之前的异步任务
if (syncFuture != null) {
syncFuture.cancel(true);
syncFuture = null;
}
if (empty || item == null) {
setText(null);
return;
@@ -59,23 +79,45 @@ public class AsyncUpdateTableCell<V, T extends IdentityEntity> extends javafx.sc
setText(format(item));
return;
}
setText("# " + item.getId());
submit(this::asyncLoadAndUpdate);
syncFuture = submit(this::asyncLoadAndUpdate);
}
/**
* 检查实体是否已初始化
*
* @param proxy
* @return
*/
protected boolean isInitialized(T proxy) {
return Hibernate.isInitialized(proxy);
}
/**
* 初始化实体
*
* @return
*/
protected T initialize() {
return getService().findById(getItem().getId());
}
/**
* 提交异步任务
*
* @param var1 异步任务
* @return
*/
Future<?> submit(Runnable var1) {
return Desktop.instance.getExecutorService().submit(var1);
}
public String format(T entity) {
return toString(entity);
}
protected void asyncLoadAndUpdate() {
if (getItem() == null) {
return;
}
T entity = initialize();
String formated;
try {
@@ -84,14 +126,26 @@ public class AsyncUpdateTableCell<V, T extends IdentityEntity> extends javafx.sc
formated = e.getMessage();
}
String texted = formated;
// 保存当前需要更新的项目信息,避免闭包引用导致的问题
final T updatedEntity = entity;
final String updatedText = texted;
final T finalCurrentLoadingItem = getItem();
Platform.runLater(() -> {
setText(texted);
setItem(entity);
// 检查单元格是否仍然显示相同的项目
// 如果单元格已被重用或当前项目已改变,则不更新
if (isEmpty() || getItem() == null) {
logger.debug("Skipping async update - cell reused ");
return;
}
if (getItem().getId() != finalCurrentLoadingItem.getId()) {
logger.debug("Skipping async update - cell item changed");
return;
}
setItem(updatedEntity);
setText(updatedText);
});
}
protected T initialize() {
return getService().findById(getItem().getId());
}
}

View File

@@ -1,11 +1,14 @@
package com.ecep.contract.manager.ui.table.cell;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import lombok.NoArgsConstructor;
import static com.ecep.contract.manager.SpringApp.getBean;
import com.ecep.contract.manager.ds.company.model.Company;
import com.ecep.contract.manager.ds.company.service.CompanyService;
import lombok.NoArgsConstructor;
/**
* 公司单元格
*/
@NoArgsConstructor
public class CompanyTableCell<V> extends AsyncUpdateTableCell<V, Company> {

View File

@@ -0,0 +1,25 @@
package com.ecep.contract.manager.ui.table.cell;
import com.ecep.contract.manager.ds.other.model.EmployeeRole;
import com.ecep.contract.manager.ds.other.service.EmployeeRoleService;
import lombok.NoArgsConstructor;
import static com.ecep.contract.manager.SpringApp.getBean;
/**
* 角色表格列
*
* @param <V>
*/
@NoArgsConstructor
public class EmployeeRoleTableCell<V> extends AsyncUpdateTableCell<V, EmployeeRole> {
public EmployeeRoleTableCell(EmployeeRoleService service) {
setService(service);
}
@Override
protected EmployeeRoleService getServiceBean() {
return getBean(EmployeeRoleService.class);
}
}

View File

@@ -162,7 +162,7 @@ public class TaskMonitorCenter {
tooltip.setText("当前运行任务数: " + activeTasks.size());
});
monitorLabel.setOnMouseClicked(event -> {
BaseController.show(TaskMonitorViewController.class, monitorLabel.getScene().getWindow());
BaseController.show(TaskMonitorViewController.class, monitorLabel.getScene().getWindow());
});
}
}

View File

@@ -1,5 +1,9 @@
package com.ecep.contract.manager.ui.task;
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 org.springframework.context.annotation.Lazy;
@@ -13,6 +17,7 @@ import com.ecep.contract.manager.ui.Message;
import com.ecep.contract.manager.util.MyDateTimeUtils;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
@@ -30,6 +35,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.text.Text;
import javafx.stage.WindowEvent;
@@ -69,10 +75,47 @@ public class TaskMonitorViewController extends BaseController {
private Button cancelTaskButton;
@FXML
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() {
}
/**
* 用于存储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
public void onShown(WindowEvent windowEvent) {
super.onShown(windowEvent);
@@ -144,6 +187,94 @@ public class TaskMonitorViewController extends BaseController {
});
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信息时发生错误"));
}
}
/**
@@ -205,7 +336,7 @@ public class TaskMonitorViewController extends BaseController {
text.setFill(Color.ORANGE);
} else if (msg.getLevel() == Level.SEVERE) {
text.setFill(Color.RED);
}else if (msg.getLevel()== Level.CONFIG) {
} else if (msg.getLevel() == Level.CONFIG) {
text.setFill(Color.GRAY);
}
vBox.getChildren().add(text);
@@ -218,4 +349,11 @@ public class TaskMonitorViewController extends BaseController {
alert.getDialogPane().setContent(scrollPane);
alert.showAndWait();
}
/**
* 运行任务监控演示
*/
public void onRunTaskMonitorDemo(ActionEvent event) {
com.ecep.contract.manager.ui.task.DemoTask.runDemo();
}
}

View File

@@ -7,6 +7,12 @@ import java.time.LocalDateTime;
import java.time.ZoneOffset;
public class MyDateTimePropertyUtils {
/**
* 本地日期时间转换为时间戳
*
* @param property
* @return
*/
public static Instant localDateTimeToInstant(SimpleObjectProperty<LocalDateTime> property) {
LocalDateTime dateTime = property.get();
if (dateTime != null) {

View File

@@ -1,14 +1,16 @@
package com.ecep.contract.manager.util;
import com.ecep.contract.manager.AppV2;
import com.ecep.contract.manager.SpringApp;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import com.ecep.contract.manager.AppV2;
import com.ecep.contract.manager.Desktop;
import com.ecep.contract.manager.SpringApp;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
public class FxmlUtils {
public static FXMLLoader newLoader(String path) {
@@ -22,8 +24,8 @@ public class FxmlUtils {
return loader;
}
public static CompletableFuture<FXMLLoader> newLoaderAsync(String path, java.util.function.Consumer<FXMLLoader> consumer) {
public static CompletableFuture<FXMLLoader> newLoaderAsync(String path,
java.util.function.Consumer<FXMLLoader> consumer) {
return CompletableFuture.supplyAsync(() -> {
FXMLLoader loader = newLoader(path);
try {
@@ -43,16 +45,15 @@ public class FxmlUtils {
/**
* 异步载入显示界面
*
* @param path fxml文件路径类地址 / 开头 根路径
* @param path fxml文件路径类地址 / 开头 根路径
* @param initializeLoader fxml 文件加载完毕后,回调函数
* @param runLater 在JavaFx线程中执行的回调函数
* @param runLater 在JavaFx线程中执行的回调函数
* @return CompletableFuture
*/
public static CompletableFuture<Void> newLoaderAsyncWithRunLater(
String path,
java.util.function.Consumer<FXMLLoader> initializeLoader,
java.util.function.Consumer<FXMLLoader> runLater
) {
java.util.function.Consumer<FXMLLoader> runLater) {
CompletableFuture<Void> future = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try {
@@ -72,7 +73,7 @@ public class FxmlUtils {
} catch (IOException e) {
future.completeExceptionally(new RuntimeException("Unable open " + path, e));
}
}).whenComplete((v, ex) -> {
}, Desktop.instance.getExecutorService()).whenComplete((v, ex) -> {
if (ex != null) {
future.completeExceptionally(ex);
}

View File

@@ -29,6 +29,7 @@
<logger name="com.ecep.contract.manager.ui.service" level="info"/>
<logger name="com.ecep.contract.manager.ui.table.AbstEntityTableTabSkin" level="debug"/>
<logger name="com.ecep.contract.manager.ui.table.cell.AsyncUpdateTableCell" level="warn"/>
<logger name="com.ecep.contract.manager.ui.AbstEntityManagerSkin" level="debug"/>
<logger name="com.ecep.contract.manager.ui.BaseController" level="debug"/>
<logger name="com.ecep.contract.manager.ui.controller" level="info"/>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import org.controlsfx.control.ToggleSwitch?>
<VBox prefHeight="400.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.cloud.u8.YongYouU8ConfigWindowController">
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="50.0" prefWidth="250.0" />
<ColumnConstraints fillWidth="false" halignment="RIGHT" hgrow="NEVER" maxWidth="5.0" minWidth="5.0" prefWidth="5.0" />
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints fillHeight="false" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="同步时公司未创建时, 公司开发日期在此之后的自动创建" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<DatePicker fx:id="auto_create_company_after" GridPane.columnIndex="2" />
<Label text="cloud.u8.contract.latestDate" GridPane.rowIndex="4" />
<TextField fx:id="contract_latest_date" GridPane.columnIndex="2" GridPane.rowIndex="4" />
<Label text="cloud.u8.contract.latestId" GridPane.rowIndex="5" />
<TextField fx:id="contract_latest_id" GridPane.columnIndex="2" GridPane.rowIndex="5" />
<Label text="cloud.u8.sync.elapse" GridPane.rowIndex="6" />
<TextField fx:id="sync_elapse" GridPane.columnIndex="2" GridPane.rowIndex="6" />
<Label text="创建在此之后日期的公司" />
<ToggleSwitch fx:id="use_latest_id" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<Label text="使用latestId同步" GridPane.rowIndex="2" />
<Label text="合同同步时是否使用最后更新的Id来判断更新范围否则使用最后更新的合同日期来判断更新范围" GridPane.columnIndex="2" GridPane.rowIndex="3" />
</children>
</GridPane>
</children>
</VBox>

View File

@@ -2,7 +2,7 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
<VBox prefHeight="400.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.ecep.contract.manager.cloud.u8.YongYouU8ManagerWindowController">
<children>
<MenuBar VBox.vgrow="NEVER">
@@ -18,7 +18,7 @@
<MenuItem mnemonicParsing="false" text="Save As…"/>
<MenuItem mnemonicParsing="false" text="Revert"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem mnemonicParsing="false" text="Preferences…"/>
<MenuItem mnemonicParsing="false" text="配置…" onAction="#onConfigAction"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem mnemonicParsing="false" text="Quit"/>
</items>

View File

@@ -1,212 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import org.controlsfx.control.TaskProgressView?>
<VBox fx:id="root" prefHeight="468.0" prefWidth="737.0" xmlns="http://javafx.com/javafx/22"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.ecep.contract.manager.ds.other.controller.HomeWindowController">
<children>
<MenuBar>
<menus>
<Menu text="文件(_F)">
<items>
<MenuItem mnemonicParsing="false" text="Action 1"/>
<MenuItem mnemonicParsing="false" onAction="#openConfigWindow" text="选项…"/>
<Menu text="数据源">
<MenuItem mnemonicParsing="false" onAction="#openGroupRKResourceWindow" text="集团相关方…"/>
<MenuItem mnemonicParsing="false" onAction="#openTycResourceWindow" text="天眼查…"/>
<MenuItem mnemonicParsing="false" onAction="#openYongYouResourceWindow" text="用友U8…"/>
</Menu>
<children>
<MenuBar>
<menus>
<Menu text="文件(_F)">
<items>
<MenuItem mnemonicParsing="false" text="Action 1"/>
<MenuItem mnemonicParsing="false" onAction="#openConfigWindow" text="选项…"/>
<Menu text="数据源">
<MenuItem mnemonicParsing="false" onAction="#openGroupRKResourceWindow" text="集团相关方…"/>
<MenuItem mnemonicParsing="false" onAction="#openTycResourceWindow" text="天眼查…"/>
</Menu>
</items>
</Menu>
<Menu mnemonicParsing="false" text="公司">
<items>
</items>
</Menu>
<Menu mnemonicParsing="false" text="公司">
<items>
</items>
</Menu>
<Menu mnemonicParsing="false" text="用友U8">
<items>
<MenuItem mnemonicParsing="false" onAction="#createNewU8ContractSyncTaskAction"
text="同步合同"/>
</items>
</Menu>
<Menu text="设置(_S)">
<items>
<MenuItem onAction="#onShowEmployeeManagerWindowAction" text="员工管理(_E)"/>
<MenuItem onAction="#onShowDepartmentManagerWindowAction" text="部门管理(_D)"/>
<MenuItem onAction="#onShowRolesManagerWindowAction" text="角色管理(_R)"/>
<MenuItem onAction="#onShowFunctionManagerWindowAction" text="功能管理(_R)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowBankManagerWindowAction" text="银行管理(_B)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowTaskMonitorWindowAction" text="任务监控(_M)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowInventoryManagerWindowAction" text="存货管理(_I)"/>
</items>
</Menu>
<Menu text="帮助(_H)">
<items>
<MenuItem mnemonicParsing="false" onAction="#openInBrowse" text="国家企业信用信息公示系统"
userData="https://www.gsxt.gov.cn/"/>
<MenuItem mnemonicParsing="false" onAction="#openInBrowse" text="天眼查-商业查询平台"
userData="https://www.tianyancha.com/"/>
<MenuItem mnemonicParsing="false" text="关于"/>
</items>
</Menu>
</menus>
</MenuBar>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="openCompanyManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;"
userData="com.ecep.contract.manager.ds.company.controller.CompanyManagerWindowController">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
<image>
<Image url="@img/f4.png"/>
</image>
</ImageView>
<Label text="公司">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openProjectManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f0.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="项目">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openContractManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f1.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="合同">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openVendorManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f2.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="供应商">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<!-- 客户 -->
<Button fx:id="openCustomManagerWindow" layoutX="138.0" layoutY="10.0" mnemonicParsing="false"
style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f3.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="客户">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<!-- 任务监控演示 -->
<Button mnemonicParsing="false" style="-fx-padding: 6px;" onAction="#onRunTaskMonitorDemo">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="任务监控演示">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
</items>
<opaqueInsets>
<Insets/>
</opaqueInsets>
<padding>
<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
</padding>
</ToolBar>
<AnchorPane prefHeight="248.0" prefWidth="2239.0" VBox.vgrow="ALWAYS"/>
<TaskProgressView fx:id="taskProgressView"/>
<HBox fx:id="statusBar" spacing="5.0">
<children>
<Label fx:id="leftStatusLabel" text="Label" wrapText="true"/>
<Pane prefHeight="12.0" prefWidth="12.0" HBox.hgrow="ALWAYS"/>
<Label fx:id="rightStatusLabel" layoutX="10.0" layoutY="10.0" text="-" wrapText="true"
HBox.hgrow="SOMETIMES">
<HBox.margin>
<Insets left="6.0"/>
</HBox.margin>
</Menu>
<Menu mnemonicParsing="false" text="用友U8">
<items>
<MenuItem mnemonicParsing="false" onAction="#createNewU8ContractSyncTaskAction"
text="同步合同"/>
<MenuItem mnemonicParsing="false" onAction="#openYongYouResourceWindow" text="用友U8…"/>
</items>
</Menu>
<Menu text="设置(_S)">
<items>
<MenuItem onAction="#onShowEmployeeManagerWindowAction" text="员工管理(_E)"/>
<MenuItem onAction="#onShowDepartmentManagerWindowAction" text="部门管理(_D)"/>
<MenuItem onAction="#onShowRolesManagerWindowAction" text="角色管理(_R)"/>
<MenuItem onAction="#onShowFunctionManagerWindowAction" text="功能管理(_R)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowBankManagerWindowAction" text="银行管理(_B)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowTaskMonitorWindowAction" text="任务监控(_M)"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem onAction="#onShowInventoryManagerWindowAction" text="存货管理(_I)"/>
</items>
</Menu>
<Menu text="帮助(_H)">
<items>
<MenuItem mnemonicParsing="false" onAction="#openInBrowse" text="国家企业信用信息公示系统"
userData="https://www.gsxt.gov.cn/"/>
<MenuItem mnemonicParsing="false" onAction="#openInBrowse" text="天眼查-商业查询平台"
userData="https://www.tianyancha.com/"/>
<MenuItem mnemonicParsing="false" text="关于"/>
</items>
</Menu>
</menus>
</MenuBar>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="openCompanyManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;"
userData="com.ecep.contract.manager.ds.company.controller.CompanyManagerWindowController">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
<image>
<Image url="@img/f4.png"/>
</image>
</ImageView>
<Label text="公司">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
<Label fx:id="taskMonitorLabel" text="任务监控"></Label>
<Label fx:id="employeeStatusLabel" layoutX="711.0" layoutY="10.0" text="管理员" wrapText="true"
HBox.hgrow="SOMETIMES">
<HBox.margin>
<Insets left="6.0"/>
</HBox.margin>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openProjectManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f0.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="项目">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</HBox>
</children>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openContractManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f1.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="合同">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="openVendorManagerWindow" mnemonicParsing="false" style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f2.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="供应商">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
<!-- 客户 -->
<Button fx:id="openCustomManagerWindow" layoutX="138.0" layoutY="10.0" mnemonicParsing="false"
style="-fx-padding: 6px;">
<graphic>
<VBox alignment="CENTER">
<children>
<ImageView fitHeight="46.0" fitWidth="64.0">
<Image url="@img/f3.png"/>
<viewport>
<Rectangle2D height="46.0" width="64.0"/>
</viewport>
</ImageView>
<Label text="客户">
<VBox.margin>
<Insets top="5.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
</graphic>
</Button>
</items>
<opaqueInsets>
<Insets/>
</opaqueInsets>
<padding>
<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
</padding>
</ToolBar>
<AnchorPane prefHeight="248.0" prefWidth="2239.0" VBox.vgrow="ALWAYS"/>
<TaskProgressView fx:id="taskProgressView"/>
<HBox fx:id="statusBar" spacing="5.0">
<children>
<Label fx:id="leftStatusLabel" text="Label" wrapText="true"/>
<Pane prefHeight="12.0" prefWidth="12.0" HBox.hgrow="ALWAYS"/>
<Label fx:id="rightStatusLabel" layoutX="10.0" layoutY="10.0" text="-" wrapText="true"
HBox.hgrow="SOMETIMES">
<HBox.margin>
<Insets left="6.0"/>
</HBox.margin>
</Label>
<Label fx:id="taskMonitorLabel" text="任务监控"/>
<Label fx:id="employeeStatusLabel" layoutX="711.0" layoutY="10.0" text="管理员" wrapText="true"
HBox.hgrow="SOMETIMES">
<HBox.margin>
<Insets left="6.0"/>
</HBox.margin>
</Label>
</children>
</HBox>
</children>
</VBox>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.ds.project.controller.ProjectTabSkinFundPlan">
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<HBox spacing="3.0">
<children>
<TextField fx:id="searchKeyField" promptText="检索关键字"/>
<Button fx:id="searchBtn" mnemonicParsing="false" text="检索"/>
<Separator orientation="VERTICAL"/>
<Button fx:id="updatePlanBtn" mnemonicParsing="false" text="更新"/>
</children>
<padding>
<Insets bottom="10.0" left="7.0" right="10.0" top="10.0"/>
</padding>
</HBox>
<TableView fx:id="table" VBox.vgrow="ALWAYS" sortable="false">
<columns>
<TableColumn fx:id="idColumn" prefWidth="60.0" sortable="true" text="ID"/>
<TableColumn fx:id="payDateColumn" prefWidth="120.0" sortable="true" text="付款日期"/>
<TableColumn fx:id="payWayColumn" prefWidth="120.0" sortable="true" text="付款方式"/>
<TableColumn fx:id="payCurrencyColumn" prefWidth="120.0" sortable="true" text="付款金额"/>
<TableColumn fx:id="payRatioColumn" prefWidth="100.0" sortable="true" text="付款比例"/>
<TableColumn fx:id="payTermColumn" prefWidth="200.0" sortable="true" text="付款条件"/>
<TableColumn fx:id="updateDateColumn" prefWidth="180.0" sortable="true" text="更新日期"/>
</columns>
</TableView>
</children>
</VBox>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
</AnchorPane>

View File

@@ -191,6 +191,8 @@
</Tab>
<Tab fx:id="contractTab" text="相关合同">
</Tab>
<Tab fx:id="fundPlanTab" text="资金计划">
</Tab>
<Tab fx:id="satisfactionTab" text="顾客满意度">
</Tab>
<Tab text="发货">

View File

@@ -8,56 +8,83 @@
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.VBox?>
<TabPane minHeight="400.0" minWidth="500.0" tabMinWidth="70.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.ui.task.TaskMonitorViewController">
<tabs>
<VBox minHeight="400.0" minWidth="500.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.manager.ui.task.TaskMonitorViewController">
<ToolBar>
<items>
<Button fx:id="runTaskMonitorDemoBtn" text="运行演示任务" onAction="#onRunTaskMonitorDemo"/>
</items>
</ToolBar>
<TabPane tabMinWidth="70.0" VBox.vgrow="ALWAYS">
<tabs>
<Tab closable="false" text="活动任务">
<content>
<VBox>
<children>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="cancelTaskButton" text="取消选中任务" />
</items>
</ToolBar>
<TableView fx:id="activeTasksTable">
<columns>
<TableColumn fx:id="activeTaskIdColumn" prefWidth="150.0" text="任务ID" />
<TableColumn fx:id="activeTaskTitleColumn" prefWidth="200.0" text="任务标题" />
<TableColumn fx:id="activeTaskProgressColumn" prefWidth="150.0" text="进度" />
<TableColumn fx:id="activeTaskStatusColumn" prefWidth="100.0" text="状态" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</VBox>
<VBox>
<children>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="cancelTaskButton" text="取消选中任务"/>
</items>
</ToolBar>
<TableView fx:id="activeTasksTable">
<columns>
<TableColumn fx:id="activeTaskIdColumn" prefWidth="150.0" text="任务ID"/>
<TableColumn fx:id="activeTaskTitleColumn" prefWidth="200.0" text="任务标题"/>
<TableColumn fx:id="activeTaskProgressColumn" prefWidth="150.0" text="进度"/>
<TableColumn fx:id="activeTaskStatusColumn" prefWidth="100.0" text="状态"/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TableView>
</children>
</VBox>
</content>
</Tab>
<Tab closable="false" text="任务历史">
<content>
<content>
<!-- 历史任务区域 -->
<VBox spacing="5.0">
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="clearHistoryButton" alignment="CENTER_RIGHT" text="清空历史" />
</items>
</ToolBar>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="clearHistoryButton" alignment="CENTER_RIGHT" text="清空历史"/>
</items>
</ToolBar>
<TableView fx:id="historyTasksTable" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="historyTaskIdColumn" prefWidth="150.0" text="任务ID" />
<TableColumn fx:id="historyTaskTitleColumn" prefWidth="200.0" text="任务标题" />
<TableColumn fx:id="historyTaskStatusColumn" prefWidth="100.0" text="状态" />
<TableColumn fx:id="historyTaskStartTimeColumn" prefWidth="150.0" text="开始时间" />
<TableColumn fx:id="historyTaskExecutionTimeColumn" prefWidth="100.0" text="执行耗时" />
<TableColumn fx:id="historyTaskIdColumn" prefWidth="150.0" text="任务ID"/>
<TableColumn fx:id="historyTaskTitleColumn" prefWidth="200.0" text="任务标题"/>
<TableColumn fx:id="historyTaskStatusColumn" prefWidth="100.0" text="状态"/>
<TableColumn fx:id="historyTaskStartTimeColumn" prefWidth="150.0" text="开始时间"/>
<TableColumn fx:id="historyTaskExecutionTimeColumn" prefWidth="100.0" text="执行耗时"/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TableView>
</VBox>
</content>
</content>
</Tab>
</tabs>
</TabPane>
<Tab closable="false" text="Executor">
<content>
<VBox spacing="5.0">
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="refreshExecutorInfoButton" text="刷新"/>
</items>
</ToolBar>
<TableView fx:id="executorInfoTable" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="executorFieldColumn" prefWidth="150.0" text="字段"/>
<TableColumn fx:id="executorValueColumn" prefWidth="200.0" text="值"/>
<TableColumn fx:id="executorDescriptionColumn" prefWidth="300.0" text="描述"/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TableView>
</VBox>
</content>
</Tab>
</tabs>
</TabPane>
</VBox>