feat: 新增WebSocket任务管理器及相关任务实现
实现WebSocketServerTaskManager用于管理WebSocket任务,并添加多个任务类: - CompanyContext和CloudRkContext接口定义 - WebSocketServerTasker接口及多个具体任务实现类 - ContractVerifyTasker合同验证任务 - ContractRepairTasker合同修复任务 - CompanyCustomerRebuildFilesTasker客户文件重建任务 - CompanyVerifyTasker企业验证任务 - CustomerFileMoveTasker客户文件移动任务 - CompanyCompositeUpdateTasker企业综合更新任务 - ProjectCostImportItemsFromContractsTasker项目成本导入任务 - 其他相关辅助任务类 这些任务类通过WebSocket与前端交互,实现各种业务功能
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
package com.ecep.contract.ds.company.tasker;
|
||||
|
||||
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.ecep.contract.util.MyStringUtils;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Setter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 合并更新
|
||||
*/
|
||||
public class CompanyCompositeUpdateTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCompositeUpdateTasker.class);
|
||||
|
||||
CloudRkCtx cloudRkCtx = new CloudRkCtx();
|
||||
// CloudYuCtx cloudYuCtx = new CloudYuCtx();
|
||||
|
||||
@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 {
|
||||
updateProgress(0.1, 1);
|
||||
syncFromCloudRk(holder);
|
||||
updateProgress(0.3, 1);
|
||||
syncFromYongYouU8(holder);
|
||||
updateProgress(0.5, 1);
|
||||
syncFromCloudTyc(holder);
|
||||
updateProgress(0.7, 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void syncFromCloudRk(MessageHolder holder) {
|
||||
holder.debug("1. 从 " + CloudServiceConstant.RK_NAME + " 更新...");
|
||||
try {
|
||||
cloudRkService = getCachedBean(CloudRkService.class);
|
||||
} catch (BeansException e) {
|
||||
holder.warn("服务未启用");
|
||||
return;
|
||||
}
|
||||
CloudRk cloudRk = cloudRkService.getOrCreateCloudRk(company);
|
||||
if (cloudRk == null) {
|
||||
holder.error("无法创建或获取 CloudRk 对象");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (cloudRkCtx.syncCompany(company, cloudRk, holder)) {
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (message.length() > 50) {
|
||||
message = message.substring(0, 50);
|
||||
}
|
||||
cloudRk.setDescription(message);
|
||||
} finally {
|
||||
cloudRk.setLatestUpdate(LocalDateTime.now());
|
||||
try {
|
||||
cloudRkService.save(cloudRk);
|
||||
} catch (Exception e) {
|
||||
holder.error("保存 CloudRk 错误: " + cloudRk.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void syncFromYongYouU8(MessageHolder holder) {
|
||||
holder.debug("2. 从 " + CloudServiceConstant.U8_NAME + " 更新...");
|
||||
try {
|
||||
yongYouU8Service = getCachedBean(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 = getCachedBean(CloudTycService.class);
|
||||
} catch (BeansException e) {
|
||||
holder.warn("未启用 " + CloudServiceConstant.TYC_NAME + " 服务");
|
||||
return;
|
||||
}
|
||||
cloudTycService.syncCompany(company, holder);
|
||||
updateProgress(1, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
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 ContractService getContractService() {
|
||||
return getCachedBean(ContractService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 核验公司名下的所有合同
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.ecep.contract.ds.project;
|
||||
|
||||
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.stream.Collectors;
|
||||
|
||||
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.handler.SessionInfo;
|
||||
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.service.WebSocketServerTasker;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.ecep.contract.util.NumberUtils;
|
||||
import com.ecep.contract.util.TaxRateUtils;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
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 setSession(SessionInfo session) {
|
||||
currentUser = () -> {
|
||||
return getEmployeeService().findById(session.getEmployeeId());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Employee getCurrentUser() {
|
||||
if (currentUser == null) {
|
||||
return null;
|
||||
}
|
||||
return currentUser.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
int contractId = argsNode.get(0).asInt();
|
||||
cost = getCachedBean(ProjectCostService.class).findById(contractId);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public ProjectCostItemService getItemService() {
|
||||
return getCachedBean(ProjectCostItemService.class);
|
||||
}
|
||||
|
||||
public ProjectCostItem save(ProjectCostItem entity) {
|
||||
return getItemService().save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void execute(MessageHolder holder) throws Exception {
|
||||
importFromContracts(cost, holder);
|
||||
return null;
|
||||
}
|
||||
|
||||
String toKey(ContractItem item) {
|
||||
return item.getTitle() + "_" + item.getSpecification();
|
||||
}
|
||||
|
||||
String toKey(ProjectCostItem item) {
|
||||
return item.getTitle() + "_" + item.getSpecification();
|
||||
}
|
||||
|
||||
ProjectCostService getCostService() {
|
||||
return getCachedBean(ProjectCostService.class);
|
||||
}
|
||||
|
||||
ContractService getContractService() {
|
||||
return getCachedBean(ContractService.class);
|
||||
}
|
||||
|
||||
ContractItemService getContractItemService() {
|
||||
return getCachedBean(ContractItemService.class);
|
||||
}
|
||||
|
||||
InventoryService getInventoryService() {
|
||||
return getCachedBean(InventoryService.class);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.ecep.contract.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -14,11 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.constant.WebSocketConstant;
|
||||
import com.ecep.contract.handler.SessionInfo;
|
||||
import com.ecep.contract.model.Voable;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@@ -58,18 +58,26 @@ public class WebSocketServerTaskManager implements InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessage(WebSocketSession session, JsonNode jsonNode) {
|
||||
public void onMessage(SessionInfo session, JsonNode jsonNode) {
|
||||
// 处理 sessionId 的消息
|
||||
String sessionId = jsonNode.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
|
||||
try {
|
||||
handleAsSessionCallback(session, sessionId, jsonNode);
|
||||
} catch (Exception e) {
|
||||
sendError(session, sessionId, e.getMessage());
|
||||
sendError(session, sessionId, WebSocketConstant.ERROR_CODE_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||
logger.warn("处理会话回调失败 (会话ID: {}): {}", sessionId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAsSessionCallback(WebSocketSession session, String sessionId, JsonNode jsonNode) {
|
||||
private void sendError(SessionInfo session, String sessionId, int errorCode, String message) {
|
||||
try {
|
||||
session.sendError(WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId, errorCode, message);
|
||||
} catch (IOException e) {
|
||||
logger.warn("发送错误消息失败 (消息ID: {}): {}", sessionId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAsSessionCallback(SessionInfo session, String sessionId, JsonNode jsonNode) {
|
||||
if (!jsonNode.has("type")) {
|
||||
throw new IllegalArgumentException("缺失 type 参数");
|
||||
}
|
||||
@@ -79,7 +87,7 @@ public class WebSocketServerTaskManager implements InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
private void createTask(WebSocketSession session, String sessionId, JsonNode jsonNode) {
|
||||
private void createTask(SessionInfo session, String sessionId, JsonNode jsonNode) {
|
||||
if (!jsonNode.has(WebSocketConstant.ARGUMENTS_FIELD_NAME)) {
|
||||
throw new IllegalArgumentException("缺失 " + WebSocketConstant.ARGUMENTS_FIELD_NAME + " 参数");
|
||||
}
|
||||
@@ -106,6 +114,7 @@ public class WebSocketServerTaskManager implements InitializingBean {
|
||||
}
|
||||
|
||||
if (tasker instanceof WebSocketServerTasker t) {
|
||||
t.setSession(session);
|
||||
t.setTitleHandler(title -> sendToSession(session, sessionId, "title", title));
|
||||
t.setMessageHandler(msg -> sendMessageToSession(session, sessionId, msg));
|
||||
t.setPropertyHandler((name, value) -> {
|
||||
@@ -125,7 +134,6 @@ public class WebSocketServerTaskManager implements InitializingBean {
|
||||
callable.call();
|
||||
sendToSession(session, sessionId, "done");
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
@@ -133,45 +141,36 @@ public class WebSocketServerTaskManager implements InitializingBean {
|
||||
|
||||
}
|
||||
|
||||
private boolean sendMessageToSession(WebSocketSession session, String sessionId, Message msg) {
|
||||
private boolean sendMessageToSession(SessionInfo session, String sessionId, Message msg) {
|
||||
return sendToSession(session, sessionId, "message", msg.getLevel().getName(), msg.getMessage());
|
||||
}
|
||||
|
||||
private boolean sendToSession(WebSocketSession session, String sessionId, String type, Object... args) {
|
||||
private boolean sendToSession(SessionInfo session, String sessionId, String type, Object... args) {
|
||||
try {
|
||||
String text = objectMapper.writeValueAsString(Map.of(
|
||||
session.send(Map.of(
|
||||
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
|
||||
"type", type,
|
||||
WebSocketConstant.ARGUMENTS_FIELD_NAME, args));
|
||||
session.sendMessage(new TextMessage(text));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// 捕获所有可能的异常,防止影响主流程
|
||||
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
||||
logger.warn("发送消息失败 (消息ID: {}): {}", sessionId, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendError(WebSocketSession session, String sessionId, String message) {
|
||||
if (session == null || !session.isOpen()) {
|
||||
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
||||
return;
|
||||
private void send(SessionInfo session, String sessionId, Object data) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (data instanceof Voable<?>) {
|
||||
map.put("data", ((Voable<?>) data).toVo());
|
||||
} else {
|
||||
map.put("data", data);
|
||||
}
|
||||
|
||||
map.put(WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId);
|
||||
map.put(WebSocketConstant.SUCCESS_FIELD_NAME, true);
|
||||
try {
|
||||
String errorMessage = objectMapper.writeValueAsString(Map.of(
|
||||
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
|
||||
WebSocketConstant.SUCCESS_FIELD_VALUE, false,
|
||||
WebSocketConstant.MESSAGE_FIELD_NAME, message));
|
||||
|
||||
// 检查会话状态并尝试发送错误消息
|
||||
if (session.isOpen()) {
|
||||
session.sendMessage(new TextMessage(errorMessage));
|
||||
} else {
|
||||
logger.warn("会话已关闭,无法发送错误消息: {}", message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 捕获所有可能的异常,防止影响主流程
|
||||
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
||||
session.send(map);
|
||||
} catch (IOException e) {
|
||||
logger.warn("发送消息失败 (消息ID: {}): {}", sessionId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.ecep.contract.service;
|
||||
|
||||
import com.ecep.contract.Message;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.ecep.contract.Message;
|
||||
import com.ecep.contract.handler.SessionInfo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public interface WebSocketServerTasker extends Callable<Object> {
|
||||
void init(JsonNode argsNode);
|
||||
|
||||
default void setSession(SessionInfo session) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息处理函数
|
||||
*/
|
||||
@@ -17,7 +23,6 @@ public interface WebSocketServerTasker extends Callable<Object> {
|
||||
|
||||
void setPropertyHandler(BiConsumer<String, Object> propertyHandler);
|
||||
|
||||
void init(JsonNode argsNode);
|
||||
|
||||
void setProgressHandler(BiConsumer<Long, Long> progressHandler);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user