Compare commits

..

2 Commits

Author SHA1 Message Date
2057c3ca67 feat: 实现客户端与服务器端Tasker通信机制及文件管理功能
refactor: 重构Tasker基类与服务获取逻辑
fix: 修复文件路径显示问题及任务注册加载机制
docs: 添加客户端与服务器端Tasker通信规则文档
style: 优化代码格式与日志输出
build: 添加tasker_mapper.json配置文件
chore: 清理无用代码与文件
2025-09-25 00:14:34 +08:00
dc764e6ed8 feat(表格单元格): 添加文件路径表格单元格基础类 2025-09-25 00:14:13 +08:00
28 changed files with 1017 additions and 445 deletions

View File

@@ -23,6 +23,7 @@
- StringConverter 创建规则与实现逻辑 `docs/task/string_converter_implementation_guide.md` - StringConverter 创建规则与实现逻辑 `docs/task/string_converter_implementation_guide.md`
- TableCell 规则与逻辑 `docs/task/table_cell_implementation_guide.md` - TableCell 规则与逻辑 `docs/task/table_cell_implementation_guide.md`
- Service 规则与逻辑 `docs/task/serservice_layer_rules.md` - Service 规则与逻辑 `docs/task/serservice_layer_rules.md`
- 客户端 Tasker 至 服务器端 Tasker 通信规则与逻辑 `docs/task/client_server_tasker_communication_rules.md`
### common 模块 ### common 模块
- Java 21 - Java 21

View File

@@ -41,7 +41,6 @@ public class WebSocketClientSession {
webSocketService.send(arguments); webSocketService.send(arguments);
} }
public void onMessage(JsonNode node) { public void onMessage(JsonNode node) {
if (node.has("type")) { if (node.has("type")) {
String type = node.get("type").asText(); String type = node.get("type").asText();
@@ -66,15 +65,15 @@ public class WebSocketClientSession {
} }
} }
private void handleAsStart(JsonNode args) {
tasker.updateMessage(java.util.logging.Level.INFO, "任务开始");
}
private void handleAsDone(JsonNode args) { private void handleAsDone(JsonNode args) {
tasker.updateMessage(java.util.logging.Level.INFO, "任务完成"); tasker.updateMessage(java.util.logging.Level.INFO, "任务完成");
close(); close();
} }
private void handleAsStart(JsonNode args) {
tasker.updateMessage(java.util.logging.Level.INFO, "任务开始");
}
private void handleAsProgress(JsonNode args) { private void handleAsProgress(JsonNode args) {
long current = args.get(0).asLong(); long current = args.get(0).asLong();
long total = args.get(1).asLong(); long total = args.get(1).asLong();
@@ -91,7 +90,8 @@ public class WebSocketClientSession {
Object object = webSocketService.getObjectMapper().convertValue(value, descriptor.getPropertyType()); Object object = webSocketService.getObjectMapper().convertValue(value, descriptor.getPropertyType());
System.out.println("object = " + object); System.out.println("object = " + object);
System.out.println("descriptor.getWriteMethod() = " + descriptor.getWriteMethod()); System.out.println("descriptor.getWriteMethod() = " + descriptor.getWriteMethod());
System.out.println("descriptor.getWriteMethod().getParameterTypes() = " + descriptor.getWriteMethod().getParameterTypes()); System.out.println("descriptor.getWriteMethod().getParameterTypes() = "
+ descriptor.getWriteMethod().getParameterTypes());
descriptor.getWriteMethod().invoke(tasker, object); descriptor.getWriteMethod().invoke(tasker, object);
} catch (Exception e) { } catch (Exception e) {
tasker.updateMessage(java.util.logging.Level.SEVERE, "属性设置失败: " + name + " = " + value); tasker.updateMessage(java.util.logging.Level.SEVERE, "属性设置失败: " + name + " = " + value);

View File

@@ -264,7 +264,6 @@ public class CompanyWindowController
public void onCompanyVerifyAction(ActionEvent event) { public void onCompanyVerifyAction(ActionEvent event) {
CompanyVo company = getEntity(); CompanyVo company = getEntity();
CompanyVerifyTasker task = new CompanyVerifyTasker(); CompanyVerifyTasker task = new CompanyVerifyTasker();
task.setCompanyService(companyService);
task.setCompany(company); task.setCompany(company);
UITools.showTaskDialogAndWait("企业合规性验证", task, null); UITools.showTaskDialogAndWait("企业合规性验证", task, null);
} }

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.controller.customer; package com.ecep.contract.controller.contract.sale_order;
import com.ecep.contract.vo.SalesOrderVo; import com.ecep.contract.vo.SalesOrderVo;
import org.slf4j.Logger; import org.slf4j.Logger;

View File

@@ -1,240 +1,34 @@
package com.ecep.contract.controller.customer; package com.ecep.contract.controller.customer;
import static com.ecep.contract.util.ExcelUtils.setCellValue; import com.ecep.contract.MessageHolder;
import com.ecep.contract.WebSocketClientTasker;
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 com.ecep.contract.service.*;
import com.ecep.contract.task.Tasker; import com.ecep.contract.task.Tasker;
import com.ecep.contract.util.CompanyUtils;
import com.ecep.contract.vo.CompanyCustomerFileVo;
import com.ecep.contract.vo.CompanyCustomerVo; import com.ecep.contract.vo.CompanyCustomerVo;
import org.apache.poi.ss.usermodel.Sheet; import lombok.Setter;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import com.ecep.contract.CustomerFileType; public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.vo.CloudTycVo;
import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
import lombok.Setter;
public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class); private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class);
@Setter @Setter
private CompanyCustomerVo customer; private CompanyCustomerVo customer;
@Setter
private CompanyService companyService;
private CompanyContactService companyContactService;
private CompanyCustomerService companyCustomerService; @Override
@Setter public String getTaskName() {
private CompanyCustomerFileService companyCustomerFileService; return getClass().getSimpleName();
private CompanyCustomerService getCompanyCustomerService() {
if (companyCustomerService == null) {
companyCustomerService = SpringApp.getBean(CompanyCustomerService.class);
}
return companyCustomerService;
} }
private CompanyContactService getCompanyContactService() { @Override
if (companyContactService == null) { public void updateProgress(long current, long total) {
companyContactService = SpringApp.getBean(CompanyContactService.class); super.updateProgress(current, total);
}
return companyContactService;
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (companyCustomerFileService == null) {
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return companyCustomerFileService;
}
private CompanyCustomerEvaluationFormFileService getCompanyCustomerEvaluationFormFileService() {
return getBean(CompanyCustomerEvaluationFormFileService.class);
}
private File getEvaluationFormTemplate() {
return getCompanyCustomerFileService().getEvaluationFormTemplate();
} }
@Override @Override
protected Object execute(MessageHolder holder) throws Exception { protected Object execute(MessageHolder holder) throws Exception {
try { updateTitle("客户评估表更新任务");
updateEvaluationForm(holder); return callRemoteTask(holder, getLocale(), customer.getId());
} catch (Exception ex) {
updateMessage(ex.getMessage());
}
return null;
} }
public void updateEvaluationForm(MessageHolder holder) {
if (!StringUtils.hasText(customer.getPath())) {
holder.error("供应商目录未设置,请先设置供应商目录");
return;
}
File template = getEvaluationFormTemplate();
if (template == null) {
holder.error("评价表模板文件未设置,请先设置评价表模板文件");
return;
}
if (!template.exists()) {
holder.error("评价表模板文件 " + template.getAbsolutePath() + " 不存在,请检查");
return;
}
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());
logger.error(e.getMessage(), e);
}
} 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("评价表已创建");
CompanyCustomerFileVo customerFile = new CompanyCustomerFileVo();
customerFile.setCustomer(customer.getId());
customerFile.setFilePath(destFile.getAbsolutePath());
customerFile.setType(CustomerFileType.General);
save(customerFile);
} catch (Exception e) {
holder.error(e.getMessage());
logger.error(e.getMessage(), e);
}
}
updateProgress(1, 1);
}
private void save(CompanyCustomerFileVo customerFile) {
getCompanyCustomerFileService().save(customerFile);
}
/**
* 更新客户评估表,依据模板创建,如果已经存在生成的文件,则更新评估表
*
* @param wb work book
* @param destFile 目标文件
*/
public void updateEvaluationForm(Workbook wb, File destFile, MessageHolder holder) throws IOException {
Integer companyId = customer.getCompanyId();
CompanyVo company = getCompanyService().findById(companyId);
Sheet sheet = wb.getSheetAt(0);
updateSheet(company, sheet, holder.sub(" - "));
updateProgress(900, 1000);
// 输出到文件
try (OutputStream fileOut = new FileOutputStream(destFile)) {
wb.write(fileOut);
} catch (FileNotFoundException e) {
holder.error("写评估表时发生文件错误,请检查评估表是否被打开中:" + e.getMessage());
logger.error(e.getMessage(), e);
}
}
private void updateSheet(CompanyVo 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);
CloudTycVo cloudTyc = cloudTycService.getOrCreateCloudTyc(company);
setCellValue(sheet, "D10", "天眼评分:" + (cloudTyc.getScore() > 0 ? cloudTyc.getScore() : ""));
// 检索评估表
List<CompanyCustomerFileVo> customerFiles = getCompanyCustomerFileService().findAllByCustomerAndType(customer,
CustomerFileType.EvaluationForm);
List<CompanyCustomerEvaluationFormFileVo> filteredList = customerFiles.stream().filter(file -> {
return file.getSignDate() != null && file.isValid();
})
.sorted(Comparator.comparing(CompanyCustomerFileVo::getSignDate))
.map(getCompanyCustomerEvaluationFormFileService()::findByCustomerFile)
.toList();
if (filteredList.isEmpty()) {
setCellValue(sheet, "C40", "首次评价");
try {
sheet.addMergedRegion(CellRangeAddress.valueOf("C40:K40"));
} catch (Exception ignored) {
}
} else {
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 (CompanyCustomerEvaluationFormFileVo form : filteredList) {
CompanyCustomerFileVo customerFile = getCompanyCustomerFileService().findById(form.getCustomerFile());
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

@@ -63,7 +63,6 @@ public class CompanyCustomerManagerWindowController
return getCachedBean(CompanyCustomerService.class); return getCachedBean(CompanyCustomerService.class);
} }
@Autowired
private CompanyService getCompanyService() { private CompanyService getCompanyService() {
return getCachedBean(CompanyService.class); return getCachedBean(CompanyService.class);
} }

View File

@@ -0,0 +1,34 @@
package com.ecep.contract.controller.customer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.WebSocketClientTasker;
import com.ecep.contract.task.Tasker;
import com.ecep.contract.vo.CompanyCustomerVo;
import lombok.Setter;
public class CompanyCustomerNextSignDateTask extends Tasker<Object> implements WebSocketClientTasker {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerNextSignDateTask.class);
@Setter
private CompanyCustomerVo customer;
@Override
public String getTaskName() {
return getClass().getSimpleName();
}
@Override
public void updateProgress(long current, long total) {
super.updateProgress(current, total);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
updateTitle("计算客户下一个评价日期");
return callRemoteTask(holder, getLocale(), customer.getId());
}
}

View File

@@ -4,24 +4,20 @@ import java.io.File;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import com.ecep.contract.controller.table.cell.CompanyCustomerFileTableTypeTableCell;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.ecep.contract.CustomerFileType; import com.ecep.contract.CustomerFileType;
import com.ecep.contract.DesktopUtils; import com.ecep.contract.DesktopUtils;
import com.ecep.contract.Message;
import com.ecep.contract.MyDateTimeUtils; import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.CompanyCustomerConstant; import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin; import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.model.BaseEnumEntity; import com.ecep.contract.controller.table.cell.CompanyCustomerFileTableTypeTableCell;
import com.ecep.contract.controller.table.cell.FilePathTableCell;
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService; import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
import com.ecep.contract.service.CompanyCustomerFileService; import com.ecep.contract.service.CompanyCustomerFileService;
import com.ecep.contract.service.CompanyCustomerFileTypeService; import com.ecep.contract.service.CompanyCustomerFileTypeService;
import com.ecep.contract.service.CompanyCustomerService; import com.ecep.contract.task.CompanyCustomerRebuildFilesTasker;
import com.ecep.contract.util.FileUtils; import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.FxmlPath; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ParamUtils; import com.ecep.contract.util.ParamUtils;
@@ -31,34 +27,24 @@ import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
import com.ecep.contract.vo.CompanyCustomerFileVo; import com.ecep.contract.vo.CompanyCustomerFileVo;
import com.ecep.contract.vo.CompanyCustomerVo; import com.ecep.contract.vo.CompanyCustomerVo;
import com.ecep.contract.vo.CompanyVo; import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.vo.CustomerFileTypeLocalVo;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.concurrent.Task;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.ButtonType; import javafx.scene.control.ButtonType;
import javafx.scene.control.MenuItem; import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.input.Dragboard; import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
import lombok.Setter;
@FxmlPath("/ui/company/customer/customer-tab-file.fxml") @FxmlPath("/ui/company/customer/customer-tab-file.fxml")
public class CustomerTabSkinFile public class CustomerTabSkinFile
extends AbstCompanyCustomerTableTabSkin<CompanyCustomerFileVo, CompanyCustomerFileViewModel> extends AbstCompanyCustomerTableTabSkin<CompanyCustomerFileVo, CompanyCustomerFileViewModel>
implements EditableEntityTableTabSkin<CompanyCustomerFileVo, CompanyCustomerFileViewModel> { implements EditableEntityTableTabSkin<CompanyCustomerFileVo, CompanyCustomerFileViewModel> {
@Setter
private CompanyCustomerFileService companyCustomerFileService;
public TableColumn<CompanyCustomerFileViewModel, Number> fileTable_idColumn; public TableColumn<CompanyCustomerFileViewModel, Number> fileTable_idColumn;
public TableColumn<CompanyCustomerFileViewModel, CustomerFileType> fileTable_typeColumn; public TableColumn<CompanyCustomerFileViewModel, CustomerFileType> fileTable_typeColumn;
public TableColumn<CompanyCustomerFileViewModel, String> fileTable_filePathColumn; public TableColumn<CompanyCustomerFileViewModel, String> fileTable_filePathColumn;
@@ -91,6 +77,14 @@ public class CustomerTabSkinFile
return getCompanyCustomerFileService(); return getCompanyCustomerFileService();
} }
public CompanyCustomerFileService getCompanyCustomerFileService() {
return getCachedBean(CompanyCustomerFileService.class);
}
public CompanyCustomerEvaluationFormFileService getEvaluationFormFileService() {
return getCachedBean(CompanyCustomerEvaluationFormFileService.class);
}
@Override @Override
public ParamUtils.Builder getSpecification(CompanyCustomerVo parent) { public ParamUtils.Builder getSpecification(CompanyCustomerVo parent) {
ParamUtils.Builder params = getSpecification(); ParamUtils.Builder params = getSpecification();
@@ -105,16 +99,18 @@ public class CustomerTabSkinFile
TableView<CompanyCustomerFileViewModel> table = getTableView(); TableView<CompanyCustomerFileViewModel> table = getTableView();
table.disableProperty().bind(viewModel.getPath().isEmpty()); table.disableProperty().bind(viewModel.getPath().isEmpty());
//
fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId()); fileTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
CompanyCustomerFileTypeService fileTypeService = getCachedBean(CompanyCustomerFileTypeService.class); CompanyCustomerFileTypeService fileTypeService = getCachedBean(CompanyCustomerFileTypeService.class);
fileTable_typeColumn.setCellValueFactory(param -> param.getValue().getType()); fileTable_typeColumn.setCellValueFactory(param -> param.getValue().getType());
fileTable_typeColumn.setCellFactory(CompanyCustomerFileTableTypeTableCell.forTableColumn(fileTypeService)); fileTable_typeColumn.setCellFactory(CompanyCustomerFileTableTypeTableCell.forTableColumn(fileTypeService));
fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath()); fileTable_filePathColumn.setCellValueFactory(param -> param.getValue().getFilePath());
fileTable_filePathColumn.setCellFactory(param -> new FileTableFilePathTableCell()); fileTable_filePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath()); fileTable_editFilePathColumn.setCellValueFactory(param -> param.getValue().getEditFilePath());
fileTable_editFilePathColumn.setCellFactory(param -> new FileTableFilePathTableCell()); fileTable_editFilePathColumn.setCellFactory(FilePathTableCell.forTableColumn(viewModel.getPath()));
fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate()); fileTable_signDateColumn.setCellValueFactory(param -> param.getValue().getSignDate());
fileTable_validColumn.setEditable(true); fileTable_validColumn.setEditable(true);
@@ -159,11 +155,12 @@ public class CustomerTabSkinFile
protected void onTableRowDoubleClickedAction(CompanyCustomerFileViewModel item) { protected void onTableRowDoubleClickedAction(CompanyCustomerFileViewModel item) {
CustomerFileType fileType = item.getType().get(); CustomerFileType fileType = item.getType().get();
if (fileType == CustomerFileType.EvaluationForm) { if (fileType == CustomerFileType.EvaluationForm) {
CompanyCustomerEvaluationFormFileVo evaluationFormFile = getCachedBean(
CompanyCustomerEvaluationFormFileService.class).findByCustomerFile(item.getId().get());
// 文件不是 Excel 文件时打开编辑UI // 文件不是 Excel 文件时打开编辑UI
if (!FileUtils.withExtensions(item.getFilePath().get(), FileUtils.XLS, if (!FileUtils.withExtensions(item.getFilePath().get(), FileUtils.XLS,
FileUtils.XLSX)) { FileUtils.XLSX)) {
CompanyCustomerEvaluationFormFileVo evaluationFormFile = getEvaluationFormFileService()
.findByCustomerFile(item.getId().get());
CompanyCustomerEvaluationFormFileWindowController.show(evaluationFormFile, CompanyCustomerEvaluationFormFileWindowController.show(evaluationFormFile,
controller.root.getScene().getWindow()); controller.root.getScene().getWindow());
return; return;
@@ -239,17 +236,13 @@ public class CustomerTabSkinFile
} }
public void onFileReBuildingAction(ActionEvent event) { public void onFileReBuildingAction(ActionEvent event) {
CompletableFuture.runAsync(() -> { CompanyCustomerRebuildFilesTasker task = new CompanyCustomerRebuildFilesTasker();
CompanyCustomerService customerService = getCompanyCustomerService(); CompanyCustomerVo customer = getEntity();
try { task.setCompanyCustomer(customer);
CompanyCustomerVo companyCustomer = customerService.findById(viewModel.getId().get()); UITools.showTaskDialogAndWait("重建客户文件", task, null);
if (customerService.reBuildingFiles(companyCustomer, (level, message) -> setStatus(message))) { if (task.isFilesUpdated()) {
loadTableDataSet(); loadTableDataSet();
} }
} catch (Exception e) {
e.printStackTrace();
}
});
} }
@Override @Override
@@ -320,74 +313,19 @@ public class CustomerTabSkinFile
// dataSet.remove(selectedItem); // dataSet.remove(selectedItem);
} }
private void initializeTask(Task<Object> task, String prefix, Consumer<String> consumer) {
task.setOnScheduled(e -> {
consumer.accept("正在" + prefix + ",请稍后...");
});
task.setOnRunning(e -> {
consumer.accept("开始" + prefix + "...");
});
task.setOnSucceeded(e -> {
consumer.accept(prefix + "同步完成...");
});
task.exceptionProperty().addListener((observable, oldValue, newValue) -> {
consumer.accept(newValue.getMessage());
});
SpringApp.getBean(ScheduledExecutorService.class).submit(task);
consumer.accept("任务已创建...");
}
public void onUpdateEvaluationFormAction(ActionEvent event) { public void onUpdateEvaluationFormAction(ActionEvent event) {
CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask(); CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask();
task.setCompanyService(getCompanyService()); CompanyCustomerVo customer = getEntity();
task.setCompanyCustomerFileService(getCompanyCustomerFileService()); task.setCustomer(customer);
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get())); UITools.showTaskDialogAndWait("更新评价表", task, null);
UITools.showTaskDialogAndWait("更新评价表", task, consumer -> {
initializeTask(task, "更新评价表", msg -> consumer.accept(Message.info(msg)));
});
loadTableDataSet(); loadTableDataSet();
} }
public void onCalcNextSignDateAction(ActionEvent event) { public void onCalcNextSignDateAction(ActionEvent event) {
UITools.showDialogAndWait("计算客户下一个评价日期", "依据已有的客户评估表和登记采购的合同计算下一个评估日期", ds -> { CompanyCustomerNextSignDateTask task = new CompanyCustomerNextSignDateTask();
CompanyCustomerVo companyCustomer = getCompanyCustomerService().findById(viewModel.getId().get()); CompanyCustomerVo customer = getEntity();
LocalDate nextSignDate = getCompanyCustomerFileService().getNextSignDate(companyCustomer, (level, msg) -> { task.setCustomer(customer);
Platform.runLater(() -> { UITools.showTaskDialogAndWait("计算客户下一个评价日期", task, null);
ds.add(msg);
});
});
if (nextSignDate != null) {
Platform.runLater(() -> {
ds.add("下一个评价日期:" + nextSignDate);
});
}
});
}
class FileTableFilePathTableCell extends TableCell<CompanyCustomerFileViewModel, String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || !StringUtils.hasText(item)) {
setText("");
return;
}
String path = viewModel.getPath().get();
if (StringUtils.hasText(path)) {
if (item.startsWith(path)) {
item = "~" + item.substring(path.length());
}
}
setText(item);
}
}
private CompanyCustomerFileService getCompanyCustomerFileService() {
if (companyCustomerFileService == null) {
companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
}
return companyCustomerFileService;
} }
} }

View File

@@ -7,7 +7,7 @@ import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.util.FxmlPath; import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.controller.contract.AbstContractTableTabSkin; import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
import com.ecep.contract.controller.contract.ContractWindowController; import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.customer.SalesOrderWindowController; import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell; import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
import com.ecep.contract.converter.EmployeeStringConverter; import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.service.SaleOrdersService; import com.ecep.contract.service.SaleOrdersService;

View File

@@ -4,7 +4,7 @@ import java.time.LocalDateTime;
import com.ecep.contract.MyDateTimeUtils; import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.SpringApp; import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.customer.SalesOrderWindowController; import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.converter.EmployeeStringConverter; import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.model.Employee; import com.ecep.contract.model.Employee;
import com.ecep.contract.model.SalesOrder; import com.ecep.contract.model.SalesOrder;

View File

@@ -1,7 +1,7 @@
package com.ecep.contract.controller.tab; package com.ecep.contract.controller.tab;
import com.ecep.contract.SpringApp; import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.customer.SalesOrderWindowController; import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin; import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.cell.EmployeeTableCell; import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.converter.EmployeeStringConverter; import com.ecep.contract.converter.EmployeeStringConverter;

View File

@@ -9,7 +9,7 @@ import java.util.stream.Collectors;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.ecep.contract.SpringApp; import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.customer.SalesOrderWindowController; import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin; import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.model.Contract; import com.ecep.contract.model.Contract;
import com.ecep.contract.service.ContractService; import com.ecep.contract.service.ContractService;

View File

@@ -0,0 +1,41 @@
package com.ecep.contract.controller.table.cell;
import javafx.beans.property.StringProperty;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
import org.springframework.util.StringUtils;
public class FilePathTableCell<V> extends TableCell<V, String> {
private StringProperty path;
/**
* 创建一个用于表格列的单元格工厂
*/
public static <T> Callback<TableColumn<T, String>, TableCell<T, String>> forTableColumn(
StringProperty parentPath) {
return param -> new FilePathTableCell<>(parentPath);
}
public FilePathTableCell(StringProperty parentPath) {
this.path = parentPath;
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || !StringUtils.hasText(item)) {
setText("");
return;
}
String path = this.path.get();
if (StringUtils.hasText(path)) {
if (item.startsWith(path)) {
item = "~" + item.substring(path.length());
}
}
setText(item);
}
}

View File

@@ -278,7 +278,6 @@ public class VendorTabSkinFile
public void onUpdateEvaluationFormAction(ActionEvent event) { public void onUpdateEvaluationFormAction(ActionEvent event) {
CompanyVendorEvaluationFormUpdateTask task = new CompanyVendorEvaluationFormUpdateTask(); CompanyVendorEvaluationFormUpdateTask task = new CompanyVendorEvaluationFormUpdateTask();
task.setCompanyService(getCompanyService());
UITools.showTaskDialogAndWait("更新评价表", task, null); UITools.showTaskDialogAndWait("更新评价表", task, null);
loadTableDataSet(); loadTableDataSet();
} }

View File

@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
import com.ecep.contract.CustomerFileType; import com.ecep.contract.CustomerFileType;
import com.ecep.contract.MessageHolder; import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp; import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.model.CompanyCustomer; import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile; import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.util.ParamUtils; import com.ecep.contract.util.ParamUtils;
@@ -27,9 +28,14 @@ import com.ecep.contract.vo.ContractVo;
@Service @Service
@CacheConfig(cacheNames = "customer-file") @CacheConfig(cacheNames = "customer-file")
public class CompanyCustomerFileService extends QueryService<CompanyCustomerFileVo, CompanyCustomerFileViewModel> { public class CompanyCustomerFileService extends QueryService<CompanyCustomerFileVo, CompanyCustomerFileViewModel> {
public File getEvaluationFormTemplate() { public File getEvaluationFormTemplate() {
throw new UnsupportedOperationException(); SysConfService confService = SpringApp.getBean(SysConfService.class);
String path = confService.getString(CompanyCustomerConstant.KEY_EVALUATION_FORM_TEMPLATE);
if (path == null) {
return null;
}
return new File(path);
} }
@Cacheable @Cacheable
@@ -100,7 +106,6 @@ public class CompanyCustomerFileService extends QueryService<CompanyCustomerFile
return SpringApp.getBean(HolidayService.class).adjustToWorkDay(setupDate.plusDays(-7)); return SpringApp.getBean(HolidayService.class).adjustToWorkDay(setupDate.plusDays(-7));
} }
public List<CompanyCustomerFileVo> findAllByCustomer(CompanyCustomerVo companyCustomer) { public List<CompanyCustomerFileVo> findAllByCustomer(CompanyCustomerVo companyCustomer) {
return findAll(ParamUtils.builder().equals("customer", companyCustomer).build(), Pageable.unpaged()) return findAll(ParamUtils.builder().equals("customer", companyCustomer).build(), Pageable.unpaged())
.getContent(); .getContent();

View File

@@ -2,6 +2,7 @@ package com.ecep.contract.service;
import java.io.File; import java.io.File;
import javafx.application.Platform;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -39,9 +40,9 @@ public class CompanyCustomerService extends QueryService<CompanyCustomerVo, Comp
/** /**
* 重建客户相关文件 * 重建客户相关文件
* *
* @param companyCustomer 客户对象 * @param companyCustomer 客户对象
* @param holder 消息持有者,用于显示任务执行过程中的消息 * @param holder 消息持有者,用于显示任务执行过程中的消息
* @return 如果文件重建成功则返回true否则返回false * @return 如果文件重建成功则返回true否则返回false
*/ */
public boolean reBuildingFiles(CompanyCustomerVo companyCustomer, MessageHolder holder) { public boolean reBuildingFiles(CompanyCustomerVo companyCustomer, MessageHolder holder) {
@@ -49,21 +50,21 @@ public class CompanyCustomerService extends QueryService<CompanyCustomerVo, Comp
if (!makePathAbsent(companyCustomer)) { if (!makePathAbsent(companyCustomer)) {
holder.addMessage(java.util.logging.Level.WARNING, "无法创建或确认客户路径,文件重建可能失败"); holder.addMessage(java.util.logging.Level.WARNING, "无法创建或确认客户路径,文件重建可能失败");
} }
// 创建并配置文件重建任务 // 创建并配置文件重建任务
CompanyCustomerRebuildFilesTasker tasker = new CompanyCustomerRebuildFilesTasker(); CompanyCustomerRebuildFilesTasker tasker = new CompanyCustomerRebuildFilesTasker();
tasker.setCompanyCustomer(companyCustomer); tasker.setCompanyCustomer(companyCustomer);
// 显示任务对话框并等待任务完成 // 显示任务对话框并等待任务完成
UITools.showTaskDialogAndWait("重建客户文件", tasker, null); UITools.showTaskDialogAndWait("重建客户文件", tasker, null);
// 返回任务执行结果 // 返回任务执行结果
return tasker.isFilesUpdated(); return tasker.isFilesUpdated();
} }
/** /**
* 检查客户路径是否不存在,如果不存在则创建并设置路径 * 检查客户路径是否不存在,如果不存在则创建并设置路径
* *
* @param companyCustomer 客户对象 * @param companyCustomer 客户对象
* @return 如果成功创建路径并设置则返回true否则返回false * @return 如果成功创建路径并设置则返回true否则返回false
*/ */
@@ -89,7 +90,7 @@ public class CompanyCustomerService extends QueryService<CompanyCustomerVo, Comp
/** /**
* 为客户创建路径 * 为客户创建路径
* *
* @param companyCustomer 客户对象 * @param companyCustomer 客户对象
* @return 创建的目录对象如果创建失败则返回null * @return 创建的目录对象如果创建失败则返回null
*/ */
@@ -113,7 +114,7 @@ public class CompanyCustomerService extends QueryService<CompanyCustomerVo, Comp
/** /**
* 格式化公司客户ID确保格式一致 * 格式化公司客户ID确保格式一致
* *
* @param id 客户ID * @param id 客户ID
* @return 格式化后的ID字符串 * @return 格式化后的ID字符串
*/ */
@@ -124,7 +125,7 @@ public class CompanyCustomerService extends QueryService<CompanyCustomerVo, Comp
/** /**
* 转义文件名,确保合法 * 转义文件名,确保合法
* *
* @param fileName 原始文件名 * @param fileName 原始文件名
* @return 转义后的文件名 * @return 转义后的文件名
*/ */

View File

@@ -1,5 +1,6 @@
package com.ecep.contract.task; package com.ecep.contract.task;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
@@ -27,36 +28,32 @@ public abstract class Tasker<T> extends Task<T> {
@Setter @Setter
protected java.util.function.Predicate<Message> messageHandler; protected java.util.function.Predicate<Message> messageHandler;
private EmployeeVo currentUser; private EmployeeVo currentUser;
@Setter private HashMap<Class<?>, Object> cachedMap = new HashMap<>();
private CompanyService companyService;
@Setter
private EmployeeService employeeService;
@Setter
private SysConfService confService;
public SysConfService getConfService() { public SysConfService getConfService() {
if (confService == null) { return getBean(SysConfService.class);
confService = getBean(SysConfService.class);
}
return confService;
} }
public CompanyService getCompanyService() { public CompanyService getCompanyService() {
if (companyService == null) { return getBean(CompanyService.class);
companyService = getBean(CompanyService.class);
}
return companyService;
} }
public EmployeeService getEmployeeService() { public EmployeeService getEmployeeService() {
if (employeeService == null) { return getBean(EmployeeService.class);
employeeService = getBean(EmployeeService.class);
}
return employeeService;
} }
protected <K> K getBean(Class<K> requiredType) throws BeansException { protected <K> K getBean(Class<K> requiredType) throws BeansException {
return SpringApp.getBean(requiredType); return getCachedBean(requiredType);
}
protected <K> K getCachedBean(Class<K> requiredType) {
@SuppressWarnings("unchecked")
K bean = (K) cachedMap.get(requiredType);
if (bean == null) {
bean = getBean(requiredType);
cachedMap.put(requiredType, bean);
}
return bean;
} }
public EmployeeVo getCurrentUser() { public EmployeeVo getCurrentUser() {
@@ -72,7 +69,6 @@ public abstract class Tasker<T> extends Task<T> {
try { try {
return execute(holder); return execute(holder);
} catch (Exception e) { } catch (Exception e) {
holder.error(e.getMessage()); holder.error(e.getMessage());
logger.error(e.getMessage(), e); logger.error(e.getMessage(), e);
throw e; throw e;

View File

@@ -0,0 +1,215 @@
# 客户端 Tasker 至 服务器端 Tasker 通信规则与逻辑
本文档总结了 Contract-Manager 项目中客户端 Tasker 与服务器端 Tasker 之间的通信规则、调用逻辑和实现模式,基于对以下文件的分析:
- `d:\idea-workspace\Contract-Manager\server\src\main\java\com\ecep\contract\ds\customer\tasker\CompanyCustomerEvaluationFormUpdateTask.java`
- `d:\idea-workspace\Contract-Manager\client\src\main\java\com\ecep\contract\controller\customer\CompanyCustomerEvaluationFormUpdateTask.java`
- `d:\idea-workspace\Contract-Manager\client\src\main\java\com\ecep\contract\controller\customer\CustomerTabSkinFile.java`
## 1. 架构设计原则
项目采用了清晰的客户端-服务器分离架构,任务处理遵循以下原则:
- **客户端轻量级**:负责任务发起、参数传递和结果展示
- **服务器端重量级**:负责实际业务逻辑处理和数据操作
- **WebSocket通信**使用WebSocket实现客户端与服务器端的任务通信和进度同步
## 2. 类命名与结构规范
### 2.1 命名规则
- 客户端与服务器端的对应Tasker类**必须使用相同的类名**(如示例中的`CompanyCustomerEvaluationFormUpdateTask`
- 客户端Tasker位于`client`模块的控制器包下
- 服务器端Tasker位于`server`模块的tasker包下
### 2.2 任务名称注册规则
- 所有服务器端Tasker类必须通过配置文件`tasker_mapper.json`进行注册,并由`WebSocketServerTaskManager`类在`afterPropertiesSet`方法中加载
- 注册格式为`"TaskClassName": "fully.qualified.ClassName"`
- 注册的Task名称将用于客户端和服务器端之间的任务识别
- 配置文件`tasker_mapper.json`应位于`src/main/resources`目录下,格式如下:
```json
{
"taskers": {
"TaskClassName": "fully.qualified.ClassName",
// 更多任务映射...
}
}
```
#### 任务名称注册实现示例
```java
@Override
public void afterPropertiesSet() throws Exception {
// 从tasker_mapper.json文件中加载任务注册信息
try {
Resource resource = resourceLoader.getResource("classpath:tasker_mapper.json");
try (InputStream inputStream = resource.getInputStream()) {
JsonNode rootNode = objectMapper.readTree(inputStream);
JsonNode taskersNode = rootNode.get("taskers");
if (taskersNode != null && taskersNode.isObject()) {
Map<String, String> taskMap = new java.util.HashMap<>();
taskersNode.fields().forEachRemaining(entry -> {
taskMap.put(entry.getKey(), entry.getValue().asText());
});
taskClzMap = taskMap;
}
}
} catch (Exception e) {
logger.error("Failed to load tasker_mapper.json", e);
// 使用默认值作为fallback
taskClzMap = Map.of();
}
}```
### 2.3 接口实现区分
- 客户端Tasker实现`WebSocketClientTasker`接口
- 服务器端Tasker实现`WebSocketServerTasker`接口
### 2.4 继承关系
- 客户端和服务器端Tasker均继承自`Tasker<Object>`基类
## 3. 客户端Tasker实现规则
客户端Tasker是任务的发起方需要遵循以下实现规则
### 3.1 核心属性
- 通常包含一个可设置的业务对象如示例中的`@Setter private CompanyCustomerVo customer;`
- 配置Logger日志记录器
### 3.2 核心方法实现
- **getTaskName()**返回任务名称通常使用类名
- **updateProgress()**继承或重写进度更新方法
- **execute()**调用`callRemoteTask()`方法将任务发送到服务器端传递必要参数
### 3.3 示例实现
```java
public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormUpdateTask.class);
@Setter
private CompanyCustomerVo customer; // 业务对象
@Override
public String getTaskName() {
return getClass().getSimpleName();
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
updateTitle("客户评估表更新任务"); // 设置任务标题
// 调用远程任务传递locale和业务对象ID
return callRemoteTask(holder, getLocale(), customer.getId());
}
}
```
## 4. 服务器端Tasker实现规则
服务器端Tasker是任务的实际执行者需要遵循以下实现规则
### 4.1 参数接收
- 实现`init(JsonNode argsNode)`方法接收客户端传递的参数
- 从参数中提取业务对象ID并加载完整业务对象
### 4.2 服务获取
- 通过`getCachedBean(Service.class)`方法获取所需的服务实例
- 可以提供辅助方法封装服务获取逻辑
### 4.3 任务执行
- 实现`execute(MessageHolder holder)`方法包含实际业务逻辑
- 使用`holder.info()/error()`等方法记录任务执行状态
- 调用`updateProgress()`方法更新任务进度
### 4.4 示例实现
```java
public class CompanyCustomerEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketServerTasker {
private CompanyCustomer customer; // 业务对象
@Override
public void init(JsonNode argsNode) {
// 从参数中提取业务对象ID并加载
int customerId = argsNode.get(0).asInt();
customer = getCachedBean(CompanyCustomerService.class).findById(customerId);
}
// 辅助方法获取服务
CompanyCustomerFileService getCompanyCustomerFileService() {
return getCachedBean(CompanyCustomerFileService.class);
}
@Override
protected Object execute(MessageHolder holder) throws Exception {
// 执行实际业务逻辑
updateEvaluationForm(holder);
return null;
}
// 具体业务逻辑实现
public void updateEvaluationForm(MessageHolder holder) {
// 业务逻辑代码...
updateProgress(1, 10); // 更新进度
// 更多业务逻辑...
}
}
```
## 5. 客户端Tasker调用流程
在UI控制器中调用客户端Tasker的标准流程如下
### 5.1 实例化与参数设置
1. 创建客户端Tasker实例
2. 设置必要的业务对象参数
### 5.2 任务执行与监控
1. 使用`UITools.showTaskDialogAndWait()`显示任务对话框
2. 调用`initializeTask()`初始化任务执行环境
3. 通过消费者函数处理任务消息
### 5.3 任务完成处理
1. 任务完成后执行必要的数据刷新操作
### 5.4 示例调用
```java
public void onUpdateEvaluationFormAction(ActionEvent event) {
// 1. 创建Tasker并设置参数
CompanyCustomerEvaluationFormUpdateTask task = new CompanyCustomerEvaluationFormUpdateTask();
task.setCustomer(getCompanyCustomerService().findById(viewModel.getId().get()));
// 2. 显示任务对话框并执行任务
UITools.showTaskDialogAndWait("更新评价表", task, consumer -> {
initializeTask(task, "更新评价表", msg -> consumer.accept(Message.info(msg)));
});
// 3. 任务完成后刷新数据
loadTableDataSet();
}
```
## 6. 任务进度管理
- 服务器端使用`updateProgress(current, total)`方法更新任务进度
- 进度值通常以0-1000或类似小范围数值表示完成百分比
- 客户端通过WebSocket接收进度更新并显示
## 7. 异常处理机制
- 服务器端使用`MessageHolder.error()`方法记录错误信息
- 客户端通过任务对话框展示错误信息
- 服务器端在关键操作点进行异常捕获和处理
## 8. 数据一致性保障
- 任务完成后客户端通常调用数据刷新方法(如`loadTableDataSet()`确保UI显示最新数据
- 服务器端负责业务数据的持久化操作
## 9. 最佳实践建议
1. **任务拆分**:复杂任务应拆分为多个小任务,便于进度跟踪和错误定位
2. **状态反馈**:在关键节点提供清晰的状态信息,增强用户体验
3. **资源释放**:确保文件流等资源正确关闭,避免资源泄露
4. **事务控制**:对于涉及多步数据操作的任务,考虑使用事务确保数据一致性
5. **错误重试**:针对网络波动等临时性问题,考虑实现任务重试机制
6. **配置管理**:使用`tasker_mapper.json`文件统一管理任务注册信息,避免在代码中硬编码任务映射
7. **异常处理**确保实现配置文件加载失败的fallback机制保证系统稳定性
8. **版本控制**:对任务配置文件进行版本控制,便于追踪变更历史
通过遵循以上规则和模式可以确保Contract-Manager项目中客户端与服务器端Tasker通信的一致性、可靠性和可维护性。

View File

@@ -10,6 +10,7 @@ import org.springframework.stereotype.Repository;
import com.ecep.contract.model.CompanyCustomer; import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile; import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
@Repository @Repository
public interface CompanyCustomerEvaluationFormFileRepository extends JpaRepository<CompanyCustomerEvaluationFormFile, Integer>, JpaSpecificationExecutor<CompanyCustomerEvaluationFormFile> { public interface CompanyCustomerEvaluationFormFileRepository extends JpaRepository<CompanyCustomerEvaluationFormFile, Integer>, JpaSpecificationExecutor<CompanyCustomerEvaluationFormFile> {
@@ -19,4 +20,6 @@ public interface CompanyCustomerEvaluationFormFileRepository extends JpaReposito
@Query(value = "SELECT * FROM COMPANY_CUSTOMER_FILE ccf left join COMPANY_CUSTOMER_EVALUATION_FORM_FILE CCEFF on ccf.ID = CCEFF.ID " + @Query(value = "SELECT * FROM COMPANY_CUSTOMER_FILE ccf left join COMPANY_CUSTOMER_EVALUATION_FORM_FILE CCEFF on ccf.ID = CCEFF.ID " +
"where ccf.CUSTOMER_ID=:customer and ccf.TYPE=:fileType", nativeQuery = true) "where ccf.CUSTOMER_ID=:customer and ccf.TYPE=:fileType", nativeQuery = true)
List<CompanyCustomerEvaluationFormFile> findAllByCustomerAndType(@Param("customer") int companyCustomerId, @Param("fileType") String type); List<CompanyCustomerEvaluationFormFile> findAllByCustomerAndType(@Param("customer") int companyCustomerId, @Param("fileType") String type);
CompanyCustomerEvaluationFormFile findByCustomerFile(CompanyCustomerFile customerFile);
} }

View File

@@ -0,0 +1,130 @@
package com.ecep.contract.ds.customer.service;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.customer.repository.CompanyCustomerEvaluationFormFileRepository;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-customer-evaluation-form-file")
public class CompanyCustomerEvaluationFormFileService
implements IEntityService<CompanyCustomerEvaluationFormFile>, QueryService<CompanyCustomerEvaluationFormFile> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileService.class);
@Lazy
@Autowired
private CompanyCustomerEvaluationFormFileRepository repository;
@Cacheable(key = "#p0")
@Override
public CompanyCustomerEvaluationFormFile findById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
public Specification<CompanyCustomerEvaluationFormFile> getSpecification(String searchText) {
if (!org.springframework.util.StringUtils.hasText(searchText)) {
return null;
}
return (root, query, builder) -> {
return builder.or(
builder.like(root.get("catalog"), "%" + searchText + "%"),
builder.like(root.get("level"), "%" + searchText + "%"));
};
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
})
@Override
public void delete(CompanyCustomerEvaluationFormFile formFile) {
repository.delete(formFile);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
})
@Override
public CompanyCustomerEvaluationFormFile save(CompanyCustomerEvaluationFormFile formFile) {
return repository.save(formFile);
}
@Override
public Page<CompanyCustomerEvaluationFormFile> findAll(Specification<CompanyCustomerEvaluationFormFile> spec,
Pageable pageable) {
return repository.findAll(spec, pageable);
}
@Override
public Page<CompanyCustomerEvaluationFormFile> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyCustomerEvaluationFormFile> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
if (paramsNode.has("customer")) {
CompanyCustomer customer = new CompanyCustomer();
customer.setId(paramsNode.get("customer").asInt());
spec = SpecificationUtils.and(spec,
(root, query, builder) -> builder.equal(root.get("customerFile").get("customer"), customer));
}
spec = SpecificationUtils.andParam(spec, paramsNode, "customerFile");
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "catalog", "level");
return findAll(spec, pageable);
}
/**
* 根据客户查询所有评估表文件
*
* @param customer 客户实体
* @return 评估表文件列表
*/
public List<CompanyCustomerEvaluationFormFile> findAllByCustomer(CompanyCustomer customer) {
return repository.findAllByCustomerFileCustomer(customer);
}
/**
* 根据客户ID和文件类型查询评估表文件
*
* @param companyCustomerId 客户ID
* @param type 文件类型
* @return 评估表文件列表
*/
public List<CompanyCustomerEvaluationFormFile> findAllByCustomerIdAndType(int companyCustomerId, String type) {
return repository.findAllByCustomerAndType(companyCustomerId, type);
}
/**
* 根据客户文件查询评估表文件
*
* @param customerFile 客户文件实体
* @return 评估表文件列表
*/
public CompanyCustomerEvaluationFormFile findByCustomerFile(CompanyCustomerFile customerFile) {
return repository.findByCustomerFile(customerFile);
}
}

View File

@@ -5,12 +5,7 @@ import java.time.LocalDate;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
import com.ecep.contract.QueryService;
import com.ecep.contract.model.*;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -27,20 +22,30 @@ import org.springframework.stereotype.Service;
import com.ecep.contract.CustomerFileType; import com.ecep.contract.CustomerFileType;
import com.ecep.contract.IEntityService; import com.ecep.contract.IEntityService;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp; import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.CompanyCustomerConstant; import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.company.service.CompanyBasicService; import com.ecep.contract.ds.company.service.CompanyBasicService;
import com.ecep.contract.ds.contract.service.ContractService; import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.customer.repository.CompanyCustomerEvaluationFormFileRepository; import com.ecep.contract.ds.customer.repository.CompanyCustomerEvaluationFormFileRepository;
import com.ecep.contract.ds.customer.repository.CompanyCustomerFileRepository; import com.ecep.contract.ds.customer.repository.CompanyCustomerFileRepository;
import com.ecep.contract.ds.other.service.SysConfService; import com.ecep.contract.ds.other.service.SysConfService;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEvaluationFormFile;
import com.ecep.contract.model.CompanyCustomerFile;
import com.ecep.contract.model.Contract;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Path;
@Lazy @Lazy
@Service @Service
@CacheConfig(cacheNames = "company-customer-file") @CacheConfig(cacheNames = "company-customer-file")
public class CompanyCustomerFileService implements IEntityService<CompanyCustomerFile>, QueryService<CompanyCustomerFile> { public class CompanyCustomerFileService
implements IEntityService<CompanyCustomerFile>, QueryService<CompanyCustomerFile> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerFileService.class); private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerFileService.class);
@Lazy @Lazy
@Autowired @Autowired
@@ -64,13 +69,9 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
}; };
} }
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@Caching( })
evict = {
@CacheEvict(key = "#p0.id"),
}
)
public void delete(CompanyCustomerFile file) { public void delete(CompanyCustomerFile file) {
if (file.getType() == CustomerFileType.EvaluationForm) { if (file.getType() == CustomerFileType.EvaluationForm) {
companyCustomerEvaluationFormFileRepository.deleteById(file.getId()); companyCustomerEvaluationFormFileRepository.deleteById(file.getId());
@@ -78,11 +79,9 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
companyCustomerFileRepository.delete(file); companyCustomerFileRepository.delete(file);
} }
@Caching( @Caching(evict = {
evict = { @CacheEvict(key = "#p0.id"),
@CacheEvict(key = "#p0.id"), })
}
)
public CompanyCustomerFile save(CompanyCustomerFile dbFile) { public CompanyCustomerFile save(CompanyCustomerFile dbFile) {
return companyCustomerFileRepository.save(dbFile); return companyCustomerFileRepository.save(dbFile);
} }
@@ -99,8 +98,8 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
@Override @Override
public Page<CompanyCustomerFile> findAll(JsonNode paramsNode, Pageable pageable) { public Page<CompanyCustomerFile> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyCustomerFile> spec = null; Specification<CompanyCustomerFile> spec = null;
if (paramsNode.has("searchText")) { if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get("searchText").asText()); spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
} }
// field // field
spec = SpecificationUtils.andParam(spec, paramsNode, "customer"); spec = SpecificationUtils.andParam(spec, paramsNode, "customer");
@@ -124,12 +123,10 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
companyCustomerFileRepository.delete(customerFile); companyCustomerFileRepository.delete(customerFile);
} }
public List<CompanyCustomerFile> findAll(Specification<CompanyCustomerFile> spec, Sort sort) { public List<CompanyCustomerFile> findAll(Specification<CompanyCustomerFile> spec, Sort sort) {
return companyCustomerFileRepository.findAll(spec, sort); return companyCustomerFileRepository.findAll(spec, sort);
} }
public List<CompanyCustomerFile> saveAll(List<CompanyCustomerFile> files) { public List<CompanyCustomerFile> saveAll(List<CompanyCustomerFile> files) {
return companyCustomerFileRepository.saveAll(files); return companyCustomerFileRepository.saveAll(files);
} }
@@ -153,7 +150,8 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
return companyCustomerEvaluationFormFileRepository.findById(id).orElse(null); return companyCustomerEvaluationFormFileRepository.findById(id).orElse(null);
} }
public CompanyCustomerEvaluationFormFile findCustomerEvaluationFormFileByCustomerFile(CompanyCustomerFile customerFile) { public CompanyCustomerEvaluationFormFile findCustomerEvaluationFormFileByCustomerFile(
CompanyCustomerFile customerFile) {
Integer id = customerFile.getId(); Integer id = customerFile.getId();
if (id == null) { if (id == null) {
CompanyCustomerFile saved = companyCustomerFileRepository.save(customerFile); CompanyCustomerFile saved = companyCustomerFileRepository.save(customerFile);
@@ -161,7 +159,8 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
customerFile.setId(id); customerFile.setId(id);
} }
CompanyCustomerEvaluationFormFile formFile = companyCustomerEvaluationFormFileRepository.findById(id).orElse(null); CompanyCustomerEvaluationFormFile formFile = companyCustomerEvaluationFormFileRepository.findById(id)
.orElse(null);
if (formFile == null) { if (formFile == null) {
formFile = new CompanyCustomerEvaluationFormFile(); formFile = new CompanyCustomerEvaluationFormFile();
formFile.setId(id); formFile.setId(id);
@@ -171,13 +170,13 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
return formFile; return formFile;
} }
public LocalDate getNextSignDate(CompanyCustomer companyCustomer, Consumer<String> state) { public LocalDate getNextSignDate(CompanyCustomer companyCustomer, MessageHolder holder) {
LocalDate miniContractDate = LocalDate.of(2022, 1, 1); LocalDate miniContractDate = LocalDate.of(2022, 1, 1);
// 检索全部合同 // 检索全部合同
ContractService contractService = SpringApp.getBean(ContractService.class); ContractService contractService = SpringApp.getBean(ContractService.class);
List<Contract> contractList = contractService.findAllByCompanyCustomer(companyCustomer, null, null); List<Contract> contractList = contractService.findAllByCompanyCustomer(companyCustomer, null, null);
if (contractList.isEmpty()) { if (contractList.isEmpty()) {
state.accept("未发现已登记的合同"); holder.error("未发现已登记的合同");
return null; return null;
} }
// 检索评估表 // 检索评估表
@@ -189,46 +188,45 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
// 没有有效的评估表的评价日期 // 没有有效的评估表的评价日期
if (latestFile == null) { if (latestFile == null) {
state.accept("未发现有效的评估表"); holder.warn("未发现有效的评估表");
// 返回最早的合同日期 // 返回最早的合同日期
Contract firstContract = contractList.stream() Contract firstContract = contractList.stream()
.filter(v -> v.getSetupDate() != null && !v.getSetupDate().isBefore(miniContractDate)) .filter(v -> v.getSetupDate() != null && !v.getSetupDate().isBefore(miniContractDate))
.min(Comparator.comparing(Contract::getSetupDate)) .min(Comparator.comparing(Contract::getSetupDate))
.orElse(null); .orElse(null);
if (firstContract == null) { if (firstContract == null) {
state.accept("最早的合同不存在?"); holder.error("最早的合同不存在?");
return null; return null;
} }
LocalDate setupDate = firstContract.getSetupDate(); LocalDate setupDate = firstContract.getSetupDate();
state.accept("依据合同 " + firstContract.getCode() + " 的日期 " + setupDate + " 推算"); holder.info("依据合同 " + firstContract.getCode() + " 的日期 " + setupDate + " 推算");
return CompanyBasicService.adjustToWorkDay(setupDate.plusDays(-7)); return CompanyBasicService.adjustToWorkDay(setupDate.plusDays(-7));
} }
// 检查失效日期起的第一个合同 // 检查失效日期起的第一个合同
LocalDate nextInValidDate = latestFile.getSignDate().plusYears(1); LocalDate nextInValidDate = latestFile.getSignDate().plusYears(1);
File file = new File(latestFile.getFilePath()); File file = new File(latestFile.getFilePath());
state.accept("依据 " + file.getName() + " 的失效期 " + nextInValidDate + " 检索合同"); holder.info("依据 " + file.getName() + " 的失效期 " + nextInValidDate + " 检索合同");
List<Contract> matchedContracts = contractList.stream() List<Contract> matchedContracts = contractList.stream()
.filter(v -> v.getSetupDate().isAfter(nextInValidDate)).toList(); .filter(v -> v.getSetupDate().isAfter(nextInValidDate)).toList();
// 没有在失效日期后的合同时,使用失效日期 // 没有在失效日期后的合同时,使用失效日期
if (matchedContracts.isEmpty()) { if (matchedContracts.isEmpty()) {
state.accept("未发现失效期 " + nextInValidDate + " 后的合同"); holder.warn("未发现失效期 " + nextInValidDate + " 后的合同");
return null; return null;
} }
state.accept("发现匹配合同 " + matchedContracts.size() + ""); holder.info("发现匹配合同 " + matchedContracts.size() + "");
// 按时间取最早一个 // 按时间取最早一个
Contract firstContract = matchedContracts.stream() Contract firstContract = matchedContracts.stream()
.min(Comparator.comparing(Contract::getSetupDate)) .min(Comparator.comparing(Contract::getSetupDate))
.orElse(null); .orElse(null);
LocalDate setupDate = firstContract.getSetupDate(); LocalDate setupDate = firstContract.getSetupDate();
state.accept("匹配失效期 " + nextInValidDate + " 后的第一个合同 " + firstContract.getCode()); holder.info("匹配失效期 " + nextInValidDate + " 后的第一个合同 " + firstContract.getCode());
state.accept("依据合同 " + firstContract.getCode() + " 的日期 " + setupDate + " 推算"); holder.info("依据合同 " + firstContract.getCode() + " 的日期 " + setupDate + " 推算");
return CompanyBasicService.adjustToWorkDay(setupDate.plusDays(-7)); return CompanyBasicService.adjustToWorkDay(setupDate.plusDays(-7));
} }
public File getEvaluationFormTemplate() { public File getEvaluationFormTemplate() {
String path = confService.getString(CompanyCustomerConstant.KEY_EVALUATION_FORM_TEMPLATE); String path = confService.getString(CompanyCustomerConstant.KEY_EVALUATION_FORM_TEMPLATE);
if (path == null) { if (path == null) {
@@ -263,5 +261,4 @@ public class CompanyCustomerFileService implements IEntityService<CompanyCustome
return companyCustomerEvaluationFormFileRepository.findAll(spec, Pageable.ofSize(10)).getContent(); return companyCustomerEvaluationFormFileRepository.findAll(spec, Pageable.ofSize(10)).getContent();
} }
} }

View File

@@ -14,6 +14,7 @@ import java.util.stream.Collectors;
import com.ecep.contract.QueryService; import com.ecep.contract.QueryService;
import com.ecep.contract.constant.CompanyCustomerConstant; import com.ecep.contract.constant.CompanyCustomerConstant;
import com.ecep.contract.model.*; import com.ecep.contract.model.*;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.util.CompanyUtils; import com.ecep.contract.util.CompanyUtils;
import com.ecep.contract.util.FileUtils; import com.ecep.contract.util.FileUtils;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
@@ -151,25 +152,25 @@ public class CompanyCustomerService extends CompanyBasicService
* 重置 客户文件 * 重置 客户文件
* *
* @param companyCustomer 客户对象 * @param companyCustomer 客户对象
* @param status 输出 * @param holder 消息持有者
*/ */
public boolean reBuildingFiles(CompanyCustomer companyCustomer, Consumer<String> status) { public boolean reBuildingFiles(CompanyCustomer companyCustomer, MessageHolder holder) {
List<CompanyCustomerFile> dbFiles = companyCustomerFileService.findAllByCustomer(companyCustomer); List<CompanyCustomerFile> dbFiles = companyCustomerFileService.findAllByCustomer(companyCustomer);
Map<String, CompanyCustomerFile> map = new HashMap<>(); Map<String, CompanyCustomerFile> map = new HashMap<>();
boolean modified = fetchDbFiles(dbFiles, map, status); boolean modified = fetchDbFiles(dbFiles, map, holder::info);
// 客户目录下 // 客户目录下
List<CompanyCustomerFile> retrieveFiles = new ArrayList<>(); List<CompanyCustomerFile> retrieveFiles = new ArrayList<>();
List<File> needMoveToCompanyPath = new ArrayList<>(); List<File> needMoveToCompanyPath = new ArrayList<>();
// TODO 客户有曾用名,可能存在多个目录 // TODO 客户有曾用名,可能存在多个目录
fetchFiles(companyCustomer.getPath(), needMoveToCompanyPath, retrieveFiles, map, status); fetchFiles(companyCustomer.getPath(), needMoveToCompanyPath, retrieveFiles, map, holder::info);
// 移动文件到公司目录下 to company path // 移动文件到公司目录下 to company path
moveFileToCompany(companyCustomer.getCompany(), needMoveToCompanyPath); moveFileToCompany(companyCustomer.getCompany(), needMoveToCompanyPath);
status.accept("导入 " + retrieveFiles.size() + " 个文件"); holder.info("导入 " + retrieveFiles.size() + " 个文件");
if (!retrieveFiles.isEmpty()) { if (!retrieveFiles.isEmpty()) {
// update db // update db
@@ -184,7 +185,7 @@ public class CompanyCustomerService extends CompanyBasicService
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file, protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file,
Consumer<String> status) { Consumer<String> status) {
dbFile.setType((T) CustomerFileType.General); dbFile.setType((T) CustomerFileType.General);
fillFile(dbFile, file, null, status); fillFile(dbFile, file, null, status);
companyCustomerFileService.save((CompanyCustomerFile) dbFile); companyCustomerFileService.save((CompanyCustomerFile) dbFile);
@@ -193,7 +194,7 @@ public class CompanyCustomerService extends CompanyBasicService
@Override @Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file, protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file,
List<File> fileList, Consumer<String> status) { List<File> fileList, Consumer<String> status) {
boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status); boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status);
if (fileList != null) { if (fileList != null) {
@@ -227,7 +228,7 @@ public class CompanyCustomerService extends CompanyBasicService
} }
private <T, F extends CompanyBasicFile<T>> void updateEvaluationFileByJsonFile(F customerFile, File jsonFile, private <T, F extends CompanyBasicFile<T>> void updateEvaluationFileByJsonFile(F customerFile, File jsonFile,
Consumer<String> status) throws IOException { Consumer<String> status) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(jsonFile); JsonNode root = objectMapper.readTree(jsonFile);
if (!root.isObject()) { if (!root.isObject()) {
@@ -250,10 +251,11 @@ public class CompanyCustomerService extends CompanyBasicService
logger.info("delete json file {}", jsonFile.getName()); logger.info("delete json file {}", jsonFile.getName());
} }
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList, protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList,
Consumer<String> status) { Consumer<String> status) {
CompanyCustomerFile customerFile = new CompanyCustomerFile(); CompanyCustomerFile customerFile = new CompanyCustomerFile();
customerFile.setType(CustomerFileType.General); customerFile.setType(CustomerFileType.General);
if (fillFile(customerFile, file, fileList, status)) { if (fillFile(customerFile, file, fileList, status)) {
@@ -261,6 +263,7 @@ public class CompanyCustomerService extends CompanyBasicService
} }
return null; return null;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected <T, F extends CompanyBasicFile<T>> boolean setFileTypeAsEvaluationForm(F file) { protected <T, F extends CompanyBasicFile<T>> boolean setFileTypeAsEvaluationForm(F file) {
@@ -276,7 +279,7 @@ public class CompanyCustomerService extends CompanyBasicService
return (fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME1) return (fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME1)
|| fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME2)) || fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME2))
&& (FileUtils.withExtensions(fileName, FileUtils.JPG, FileUtils.JPEG, && (FileUtils.withExtensions(fileName, FileUtils.JPG, FileUtils.JPEG,
FileUtils.PDF)); FileUtils.PDF));
} }
public boolean makePathAbsent(CompanyCustomer companyCustomer) { public boolean makePathAbsent(CompanyCustomer companyCustomer) {
@@ -410,5 +413,4 @@ public class CompanyCustomerService extends CompanyBasicService
return customerCatalogRepository.save(catalog); return customerCatalogRepository.save(catalog);
} }
} }

View File

@@ -0,0 +1,214 @@
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.CompanyCustomerFileService;
import com.ecep.contract.ds.customer.service.CompanyCustomerEvaluationFormFileService;
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.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
import com.ecep.contract.vo.CompanyCustomerFileVo;
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

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

View File

@@ -0,0 +1,76 @@
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);
}
@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("客户文件重建成功");
updateProperty("filesUpdated", true);
} else {
holder.info("客户文件重建完成,但没有更新任何文件");
updateProperty("filesUpdated", false);
}
updateProgress(100, 100);
return result;
} catch (Exception e) {
holder.error("客户文件重建失败:" + e.getMessage());
updateProgress(100, 100);
throw e;
}
}
}

View File

@@ -1,25 +1,27 @@
package com.ecep.contract.service; package com.ecep.contract.service;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
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.Message;
import com.ecep.contract.constant.WebSocketConstant; import com.ecep.contract.constant.WebSocketConstant;
import com.ecep.contract.ui.Tasker; import com.ecep.contract.ui.Tasker;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@Service @Service
public class WebSocketServerTaskManager implements InitializingBean { public class WebSocketServerTaskManager implements InitializingBean {
@@ -28,16 +30,31 @@ public class WebSocketServerTaskManager implements InitializingBean {
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
@Autowired
private ResourceLoader resourceLoader;
private Map<String, String> taskClzMap = Map.of(); private Map<String, String> taskClzMap = Map.of();
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
taskClzMap = Map.of( // 从tasker_mapper.json文件中加载任务注册信息
"ContractSyncTask", "com.ecep.contract.cloud.u8.ContractSyncTask", try {
"ContractRepairTask", "com.ecep.contract.ds.contract.tasker.ContractRepairTask", Resource resource = resourceLoader.getResource("classpath:tasker_mapper.json");
"ContractVerifyTask", "com.ecep.contract.ds.contract.tasker.ContractVerifyTask", try (InputStream inputStream = resource.getInputStream()) {
"ProjectCostImportItemsFromContractsTasker", "com.ecep.contract.ds.project.ProjectCostImportItemsFromContractsTasker" JsonNode rootNode = objectMapper.readTree(inputStream);
); JsonNode taskersNode = rootNode.get("taskers");
if (taskersNode != null && taskersNode.isObject()) {
Map<String, String> taskMap = new java.util.HashMap<>();
taskersNode.fields().forEachRemaining(entry -> {
taskMap.put(entry.getKey(), entry.getValue().asText());
});
taskClzMap = taskMap;
}
}
} catch (Exception e) {
logger.error("Failed to load tasker_mapper.json", e);
// 使用默认值作为fallback
taskClzMap = Map.of();
}
} }
public void onMessage(WebSocketSession session, JsonNode jsonNode) { public void onMessage(WebSocketSession session, JsonNode jsonNode) {
@@ -51,7 +68,6 @@ public class WebSocketServerTaskManager implements InitializingBean {
} }
} }
private void handleAsSessionCallback(WebSocketSession session, String sessionId, JsonNode jsonNode) { private void handleAsSessionCallback(WebSocketSession session, String sessionId, JsonNode jsonNode) {
if (!jsonNode.has("type")) { if (!jsonNode.has("type")) {
throw new IllegalArgumentException("缺失 type 参数"); throw new IllegalArgumentException("缺失 type 参数");
@@ -120,8 +136,7 @@ public class WebSocketServerTaskManager implements InitializingBean {
String text = objectMapper.writeValueAsString(Map.of( String text = objectMapper.writeValueAsString(Map.of(
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId, WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
"type", type, "type", type,
WebSocketConstant.ARGUMENTS_FIELD_NAME, args WebSocketConstant.ARGUMENTS_FIELD_NAME, args));
));
session.sendMessage(new TextMessage(text)); session.sendMessage(new TextMessage(text));
} catch (IOException e) { } catch (IOException e) {
// 捕获所有可能的异常,防止影响主流程 // 捕获所有可能的异常,防止影响主流程
@@ -130,7 +145,6 @@ public class WebSocketServerTaskManager implements InitializingBean {
return true; return true;
} }
private void sendError(WebSocketSession session, String sessionId, String message) { private void sendError(WebSocketSession session, String sessionId, String message) {
if (session == null || !session.isOpen()) { if (session == null || !session.isOpen()) {
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message); logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
@@ -141,8 +155,7 @@ public class WebSocketServerTaskManager implements InitializingBean {
String errorMessage = objectMapper.writeValueAsString(Map.of( String errorMessage = objectMapper.writeValueAsString(Map.of(
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId, WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
WebSocketConstant.SUCCESS_FIELD_VALUE, false, WebSocketConstant.SUCCESS_FIELD_VALUE, false,
WebSocketConstant.MESSAGE_FIELD_NAME, message WebSocketConstant.MESSAGE_FIELD_NAME, message));
));
// 检查会话状态并尝试发送错误消息 // 检查会话状态并尝试发送错误消息
if (session.isOpen()) { if (session.isOpen()) {

View File

@@ -0,0 +1,11 @@
{
"taskers": {
"ContractSyncTask": "com.ecep.contract.cloud.u8.ContractSyncTask",
"ContractRepairTask": "com.ecep.contract.ds.contract.tasker.ContractRepairTask",
"ContractVerifyTask": "com.ecep.contract.ds.contract.tasker.ContractVerifyTask",
"ProjectCostImportItemsFromContractsTasker": "com.ecep.contract.ds.project.ProjectCostImportItemsFromContractsTasker",
"CompanyCustomerEvaluationFormUpdateTask": "com.ecep.contract.ds.customer.tasker.CompanyCustomerEvaluationFormUpdateTask",
"CompanyCustomerNextSignDateTask": "com.ecep.contract.ds.customer.tasker.CompanyCustomerNextSignDateTask",
"CompanyCustomerRebuildFilesTasker": "com.ecep.contract.ds.customer.tasker.CompanyCustomerRebuildFilesTasker"
}
}

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>