拆分模块
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 集团相关方平台同步任务
|
||||
*/
|
||||
public class CloudRkSyncTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CloudRkSyncTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("集团相关方平台");
|
||||
updateProgress(1, 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.constant.CloudServiceConstant;
|
||||
import com.ecep.contract.model.Company;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 合并更新
|
||||
*/
|
||||
public class CompanyCompositeUpdateTasker extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCompositeUpdateTasker.class);
|
||||
@Setter
|
||||
private Company company;
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("合并更新 " + company.getName());
|
||||
|
||||
holder.debug("1. 从 " + CloudServiceConstant.RK_NAME + " 更新...");
|
||||
updateProgress(0.1, 1);
|
||||
holder.debug("2. 从 " + CloudServiceConstant.U8_NAME + " 更新...");
|
||||
updateProgress(0.3, 1);
|
||||
holder.debug("3. 从 " + CloudServiceConstant.TYC_NAME + " 更新...");
|
||||
updateProgress(1, 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 对所有公司的文件进行重置
|
||||
*/
|
||||
public class CompanyFilesRebuildTasker extends Tasker<Object> {
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("合同文件重置");
|
||||
holder.debug("遍历所有公司,对每个可以公司的文件进行“重置”操作");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 更新供应商评价表
|
||||
*/
|
||||
public class CompanyVendorEvaluationFormUpdateTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompanyVendorEvaluationFormUpdateTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("更新供应商评价表");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.model.Company;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class CompanyVerifyTasker extends Tasker<Object> {
|
||||
@Getter
|
||||
@Setter
|
||||
private Company company;
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("验证企业是否符合合规要求");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.model.Company;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ContractRepairByCompanyTask extends Tasker<Object> {
|
||||
@Getter
|
||||
@Setter
|
||||
private Company company;
|
||||
@Getter
|
||||
boolean repaired = false;
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("同步修复 " + company.getName() + " 下的所有合同");
|
||||
updateProgress(1, 1);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.model.Contract;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ContractRepairTask extends Tasker<Object> {
|
||||
@Getter
|
||||
@Setter
|
||||
private Contract contract;
|
||||
|
||||
@Getter
|
||||
boolean repaired = false;
|
||||
@Getter
|
||||
protected boolean filesUpdated = false;
|
||||
@Getter
|
||||
protected boolean itemsUpdated = false;
|
||||
@Getter
|
||||
protected boolean playPlanUpdated = false;
|
||||
@Getter
|
||||
protected boolean saleOrderUpdated = false;
|
||||
|
||||
public ContractRepairTask() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("修复合同 " + contract.toPrettyString());
|
||||
updateProgress(1, 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 合同同步任务
|
||||
*/
|
||||
public class ContractSyncTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ContractSyncTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("用友U8系统-同步合同");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.NumberFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.ecep.contract.CompanyCustomerFileType;
|
||||
import com.ecep.contract.ContractFileType;
|
||||
import com.ecep.contract.ContractPayWay;
|
||||
import com.ecep.contract.Desktop;
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.MyDateTimeUtils;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.ds.project.ProjectCostImportItemsFromContractsTasker;
|
||||
import com.ecep.contract.model.Company;
|
||||
import com.ecep.contract.model.CompanyCustomer;
|
||||
import com.ecep.contract.model.CompanyCustomerFile;
|
||||
import com.ecep.contract.model.CompanyExtendInfo;
|
||||
import com.ecep.contract.model.Contract;
|
||||
import com.ecep.contract.model.ContractBidVendor;
|
||||
import com.ecep.contract.model.ContractFile;
|
||||
import com.ecep.contract.model.ContractFileTypeLocal;
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.model.ExtendVendorInfo;
|
||||
import com.ecep.contract.model.Project;
|
||||
import com.ecep.contract.model.ProjectBid;
|
||||
import com.ecep.contract.model.ProjectCost;
|
||||
import com.ecep.contract.model.ProjectQuotation;
|
||||
import com.ecep.contract.model.ProjectSaleType;
|
||||
import com.ecep.contract.model.ProjectSaleTypeRequireFileType;
|
||||
import com.ecep.contract.model.VendorGroup;
|
||||
import com.ecep.contract.model.VendorGroupRequireFileType;
|
||||
import com.ecep.contract.service.CompanyCustomerService;
|
||||
import com.ecep.contract.service.CompanyFileService;
|
||||
import com.ecep.contract.service.CompanyService;
|
||||
import com.ecep.contract.service.CompanyVendorService;
|
||||
import com.ecep.contract.service.ContractBidVendorService;
|
||||
import com.ecep.contract.service.ContractFileService;
|
||||
import com.ecep.contract.service.ContractService;
|
||||
import com.ecep.contract.service.EmployeeService;
|
||||
import com.ecep.contract.service.ExtendVendorInfoService;
|
||||
import com.ecep.contract.service.ProjectBidService;
|
||||
import com.ecep.contract.service.ProjectCostService;
|
||||
import com.ecep.contract.service.ProjectQuotationService;
|
||||
import com.ecep.contract.service.ProjectSaleTypeRequireFileTypeService;
|
||||
import com.ecep.contract.service.ProjectSaleTypeService;
|
||||
import com.ecep.contract.service.ProjectService;
|
||||
import com.ecep.contract.service.VendorGroupRequireFileTypeService;
|
||||
import com.ecep.contract.service.VendorGroupService;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.util.converter.NumberStringConverter;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ContractVerifyComm {
|
||||
// Project
|
||||
private ProjectService projectService;
|
||||
private ProjectSaleTypeRequireFileTypeService saleTypeRequireFileTypeService;
|
||||
private ProjectSaleTypeService saleTypeService;
|
||||
private ProjectCostService projectCostService;
|
||||
private ProjectQuotationService projectQuotationService;
|
||||
private ProjectBidService projectBidService;
|
||||
// Contract
|
||||
private ContractService contractService;
|
||||
private ContractFileService contractFileService;
|
||||
private ContractBidVendorService contractBidVendorService;
|
||||
|
||||
// Company
|
||||
private CompanyService companyService;
|
||||
private CompanyFileService companyFileService;
|
||||
// Vendor
|
||||
private CompanyVendorService companyVendorService;
|
||||
private VendorGroupService vendorGroupService;
|
||||
private VendorGroupRequireFileTypeService vendorGroupRequireFileTypeService;
|
||||
private ExtendVendorInfoService extendVendorInfoService;
|
||||
// Customer
|
||||
private CompanyCustomerService companyCustomerService;
|
||||
private CompanyCustomerFileService companyCustomerFileService;
|
||||
private CompanyExtendInfoService companyExtendInfoService;
|
||||
// Employee
|
||||
private EmployeeService employeeService;
|
||||
|
||||
private ProjectService getProjectService() {
|
||||
if (projectService == null) {
|
||||
projectService = SpringApp.getBean(ProjectService.class);
|
||||
}
|
||||
return projectService;
|
||||
}
|
||||
|
||||
private ProjectSaleTypeService getSaleTypeService() {
|
||||
if (saleTypeService == null) {
|
||||
saleTypeService = SpringApp.getBean(ProjectSaleTypeService.class);
|
||||
}
|
||||
return saleTypeService;
|
||||
}
|
||||
|
||||
ProjectCostService getProjectCostService() {
|
||||
if (projectCostService == null) {
|
||||
projectCostService = SpringApp.getBean(ProjectCostService.class);
|
||||
}
|
||||
return projectCostService;
|
||||
}
|
||||
|
||||
ProjectQuotationService getProjectQuotationService() {
|
||||
if (projectQuotationService == null) {
|
||||
projectQuotationService = SpringApp.getBean(ProjectQuotationService.class);
|
||||
}
|
||||
return projectQuotationService;
|
||||
}
|
||||
|
||||
ProjectBidService getProjectBidService() {
|
||||
if (projectBidService == null) {
|
||||
projectBidService = SpringApp.getBean(ProjectBidService.class);
|
||||
}
|
||||
return projectBidService;
|
||||
}
|
||||
|
||||
private ProjectSaleTypeRequireFileTypeService getSaleTypeRequireFileTypeService() {
|
||||
if (saleTypeRequireFileTypeService == null) {
|
||||
saleTypeRequireFileTypeService = SpringApp.getBean(ProjectSaleTypeRequireFileTypeService.class);
|
||||
}
|
||||
return saleTypeRequireFileTypeService;
|
||||
}
|
||||
|
||||
public ContractService getContractService() {
|
||||
if (contractService == null) {
|
||||
contractService = SpringApp.getBean(ContractService.class);
|
||||
}
|
||||
return contractService;
|
||||
}
|
||||
|
||||
private ContractFileService getContractFileService() {
|
||||
if (contractFileService == null) {
|
||||
contractFileService = SpringApp.getBean(ContractFileService.class);
|
||||
}
|
||||
return contractFileService;
|
||||
}
|
||||
|
||||
private ContractBidVendorService getContractBidVendorService() {
|
||||
if (contractBidVendorService == null) {
|
||||
contractBidVendorService = SpringApp.getBean(ContractBidVendorService.class);
|
||||
}
|
||||
return contractBidVendorService;
|
||||
}
|
||||
|
||||
private CompanyService getCompanyService() {
|
||||
if (companyService == null) {
|
||||
companyService = SpringApp.getBean(CompanyService.class);
|
||||
}
|
||||
return companyService;
|
||||
}
|
||||
|
||||
private CompanyFileService getCompanyFileService() {
|
||||
if (companyFileService == null) {
|
||||
companyFileService = SpringApp.getBean(CompanyFileService.class);
|
||||
}
|
||||
return companyFileService;
|
||||
}
|
||||
|
||||
private VendorGroupService getVendorGroupService() {
|
||||
if (vendorGroupService == null) {
|
||||
vendorGroupService = SpringApp.getBean(VendorGroupService.class);
|
||||
}
|
||||
return vendorGroupService;
|
||||
}
|
||||
|
||||
private VendorGroupRequireFileTypeService getVendorGroupRequireFileTypeService() {
|
||||
if (vendorGroupRequireFileTypeService == null) {
|
||||
vendorGroupRequireFileTypeService = SpringApp.getBean(VendorGroupRequireFileTypeService.class);
|
||||
}
|
||||
return vendorGroupRequireFileTypeService;
|
||||
}
|
||||
|
||||
private ExtendVendorInfoService getExtendVendorInfoService() {
|
||||
if (extendVendorInfoService == null) {
|
||||
extendVendorInfoService = SpringApp.getBean(ExtendVendorInfoService.class);
|
||||
}
|
||||
return extendVendorInfoService;
|
||||
}
|
||||
|
||||
private CompanyVendorService getCompanyVendorService() {
|
||||
if (companyVendorService == null) {
|
||||
companyVendorService = SpringApp.getBean(CompanyVendorService.class);
|
||||
}
|
||||
return companyVendorService;
|
||||
}
|
||||
|
||||
private CompanyCustomerService getCompanyCustomerService() {
|
||||
if (companyCustomerService == null) {
|
||||
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
|
||||
}
|
||||
return companyCustomerService;
|
||||
}
|
||||
|
||||
private CompanyCustomerFileService getCompanyCustomerFileService() {
|
||||
if (companyCustomerFileService == null) {
|
||||
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
|
||||
}
|
||||
return companyCustomerFileService;
|
||||
}
|
||||
|
||||
private CompanyExtendInfoService getCompanyExtendInfoService() {
|
||||
if (companyExtendInfoService == null) {
|
||||
companyExtendInfoService = SpringApp.getBean(CompanyExtendInfoService.class);
|
||||
}
|
||||
return companyExtendInfoService;
|
||||
}
|
||||
|
||||
private EmployeeService getEmployeeService() {
|
||||
if (employeeService == null) {
|
||||
employeeService = SpringApp.getBean(EmployeeService.class);
|
||||
}
|
||||
return employeeService;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private ObservableMap<ContractFileType, ContractFileTypeLocal> fileTypeLocalMap = null;
|
||||
private Locale locale = Locale.getDefault();
|
||||
private Contract contract;
|
||||
|
||||
/**
|
||||
* 是否验证企业存储目录
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCompanyPath = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证企业状态
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCompanyStatus = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证企业文件(资信报告)
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCompanyCredit = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证客户
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCustomer = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证客户文件(合同文件)
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCustomerFiles = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证客户子合同日期
|
||||
*/
|
||||
private SimpleBooleanProperty verifyCustomerSubContractDate = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证供应商
|
||||
*/
|
||||
private SimpleBooleanProperty verifyVendor = new SimpleBooleanProperty(true);
|
||||
/**
|
||||
* 是否验证供应商文件(供应商合同)
|
||||
*/
|
||||
private SimpleBooleanProperty verifyVendorFiles = new SimpleBooleanProperty(true);
|
||||
|
||||
/**
|
||||
* 合同合规性检测
|
||||
*
|
||||
* @param contract 要验证的合同对象
|
||||
* @param holder 输出
|
||||
*/
|
||||
public void verify(Contract contract, MessageHolder holder) {
|
||||
LocalDate setupDate = contract.getSetupDate();
|
||||
if (setupDate == null) {
|
||||
holder.error("未设置合同提交日期");
|
||||
return;
|
||||
}
|
||||
|
||||
Company company = contract.getCompany();
|
||||
if (company == null) {
|
||||
holder.error("未关联企业");
|
||||
return;
|
||||
}
|
||||
if (!Hibernate.isInitialized(company)) {
|
||||
company = getCompanyService().findById(company.getId());
|
||||
}
|
||||
verify(company, contract, holder);
|
||||
}
|
||||
|
||||
public void verify(Company company, Contract contract, MessageHolder holder) {
|
||||
LocalDate setupDate = contract.getSetupDate();
|
||||
|
||||
Employee employee = contract.getEmployee();
|
||||
if (employee == null) {
|
||||
holder.error("未关联业务员");
|
||||
}
|
||||
|
||||
if (contract.getAmount() == null || contract.getAmount() <= 0) {
|
||||
holder.error("合同金额小于等于0");
|
||||
}
|
||||
|
||||
CompanyExtendInfo companyExtendInfo = getCompanyExtendInfoService().findByCompany(company);
|
||||
|
||||
if (companyExtendInfo.isDisableVerify()) {
|
||||
holder.debug("公司设定不做校验");
|
||||
} else {
|
||||
if (verifyCompanyPath.get()) {
|
||||
if (!CompanyFileUtils.exists(company.getPath())) {
|
||||
holder.error("公司目录未设置");
|
||||
} else {
|
||||
File basePath = getCompanyService().getBasePath();
|
||||
if (!company.getPath().startsWith(basePath.getAbsolutePath())) {
|
||||
holder.error("公司目录未在规定目录下");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verifyCompanyStatus.get()) {
|
||||
getCompanyService().verifyEnterpriseStatus(company, setupDate, msg -> {
|
||||
holder.error(company.getName() + ":" + msg);
|
||||
});
|
||||
}
|
||||
|
||||
if (verifyCompanyCredit.get()) {
|
||||
getCompanyFileService().verify(company, contract.getSetupDate(), msg -> {
|
||||
holder.error(company.getName() + ":" + msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 合同类型
|
||||
switch (contract.getPayWay()) {
|
||||
case RECEIVE -> {
|
||||
// 销售合同
|
||||
verifyAsCustomer(company, companyExtendInfo, contract, holder);
|
||||
|
||||
// 销售合同下的采购合同
|
||||
List<Contract> list = getContractService().findAllByParent(contract);
|
||||
if (!list.isEmpty()) {
|
||||
for (Contract v : list) {
|
||||
MessageHolder subHolder = holder.sub(v.getCode() + " -> ");
|
||||
subHolder.info("采购合同 : " + v.getName());
|
||||
verify(v, subHolder);
|
||||
if (CompanyFileUtils.exists(v.getPath())) {
|
||||
if (!v.getPath().startsWith(contract.getPath())) {
|
||||
holder.error("合同目录未在规定目录下");
|
||||
}
|
||||
} else {
|
||||
holder.error("合同目录未设置");
|
||||
}
|
||||
}
|
||||
|
||||
DoubleSummaryStatistics statistics = list.stream().mapToDouble(c -> {
|
||||
return Objects.requireNonNullElse(c.getAmount(), 0.0);
|
||||
}).summaryStatistics();
|
||||
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||
|
||||
holder.debug("采购合同金额合计:" + numberFormat.format(statistics.getSum()));
|
||||
holder.debug("采购合同金额平均值:" + numberFormat.format(statistics.getAverage()));
|
||||
holder.debug("采购合同金额最大值:" + numberFormat.format(statistics.getMax()));
|
||||
holder.debug("采购合同金额最小值:" + numberFormat.format(statistics.getMin()));
|
||||
if (contract.getAmount() != null && statistics.getSum() > contract.getAmount()) {
|
||||
holder.warn("采购合同金额合计大于销售合同金额");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PAY -> {
|
||||
verifyAsVendor(company, contract, holder);
|
||||
break;
|
||||
}
|
||||
case OTHER -> {
|
||||
holder.error("合同付款类型:其他");
|
||||
break;
|
||||
}
|
||||
default -> {
|
||||
holder.error("合同付款类型:未设置");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyAsVendor(Company company, Contract contract, MessageHolder holder) {
|
||||
// 供应商,检查评价表
|
||||
ExtendVendorInfo vendorInfo = getExtendVendorInfoService().findByContract(contract);
|
||||
if (vendorInfo == null) {
|
||||
ExtendVendorInfo info = getExtendVendorInfoService().newInstanceByContract(contract);
|
||||
vendorInfo = getExtendVendorInfoService().save(info);
|
||||
holder.info("创建供应商信息 #" + vendorInfo.getId());
|
||||
}
|
||||
VendorGroup group = vendorInfo.getGroup();
|
||||
boolean assignedProvider = vendorInfo.isAssignedProvider();
|
||||
if (assignedProvider) {
|
||||
holder.debug("采购信息中设定为指定供应商");
|
||||
}
|
||||
|
||||
if (group != null) {
|
||||
if (!Hibernate.isInitialized(group)) {
|
||||
group = getVendorGroupService().findById(group.getId());
|
||||
vendorInfo.setGroup(group);
|
||||
}
|
||||
}
|
||||
if (verifyCustomerSubContractDate.get()) {
|
||||
// 检查子合同日期是否在销售合同之后
|
||||
if (!vendorInfo.isPrePurchase()) {
|
||||
String parentCode = contract.getParentCode();
|
||||
if (StringUtils.hasText(parentCode)) {
|
||||
Contract parent = getContractService().findByCode(parentCode);
|
||||
if (parent != null) {
|
||||
if (contract.getSetupDate().isBefore(parent.getSetupDate())) {
|
||||
holder.warn("采购合同:" + contract.getCode() + " 提交日期在销售合同之前");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (verifyVendor.get()) {
|
||||
getCompanyVendorService().verify(contract, holder);
|
||||
}
|
||||
|
||||
if (verifyVendorFiles.get()) {
|
||||
holder.debug("核验文件...");
|
||||
verifyVendorFile(group, assignedProvider, contract, holder);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyVendorFile(VendorGroup group, boolean assignedProvider, Contract contract,
|
||||
MessageHolder holder) {
|
||||
if (group == null) {
|
||||
return;
|
||||
}
|
||||
boolean loseFile = false;
|
||||
List<ContractFile> files = getContractFileService().findAllByContract(contract);
|
||||
List<VendorGroupRequireFileType> list = getVendorGroupRequireFileTypeService().findByGroupId(group.getId());
|
||||
if (list != null && !list.isEmpty()) {
|
||||
for (VendorGroupRequireFileType item : list) {
|
||||
ContractFileType fileType = item.getFileType();
|
||||
if (fileType == null) {
|
||||
continue;
|
||||
}
|
||||
if (fileType == ContractFileType.QuotationSheet && assignedProvider
|
||||
&& group.isRequireQuotationSheetForBid()) {
|
||||
continue;
|
||||
}
|
||||
if (!hasFileType(files, fileType)) {
|
||||
holder.error("供应商" + getFileTypeLocalValue(fileType) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 供应商比价
|
||||
if (group.isPriceComparison()) {
|
||||
if (assignedProvider) {
|
||||
holder.debug("指定供应商, 跳过供应商比价");
|
||||
} else {
|
||||
boolean requireQuotation = group.isRequireQuotationSheetForBid();
|
||||
List<ContractBidVendor> bidVendors = getContractBidVendorService().findByContract(contract);
|
||||
if (bidVendors == null || bidVendors.isEmpty()) {
|
||||
holder.error("未上报供应商比价");
|
||||
} else {
|
||||
for (ContractBidVendor bidVendor : bidVendors) {
|
||||
ContractFile contractFile = bidVendor.getQuotationSheet();
|
||||
if (contractFile == null) {
|
||||
if (requireQuotation && bidVendor.getCompany().equals(contract.getCompany())) {
|
||||
holder.debug("供应商类型启用了允许选中供应商不必须要有报价表");
|
||||
} else {
|
||||
holder.error("供应商比价:" + bidVendor.getCompany().getName() + " 未上传/关联报价表");
|
||||
loseFile = true;
|
||||
}
|
||||
} else {
|
||||
if (!Hibernate.isInitialized(contractFile)) {
|
||||
contractFile = getContractFileService().findById(contractFile.getId());
|
||||
}
|
||||
ContractFileType type = contractFile.getType();
|
||||
if (type != ContractFileType.QuotationSheet) {
|
||||
holder.error("供应商比价:" + contractFile.getFileName() + " 报价表记录异常,类型错误");
|
||||
}
|
||||
File file = new File(contract.getPath(), contractFile.getFileName());
|
||||
if (!file.exists()) {
|
||||
holder.error("供应商比价:" + file.getName() + " 报价表记录异常,文件不存在");
|
||||
loseFile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loseFile && files.isEmpty()) {
|
||||
holder.warn("!上传文件空!");
|
||||
}
|
||||
}
|
||||
|
||||
ContractFileTypeLocal getFileTypeLocal(ContractFileType type) {
|
||||
if (fileTypeLocalMap == null) {
|
||||
fileTypeLocalMap = FXCollections
|
||||
.observableMap(getContractFileService().findAllFileTypes(getLocale().toLanguageTag()));
|
||||
}
|
||||
return fileTypeLocalMap.get(type);
|
||||
}
|
||||
|
||||
String getFileTypeLocalValue(ContractFileType type) {
|
||||
ContractFileTypeLocal fileTypeLocal = getFileTypeLocal(type);
|
||||
if (fileTypeLocal == null) {
|
||||
return type.name();
|
||||
}
|
||||
return fileTypeLocal.getValue();
|
||||
}
|
||||
|
||||
private boolean hasFileType(List<ContractFile> files, ContractFileType fileType) {
|
||||
if (files == null || files.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (ContractFile file : files) {
|
||||
if (file.getType() == fileType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void verifyAsCustomer(Company company, CompanyExtendInfo companyExtendInfo, Contract contract,
|
||||
MessageHolder holder) {
|
||||
boolean valiad = true;
|
||||
Project project = contract.getProject();
|
||||
if (project == null) {
|
||||
// 收款的合同时,检查是否关联了项目,如果没有则创建
|
||||
if (contract.getPayWay() == ContractPayWay.RECEIVE) {
|
||||
project = getProjectService().findByCode(contract.getCode());
|
||||
if (project == null) {
|
||||
holder.info("创建关联项目");
|
||||
try {
|
||||
project = getProjectService().newInstanceByContract(contract);
|
||||
project = getProjectService().save(project);
|
||||
} catch (Exception e) {
|
||||
holder.error("创建关联项目失败: " + e.getMessage());
|
||||
throw new RuntimeException("code=" + contract.getCode(), e);
|
||||
}
|
||||
}
|
||||
contract.setProject(project);
|
||||
contract = getContractService().save(contract);
|
||||
}
|
||||
}
|
||||
if (project != null) {
|
||||
if (!Hibernate.isInitialized(project)) {
|
||||
project = getProjectService().findById(project.getId());
|
||||
// fixed
|
||||
contract.setProject(project);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (project != null) {
|
||||
holder.info("验证项目信息:" + project.getCode() + " " + project.getName());
|
||||
verifyProject(contract, project, holder.sub("项目"));
|
||||
|
||||
ProjectSaleType saleType = project.getSaleType();
|
||||
if (saleType != null) {
|
||||
if (CompanyFileUtils.exists(contract.getPath())) {
|
||||
if (!Hibernate.isInitialized(saleType)) {
|
||||
saleType = getSaleTypeService().findById(saleType.getId());
|
||||
project.setSaleType(saleType);
|
||||
}
|
||||
if (!contract.getPath().startsWith(saleType.getPath())) {
|
||||
holder.error("合同目录未在销售类型目录下");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!companyExtendInfo.isDisableVerify()) {
|
||||
if (!verifyCustomerContract(contract, holder)) {
|
||||
valiad = false;
|
||||
}
|
||||
|
||||
if (verifyCustomerFiles.get()) {
|
||||
holder.debug("核验文件...");
|
||||
if (!verifyContractFileAsCustomer(project, contract, holder)) {
|
||||
valiad = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户,检查评估表
|
||||
*/
|
||||
private boolean verifyCustomerContract(Contract contract, MessageHolder holder) {
|
||||
if (!verifyCustomer.get()) {
|
||||
return false;
|
||||
}
|
||||
boolean valid = true;
|
||||
Company company = contract.getCompany();
|
||||
if (company == null) {
|
||||
holder.warn("合同未关联公司");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
CompanyCustomer companyCustomer = getCompanyCustomerService().findByCompany(company);
|
||||
if (companyCustomer == null) {
|
||||
holder.warn("合同未关联客户");
|
||||
valid = false;
|
||||
} else {
|
||||
if (!StringUtils.hasText(companyCustomer.getPath())) {
|
||||
holder.warn("客户未设置文件夹");
|
||||
valid = false;
|
||||
}
|
||||
LocalDate developDate = companyCustomer.getDevelopDate();
|
||||
if (developDate == null) {
|
||||
holder.warn("客户未设置开发日期");
|
||||
valid = false;
|
||||
}
|
||||
if (!verifyCustomerFileByContract(companyCustomer, contract, holder)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
private boolean verifyCustomerFileByContract(CompanyCustomer companyCustomer, Contract contract,
|
||||
MessageHolder holder) {
|
||||
List<LocalDate> verifyDates = new ArrayList<>();
|
||||
LocalDate minDate = LocalDate.of(2022, 1, 1);
|
||||
LocalDate developDate = companyCustomer.getDevelopDate();
|
||||
if (developDate != null) {
|
||||
if (developDate.isAfter(minDate)) {
|
||||
minDate = developDate;
|
||||
}
|
||||
}
|
||||
if (contract.getSetupDate() != null) {
|
||||
LocalDate setupDate = contract.getSetupDate();
|
||||
if (setupDate.isBefore(developDate)) {
|
||||
holder.error("合同提交日期 " + setupDate + " 早于客户开发日期 " + developDate + ".");
|
||||
return false;
|
||||
}
|
||||
if (setupDate.isBefore(minDate)) {
|
||||
holder.debug("合同提交日期 " + setupDate + " 早于 " + minDate + ", 跳过");
|
||||
} else {
|
||||
verifyDates.add(setupDate);
|
||||
}
|
||||
}
|
||||
if (contract.getStartDate() != null) {
|
||||
LocalDate startDate = contract.getStartDate();
|
||||
if (startDate.isBefore(developDate)) {
|
||||
holder.warn("合同起始日期 " + startDate + " 早于客户开发日期 " + developDate + ".");
|
||||
}
|
||||
|
||||
if (startDate.isBefore(minDate)) {
|
||||
holder.debug("合同起始日期 " + startDate + " 早于 " + minDate + ", 跳过");
|
||||
} else {
|
||||
verifyDates.add(startDate);
|
||||
}
|
||||
}
|
||||
if (contract.getOrderDate() != null) {
|
||||
LocalDate orderDate = contract.getOrderDate();
|
||||
if (orderDate.isBefore(developDate)) {
|
||||
holder.error("合同签订日期 " + orderDate + " 早于客户开发日期 " + developDate + ".");
|
||||
return false;
|
||||
}
|
||||
if (orderDate.isBefore(minDate)) {
|
||||
holder.debug("合同签订日期 " + orderDate + " 早于 " + minDate + ", 跳过");
|
||||
} else {
|
||||
verifyDates.add(orderDate);
|
||||
}
|
||||
}
|
||||
|
||||
if (verifyDates.isEmpty()) {
|
||||
holder.warn("合同没有符合可核验的日期");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 客户
|
||||
List<CompanyCustomerFile> files = getCompanyCustomerFileService().findAllByCustomer(companyCustomer);
|
||||
if (files == null || files.isEmpty()) {
|
||||
holder.warn("未见客户评估表");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (LocalDate verifyDate : verifyDates) {
|
||||
CompanyCustomerFile customerFile = files.stream()
|
||||
.filter(v -> v.getSignDate() != null && v.isValid())
|
||||
.filter(v -> v.getType() == CompanyCustomerFileType.EvaluationForm)
|
||||
.filter(v -> MyDateTimeUtils.dateValidFilter(verifyDate, v.getSignDate(),
|
||||
v.getSignDate().plusYears(1), 7))
|
||||
.findFirst().orElse(null);
|
||||
if (customerFile != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CompanyCustomerFile latestFile = files.stream()
|
||||
.filter(v -> v.getSignDate() != null && v.isValid())
|
||||
.filter(v -> v.getType() == CompanyCustomerFileType.EvaluationForm)
|
||||
.max(Comparator.comparing(CompanyCustomerFile::getSignDate))
|
||||
.orElse(null);
|
||||
|
||||
if (latestFile == null) {
|
||||
holder.error("未匹配的客户评估");
|
||||
return false;
|
||||
}
|
||||
|
||||
holder.error("客户评估已过期,最后一个客户评估报告日期:" + latestFile.getSignDate() + ", 检测日期:"
|
||||
+ verifyDates.stream().map(LocalDate::toString).collect(Collectors.joining(", ")));
|
||||
return false;
|
||||
}
|
||||
|
||||
private void verifyProject(Contract contract, Project project, MessageHolder holder) {
|
||||
ProjectSaleType saleType = project.getSaleType();
|
||||
if (saleType == null) {
|
||||
String code = contract.getCode();
|
||||
if (code != null && code.length() > 5) {
|
||||
saleType = getSaleTypeService().findByCode(code.substring(0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (saleType == null) {
|
||||
holder.warn("销售类型未设置");
|
||||
}
|
||||
if (project.getAmount() == null || project.getAmount() <= 0) {
|
||||
holder.error("金额小于等于0");
|
||||
}
|
||||
|
||||
//
|
||||
boolean needImport = false;
|
||||
|
||||
ProjectCost autoCost = getProjectCostService().findAutoCostByProject(project);
|
||||
if (autoCost == null) {
|
||||
// 创建 V0 项目成本
|
||||
ProjectCost cost = getProjectCostService().newInstanceByProject(project);
|
||||
cost.setVersion(0);
|
||||
cost.setApplicant(getEmployeeService().findById(EmployeeService.DEFAULT_SYSTEM_EMPLOYEE_ID));
|
||||
cost.setApplyTime(LocalDateTime.now());
|
||||
cost.setDescription("自动导入");
|
||||
autoCost = getProjectCostService().save(cost);
|
||||
needImport = true;
|
||||
} else {
|
||||
// 检查 V0 项目成本
|
||||
if (autoCost.getGrossProfitMargin() <= 0) {
|
||||
NumberFormat instance = NumberFormat.getNumberInstance(getLocale());
|
||||
instance.setMaximumFractionDigits(2);
|
||||
NumberStringConverter converter = new NumberStringConverter(instance);
|
||||
holder.warn("V0项目成本毛利率异常:" + converter.toString(autoCost.getGrossProfitMargin()) + "<=0%");
|
||||
needImport = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needImport && !autoCost.isImportLock()) {
|
||||
ProjectCostImportItemsFromContractsTasker tasker = new ProjectCostImportItemsFromContractsTasker();
|
||||
tasker.setCost(autoCost);
|
||||
tasker.setCurrentUser(() -> getEmployeeService().findById(Desktop.instance.getActiveEmployeeId()));
|
||||
autoCost.setApplyTime(LocalDateTime.now());
|
||||
holder.debug("更新V0项目成本");
|
||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(tasker);
|
||||
}
|
||||
|
||||
// 检查最新的项目成本,默认 V1
|
||||
ProjectCost latestCost = getProjectCostService().findLatestByProject(project);
|
||||
if (latestCost == null) {
|
||||
ProjectCost cost = getProjectCostService().newInstanceByProject(project);
|
||||
cost.setVersion(1);
|
||||
Employee applicant = project.getApplicant();
|
||||
if (applicant == null) {
|
||||
applicant = contract.getEmployee();
|
||||
}
|
||||
cost.setApplicant(applicant);
|
||||
cost.setApplyTime(project.getCreated().atTime(LocalTime.now()));
|
||||
latestCost = getProjectCostService().save(cost);
|
||||
} else {
|
||||
//
|
||||
if (latestCost.getGrossProfitMargin() <= 0) {
|
||||
NumberFormat instance = NumberFormat.getNumberInstance(getLocale());
|
||||
instance.setMaximumFractionDigits(2);
|
||||
NumberStringConverter converter = new NumberStringConverter(instance);
|
||||
holder.warn("V" + latestCost.getVersion() + "项目成本毛利率异常:"
|
||||
+ converter.toString(latestCost.getGrossProfitMargin()) + "<=0%");
|
||||
} else if (latestCost.getGrossProfitMargin() >= 50) {
|
||||
NumberFormat instance = NumberFormat.getNumberInstance(getLocale());
|
||||
instance.setMaximumFractionDigits(2);
|
||||
NumberStringConverter converter = new NumberStringConverter(instance);
|
||||
holder.warn("V" + latestCost.getVersion() + "项目成本毛利率异常:"
|
||||
+ converter.toString(latestCost.getGrossProfitMargin()) + ">=50%");
|
||||
}
|
||||
}
|
||||
|
||||
if (project.isUseBid()) {
|
||||
List<ProjectBid> bids = getProjectBidService().findAllByProject(project);
|
||||
if (bids.isEmpty()) {
|
||||
holder.warn("投标未创建");
|
||||
}
|
||||
}
|
||||
if (project.isUseOffer()) {
|
||||
List<ProjectQuotation> quotations = getProjectQuotationService().findAllByProject(project);
|
||||
if (quotations.isEmpty()) {
|
||||
holder.warn("报价未创建");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyContractFileAsCustomer(Project project, Contract contract, MessageHolder holder) {
|
||||
boolean loseFile = false;
|
||||
List<ContractFile> files = getContractFileService().findAllByContract(contract);
|
||||
if (project != null) {
|
||||
// 投标
|
||||
if (project.isUseBid()) {
|
||||
// 投标审批表
|
||||
if (!hasFileType(files, ContractFileType.BidApprovalForm)) {
|
||||
holder.error(getFileTypeLocalValue(ContractFileType.BidApprovalForm) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
// 中标通知书
|
||||
if (!hasFileType(files, ContractFileType.BidAcceptanceLetter)) {
|
||||
holder.error(getFileTypeLocalValue(ContractFileType.BidAcceptanceLetter) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
}
|
||||
// 报价
|
||||
if (project.isUseOffer()) {
|
||||
if (!hasFileType(files, ContractFileType.QuotationApprovalForm)) {
|
||||
holder.error(getFileTypeLocalValue(ContractFileType.QuotationApprovalForm) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectSaleType saleType = project.getSaleType();
|
||||
if (saleType != null) {
|
||||
List<ProjectSaleTypeRequireFileType> list = getSaleTypeRequireFileTypeService()
|
||||
.findBySaleTypeId(saleType.getId());
|
||||
for (ProjectSaleTypeRequireFileType item : list) {
|
||||
ContractFileType fileType = item.getFileType();
|
||||
if (fileType != null) {
|
||||
if (!hasFileType(files, fileType)) {
|
||||
holder.error(getFileTypeLocalValue(fileType) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Hibernate.isInitialized(saleType)) {
|
||||
saleType = getSaleTypeService().findById(saleType.getId());
|
||||
}
|
||||
if (saleType.isCriticalProjectDecision()) {
|
||||
if (contract.getAmount() != null && contract.getAmount() >= saleType.getCriticalProjectLimit()) {
|
||||
holder.debug("合同金额 " + contract.getAmount() + " 超过 " + saleType.getCriticalProjectLimit());
|
||||
if (!hasFileType(files, ContractFileType.CriticalProjectDecisionRecord)) {
|
||||
holder.error(getFileTypeLocalValue(ContractFileType.CriticalProjectDecisionRecord) + "未上传");
|
||||
loseFile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loseFile && files.isEmpty()) {
|
||||
holder.warn("!上传文件空!");
|
||||
}
|
||||
|
||||
return !loseFile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import static com.ecep.contract.util.ExcelUtils.setCellValue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.controller.contract.ContractVerifyWindowController;
|
||||
import com.ecep.contract.model.Contract;
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.service.ContractService;
|
||||
import com.ecep.contract.service.EmployeeService;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 导出验证结果为Excel文件
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ContractVerifyResultExportAsExcelFileTasker extends Tasker<Object> {
|
||||
|
||||
File destFile;
|
||||
|
||||
List<ContractVerifyWindowController.Model> models;
|
||||
|
||||
EmployeeService employeeService;
|
||||
ContractService contractService;
|
||||
|
||||
public EmployeeService getEmployeeService() {
|
||||
if (employeeService == null) {
|
||||
employeeService = SpringApp.getBean(EmployeeService.class);
|
||||
}
|
||||
return employeeService;
|
||||
}
|
||||
|
||||
public ContractService getContractService() {
|
||||
if (contractService == null) {
|
||||
contractService = SpringApp.getBean(ContractService.class);
|
||||
}
|
||||
return contractService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
holder.debug("共有信息 " + models.size() + " 条");
|
||||
|
||||
Employee unnamed = new Employee();
|
||||
unnamed.setName("未命名");
|
||||
|
||||
// 按员工分组
|
||||
Map<Employee, List<ContractVerifyWindowController.Model>> listMap = models.stream()
|
||||
.collect(Collectors.groupingBy(model -> {
|
||||
Employee employee = model.getEmployee().get();
|
||||
if (employee == null) {
|
||||
employee = unnamed;
|
||||
}
|
||||
return employee;
|
||||
}));
|
||||
|
||||
holder.debug("按员工分为 " + listMap.size() + " 组");
|
||||
updateProgress(0, listMap.size());
|
||||
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
String[] headers = { "合同编号", "合同名称", "验证结果", "路径" };
|
||||
int[] columnWidths = { 14, 25, 50, 20 };
|
||||
|
||||
// 创建样式:启用自动换行
|
||||
CellStyle wrapStyle = wb.createCellStyle();
|
||||
wrapStyle.setWrapText(true); // 关键设置:自动换行
|
||||
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
for (Map.Entry<Employee, List<ContractVerifyWindowController.Model>> entry : listMap.entrySet()) {
|
||||
Employee employee = entry.getKey();
|
||||
List<ContractVerifyWindowController.Model> list = entry.getValue();
|
||||
|
||||
//
|
||||
if (!Hibernate.isInitialized(employee)) {
|
||||
employee = getEmployeeService().findById(employee.getId());
|
||||
}
|
||||
holder.info("- " + employee.getName());
|
||||
// 创建工作表
|
||||
String sheetName = employee.getName();
|
||||
if (!StringUtils.hasText(sheetName)) {
|
||||
sheetName = "未命名";
|
||||
}
|
||||
Sheet sheet = wb.createSheet(sheetName);
|
||||
// 创建表头行
|
||||
Row headerRow = sheet.createRow(0);
|
||||
|
||||
// 创建表头单元格并设置值
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
|
||||
sheet.setColumnWidth(i, columnWidths[i] * 256);
|
||||
}
|
||||
|
||||
list.sort(Comparator.comparing(o -> o.getCode().get()));
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
try {
|
||||
ContractVerifyWindowController.Model model = list.get(i);
|
||||
Contract contract = getContractService().findByCode(model.getCode().get());
|
||||
Row row = sheet.createRow(i + 1);
|
||||
setCellValue(sheet, row.getRowNum(), 0, model.getCode().get());
|
||||
setCellValue(sheet, row.getRowNum(), 1, model.getName().get());
|
||||
Map<String, List<ContractVerifyWindowController.MessageExt>> messages = model.getMessages()
|
||||
.get()
|
||||
.stream()
|
||||
.filter(v -> v.getLevel().intValue() > Level.INFO.intValue())
|
||||
.collect(Collectors.groupingBy(v -> {
|
||||
if (v.getPrefix() == null) {
|
||||
return "";
|
||||
}
|
||||
return v.getPrefix();
|
||||
}));
|
||||
|
||||
setCellValue(sheet, row.getRowNum(), 2, messages.entrySet().stream()
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.map((e) -> {
|
||||
return e.getKey() + e.getValue().stream()
|
||||
.map(Message::getMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
}).collect(Collectors.joining("\n"))).setCellStyle(wrapStyle);
|
||||
|
||||
if (contract != null) {
|
||||
setCellValue(sheet, row.getRowNum(), 3, contract.getPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
holder.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
sheet.autoSizeColumn(2);
|
||||
updateProgress(counter.incrementAndGet(), listMap.size());
|
||||
}
|
||||
|
||||
holder.debug("写入文件:" + destFile.getAbsolutePath());
|
||||
try (OutputStream fileOut = new FileOutputStream(destFile)) {
|
||||
wb.write(fileOut);
|
||||
}
|
||||
updateProgress(1, 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 同步客户任务
|
||||
*/
|
||||
public class CustomerSyncTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CustomerSyncTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("用友U8系统-同步客户");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
90
client/src/main/java/com/ecep/contract/task/DemoTask.java
Normal file
90
client/src/main/java/com/ecep/contract/task/DemoTask.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.ecep.contract.Desktop;
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 演示用的测试任务类,用于测试任务监控系统
|
||||
*/
|
||||
public class DemoTask extends Tasker<Object> {
|
||||
|
||||
private final String taskName;
|
||||
private final int durationSeconds;
|
||||
private final boolean shouldFail;
|
||||
private final Random random = new Random();
|
||||
|
||||
public DemoTask(String taskName, int durationSeconds, boolean shouldFail) {
|
||||
this.taskName = taskName;
|
||||
this.durationSeconds = durationSeconds;
|
||||
this.shouldFail = shouldFail;
|
||||
// 设置任务标题
|
||||
updateTitle("演示任务: " + taskName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并启动一个测试任务
|
||||
*/
|
||||
public static void startDemoTask(String name, int duration, boolean fail) {
|
||||
DemoTask task = new DemoTask(name, duration, fail);
|
||||
// 启动任务
|
||||
Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行多个测试任务的演示
|
||||
*/
|
||||
public static void runDemo() {
|
||||
// 创建几个不同类型的测试任务
|
||||
startDemoTask("正常完成任务", 5, false);
|
||||
|
||||
// 延迟启动其他任务
|
||||
Desktop.instance.getExecutorService().submit(() -> {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
startDemoTask("失败任务示例", 3, true);
|
||||
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
startDemoTask("长时间运行任务", 10, false);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
holder.info("开始执行任务: " + taskName);
|
||||
|
||||
// 模拟任务执行过程
|
||||
for (int i = 0; i < durationSeconds; i++) {
|
||||
// 检查任务是否被取消
|
||||
if (isCancelled()) {
|
||||
holder.info("任务被取消: " + taskName);
|
||||
updateMessage("任务已取消");
|
||||
return "任务已取消";
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
double progress = (double) i / durationSeconds;
|
||||
updateProgress(progress, 1.0);
|
||||
updateMessage("处理中... (" + (i + 1) + " / " + durationSeconds + ")");
|
||||
|
||||
// 模拟随机延迟
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
// 根据配置决定任务是成功还是失败
|
||||
if (shouldFail) {
|
||||
holder.warn("任务故意失败:" + taskName);
|
||||
throw new Exception("这是一个测试用的任务失败异常");
|
||||
}
|
||||
|
||||
holder.info("任务执行完成: " + taskName);
|
||||
updateProgress(1.0, 1.0);
|
||||
updateMessage("任务完成");
|
||||
return "任务执行成功: " + taskName;
|
||||
}
|
||||
}
|
||||
273
client/src/main/java/com/ecep/contract/task/MonitoredTask.java
Normal file
273
client/src/main/java/com/ecep/contract/task/MonitoredTask.java
Normal file
@@ -0,0 +1,273 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.Desktop;
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.TaskStatus;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 被监控的任务包装类
|
||||
*/
|
||||
public class MonitoredTask<T> implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MonitoredTask.class);
|
||||
@Getter
|
||||
private final Tasker<T> delegate;
|
||||
@Getter
|
||||
private final String taskId;
|
||||
@Getter
|
||||
private final long startTime;
|
||||
@Setter
|
||||
private Long timeoutSeconds;
|
||||
private ScheduledFuture<?> timeoutExecutor;
|
||||
private final SimpleStringProperty title = new SimpleStringProperty("-");
|
||||
private final SimpleStringProperty message = new SimpleStringProperty("");
|
||||
private final ObservableList<Message> messages = FXCollections.observableArrayList();
|
||||
private final SimpleObjectProperty<TaskStatus> state = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Throwable> exception = new SimpleObjectProperty<>(this, "exception");
|
||||
private final ObjectProperty<Runnable> succeededProperty = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Runnable> failedProperty = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Runnable> cancelledHandler = new SimpleObjectProperty<>();
|
||||
private final AtomicBoolean timeoutHandled = new AtomicBoolean(false);
|
||||
|
||||
public MonitoredTask(Tasker<T> delegate, String taskId) {
|
||||
this.delegate = delegate;
|
||||
this.taskId = taskId;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
// 绑定属性
|
||||
title.set(delegate.getTitle());
|
||||
delegate.titleProperty().addListener(this::titleChanged);
|
||||
message.set(delegate.getMessage());
|
||||
delegate.messageProperty().addListener(this::messageChanged);
|
||||
delegate.stateProperty().addListener(this::stateChanged);
|
||||
delegate.setMessageHandler(this::messageHandler);
|
||||
delegate.totalWorkProperty();
|
||||
}
|
||||
|
||||
private boolean messageHandler(Message message) {
|
||||
messages.add(message);
|
||||
// return false allow the messageProperty to update
|
||||
return false;
|
||||
}
|
||||
|
||||
public ObservableList<Message> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
private void titleChanged(ObservableValue<? extends String> observable, String oldValue, String newValue) {
|
||||
titleProperty().set(newValue);
|
||||
}
|
||||
|
||||
private void messageChanged(ObservableValue<? extends String> observable, String oldValue, String newValue) {
|
||||
messageProperty().set(newValue);
|
||||
}
|
||||
|
||||
private void stateChanged(ObservableValue<? extends Worker.State> observable, Worker.State oldValue,
|
||||
Worker.State newValue) {
|
||||
switch (newValue) {
|
||||
case RUNNING: {
|
||||
if (stateProperty().get() != TaskStatus.RUNNING) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("任务{} 的 delegate 状态从 {} 变更为 {} 时, monitor的状态 {} 不是 RUNNING", taskId, oldValue,
|
||||
newValue, stateProperty().get());
|
||||
}
|
||||
}
|
||||
stateProperty().set(TaskStatus.RUNNING);
|
||||
break;
|
||||
}
|
||||
case CANCELLED:
|
||||
stateProperty().set(TaskStatus.CANCELLED);
|
||||
cancelledProperty().get().run();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("任务 {} 开始执行", taskId);
|
||||
}
|
||||
stateProperty().set(TaskStatus.RUNNING);
|
||||
try {
|
||||
call();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("任务 {} 执行完成", taskId);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
exceptionProperty().set(e);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("任务 {} 执行异常", taskId, e);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (exceptionProperty().get() == null) {
|
||||
stateProperty().set(TaskStatus.SUCCEEDED);
|
||||
succeededProperty().get().run();
|
||||
} else {
|
||||
stateProperty().set(TaskStatus.FAILED);
|
||||
failedProperty().get().run();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("任务 {} 回调执行异常", taskId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public StringProperty messageProperty() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ReadOnlyDoubleProperty progressProperty() {
|
||||
return delegate.progressProperty();
|
||||
}
|
||||
|
||||
public ReadOnlyDoubleProperty totalWorkProperty() {
|
||||
return delegate.totalWorkProperty();
|
||||
}
|
||||
|
||||
public ObjectProperty<TaskStatus> stateProperty() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public ObjectProperty<Throwable> exceptionProperty() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public ObjectProperty<Runnable> succeededProperty() {
|
||||
return succeededProperty;
|
||||
}
|
||||
|
||||
public ObjectProperty<Runnable> failedProperty() {
|
||||
return failedProperty;
|
||||
}
|
||||
|
||||
public ObjectProperty<Runnable> cancelledProperty() {
|
||||
return cancelledHandler;
|
||||
}
|
||||
|
||||
protected T call() throws Exception {
|
||||
// 设置超时监控
|
||||
if (timeoutSeconds != null) {
|
||||
setupTimeoutMonitor();
|
||||
}
|
||||
|
||||
try {
|
||||
delegate.run();
|
||||
return delegate.get();
|
||||
} finally {
|
||||
// 取消超时监控
|
||||
if (timeoutExecutor != null) {
|
||||
timeoutExecutor.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置超时监控
|
||||
*/
|
||||
private void setupTimeoutMonitor() {
|
||||
timeoutExecutor = getExecutorService().schedule(() -> {
|
||||
if (isRunning() && timeoutHandled.compareAndSet(false, true)) {
|
||||
updateMessage("任务执行超时,正在取消...");
|
||||
stateProperty().set(TaskStatus.TIMED_OUT);
|
||||
delegate.cancel();
|
||||
}
|
||||
}, timeoutSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void submit() {
|
||||
getExecutorService().submit(this);
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> schedule(long delay, TimeUnit unit) {
|
||||
ScheduledFuture<?> schedule = getExecutorService().schedule(this, delay, unit);
|
||||
stateProperty().set(TaskStatus.DELAYED);
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private void updateMessage(String string) {
|
||||
messageProperty().set(string);
|
||||
}
|
||||
|
||||
public boolean cancel() {
|
||||
if (delegate.cancel()) {
|
||||
stateProperty().set(TaskStatus.CANCELLED);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务执行耗时
|
||||
*/
|
||||
public long getExecutionTime() {
|
||||
return System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
public boolean isFailed() {
|
||||
return delegate.getState() == javafx.concurrent.Worker.State.FAILED;
|
||||
}
|
||||
|
||||
public boolean isSucceeded() {
|
||||
return delegate.getState() == javafx.concurrent.Worker.State.SUCCEEDED;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return delegate.getState() == javafx.concurrent.Worker.State.RUNNING;
|
||||
}
|
||||
|
||||
public void setTimeout(Long timeoutSeconds2) {
|
||||
this.timeoutSeconds = timeoutSeconds2;
|
||||
}
|
||||
|
||||
public void setOnSucceeded(Runnable value) {
|
||||
succeededProperty.set(value);
|
||||
}
|
||||
|
||||
public void setOnFailed(Runnable value) {
|
||||
failedProperty.set(value);
|
||||
}
|
||||
|
||||
public void setOnCancelled(Runnable value) {
|
||||
cancelledHandler.set(value);
|
||||
}
|
||||
|
||||
public void setOnRunning(EventHandler<WorkerStateEvent> value) {
|
||||
delegate.setOnRunning(value);
|
||||
}
|
||||
|
||||
ScheduledExecutorService getExecutorService() {
|
||||
return Desktop.instance.getExecutorService();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class OldVersionSyncCustomerTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OldVersionSyncCustomerTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("老版本-同步客户数据");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
public class OldVersionSyncVendorTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OldVersionSyncVendorTask.class);
|
||||
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception { updateTitle("老版本-同步供应商数据");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
71
client/src/main/java/com/ecep/contract/task/TaskHistory.java
Normal file
71
client/src/main/java/com/ecep/contract/task/TaskHistory.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.TaskStatus;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务历史记录
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskHistory {
|
||||
private String taskId;
|
||||
private String title;
|
||||
private TaskStatus status;
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
private String errorMessage;
|
||||
private List<Message> messages;
|
||||
|
||||
/**
|
||||
* 获取任务执行耗时(毫秒)
|
||||
*/
|
||||
public long getExecutionTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化的执行耗时
|
||||
*/
|
||||
public String getFormattedExecutionTime() {
|
||||
long ms = getExecutionTime();
|
||||
if (ms < 1000) {
|
||||
return ms + "ms";
|
||||
} else if (ms < 60000) {
|
||||
return String.format("%.2fs", ms / 1000.0);
|
||||
} else {
|
||||
return String.format("%dm %.2fs", ms / 60000, (ms % 60000) / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
public TaskHistory(MonitoredTask<?> task, TaskStatus status2, long timeMillis, Object object) {
|
||||
this.taskId = task.getTaskId();
|
||||
this.title = task.titleProperty().get();
|
||||
this.status = status2;
|
||||
this.startTime = task.getStartTime();
|
||||
this.endTime = timeMillis;
|
||||
this.messages = new ArrayList<>(task.getMessages());
|
||||
|
||||
if (object == null) {
|
||||
if (task.exceptionProperty().get() != null) {
|
||||
// 打印异常栈 到 字符串
|
||||
StringWriter sw = new StringWriter();
|
||||
task.exceptionProperty().get().printStackTrace(new java.io.PrintWriter(sw));
|
||||
String exceptionStack = sw.toString();
|
||||
setErrorMessage(exceptionStack);
|
||||
}
|
||||
} else {
|
||||
setErrorMessage(object.toString());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.Desktop;
|
||||
import com.ecep.contract.TaskStatus;
|
||||
import com.ecep.contract.controller.BaseController;
|
||||
import com.ecep.contract.controller.TaskMonitorViewController;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 任务监控中心 - 统一管理所有异步任务
|
||||
*/
|
||||
public class TaskMonitorCenter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TaskMonitorCenter.class);
|
||||
|
||||
// 正在运行的任务列表
|
||||
@Getter
|
||||
private final ObservableList<MonitoredTask<?>> activeTasks = FXCollections.observableArrayList();
|
||||
|
||||
// 任务历史记录
|
||||
@Getter
|
||||
private final ObservableList<TaskHistory> taskHistory = FXCollections.observableArrayList();
|
||||
|
||||
// 任务ID映射
|
||||
private final Map<String, MonitoredTask<?>> taskMap = new ConcurrentHashMap<>();
|
||||
|
||||
// 任务计数器
|
||||
private final AtomicInteger taskCounter = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* 注册并启动任务
|
||||
*/
|
||||
public <T> MonitoredTask<T> registerTask(Tasker<T> task) {
|
||||
return registerTask(task, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册并启动任务,支持设置超时时间
|
||||
*/
|
||||
public <T> MonitoredTask<T> registerTask(Tasker<T> task, Long timeoutSeconds) {
|
||||
String taskId = generateTaskId();
|
||||
MonitoredTask<T> monitoredTask = new MonitoredTask<>(task, taskId);
|
||||
|
||||
// 设置超时处理
|
||||
if (timeoutSeconds != null && timeoutSeconds > 0) {
|
||||
monitoredTask.setTimeout(timeoutSeconds);
|
||||
}
|
||||
|
||||
// 添加任务状态变更监听器
|
||||
monitoredTask.setOnSucceeded(() -> {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("任务[{}]执行成功: {}", taskId, monitoredTask.titleProperty().get());
|
||||
}
|
||||
recordHistory(monitoredTask, TaskStatus.SUCCEEDED);
|
||||
removeActiveTask(monitoredTask);
|
||||
});
|
||||
|
||||
monitoredTask.setOnFailed(() -> {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("任务[{}]执行失败: {}", taskId, monitoredTask.titleProperty().get(),
|
||||
monitoredTask.exceptionProperty().get());
|
||||
}
|
||||
recordHistory(monitoredTask, TaskStatus.FAILED);
|
||||
removeActiveTask(monitoredTask);
|
||||
});
|
||||
|
||||
monitoredTask.setOnCancelled(() -> {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("任务[{}]被取消: {}", taskId, monitoredTask.titleProperty().get());
|
||||
}
|
||||
recordHistory(monitoredTask, TaskStatus.CANCELLED);
|
||||
removeActiveTask(monitoredTask);
|
||||
});
|
||||
|
||||
// 保存任务引用并启动
|
||||
taskMap.put(taskId, monitoredTask);
|
||||
activeTasks.add(monitoredTask);
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("任务[{}]已注册并启动: {}", taskId, monitoredTask.titleProperty().get());
|
||||
}
|
||||
return monitoredTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*/
|
||||
public boolean cancelTask(String taskId) {
|
||||
MonitoredTask<?> task = taskMap.get(taskId);
|
||||
if (task != null && task.isRunning()) {
|
||||
task.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有任务
|
||||
*/
|
||||
public void cancelAllTasks() {
|
||||
for (MonitoredTask<?> task : new ArrayList<>(activeTasks)) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从活动列表中移除任务
|
||||
*/
|
||||
private void removeActiveTask(MonitoredTask<?> task) {
|
||||
Platform.runLater(() -> activeTasks.remove(task));
|
||||
taskMap.remove(task.getTaskId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务历史
|
||||
*/
|
||||
private void recordHistory(MonitoredTask<?> task, TaskStatus status) {
|
||||
TaskHistory history = new TaskHistory(
|
||||
task,
|
||||
status,
|
||||
System.currentTimeMillis(), null);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
taskHistory.add(0, history);
|
||||
// 只保留最近100条历史记录
|
||||
if (taskHistory.size() > 100) {
|
||||
taskHistory.remove(taskHistory.size() - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成任务ID
|
||||
*/
|
||||
private String generateTaskId() {
|
||||
return "task-" + UUID.randomUUID().toString().substring(0, 8) + "-" + taskCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
public <T> void registerAndStartTask(Tasker<T> task) {
|
||||
MonitoredTask<T> registerTask = registerTask(task);
|
||||
Desktop.instance.getExecutorService().submit(registerTask);
|
||||
}
|
||||
|
||||
public void bindStatusLabel(Label monitorLabel) {
|
||||
Tooltip tooltip = new Tooltip("当前运行任务数: " + activeTasks.size());
|
||||
monitorLabel.setTooltip(tooltip);
|
||||
activeTasks.addListener((ListChangeListener<MonitoredTask<?>>) change -> {
|
||||
tooltip.setText("当前运行任务数: " + activeTasks.size());
|
||||
});
|
||||
monitorLabel.setOnMouseClicked(event -> {
|
||||
BaseController.show(TaskMonitorViewController.class, monitorLabel.getScene().getWindow());
|
||||
});
|
||||
}
|
||||
}
|
||||
175
client/src/main/java/com/ecep/contract/task/Tasker.java
Normal file
175
client/src/main/java/com/ecep/contract/task/Tasker.java
Normal file
@@ -0,0 +1,175 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
|
||||
import com.ecep.contract.Desktop;
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.service.CompanyService;
|
||||
import com.ecep.contract.service.EmployeeService;
|
||||
import com.ecep.contract.service.SysConfService;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.scene.control.ListCell;
|
||||
import lombok.Setter;
|
||||
|
||||
public abstract class Tasker<T> extends Task<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Tasker.class);
|
||||
@Setter
|
||||
protected java.util.function.Predicate<Message> messageHandler;
|
||||
private Employee currentUser;
|
||||
@Setter
|
||||
private CompanyService companyService;
|
||||
@Setter
|
||||
private EmployeeService employeeService;
|
||||
@Setter
|
||||
private SysConfService confService;
|
||||
|
||||
public SysConfService getConfService() {
|
||||
if (confService == null) {
|
||||
confService = getBean(SysConfService.class);
|
||||
}
|
||||
return confService;
|
||||
}
|
||||
|
||||
public CompanyService getCompanyService() {
|
||||
if (companyService == null) {
|
||||
companyService = getBean(CompanyService.class);
|
||||
}
|
||||
return companyService;
|
||||
}
|
||||
|
||||
public EmployeeService getEmployeeService() {
|
||||
if (employeeService == null) {
|
||||
employeeService = getBean(EmployeeService.class);
|
||||
}
|
||||
return employeeService;
|
||||
}
|
||||
|
||||
protected <K> K getBean(Class<K> requiredType) throws BeansException {
|
||||
return SpringApp.getBean(requiredType);
|
||||
}
|
||||
|
||||
public Employee getCurrentUser() {
|
||||
if (currentUser == null) {
|
||||
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
|
||||
}
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T call() throws Exception {
|
||||
MessageHolderImpl holder = new MessageHolderImpl();
|
||||
try {
|
||||
return execute(holder);
|
||||
} catch (Exception e) {
|
||||
|
||||
holder.error(e.getMessage());
|
||||
logger.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T execute(MessageHolder holder) throws Exception;
|
||||
|
||||
@Override
|
||||
protected void updateMessage(String message) {
|
||||
updateMessage(Level.INFO, message);
|
||||
}
|
||||
|
||||
protected void updateMessage(Level level, String message) {
|
||||
if (messageHandler != null) {
|
||||
if (messageHandler.test(new Message(level, message))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.updateMessage(message);
|
||||
}
|
||||
|
||||
protected void skipUpdateCheckUpdateMessage(String message) {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
((StringProperty) messageProperty()).set(message);
|
||||
} else {
|
||||
Platform.runLater(() -> ((StringProperty) messageProperty()).set(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduled() {
|
||||
super.scheduled();
|
||||
updateMessage(Level.FINE, "任务进入调度器");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void running() {
|
||||
super.running();
|
||||
updateMessage(Level.FINE, "任务开始执行");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelled() {
|
||||
super.cancelled();
|
||||
updateMessage(Level.SEVERE, "任务被取消");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed() {
|
||||
super.failed();
|
||||
Throwable exception = getException();
|
||||
if (exception == null) {
|
||||
updateMessage(Level.SEVERE, "任务执行失败");
|
||||
} else {
|
||||
updateMessage(Level.SEVERE, "任务执行失败:" + exception.getMessage());
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
super.succeeded();
|
||||
updateMessage(Level.FINE, "任务执行完毕");
|
||||
}
|
||||
|
||||
public static class MessageListCell extends ListCell<Message> {
|
||||
@Override
|
||||
protected void updateItem(Message item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item == null || empty) {
|
||||
setText(null);
|
||||
getStyleClass().clear();
|
||||
} else {
|
||||
setText(item.getMessage());
|
||||
getStyleClass().removeIf(s -> s.startsWith("row-"));
|
||||
getStyleClass().add("row-" + item.getLevel().getName().toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误消息处理
|
||||
*/
|
||||
public class MessageHolderImpl implements MessageHolder {
|
||||
public MessageHolderImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessage(Level level, String message) {
|
||||
updateMessage(level, message);
|
||||
}
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return Desktop.instance.getActiveEmployee().localeProperty().get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
|
||||
/**
|
||||
* 供应商同步任务
|
||||
*/
|
||||
public class VendorSyncTask extends Tasker<Object> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(VendorSyncTask.class);
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("用友U8系统-同步供应商");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user