feat: 新增WebSocket任务管理器及相关任务实现

实现WebSocketServerTaskManager用于管理WebSocket任务,并添加多个任务类:
- CompanyContext和CloudRkContext接口定义
- WebSocketServerTasker接口及多个具体任务实现类
- ContractVerifyTasker合同验证任务
- ContractRepairTasker合同修复任务
- CompanyCustomerRebuildFilesTasker客户文件重建任务
- CompanyVerifyTasker企业验证任务
- CustomerFileMoveTasker客户文件移动任务
- CompanyCompositeUpdateTasker企业综合更新任务
- ProjectCostImportItemsFromContractsTasker项目成本导入任务
- 其他相关辅助任务类

这些任务类通过WebSocket与前端交互,实现各种业务功能
This commit is contained in:
2025-09-28 18:18:32 +08:00
parent 510952d72e
commit df6188db40
13 changed files with 124 additions and 104 deletions

View File

@@ -0,0 +1,4 @@
package com.ecep.contract.ds.company;
public interface CompanyContext {
}

View File

@@ -1,161 +0,0 @@
package com.ecep.contract.ds.company.tasker;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.cloud.rk.CloudRkService;
import com.ecep.contract.cloud.rk.ctx.CloudRkCtx;
import com.ecep.contract.cloud.tyc.CloudTycService;
import com.ecep.contract.cloud.u8.YongYouU8Service;
import com.ecep.contract.cloud.u8.ctx.CompanyCtx;
import com.ecep.contract.cloud.u8.ctx.ContractCtx;
import com.ecep.contract.cloud.u8.ctx.CustomerCtx;
import com.ecep.contract.cloud.u8.ctx.VendorCtx;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.model.CloudRk;
import com.ecep.contract.model.CloudYu;
import com.ecep.contract.model.Company;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Setter;
/**
* 合并更新
*/
public class CompanyCompositeUpdateTasker extends Tasker<Object> implements WebSocketServerTasker {
private static final Logger logger = LoggerFactory.getLogger(CompanyCompositeUpdateTasker.class);
CloudRkCtx cloudRkCtx = new CloudRkCtx();
@Setter
private CloudRkService cloudRkService;
@Setter
private CloudTycService cloudTycService;
@Setter
private YongYouU8Service yongYouU8Service;
@Setter
private Company company;
@Override
public void init(JsonNode argsNode) {
int companyId = argsNode.get(0).asInt();
company = getCachedBean(com.ecep.contract.ds.company.service.CompanyService.class).findById(companyId);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
holder.debug("1. 从 " + CloudServiceConstant.RK_NAME + " 更新...");
updateProgress(0.1, 1);
syncFromCloudRk(holder);
holder.debug("2. 从 " + CloudServiceConstant.U8_NAME + " 更新...");
updateProgress(0.3, 1);
syncFromYongYouU8(holder);
holder.debug("3. 从 " + CloudServiceConstant.TYC_NAME + " 更新...");
syncFromCloudTyc(holder);
updateProgress(0.9, 1);
return null;
}
private void syncFromCloudRk(MessageHolder holder) {
holder.debug("1. 从 " + CloudServiceConstant.RK_NAME + " 更新...");
try {
cloudRkService = getBean(CloudRkService.class);
} catch (BeansException e) {
holder.warn("未启用 " + CloudServiceConstant.RK_NAME + " 服务");
return;
}
CloudRk cloudRk = cloudRkService.getOrCreateCloudRk(company);
if (cloudRk == null) {
holder.error("无法创建或获取 CloudRk 对象");
return;
}
try {
cloudRkCtx.setCloudRkService(cloudRkService);
if (cloudRkCtx.syncCompany(company, cloudRk, holder)) {
}
} catch (Exception e) {
cloudRk.setDescription(e.getMessage());
} finally {
cloudRk.setLatestUpdate(LocalDateTime.now());
cloudRkService.save(cloudRk);
}
}
private void syncFromYongYouU8(MessageHolder holder) {
holder.debug("2. 从 " + CloudServiceConstant.U8_NAME + " 更新...");
try {
yongYouU8Service = getBean(YongYouU8Service.class);
} catch (BeansException e) {
holder.warn("未启用 " + CloudServiceConstant.U8_NAME + " 服务");
return;
}
CloudYu cloudYu = yongYouU8Service.getOrCreateCloudYu(company);
if (cloudYu == null) {
holder.error("无法创建或获取 CloudYu 对象");
return;
}
try {
CompanyCtx companyCtx = new CompanyCtx();
yongYouU8Service.initialize(companyCtx);
if (companyCtx.syncCompany(company, holder)) {
holder.info("更新");
}
VendorCtx vendorCtx = new VendorCtx();
yongYouU8Service.initialize(vendorCtx);
vendorCtx.setCompanyCtx(companyCtx);
if (vendorCtx.syncVendor(company, holder)) {
cloudYu.setVendorUpdateDate(LocalDate.now());
}
CustomerCtx customerCtx = new CustomerCtx();
yongYouU8Service.initialize(customerCtx);
customerCtx.setCompanyCtx(companyCtx);
if (customerCtx.syncCustomer(company, holder)) {
cloudYu.setCustomerUpdateDate(LocalDate.now());
}
ContractCtx contractCtx = new ContractCtx();
yongYouU8Service.initialize(contractCtx);
contractCtx.syncContract(company, holder);
cloudYu.setCloudLatest(LocalDateTime.now());
cloudYu.setExceptionMessage("");
} catch (Exception e) {
String message = e.getMessage();
holder.error("同步过程中发生错误: " + message);
// 保留255个字符
if (message.length() > 255) {
message = message.substring(0, 255);
}
cloudYu.setExceptionMessage(message);
} finally {
cloudYu.setLatestUpdate(LocalDateTime.now());
yongYouU8Service.save(cloudYu);
}
}
private void syncFromCloudTyc(MessageHolder holder) {
holder.debug("3. 从 " + CloudServiceConstant.TYC_NAME + " 更新...");
try {
cloudTycService = getBean(CloudTycService.class);
} catch (BeansException e) {
holder.warn("未启用 " + CloudServiceConstant.TYC_NAME + " 服务");
return;
}
cloudTycService.syncCompany(company, holder);
updateProgress(1, 1);
}
}

View File

@@ -1,96 +0,0 @@
package com.ecep.contract.ds.company.tasker;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.contract.tasker.ContractVerifyComm;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Getter;
import lombok.Setter;
public class CompanyVerifyTasker extends Tasker<Object> implements WebSocketServerTasker {
@Getter
@Setter
private Company company;
@Getter
@Setter
boolean passed = false;
ContractVerifyComm comm = new ContractVerifyComm();
public CompanyService getCompanyService() {
return getCachedBean(CompanyService.class);
}
public ContractService getContractService() {
return getCachedBean(ContractService.class);
}
/**
* 核验公司名下的所有合同
*
* @param company 公司
* @param holder 输出
*/
@Override
public void init(JsonNode argsNode) {
int companyId = argsNode.get(0).asInt();
company = getCompanyService().findById(companyId);
comm.setVerifyCompanyPath(false);
comm.setVerifyCompanyStatus(false);
comm.setVerifyCompanyCredit(false);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
updateTitle("验证企业是否符合合规要求");
verify(company, holder);
if (passed) {
holder.info("合规验证通过");
} else {
holder.error("合规验证不通过");
}
updateProperty("passed", passed);
return null;
}
private void verify(Company company, MessageHolder holder) {
LocalDate now = LocalDate.now();
if (getCompanyService().verifyEnterpriseStatus(company, now, holder)) {
passed = false;
}
// 验证所有的合同
List<Contract> list = getContractService().findAllByCompany(company);
if (list.isEmpty()) {
holder.debug("!没有相关合同!");
return;
}
holder.debug("检索到相关合同 " + list.size() + "");
AtomicInteger counter = new AtomicInteger(0);
long total = list.size();
for (Contract contract : list) {
holder.debug("核验合同:" + contract.getCode() + ", " + contract.getName());
if (!comm.verify(company, contract, holder.sub("-- "))) {
passed = false;
}
updateProgress(counter.incrementAndGet(), total);
}
updateProgress(1, 1);
}
}

View File

@@ -1,65 +0,0 @@
package com.ecep.contract.ds.contract.tasker;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.service.WebSocketServerTasker;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.util.StringUtils;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.model.Contract;
import lombok.Getter;
import lombok.Setter;
/**
* 合同修复任务
*/
public class ContractRepairTask extends AbstContractRepairTasker implements WebSocketServerTasker {
@Getter
private Contract contract;
@Getter
boolean repaired = false;
private final ContractRepairComm comm = new ContractRepairComm();
public ContractRepairTask() {
}
public void init(JsonNode argsNode) {
int contractId = argsNode.get(0).asInt();
contract = getCachedBean(ContractService.class).findById(contractId);
}
@Override
protected void repair(MessageHolder holder) {
updateTitle("修复合同 " + contract.toPrettyString());
Contract parent = null;
if (contract.getPayWay() == ContractPayWay.PAY) {
if (StringUtils.hasText(contract.getParentCode())) {
parent = contractCtx.findContractByCode(contract.getParentCode());
}
}
AtomicReference<Contract> contractProperty = new AtomicReference<>(contract);
if (repair(contractProperty, parent, holder, progress -> updateProgress(progress, 1))) {
repaired = true;
updateProperty("repaired", true);
}
setContract(contractProperty.get());
updateProgress(1, 1);
}
public void setContract(Contract contract) {
this.contract = contract;
updateProperty("contract", contract);
}
}

View File

@@ -1,53 +0,0 @@
package com.ecep.contract.ds.contract.tasker;
import java.util.Locale;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Getter;
import lombok.Setter;
public class ContractVerifyTasker extends Tasker<Object> implements WebSocketServerTasker {
@Getter
@Setter
private Contract contract;
ContractVerifyComm comm = new ContractVerifyComm();
@Getter
@Setter
boolean passed = true;
@Override
protected Object execute(MessageHolder holder) {
updateTitle("验证合同 " + contract.getCode() + " 及其子合同是否符合合规要求");
if (!comm.verify(contract, holder)) {
passed = false;
}
if (passed) {
holder.info("合规验证通过");
} else {
holder.error("合规验证不通过");
}
return null;
}
public Locale getLocale() {
return comm.getLocale();
}
public void setLocale(Locale locale) {
comm.setLocale(locale);
}
@Override
public void init(JsonNode argsNode) {
int contractId = argsNode.get(0).asInt();
contract = getCachedBean(ContractService.class).findById(contractId);
}
}

View File

@@ -1,212 +0,0 @@
package com.ecep.contract.ds.customer.tasker;
import static com.ecep.contract.util.ExcelUtils.setCellValue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import com.ecep.contract.CustomerFileType;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.cloud.tyc.CloudTycService;
import com.ecep.contract.ds.customer.service.CompanyCustomerEvaluationFormFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.CloudTyc;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.CompanyUtils;
import com.fasterxml.jackson.databind.JsonNode;
public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketServerTasker {
private CompanyCustomer customer;
@Override
public void init(JsonNode argsNode) {
int customerId = argsNode.get(0).asInt();
customer = getCachedBean(CompanyCustomerService.class).findById(customerId);
}
CompanyCustomerFileService getCompanyCustomerFileService() {
return getCachedBean(CompanyCustomerFileService.class);
}
CompanyCustomerEvaluationFormFileService getCompanyCustomerEvaluationFormFileService() {
return getCachedBean(CompanyCustomerEvaluationFormFileService.class);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
updateEvaluationForm(holder);
return null;
}
public void updateEvaluationForm(MessageHolder holder) {
if (!StringUtils.hasText(customer.getPath())) {
holder.error("供应商目录未设置,请先设置供应商目录");
return;
}
File template = getCompanyCustomerFileService().getEvaluationFormTemplate();
if (template == null) {
holder.error("评价表模板文件未设置,请先设置评价表模板文件");
return;
}
if (!template.exists()) {
holder.error("评价表模板文件 " + template.getAbsolutePath() + " 不存在,请检查");
return;
}
updateProgress(1, 10);
File dir = new File(customer.getPath());
String template_file_name = template.getName();
File destFile = new File(dir, template_file_name);
if (destFile.exists()) {
holder.info("表单文件已经存在," + destFile.getName());
try (
InputStream inp = new FileInputStream(destFile);
Workbook wb = WorkbookFactory.create(inp)) {
updateEvaluationForm(wb, destFile, holder);
holder.info("评价表已更新");
} catch (Exception e) {
holder.error(e.getMessage());
}
} else {
holder.info("根据模板 " + template_file_name + " 创建表单 " + destFile.getName());
try (InputStream inp = new FileInputStream(template); Workbook wb = WorkbookFactory.create(inp)) {
updateEvaluationForm(wb, destFile, holder);
holder.info("评价表已创建");
CompanyCustomerFile customerFile = new CompanyCustomerFile();
customerFile.setCustomer(customer);
customerFile.setFilePath(destFile.getAbsolutePath());
customerFile.setType(CustomerFileType.General);
getCompanyCustomerFileService().save(customerFile);
} catch (Exception e) {
holder.error(e.getMessage());
}
}
updateProgress(10, 10);
}
/**
* 更新客户评估表,依据模板创建,如果已经存在生成的文件,则更新评估表
*
* @param wb work book
* @param destFile 目标文件
*/
public void updateEvaluationForm(Workbook wb, File destFile, MessageHolder holder) throws IOException {
updateProgress(2, 10);
Company company = customer.getCompany();
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
customer.setCompany(company);
}
Sheet sheet = wb.getSheetAt(0);
updateSheet(company, sheet, holder.sub(" - "));
updateProgress(8, 10);
// 输出到文件
try (OutputStream fileOut = new FileOutputStream(destFile)) {
wb.write(fileOut);
} catch (FileNotFoundException e) {
holder.error("写评估表时发生文件错误,请检查评估表是否被打开中:" + e.getMessage());
}
}
private void updateSheet(Company company, Sheet sheet, MessageHolder holder) {
setCellValue(sheet, "B3", "客户编号:" + CompanyUtils.formatCompanyVendorId(customer.getId()));
setCellValue(sheet, "B4", "客户名称:" + company.getName());
LocalDate suggestDate = getCompanyCustomerFileService().getNextSignDate(customer, holder);
if (suggestDate == null) {
suggestDate = LocalDate.now();
}
setCellValue(sheet, "H3", "评定时间:" + suggestDate);
setCellValue(sheet, "H4", "统一社会信用代码:");
setCellValue(sheet, "H5", company.getUniscid());
// 注册所属地
setCellValue(sheet, "B5", "注册所属地:" + company.getDistrict());
// 经营状态
setCellValue(sheet, "D6", "经营状态:" + company.getEntStatus());
// 成立日期
setCellValue(sheet, "H6", "成立日期:" + company.getSetupDate());
// 所属行业
setCellValue(sheet, "D7", "所属行业:" + company.getIndustry());
setCellValue(sheet, "D8",
"注册资金:" + company.getRegisteredCapital() + " " + company.getRegisteredCapitalCurrency());
// 企业类型
setCellValue(sheet, "H10", "企业类型:" + company.getEntType());
// 天眼评分
CloudTycService cloudTycService = SpringApp.getBean(CloudTycService.class);
CloudTyc cloudTyc = cloudTycService.getOrCreateCloudTyc(company);
setCellValue(sheet, "D10", "天眼评分:" + (cloudTyc.getScore() > 0 ? cloudTyc.getScore() : ""));
// 检索评估表
List<CompanyCustomerFile> customerFiles = getCompanyCustomerFileService().findAllByCustomerAndType(customer,
CustomerFileType.EvaluationForm);
List<CompanyCustomerEvaluationFormFile> filteredList = customerFiles.stream().filter(file -> {
return file.getSignDate() != null && file.isValid();
})
.sorted(Comparator.comparing(CompanyCustomerFile::getSignDate))
.map(getCompanyCustomerEvaluationFormFileService()::findByCustomerFile)
.toList();
if (filteredList.isEmpty()) {
setCellValue(sheet, "C40", "首次评价");
try {
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:K40"));
} catch (Exception ignored) {
}
} else {
holder.info("客户有 " + filteredList.size() + " 条评价表");
setCellValue(sheet, "C40", "评价日期");
try {
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:D40"));
} catch (Exception ignored) {
}
setCellValue(sheet, "E40", "经济指标");
setCellValue(sheet, "F40", "综合指标");
setCellValue(sheet, "G40", "资信等级");
String[] CreditLevelTitles = new String[] { "-", "差★", " 一般★★", " 较好★★★", " 好★★★★", " " };
int baseRow = 40;
for (CompanyCustomerEvaluationFormFile form : filteredList) {
CompanyCustomerFile customerFile = form.getCustomerFile();
if (!Hibernate.isInitialized(customerFile)) {
customerFile = getCompanyCustomerFileService().findById(customerFile.getId());
}
setCellValue(sheet, baseRow, 2, String.valueOf(customerFile.getSignDate()));
setCellValue(sheet, baseRow, 4, form.getCatalog());
setCellValue(sheet, baseRow, 5, form.getLevel());
if (form.getCreditLevel() == null) {
setCellValue(sheet, baseRow, 6, "-");
} else {
setCellValue(sheet, baseRow, 6, CreditLevelTitles[form.getCreditLevel()]);
}
try {
sheet.addMergedRegion(new CellRangeAddress(baseRow, baseRow, 2, 3));
} catch (Exception ignored) {
}
baseRow++;
}
}
}
}

View File

@@ -1,117 +0,0 @@
package com.ecep.contract.ds.customer.tasker;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import com.ecep.contract.CustomerFileType;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.model.Contract;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
public class CompanyCustomerNextSignDateTask extends Tasker<Object> implements WebSocketServerTasker {
private CompanyCustomer customer;
@Override
public void init(JsonNode argsNode) {
int customerId = argsNode.get(0).asInt();
customer = getCachedBean(CompanyCustomerService.class).findById(customerId);
}
CompanyCustomerFileService getCompanyCustomerFileService() {
return getCachedBean(CompanyCustomerFileService.class);
}
CompanyCustomerService getCompanyCustomerService() {
return getCachedBean(CompanyCustomerService.class);
}
ContractService getContractService() {
return getCachedBean(ContractService.class);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
holder.info("开始计算客户下一个评价日期");
LocalDate nextSignDate = calculateNextSignDate(holder);
if (nextSignDate != null) {
holder.info("下一个评价日期:" + nextSignDate);
} else {
holder.warn("无法计算下一个评价日期");
}
return nextSignDate;
}
private LocalDate calculateNextSignDate(MessageHolder holder) {
LocalDate miniContractDate = LocalDate.of(2022, 1, 1);
// 检索全部合同
ContractService contractService = getContractService();
List<Contract> contractList = contractService.findAllByCompanyCustomer(customer, null, null);
if (contractList.isEmpty()) {
holder.error("未发现已登记的合同");
return null;
}
holder.info("发现" + contractList.size() + "份合同");
// 检索评估表
List<CompanyCustomerFile> files = getCompanyCustomerFileService().findAllByCustomerAndType(customer,
CustomerFileType.EvaluationForm);
CompanyCustomerFile latestFile = files.stream()
.filter(v -> v.getSignDate() != null && v.isValid())
.max(Comparator.comparing(CompanyCustomerFile::getSignDate))
.orElse(null);
// 没有有效的评估表的评价日期
if (latestFile == null) {
holder.warn("未发现有效的评估表");
// 返回最早的合同日期
Contract firstContract = contractList.stream()
.filter(v -> v.getSetupDate() != null && !v.getSetupDate().isBefore(miniContractDate))
.min(Comparator.comparing(Contract::getSetupDate))
.orElse(null);
if (firstContract == null) {
holder.error("最早的合同不存在");
return null;
}
LocalDate setupDate = firstContract.getSetupDate();
holder.info("选择最早的合同:" + firstContract.getCode() + ",日期:" + setupDate);
return setupDate.plusDays(-7);
}
// 有有效的评估表,计算下一个日期
LocalDate lastSignDate = latestFile.getSignDate();
holder.info("最近一次有效的评估表日期:" + lastSignDate);
// 查找最近一次评估日期之后的合同
Contract firstContractAfter = contractList.stream()
.filter(v -> v.getSetupDate() != null && v.getSetupDate().isAfter(lastSignDate))
.min(Comparator.comparing(Contract::getSetupDate))
.orElse(null);
if (firstContractAfter == null) {
holder.warn("没有找到在" + lastSignDate + "之后的合同");
// 返回当前日期作为建议
return LocalDate.now();
}
LocalDate setupDate = firstContractAfter.getSetupDate();
holder.info("找到最近一次评估日期后的第一个合同:" + firstContractAfter.getCode() + ",日期:" + setupDate);
return setupDate.plusDays(-7);
}
}

View File

@@ -1,80 +0,0 @@
package com.ecep.contract.ds.customer.tasker;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
/**
* 客户文件重建任务器
* 用于重建客户相关文件,同步文件系统和数据库记录
*/
public class CompanyCustomerRebuildFilesTasker extends Tasker<Object> implements WebSocketServerTasker {
private CompanyCustomer customer;
CompanyCustomerService getCompanyCustomerService() {
return getCachedBean(CompanyCustomerService.class);
}
@Override
public void init(JsonNode argsNode) {
int customerId = argsNode.get(0).asInt();
customer = getCompanyCustomerService().findById(customerId);
}
void setFilesUpdated(boolean filesUpdated) {
updateProperty("filesUpdated", filesUpdated);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
updateTitle("重建客户文件");
updateProgress(0, 100);
if (customer == null) {
holder.error("客户不存在");
return false;
}
if (!StringUtils.hasText(customer.getPath())) {
holder.error("客户路径未设置,无法重建文件");
updateProgress(100, 100);
return false;
}
Company company = customer.getCompany();
if (!Hibernate.isInitialized(company)) {
company = getCachedBean(CompanyService.class).findById(company.getId());
}
try {
holder.info("开始重建客户文件:" + company.getName());
holder.info("客户路径:" + customer.getPath());
boolean result = getCompanyCustomerService().reBuildingFiles(customer, holder);
if (result) {
holder.info("客户文件重建成功");
setFilesUpdated(true);
} else {
holder.info("客户文件重建完成,但没有更新任何文件");
setFilesUpdated(false);
}
updateProgress(100, 100);
return result;
} catch (Exception e) {
holder.error("客户文件重建失败:" + e.getMessage());
updateProgress(100, 100);
throw e;
}
}
}

View File

@@ -1,112 +0,0 @@
package com.ecep.contract.ds.customer.tasker;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode;
import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;
import java.io.File;
import java.util.logging.Level;
/**
* 客户文件移动到公司目录任务器
* 用于将客户相关文件从客户目录移动到公司目录
*/
public class CustomerFileMoveTasker extends Tasker<Object> implements WebSocketServerTasker {
private CompanyCustomerFile file;
@Override
public void init(JsonNode argsNode) {
if (argsNode != null && argsNode.size() > 0) {
this.file = getCompanyCustomerFileService().findById(argsNode.get(0).asInt());
}
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
try {
updateTitle("移动文件到公司目录");
holder.addMessage(Level.INFO, "开始执行文件移动任务...");
if (file == null) {
throw new IllegalArgumentException("文件信息不能为空");
}
// 获取客户信息
CompanyCustomer customer = file.getCustomer();
if (customer == null) {
throw new IllegalArgumentException("客户不存在");
}
if (!Hibernate.isInitialized(customer)) {
customer = getCompanyCustomerService().findById(customer.getId());
}
// 获取公司信息
Company company = customer.getCompany();
if (company == null) {
throw new IllegalArgumentException("公司不存在: " + customer.getCompany());
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
}
if (!StringUtils.hasText(company.getPath())) {
throw new IllegalArgumentException("公司目录未设置");
}
File companyPath = new File(company.getPath());
if (!companyPath.exists()) {
throw new IllegalArgumentException("公司目录设置异常,无法访问: " + company.getPath());
}
// 移动文件
boolean moveFile = moveFile(file.getFilePath(), companyPath, holder);;
moveFile(file.getEditFilePath(), companyPath, holder);
// 删除数据库记录
if (moveFile) {
getCompanyCustomerFileService().delete(file);
holder.addMessage(Level.INFO, "文件移动任务执行完成");
updateProperty("filesUpdated", true);
}
return true;
} catch (Exception e) {
holder.addMessage(Level.SEVERE, "文件移动失败: " + e.getMessage());
throw e;
}
}
private boolean moveFile(String filePath, File companyPath, MessageHolder holder) {
if (StringUtils.hasText(filePath)) {
File file = new File(filePath);
if (file.exists()) {
File dest = new File(companyPath, file.getName());
if (file.renameTo(dest)) {
holder.info(file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
return true;
} else {
holder.error("无法移动文件: " + file.getAbsolutePath());
}
}
}
return false;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
return getCachedBean(CompanyCustomerFileService.class);
}
private CompanyCustomerService getCompanyCustomerService() {
return getCachedBean(CompanyCustomerService.class);
}
}

View File

@@ -1,364 +0,0 @@
package com.ecep.contract.ds.project;
import static com.ecep.contract.SpringApp.getBean;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import com.ecep.contract.service.WebSocketServerTasker;
import com.ecep.contract.ui.MessageHolderImpl;
import com.fasterxml.jackson.databind.JsonNode;
import org.hibernate.Hibernate;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.ds.contract.service.ContractItemService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.ds.project.service.ProjectCostItemService;
import com.ecep.contract.ds.project.service.ProjectCostService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectCostItem;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.NumberUtils;
import com.ecep.contract.util.TaxRateUtils;
import lombok.Setter;
public class ProjectCostImportItemsFromContractsTasker extends Tasker<Object> implements WebSocketServerTasker {
@Setter
private Supplier<Employee> currentUser;
@Setter
ProjectCost cost;
AtomicBoolean verified = new AtomicBoolean(true);
@Override
public void init(JsonNode argsNode) {
int contractId = argsNode.get(0).asInt();
cost = getCachedBean(ProjectCostService.class).findById(contractId);
}
@Override
protected Void execute(MessageHolder holder) throws Exception {
importFromContracts(cost, holder);
return null;
}
public void importFromContracts(ProjectCost projectCost, MessageHolder holder) {
Project project = projectCost.getProject();
List<ProjectCostItem> projectCostItems = getItemService().findByCost(projectCost);
List<Contract> salesContracts = getContractService().findAllSalesByProject(project);
holder.debug("检索到 " + salesContracts.size() + " 个销售合同, 导入合同数据...");
//
List<ContractItem> contractItems = new ArrayList<>();
// 根据存货匹配,可多个相同的存货
Map<Inventory, List<ContractItem>> map = new HashMap<>();
for (Contract salesContract : salesContracts) {
// 收集销售合同的条目
List<ContractItem> list = getContractItemService().findAllByContract(salesContract);
contractItems.addAll(list);
holder.debug("销售合同:" + salesContract.getCode() + " " + salesContract.getName() + " 收集条目:" + list.size());
// 销售合同对应的采购合同的条目
List<Contract> purchaseContracts = getContractService().findAllByParent(salesContract);
holder.debug("检索到对应采购合同:" + purchaseContracts.size());
for (Contract contract : purchaseContracts) {
List<ContractItem> items = getContractItemService().findAllByContract(contract);
holder.debug("采购合同:" + contract.getCode() + " " + contract.getName() + " 收集条目:" + items.size());
for (ContractItem item : items) {
// 根据存货汇总
map.computeIfAbsent(item.getInventory(), k -> new ArrayList<>()).add(item);
}
}
}
compositeIn(contractItems, projectCostItems, holder);
compositeOut(map, projectCostItems, holder);
up(projectCost, projectCostItems);
getCostService().save(projectCost);
}
/**
* 计算统计数
*
* @param projectCost
* @param items
*/
public void up(ProjectCost projectCost, List<ProjectCostItem> items) {
// 含税进项总金额
double inTaxAmount = 0;
// 不含税进项总金额
double inExclusiveTaxAmount = 0;
// 含税出项总金额
double outTaxAmount = 0;
// 不含税出项总金额
double outExclusiveTaxAmount = 0;
for (ProjectCostItem row : items) {
inTaxAmount += TaxRateUtils.toInclusiveTax(row.getInExclusiveTaxPrice(), row.getInTaxRate())
* row.getInQuantity();
inExclusiveTaxAmount += row.getInExclusiveTaxPrice() * row.getInQuantity();
outTaxAmount += TaxRateUtils.toExclusiveTax(row.getOutExclusiveTaxPrice(), row.getOutTaxRate())
* row.getOutQuantity();
outExclusiveTaxAmount += row.getOutExclusiveTaxPrice() * row.getOutQuantity();
}
projectCost.setInTaxAmount(inTaxAmount);
projectCost.setInExclusiveTaxAmount(inExclusiveTaxAmount);
projectCost.setOutTaxAmount(outTaxAmount);
projectCost.setOutExclusiveTaxAmount(outExclusiveTaxAmount);
projectCost.setStampTaxFee((float) (inTaxAmount / 100 * projectCost.getStampTax()));
projectCost.setTaxAndSurchargesFee(
(float) (((inTaxAmount - inExclusiveTaxAmount) - (outTaxAmount - outExclusiveTaxAmount))
* projectCost.getTaxAndSurcharges() / 100));
projectCost.setGrossProfitMargin(
(float) (inExclusiveTaxAmount - outExclusiveTaxAmount - projectCost.getStampTaxFee()
- projectCost.getTaxAndSurchargesFee()) * projectCost.getTaxAndSurcharges() / 100);
}
String toKey(ContractItem item) {
return item.getTitle() + "_" + item.getSpecification();
}
String toKey(ProjectCostItem item) {
return item.getTitle() + "_" + item.getSpecification();
}
private void compositeOut(Map<Inventory, List<ContractItem>> map, List<ProjectCostItem> results,
MessageHolder holder) {
// 根据存货匹配,可对多个相同的存货进行合并
for (Map.Entry<Inventory, List<ContractItem>> entry : map.entrySet()) {
Inventory inventory = Hibernate.isInitialized(entry.getKey()) ? entry.getKey()
: getInventoryService().findById(entry.getKey().getId());
if (inventory == null) {
// 存货编号没有,则使用 项目名称+规格 来判断是否有数据
entry.getValue().stream().filter(item -> item.getInventory() == null)
.collect(Collectors.groupingBy(this::toKey)).forEach((key, value) -> {
List<ProjectCostItem> list = results.stream()
.filter(v -> v.getInventory() == null && key.equals(toKey(v))).toList();
compositeOut(null, value, list, results);
});
} else {
List<ProjectCostItem> list = results.stream().filter(v -> Objects.equals(v.getInventory(), inventory))
.toList();
compositeOut(inventory, entry.getValue(), list, results);
}
}
}
private void compositeIn(List<ContractItem> contractItems, List<ProjectCostItem> results, MessageHolder holder) {
// 存货编号没有,则使用 项目名称+规格 来判断是否有数据
contractItems.stream().filter(item -> item.getInventory() == null).collect(Collectors.groupingBy(this::toKey))
.forEach((key, value) -> {
List<ProjectCostItem> list = results.stream()
.filter(v -> v.getInventory() == null && key.equals(toKey(v))).toList();
compositeIn(null, value, list, results, holder);
});
// 根据存货编号来判断是否有数据
contractItems.stream().filter(item -> item.getInventory() != null)
.collect(Collectors.groupingBy(ContractItem::getInventory)).forEach((inventory, value) -> {
List<ProjectCostItem> list = results.stream()
.filter(v -> Objects.equals(v.getInventory(), inventory)).toList();
compositeIn(inventory, value, list, results, holder);
});
}
private void compositeIn(
Inventory inventory,
List<ContractItem> contractItems, List<ProjectCostItem> costItems,
List<ProjectCostItem> results, MessageHolder holder) {
// contractItems 对应的 costItems
// 同步处理 contractItems 中的多个 ContractItem 和 costItems 中的多个 ProjectCostItem
for (int i = 0; i < Math.max(contractItems.size(), costItems.size()); i++) {
ContractItem contractItem = i < contractItems.size() ? contractItems.get(i) : null;
ProjectCostItem projectCostItem = i < costItems.size() ? costItems.get(i) : null;
if (contractItem == null) {
if (projectCostItem != null) {
if (results.remove(projectCostItem)) {
getItemService().delete(projectCostItem);
holder.warn("删除成本项:" + projectCostItem.toPrettyString());
}
}
continue;
}
if (projectCostItem == null) {
// 添加新的项目
projectCostItem = new ProjectCostItem();
projectCostItem.setCost(cost);
projectCostItem.setInventory(contractItem.getInventory());
projectCostItem.setTitle(contractItem.getTitle());
projectCostItem.setSpecification(contractItem.getSpecification());
projectCostItem.setUnit(contractItem.getUnit());
projectCostItem.setCreator(currentUser.get());
projectCostItem.setCreateDate(LocalDateTime.now());
projectCostItem = save(projectCostItem);
results.add(projectCostItem);
holder.info("新增成本项:" + projectCostItem.toPrettyString());
} else {
projectCostItem.setUpdater(currentUser.get());
projectCostItem.setUpdateDate(LocalDateTime.now());
}
// 更新已存在的项目
if (!Objects.equals(projectCostItem.getInventory(), contractItem.getInventory())) {
projectCostItem.setInventory(contractItem.getInventory());
holder.info("更新进项成本的库存为:" + contractItem.getInventory().toPrettyString());
}
if (!Objects.equals(projectCostItem.getTitle(), contractItem.getTitle())) {
projectCostItem.setTitle(contractItem.getTitle());
holder.info("更新进项成本的标题为:" + contractItem.getTitle());
}
if (!Objects.equals(projectCostItem.getSpecification(), contractItem.getSpecification())) {
projectCostItem.setSpecification(contractItem.getSpecification());
holder.info("更新进项成本的规格为:" + contractItem.getSpecification());
}
if (!Objects.equals(projectCostItem.getUnit(), contractItem.getUnit())) {
projectCostItem.setUnit(contractItem.getUnit());
holder.info("更新进项成本的计量单位为:" + contractItem.getUnit());
}
transportInTaxRate(projectCostItem, contractItem, holder);
if (!NumberUtils.equals(projectCostItem.getInQuantity(), contractItem.getQuantity())) {
projectCostItem.setInQuantity(contractItem.getQuantity());
holder.info("更新进项成本的数量为:" + contractItem.getQuantity());
}
save(projectCostItem);
}
}
/**
* 处理不同时期的税率将2019-04-01后的17%的税率改为11%
*/
private void transportInTaxRate(ProjectCostItem costItem, ContractItem contractItem, MessageHolder holder) {
if (contractItem.getTaxRate() >= TaxRateUtils.TAX_RATE_GENERAL_OLDEST) {
// 2019-04-01 开始税率调整为11%
if (TaxRateUtils.isAfterTaxRateChangeTo13(cost.getApplyTime())) {
// 含税价
double v = TaxRateUtils.toInclusiveTax(contractItem.getExclusiveTaxPrice(), contractItem.getTaxRate());
costItem.setInTaxRate(TaxRateUtils.TAX_RATE_GENERAL);
double exclusiveTaxPrice = TaxRateUtils.toExclusiveTax(v, costItem.getInTaxRate());
if (!NumberUtils.equals(costItem.getInExclusiveTaxPrice(), exclusiveTaxPrice)) {
costItem.setInExclusiveTaxPrice(exclusiveTaxPrice);
holder.warn("成本进项税自动调整 " + contractItem.getTaxRate() + " -> " + costItem.getInTaxRate()
+ ", 进项未税价格调整 " + contractItem.getExclusiveTaxPrice() + " -> "
+ costItem.getInExclusiveTaxPrice() + "!");
}
return;
}
}
if (!NumberUtils.equals(costItem.getInTaxRate(), contractItem.getTaxRate())) {
costItem.setInTaxRate(contractItem.getTaxRate());
holder.warn("更新进项成本的税率为:" + costItem.getInTaxRate());
}
if (!NumberUtils.equals(costItem.getInExclusiveTaxPrice(), contractItem.getExclusiveTaxPrice())) {
costItem.setInExclusiveTaxPrice(contractItem.getExclusiveTaxPrice());
holder.warn("更新进项成本的未税价格为:" + costItem.getInExclusiveTaxPrice());
}
}
/**
* 合并合同条目为一条成本条目
*
* @param inventory 存货
* @param contractItems 合同中的条目
* @param costItems 成本表中的条目
* @param results 最总输出的成本表条目列表
*/
private void compositeOut(
Inventory inventory,
List<ContractItem> contractItems, List<ProjectCostItem> costItems,
List<ProjectCostItem> results) {
// 合计总金额
double totalFee = contractItems.stream().mapToDouble(v -> v.getTaxPrice() * v.getQuantity()).sum();
// 如果有存货则使用存货的税率否则使用13%
float taxRate = inventory == null ? TaxRateUtils.TAX_RATE_GENERAL : inventory.getPurchasePrice().getTaxRate();
if (taxRate >= TaxRateUtils.TAX_RATE_GENERAL_OLDEST
&& TaxRateUtils.isAfterTaxRateChangeTo13(cost.getApplyTime())) {
taxRate = TaxRateUtils.TAX_RATE_GENERAL;
}
// 合计数量
double totalQuantity = contractItems.stream().mapToDouble(ContractItem::getQuantity).sum();
// 计算含税采购单价
double taxPrice = totalQuantity == 0 ? totalFee : (totalFee / totalQuantity);
// 计算不含税采购单价
double outExclusiveTaxPrice = TaxRateUtils.toExclusiveTax(taxPrice, taxRate);
ProjectCostItem projectCostItem = null;
if (costItems.isEmpty()) {
// 成本条目不存在,则创建成本条目
projectCostItem = new ProjectCostItem();
projectCostItem.setCost(cost);
projectCostItem.setInventory(inventory);
if (inventory != null) {
projectCostItem.setTitle(inventory.getName());
projectCostItem.setSpecification(inventory.getSpecification());
projectCostItem.setUnit(inventory.getUnit());
} else {
ContractItem first = contractItems.getFirst();
projectCostItem.setTitle(first.getTitle());
projectCostItem.setSpecification(first.getSpecification());
projectCostItem.setUnit(first.getUnit());
}
projectCostItem.setCreator(currentUser.get());
projectCostItem.setCreateDate(LocalDateTime.now());
projectCostItem = save(projectCostItem);
results.add(projectCostItem);
} else {
projectCostItem = costItems.getFirst();
projectCostItem.setUpdater(currentUser.get());
projectCostItem.setUpdateDate(LocalDateTime.now());
}
projectCostItem.setOutTaxRate(taxRate);
projectCostItem.setOutExclusiveTaxPrice(outExclusiveTaxPrice);
projectCostItem.setOutQuantity(totalQuantity);
save(projectCostItem);
}
ProjectCostService getCostService() {
return getCachedBean(ProjectCostService.class);
}
public ProjectCostItemService getItemService() {
return getCachedBean(ProjectCostItemService.class);
}
ContractService getContractService() {
return getCachedBean(ContractService.class);
}
ContractItemService getContractItemService() {
return getCachedBean(ContractItemService.class);
}
InventoryService getInventoryService() {
return getCachedBean(InventoryService.class);
}
public ProjectCostItem save(ProjectCostItem entity) {
return getItemService().save(entity);
}
}