diff --git a/.trae/rules/tasker_implementation_guide.md b/.trae/rules/tasker_implementation_guide.md new file mode 100644 index 0000000..6c2bafe --- /dev/null +++ b/.trae/rules/tasker_implementation_guide.md @@ -0,0 +1,182 @@ +# Tasker实现规范指南 + +## 1. Tasker架构概述 + +Tasker是系统中用于执行异步任务的核心框架,主要用于处理耗时操作并提供实时进度反馈。Tasker架构包括: + +- **Tasker基类**:提供任务执行的基础框架和通信功能 +- **WebSocketServerTasker接口**:定义WebSocket通信任务的规范 +- **MessageHolder**:用于在任务执行过程中传递消息和更新进度 + +## 2. 服务器端Tasker实现规范 + +### 2.1 基本结构 + +服务器端Tasker实现应遵循以下结构: + +```java +@Data +@EqualsAndHashCode(callSuper = true) +public class YourTaskNameTask extends Tasker implements WebSocketServerTasker { + // 服务依赖 + @Setter + private YourService service; + + @Override + public void init(JsonNode argsNode) { + // 初始化任务标题 + updateTitle("任务标题"); + // 初始化参数 + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + // 实现业务逻辑 + // 使用holder.info()、holder.error()等方法反馈消息 + return null; + } +} +``` + +### 2.2 关键注意事项 + +1. **继承与实现**: + - 必须继承`Tasker`基类 + - 必须实现`WebSocketServerTasker`接口 + +2. **方法实现**: + - **不要重复实现**Tasker基类已提供的handler设置方法:`setMessageHandler`、`setTitleHandler`、`setPropertyHandler`、`setProgressHandler` + - 必须实现`init`方法,设置任务标题和初始化参数 + - 必须实现`execute`方法,实现具体业务逻辑 + +3. **进度和消息反馈**: + - 使用`updateProgress`方法提供进度更新 + - 使用`MessageHolder`的`info`、`error`、`debug`等方法提供消息反馈 + +4. **任务取消**: + - 在适当的地方检查`isCancelled()`状态,支持任务取消 + +5. **注册要求**: + - 任务类必须在`tasker_mapper.json`中注册 + +## 3. 客户端Tasker实现规范 + +客户端Tasker实现应遵循: + +```java +public class YourTaskNameTask extends Tasker { + @Override + protected Object execute(MessageHolder holder) throws Exception { + // 客户端任务逻辑 + return null; + } +} +``` + +## 4. 通信规范 + +### 4.1 命名规范 + +- 客户端和服务器端对应的Tasker类应使用**相同的类名** +- 示例:客户端的`ContractSyncAllTask`对应服务器端的`ContractSyncAllTask` + +### 4.2 消息传递 + +- 使用`MessageHolder`进行消息传递 +- 支持不同级别的消息:INFO、ERROR、DEBUG + +## 5. 最佳实践 + +1. **任务拆分**: + - 将大型任务拆分为多个小任务,提高可维护性 + +2. **异常处理**: + - 捕获并正确处理异常,使用`holder.error()`提供详细错误信息 + +3. **资源管理**: + - 确保及时释放资源,特别是在任务取消的情况下 + +4. **日志记录**: + - 使用`holder.debug()`记录调试信息 + - 使用`holder.info()`记录进度信息 + +5. **状态检查**: + - 定期检查`isCancelled()`状态,确保任务可以被及时取消 + +## 6. 代码示例 + +### 6.1 服务器端Tasker示例 + +```java +@Data +@EqualsAndHashCode(callSuper = true) +public class ExampleSyncTask extends Tasker implements WebSocketServerTasker { + @Setter + private ExampleService exampleService; + + @Override + public void init(JsonNode argsNode) { + updateTitle("示例同步任务"); + // 从argsNode初始化参数 + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + try { + // 获取总记录数 + long total = exampleService.getTotalCount(); + holder.info("开始同步数据,共" + total + "条记录"); + + // 分批处理 + long processed = 0; + List entities = exampleService.getAllEntities(); + + for (ExampleEntity entity : entities) { + // 检查是否取消 + if (isCancelled()) { + holder.info("任务已取消"); + return null; + } + + // 处理单个实体 + exampleService.processEntity(entity); + processed++; + + // 更新进度 + updateProgress(processed, total); + if (processed % 100 == 0) { + holder.info("已处理" + processed + "/" + total + "条记录"); + } + } + + holder.info("同步完成,共处理" + processed + "条记录"); + return processed; + } catch (Exception e) { + holder.error("同步失败: " + e.getMessage()); + throw e; + } + } +} +``` + +## 7. 常见错误和避免方法 + +1. **重复实现handler方法**: + - 错误:在子类中重复实现setMessageHandler等方法 + - 解决:删除子类中的这些方法,直接使用Tasker基类提供的实现 + +2. **缺少任务注册**: + - 错误:任务类没有在tasker_mapper.json中注册 + - 解决:确保所有服务器端Tasker类都在配置文件中正确注册 + +3. **类名不匹配**: + - 错误:客户端和服务器端Tasker类名不一致 + - 解决:确保两端使用相同的类名 + +4. **进度更新不正确**: + - 错误:进度计算错误或没有更新 + - 解决:正确计算和更新进度,提高用户体验 + +5. **资源泄漏**: + - 错误:未及时关闭数据库连接等资源 + - 解决:使用try-with-resources或在finally块中关闭资源 \ No newline at end of file diff --git a/README.md b/README.md index 2924e1c..d80a645 100644 --- a/README.md +++ b/README.md @@ -178,8 +178,11 @@ Contract-Manager/ 2. 配置连接到服务端的WebSocket地址 3. 打包为可执行jar或使用jpackage创建安装包 -## 开发环境要求 +## Tasker实现规范指南 +有关Tasker框架的详细实现规范,请参阅 [Tasker实现规范指南](docs/task/tasker_implementation_guide.md)。 + +## 开发环境要求 - JDK 21 - Maven 3.6+ - MySQL 8.0+ diff --git a/client/pom.xml b/client/pom.xml index 1772f98..2297611 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -6,12 +6,12 @@ com.ecep.contract Contract-Manager - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT com.ecep.contract client - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT ${java.version} @@ -22,7 +22,7 @@ com.ecep.contract common - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT org.springframework.boot diff --git a/client/src/main/java/com/ecep/contract/WebSocketClientService.java b/client/src/main/java/com/ecep/contract/WebSocketClientService.java index 83fd45a..6d18d99 100644 --- a/client/src/main/java/com/ecep/contract/WebSocketClientService.java +++ b/client/src/main/java/com/ecep/contract/WebSocketClientService.java @@ -206,6 +206,12 @@ public class WebSocketClientService { send(objectMapper.writeValueAsString(message)); } + /** + * WebSocketServerCallbackManage#onMessage 负责接收处理 + * + * @param msg + * @return + */ public CompletableFuture send(SimpleMessage msg) { CompletableFuture future = new CompletableFuture<>(); try { diff --git a/client/src/main/java/com/ecep/contract/controller/cloud/u8/YongYouU8ManagerWindowController.java b/client/src/main/java/com/ecep/contract/controller/cloud/u8/YongYouU8ManagerWindowController.java index 6c699dd..3c396a4 100644 --- a/client/src/main/java/com/ecep/contract/controller/cloud/u8/YongYouU8ManagerWindowController.java +++ b/client/src/main/java/com/ecep/contract/controller/cloud/u8/YongYouU8ManagerWindowController.java @@ -91,27 +91,32 @@ public class YongYouU8ManagerWindowController public void onContractGroupSyncAction(ActionEvent event) { ContractGroupSyncTask task = new ContractGroupSyncTask(); - Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); + UITools.showTaskDialogAndWait("合同组数据同步", task, null); + // Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); } public void onContractTypeSyncAction(ActionEvent event) { ContractTypeSyncTask task = new ContractTypeSyncTask(); - Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); + UITools.showTaskDialogAndWait("合同类型数据同步", task, null); + // Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); } public void onContractKindSyncAction(ActionEvent event) { ContractKindSyncTask task = new ContractKindSyncTask(); - Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); + UITools.showTaskDialogAndWait("合同类型数据同步", task, null); + // Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); } public void onVendorClassSyncAction(ActionEvent event) { VendorClassSyncTask task = new VendorClassSyncTask(); - Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); + UITools.showTaskDialogAndWait("客户分类数据同步", task, null); + // Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); } public void onCustomerClassSyncAction(ActionEvent event) { CustomerClassSyncTask task = new CustomerClassSyncTask(); - Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); + UITools.showTaskDialogAndWait("客户分类数据同步", task, null); + // Desktop.instance.getTaskMonitorCenter().registerAndStartTask(task); } } diff --git a/client/src/main/java/com/ecep/contract/controller/employee/EmployeeManagerSkin.java b/client/src/main/java/com/ecep/contract/controller/employee/EmployeeManagerSkin.java index a3c96b5..c7eb506 100644 --- a/client/src/main/java/com/ecep/contract/controller/employee/EmployeeManagerSkin.java +++ b/client/src/main/java/com/ecep/contract/controller/employee/EmployeeManagerSkin.java @@ -47,7 +47,7 @@ public class EmployeeManagerSkin controller.accountColumn.setCellValueFactory(param -> param.getValue().getAccount()); controller.departmentColumn.setCellValueFactory(param -> param.getValue().getDepartment()); - controller.departmentColumn.setCellFactory(param -> new DepartmentTableCell<>(getDepartmentService())); + controller.departmentColumn.setCellFactory(DepartmentTableCell.forTableColumn(getDepartmentService())); controller.emailColumn.setCellValueFactory(param -> param.getValue().getEmail()); controller.createdColumn.setCellValueFactory(param -> param.getValue().getCreated()); diff --git a/client/src/main/java/com/ecep/contract/controller/permission/AbstEmployeeFunctionBasedTabSkin.java b/client/src/main/java/com/ecep/contract/controller/permission/AbstEmployeeFunctionBasedTabSkin.java new file mode 100644 index 0000000..cd8aed4 --- /dev/null +++ b/client/src/main/java/com/ecep/contract/controller/permission/AbstEmployeeFunctionBasedTabSkin.java @@ -0,0 +1,24 @@ +package com.ecep.contract.controller.permission; + +import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin; +import com.ecep.contract.service.FunctionService; +import com.ecep.contract.service.PermissionService; +import com.ecep.contract.vm.FunctionViewModel; +import com.ecep.contract.vo.FunctionVo; + +public abstract class AbstEmployeeFunctionBasedTabSkin + extends AbstEntityBasedTabSkin { + + public AbstEmployeeFunctionBasedTabSkin(EmployeeFunctionWindowController controller) { + super(controller); + } + + FunctionService getFunctionService() { + return getCachedBean(FunctionService.class); + } + + public PermissionService getPermissionService() { + return controller.permissionService; + } + +} \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionTabSkinBase.java b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionTabSkinBase.java new file mode 100644 index 0000000..35aea69 --- /dev/null +++ b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionTabSkinBase.java @@ -0,0 +1,27 @@ +package com.ecep.contract.controller.permission; + +import com.ecep.contract.controller.tab.TabSkin; + +import javafx.scene.control.Tab; + +public class EmployeeFunctionTabSkinBase extends AbstEmployeeFunctionBasedTabSkin implements TabSkin { + + public EmployeeFunctionTabSkinBase(EmployeeFunctionWindowController controller) { + super(controller); + } + + @Override + public Tab getTab() { + return controller.baseInfoTab; + } + + @Override + public void initializeTab() { + controller.nameField.textProperty().bindBidirectional(viewModel.getName()); + controller.keyField.textProperty().bindBidirectional(viewModel.getKey()); + controller.controllerField.textProperty().bindBidirectional(viewModel.getController()); + controller.iconField.textProperty().bindBidirectional(viewModel.getIcon()); + controller.descriptionField.textProperty().bindBidirectional(viewModel.getDescription()); + controller.activeField.selectedProperty().bindBidirectional(viewModel.getActive()); + } +} \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionWindowController.java b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionWindowController.java new file mode 100644 index 0000000..5fa3759 --- /dev/null +++ b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionWindowController.java @@ -0,0 +1,83 @@ +package com.ecep.contract.controller.permission; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import com.ecep.contract.controller.AbstEntityController; +import com.ecep.contract.service.FunctionService; +import com.ecep.contract.service.PermissionService; +import com.ecep.contract.util.FxmlPath; +import com.ecep.contract.vm.FunctionViewModel; +import com.ecep.contract.vo.FunctionVo; + +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.BorderPane; +import javafx.stage.Window; +import javafx.stage.WindowEvent; + + +@Lazy +@Scope("prototype") +@Component +@FxmlPath("/ui/employee/function.fxml") +public class EmployeeFunctionWindowController extends AbstEntityController { + private static final Logger logger = LoggerFactory.getLogger(EmployeeFunctionWindowController.class); + + + public static void show(FunctionViewModel viewModel, Window window) { + show(EmployeeFunctionWindowController.class, viewModel, window); + } + + public BorderPane root; + public TabPane tabPane; + + /* + * 基本信息标签页 + */ + public Tab baseInfoTab; + public TextField nameField; + public TextField keyField; + public TextField controllerField; + public TextField iconField; + public TextField descriptionField; + + @FXML + public CheckBox activeField; + public Label versionLabel; + + /* + * 权限标签页 + */ + public Tab permissionTab; + + @Autowired + PermissionService permissionService; + @Autowired + FunctionService functionService; + + @Override + public void onShown(WindowEvent windowEvent) { + super.onShown(windowEvent); + getTitle().bind(viewModel.getName().map(name -> "[" + viewModel.getId().get() + "] " + name + " 功能详情")); + } + + @Override + protected void registerTabSkins() { + registerTabSkin(baseInfoTab, tab -> new EmployeeFunctionTabSkinBase(this)); + } + + @Override + public FunctionService getViewModelService() { + return functionService; + } +} \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionsManagerWindowController.java b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionsManagerWindowController.java index 327c6f0..b132f14 100644 --- a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionsManagerWindowController.java +++ b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeFunctionsManagerWindowController.java @@ -61,8 +61,6 @@ public class EmployeeFunctionsManagerWindowController public void onCreateNewAction(ActionEvent event) { } - public void onReBuildFilesAction(ActionEvent event) { - } @Override public FunctionService getViewModelService() { diff --git a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleManagerWindowController.java b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleManagerWindowController.java index 2f5feb7..e10c687 100644 --- a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleManagerWindowController.java +++ b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleManagerWindowController.java @@ -54,9 +54,6 @@ public class EmployeeRoleManagerWindowController public void onCreateNewAction(ActionEvent event) { } - public void onReBuildFilesAction(ActionEvent event) { - } - @Override public EmployeeRoleService getViewModelService() { return employeeRoleService; diff --git a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleTabSkinFunctions.java b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleTabSkinFunctions.java index 99b909e..4028740 100644 --- a/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleTabSkinFunctions.java +++ b/client/src/main/java/com/ecep/contract/controller/permission/EmployeeRoleTabSkinFunctions.java @@ -50,7 +50,7 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin { } private void loadSelectedRoles() { - List selectedRoles = getRoleService().getFunctionsByRoleId(viewModel.getId().get()); + List selectedRoles = getRoleService().getFunctionsByRole(getEntity()); if (selectedRoles != null) { functionsField.getTargetItems().setAll(selectedRoles); } @@ -100,8 +100,7 @@ public class EmployeeRoleTabSkinFunctions extends AbstEmployeeRoleBasedTabSkin { private void saveRoles(ActionEvent event) { EmployeeRoleVo entity = getEntity(); - entity.setFunctions(functionsField.getTargetItems()); - save(entity); + getRoleService().saveRoleFunctions(entity, functionsField.getTargetItems()); loadSelectedRoles(); } } diff --git a/client/src/main/java/com/ecep/contract/controller/tab/CompanyTabSkinOldName.java b/client/src/main/java/com/ecep/contract/controller/tab/CompanyTabSkinOldName.java index cc629be..aaa33ad 100644 --- a/client/src/main/java/com/ecep/contract/controller/tab/CompanyTabSkinOldName.java +++ b/client/src/main/java/com/ecep/contract/controller/tab/CompanyTabSkinOldName.java @@ -6,8 +6,12 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; +import java.util.logging.Level; +import com.ecep.contract.task.CompanyMergeClientTasker; import com.ecep.contract.util.ParamUtils; +import com.ecep.contract.util.UITools; import com.ecep.contract.vo.CompanyOldNameVo; import com.ecep.contract.vo.CompanyVo; import org.springframework.util.StringUtils; @@ -138,53 +142,20 @@ public class CompanyTabSkinOldName } private void onTableMergeAction(ActionEvent event) { - CompanyVo updater = getParent(); - HashSet nameSet = new HashSet<>(); - nameSet.add(updater.getName()); + // 收集所有曾用名 + List nameList = dataSet.stream() + .map(viewModel -> viewModel.getName().get()) + .filter(Objects::nonNull) + .toList(); - List removes = new ArrayList<>(); - for (CompanyOldNameViewModel viewModel : dataSet) { + // 创建独立的WebSocket客户端任务器 + CompanyMergeClientTasker task = new CompanyMergeClientTasker(); - if (!nameSet.add(viewModel.getName().get())) { - // fixed 曾用名中有重复时,删除重复的 - deleteRow(viewModel); - removes.add(viewModel); - } - } - if (!removes.isEmpty()) { - Platform.runLater(() -> { - dataSet.removeAll(removes); - }); - setStatus("移除重复的曾用名" + removes.size()); - } + task.setCompany(getParent()); + task.setNameList(nameList); + UITools.showTaskDialogAndWait("合并曾用名", task, null); - int size = nameSet.size(); - int count = 1; - int merge = 0; - for (String name : nameSet) { - controller.setRightStatus(count + "/" + size); - if (StringUtils.hasText(name)) { - List list = getParentService().findAllByName(name); - for (CompanyVo company : list) { - // fixed 曾用名中有可能有 updater 的名字,会导致自己删除自己 - if (Objects.equals(company.getId(), updater.getId())) { - continue; - } - try { - getCompanyService().merge(company, updater); - setStatus("并户 " + company.getName() + "[" + company.getId() + "] 到当前公司"); - merge++; - } catch (Exception e) { - throw new RuntimeException("合并 " + company.getName() + " -> " + updater.getName() + " 失败", e); - } - } - } - count++; - } - if (merge == 0) { - setStatus("没有需要并户的公司"); - } - controller.setRightStatus(""); + loadTableDataSet(); } @Override @@ -201,7 +172,7 @@ public class CompanyTabSkinOldName return; } String path = viewModel.getPath().get(); - if (StringUtils.hasText(path)) { + if (org.springframework.util.StringUtils.hasText(path)) { if (item.startsWith(path)) { item = "~" + item.substring(path.length()); } @@ -216,4 +187,4 @@ public class CompanyTabSkinOldName } return companyOldNameService; } -} +} \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/controller/table/cell/DepartmentTableCell.java b/client/src/main/java/com/ecep/contract/controller/table/cell/DepartmentTableCell.java index 19824fe..a1c53ca 100644 --- a/client/src/main/java/com/ecep/contract/controller/table/cell/DepartmentTableCell.java +++ b/client/src/main/java/com/ecep/contract/controller/table/cell/DepartmentTableCell.java @@ -4,10 +4,15 @@ import com.ecep.contract.SpringApp; import com.ecep.contract.service.DepartmentService; import com.ecep.contract.vo.DepartmentVo; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.util.Callback; + import lombok.NoArgsConstructor; @NoArgsConstructor public class DepartmentTableCell extends AsyncUpdateTableCell { + public DepartmentTableCell(DepartmentService service) { setService(service); } @@ -17,4 +22,20 @@ public class DepartmentTableCell extends AsyncUpdateTableCell 表格行数据类型 + * @return 表格列的回调函数 + */ + public static Callback, TableCell> forTableColumn(DepartmentService service) { + return column -> new DepartmentTableCell<>(service); + } + + + @Override + public String format(DepartmentVo entity) { + return getService().getStringConverter().toString(entity); + } } diff --git a/client/src/main/java/com/ecep/contract/converter/DepartmentStringConverter.java b/client/src/main/java/com/ecep/contract/converter/DepartmentStringConverter.java index b4c02d8..02e94e1 100644 --- a/client/src/main/java/com/ecep/contract/converter/DepartmentStringConverter.java +++ b/client/src/main/java/com/ecep/contract/converter/DepartmentStringConverter.java @@ -1,30 +1,37 @@ package com.ecep.contract.converter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - import com.ecep.contract.service.DepartmentService; import com.ecep.contract.vo.DepartmentVo; +import javafx.util.StringConverter; -import jakarta.annotation.PostConstruct; - -@Lazy -@Component -public class DepartmentStringConverter extends EntityStringConverter { - @Lazy - @Autowired +/** + * 部门字符串转换器 + */ +public class DepartmentStringConverter extends StringConverter { private DepartmentService service; public DepartmentStringConverter() { } - @PostConstruct - private void init() { - setInitialized(department -> service.findById(department.getId())); - setSuggestion(service::search); + public DepartmentStringConverter(DepartmentService service) { + this.service = service; } + @Override + public String toString(DepartmentVo department) { + if (department == null) { + return "-"; + } + return department.getCode() + " " + department.getName(); + } + + @Override + public DepartmentVo fromString(String string) { + if (service == null || string == null || string.trim().isEmpty()) { + return null; + } + return service.findByCode(string.trim()); + } } \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/converter/VendorGroupStringConverter.java b/client/src/main/java/com/ecep/contract/converter/VendorGroupStringConverter.java index 1158db1..664c034 100644 --- a/client/src/main/java/com/ecep/contract/converter/VendorGroupStringConverter.java +++ b/client/src/main/java/com/ecep/contract/converter/VendorGroupStringConverter.java @@ -1,30 +1,26 @@ package com.ecep.contract.converter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; +import javafx.util.StringConverter; import com.ecep.contract.service.VendorGroupService; import com.ecep.contract.vo.VendorGroupVo; import jakarta.annotation.PostConstruct; -@Lazy -@Component -public class VendorGroupStringConverter extends EntityStringConverter { - @Lazy - @Autowired +public class VendorGroupStringConverter extends StringConverter { private VendorGroupService service; - public VendorGroupStringConverter() { - + public VendorGroupStringConverter(VendorGroupService service) { + this.service = service; } - @PostConstruct - private void init() { - setInitialized(group -> service.findById(group.getId())); - setSuggestion(service::search); + @Override + public String toString(VendorGroupVo object) { + return object == null ? "" : object.getCode() + " " + object.getName(); } - + @Override + public VendorGroupVo fromString(String string) { + return service.findByCode(string); + } } \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/service/CloudRkService.java b/client/src/main/java/com/ecep/contract/service/CloudRkService.java index 338c31f..6c15235 100644 --- a/client/src/main/java/com/ecep/contract/service/CloudRkService.java +++ b/client/src/main/java/com/ecep/contract/service/CloudRkService.java @@ -1,9 +1,11 @@ package com.ecep.contract.service; +import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.controlsfx.control.TaskProgressView; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -21,6 +23,8 @@ import lombok.Data; @Service public class CloudRkService extends QueryService { + + @Data @JsonIgnoreProperties(ignoreUnknown = true) public static class EntInfo { @@ -69,6 +73,10 @@ public class CloudRkService extends QueryService { .findFirst().orElse(null); } + public Page findAllByCompany(CompanyVo company) { + return findAll(ParamUtils.builder().equals("company", company.getId()).build(), Pageable.unpaged()); + } + public boolean checkBlackListUpdateElapse(CompanyVo company, CloudRkVo cloudRk) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'checkBlackListUpdateElapse'"); @@ -78,4 +86,12 @@ public class CloudRkService extends QueryService { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'updateBlackList'"); } + + public void mergeTo(CompanyVo from, CompanyVo to) { + List list = findAllByCompany(from).getContent(); + for (CloudRkVo item : list) { + item.setCompanyId(to.getId()); + save(item); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/CloudTycService.java b/client/src/main/java/com/ecep/contract/service/CloudTycService.java index 2518fbb..6ab21e7 100644 --- a/client/src/main/java/com/ecep/contract/service/CloudTycService.java +++ b/client/src/main/java/com/ecep/contract/service/CloudTycService.java @@ -1,7 +1,9 @@ package com.ecep.contract.service; import java.time.LocalDateTime; +import java.util.List; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -77,4 +79,16 @@ public class CloudTycService extends QueryService findAllByCompany(CompanyVo company) { + return findAll(ParamUtils.builder().equals("company", company.getId()).build(), Pageable.unpaged()); + } + + public void mergeTo(CompanyVo from, CompanyVo to) { + List list = findAllByCompany(from).getContent(); + for (CloudTycVo item : list) { + item.setCompanyId(to.getId()); + save(item); + } + + } } diff --git a/client/src/main/java/com/ecep/contract/service/CompanyContactService.java b/client/src/main/java/com/ecep/contract/service/CompanyContactService.java index 9a7499e..04682ae 100644 --- a/client/src/main/java/com/ecep/contract/service/CompanyContactService.java +++ b/client/src/main/java/com/ecep/contract/service/CompanyContactService.java @@ -1,5 +1,6 @@ package com.ecep.contract.service; +import java.time.LocalDate; import java.util.List; import org.springframework.data.domain.Page; @@ -32,4 +33,21 @@ public class CompanyContactService extends QueryService findAllByCompany(CompanyVo company, LocalDate beginDate, LocalDate endDate) { + return findAll(ParamUtils.builder() + .equals("company", company.getId()) + .between("setupDate", beginDate, endDate) + .build(), Pageable.unpaged()).getContent(); + } + + public void mergeTo(CompanyVo from, CompanyVo to) { + List contacts = findAllByCompany(from, LocalDate.MIN, LocalDate.MAX); + if (contacts == null || contacts.isEmpty()) { + return; + } + for (CompanyContactVo contact : contacts) { + contact.setCompanyId(to.getId()); + save(contact); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/CompanyCustomerEntityService.java b/client/src/main/java/com/ecep/contract/service/CompanyCustomerEntityService.java index d6e474c..2e0876a 100644 --- a/client/src/main/java/com/ecep/contract/service/CompanyCustomerEntityService.java +++ b/client/src/main/java/com/ecep/contract/service/CompanyCustomerEntityService.java @@ -17,4 +17,12 @@ public class CompanyCustomerEntityService extends QueryService fromEntities = findAllByCustomer(from); + for (CompanyCustomerEntityVo fromEntity : fromEntities) { + fromEntity.setCustomerId(to.getId()); + save(fromEntity); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/CompanyCustomerFileService.java b/client/src/main/java/com/ecep/contract/service/CompanyCustomerFileService.java index 49bd2a0..e64afec 100644 --- a/client/src/main/java/com/ecep/contract/service/CompanyCustomerFileService.java +++ b/client/src/main/java/com/ecep/contract/service/CompanyCustomerFileService.java @@ -121,4 +121,12 @@ public class CompanyCustomerFileService extends QueryService fromFiles = findAllByCustomer(from); + for (CustomerFileVo fromFile : fromFiles) { + fromFile.setCustomer(to.getId()); + save(fromFile); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/CompanyOldNameService.java b/client/src/main/java/com/ecep/contract/service/CompanyOldNameService.java index ebcedf3..65dbffe 100644 --- a/client/src/main/java/com/ecep/contract/service/CompanyOldNameService.java +++ b/client/src/main/java/com/ecep/contract/service/CompanyOldNameService.java @@ -4,6 +4,7 @@ import java.io.File; import java.time.LocalDate; import java.util.List; +import com.ecep.contract.util.MyStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -70,16 +71,7 @@ public class CompanyOldNameService extends QueryService page = findAll(ParamUtils.builder() - .equals("ambiguity", false) - .equals("company", company.getId()) - .and(b -> b.isNotNull("beginDate").greaterThan("beginDate", localDate)) - .and(b -> b.isNotNull("endDate").lessThan("endDate", localDate)) - .build(), Pageable.ofSize(1)); - if (page.isEmpty()) { - return null; - } - return page.getContent().getFirst(); + return null; } public List findAllByCompanyAndName(CompanyVo company, String oldName) { @@ -90,4 +82,13 @@ public class CompanyOldNameService extends QueryService findAllByCompany(IdentityEntity company) { return findAll(ParamUtils.equal("company", company.getId()), Pageable.unpaged()).getContent(); } + + public void mergeTo(CompanyVo from, CompanyVo to) { + List fromOldNames = findAllByCompany(from); + for (CompanyOldNameVo fromOldName : fromOldNames) { + fromOldName.setMemo(MyStringUtils.appendIfAbsent(fromOldName.getMemo(), "转自 " + from.getId())); + fromOldName.setCompanyId(to.getId()); + save(fromOldName); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/CompanyService.java b/client/src/main/java/com/ecep/contract/service/CompanyService.java index 74cfbb3..e4ef40f 100644 --- a/client/src/main/java/com/ecep/contract/service/CompanyService.java +++ b/client/src/main/java/com/ecep/contract/service/CompanyService.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.ecep.contract.SpringApp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; @@ -62,9 +63,22 @@ public class CompanyService extends QueryService { return findAll(params, Pageable.unpaged()).getContent(); } - public void merge(CompanyVo company, CompanyVo updater) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'merge'"); + public void merge(CompanyVo from, CompanyVo to) { + + SpringApp.getBean(CloudRkService.class).mergeTo(from, to); + SpringApp.getBean(CloudTycService.class).mergeTo(from, to); + SpringApp.getBean(YongYouU8Service.class).mergeTo(from, to); + + SpringApp.getBean(CompanyOldNameService.class).mergeTo(from, to); + SpringApp.getBean(CompanyContactService.class).mergeTo(from, to); + + // 供应商和客户 + SpringApp.getBean(VendorService.class).mergeTo(from, to); + SpringApp.getBean(CustomerService.class).mergeTo(from, to); + + SpringApp.getBean(ContractService.class).mergeTo(from, to); + SpringApp.getBean(CompanyContactService.class).mergeTo(from, to); + delete(from); } public CompanyVo createNewCompany(String newCompanyName) { diff --git a/client/src/main/java/com/ecep/contract/service/ContractService.java b/client/src/main/java/com/ecep/contract/service/ContractService.java index 032b97a..46470c7 100644 --- a/client/src/main/java/com/ecep/contract/service/ContractService.java +++ b/client/src/main/java/com/ecep/contract/service/ContractService.java @@ -5,6 +5,9 @@ import java.time.LocalDate; import java.util.List; import java.util.Objects; +import com.ecep.contract.SpringApp; +import com.ecep.contract.util.MyStringUtils; +import com.ecep.contract.vo.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; @@ -20,10 +23,6 @@ import com.ecep.contract.constant.ContractConstant; import com.ecep.contract.util.ContractUtils; import com.ecep.contract.util.ParamUtils; import com.ecep.contract.vm.ContractViewModel; -import com.ecep.contract.vo.ContractFileVo; -import com.ecep.contract.vo.ContractVo; -import com.ecep.contract.vo.ProjectVo; -import com.ecep.contract.vo.VendorVo; @Service @CacheConfig(cacheNames = "contract") @@ -158,10 +157,24 @@ public class ContractService extends QueryService .build(), Pageable.unpaged()).getContent(); } + public List findAllByCompanyCustomer(CustomerVo customer, LocalDate beginDate, LocalDate endDate) { + return findAll(ParamUtils.builder() + .equals("company", customer.getCompanyId()) + .between("setupDate", beginDate, endDate) + .build(), Pageable.unpaged()).getContent(); + } + + public List findAllByCompany(CompanyVo company, LocalDate beginDate, LocalDate endDate) { + return findAll(ParamUtils.builder() + .equals("company", company.getId()) + .between("setupDate", beginDate, endDate) + .build(), Pageable.unpaged()).getContent(); + } + public List findAllSalesByProject(ProjectVo project) { return findAll(ParamUtils.builder() - .equals("parentCode", "") - .equals("project", project.getId()).build(), + .equals("parentCode", "") + .equals("project", project.getId()).build(), Pageable.unpaged()).getContent(); } @@ -183,4 +196,18 @@ public class ContractService extends QueryService // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'syncContractFile'"); } + + public void mergeTo(CompanyVo from, CompanyVo to) { + + List contracts = findAllByCompany(from, LocalDate.MIN, LocalDate.MAX); + if (contracts == null || contracts.isEmpty()) { + return; + } + + for (ContractVo contract : contracts) { + contract.setDescription(MyStringUtils.appendIfAbsent(contract.getDescription(), "转自 " + from.getId())); + contract.setCompanyId(to.getId()); + save(contract); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/ContractTypeService.java b/client/src/main/java/com/ecep/contract/service/ContractTypeService.java index d92a345..cdaf70b 100644 --- a/client/src/main/java/com/ecep/contract/service/ContractTypeService.java +++ b/client/src/main/java/com/ecep/contract/service/ContractTypeService.java @@ -12,8 +12,6 @@ import org.springframework.stereotype.Service; import com.ecep.contract.vm.ContractTypeViewModel; import com.ecep.contract.vo.ContractTypeVo; -import javafx.util.StringConverter; - @Service @CacheConfig(cacheNames = "contract-type") public class ContractTypeService extends QueryService { diff --git a/client/src/main/java/com/ecep/contract/service/CustomerService.java b/client/src/main/java/com/ecep/contract/service/CustomerService.java index 4fc0381..a004add 100644 --- a/client/src/main/java/com/ecep/contract/service/CustomerService.java +++ b/client/src/main/java/com/ecep/contract/service/CustomerService.java @@ -30,11 +30,7 @@ public class CustomerService extends QueryService page = findAll(ParamUtils.equal("company", company.getId()), Pageable.ofSize(1)); - if (page.isEmpty()) { - return null; - } - return page.getContent().getFirst(); + return findOneByProperty("company", company.getId()); } /** @@ -140,4 +136,43 @@ public class CustomerService extends QueryService|]", "_"); } + + public void mergeTo(CompanyVo from, CompanyVo to) { + CustomerVo fromCustomer = findByCompany(from); + if (fromCustomer == null) { + return; + } + + CustomerVo toCustomer = findByCompany(to); + if (toCustomer == null) { + // 直接修改关联 + fromCustomer.setCompanyId(to.getId()); + save(fromCustomer); + return; + } + + mergeTo(fromCustomer, toCustomer); + + } + + /** + * 合并客户 + * 1. 删除源客户对象 + * 2. 删除源客户对象关联的文件 + * 3. 修改目标客户对象的关联公司 + * 4. 修改目标客户对象的关联文件 + * + * @param from + * @param to + */ + public void mergeTo(CustomerVo from, CustomerVo to) { + // file + CompanyCustomerFileService companyCustomerFileService = SpringApp.getBean(CompanyCustomerFileService.class); + companyCustomerFileService.mergeTo(from, to); + // entity + CompanyCustomerEntityService companyCustomerEntityService = SpringApp.getBean(CompanyCustomerEntityService.class); + companyCustomerEntityService.mergeTo(from, to); + // 删除源客户对象 + delete(from); + } } diff --git a/client/src/main/java/com/ecep/contract/service/DepartmentService.java b/client/src/main/java/com/ecep/contract/service/DepartmentService.java index e8a74c0..d7d41b0 100644 --- a/client/src/main/java/com/ecep/contract/service/DepartmentService.java +++ b/client/src/main/java/com/ecep/contract/service/DepartmentService.java @@ -1,11 +1,81 @@ package com.ecep.contract.service; +import java.util.List; + +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.stereotype.Service; +import com.ecep.contract.converter.DepartmentStringConverter; import com.ecep.contract.vm.DepartmentViewModel; import com.ecep.contract.vo.DepartmentVo; +import javafx.util.StringConverter; + @Service +@CacheConfig(cacheNames = "department") public class DepartmentService extends QueryService { + private final DepartmentStringConverter stringConverter = new DepartmentStringConverter(this); + + @Cacheable(key = "#p0") + @Override + public DepartmentVo findById(Integer id) { + return super.findById(id); + } + + public DepartmentVo findByName(String name) { + try { + return async("findByName", name, String.class).handle((response, ex) -> { + DepartmentVo newEntity = createNewEntity(); + return updateValue(newEntity, response); + }).get(); + } catch (Exception e) { + throw new RuntimeException("查询实体失败" + name, e); + } + } + + public DepartmentVo findByCode(String code) { + try { + return async("findByCode", code, String.class).handle((response, ex) -> { + if (ex != null) { + throw new RuntimeException("远程方法+findByCode+调用失败", ex); + } + DepartmentVo newEntity = createNewEntity(); + return updateValue(newEntity, response); + }).get(); + } catch (Exception e) { + throw new RuntimeException("查询实体失败" + code, e); + } + } + + @Cacheable(key = "'departments'") + @Override + public List findAll() { + return super.findAll(); + } + + @Caching(evict = { + @CacheEvict(key = "#p0.id"), @CacheEvict(key = "'departments'"), + }) + @Override + public void delete(DepartmentVo entity) { + super.delete(entity); + } + + @Caching(evict = { + @CacheEvict(key = "#p0.id"), @CacheEvict(key = "'departments'"), + }) + @Override + public DepartmentVo save(DepartmentVo entity) { + return super.save(entity); + } + + @Override + public StringConverter getStringConverter() { + return stringConverter; + } + } diff --git a/client/src/main/java/com/ecep/contract/service/EmployeeRoleService.java b/client/src/main/java/com/ecep/contract/service/EmployeeRoleService.java index 42207b0..e762333 100644 --- a/client/src/main/java/com/ecep/contract/service/EmployeeRoleService.java +++ b/client/src/main/java/com/ecep/contract/service/EmployeeRoleService.java @@ -1,7 +1,11 @@ package com.ecep.contract.service; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import com.ecep.contract.vo.DepartmentVo; +import javafx.collections.ObservableList; import org.springframework.stereotype.Service; import com.ecep.contract.vm.EmployeeRoleViewModel; @@ -11,11 +15,38 @@ import com.ecep.contract.vo.FunctionVo; @Service public class EmployeeRoleService extends QueryService { - public List getFunctionsByRoleId(int roleId) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFunctionsByRoleId'"); + public List getFunctionsByRole(EmployeeRoleVo role) { + try { + return async("getFunctionsByRoleId", role.getId(), Integer.class).handle((response, ex) -> { + if (ex != null) { + throw new RuntimeException("远程方法+getFunctionsByRoleId+调用失败", ex); + } + List list = new ArrayList<>(); + try { + objectMapper.readerForUpdating(list) + .forType(objectMapper.getTypeFactory().constructCollectionType(List.class, FunctionVo.class)) + .readValue(response); + } catch (IOException e) { + throw new RuntimeException(e); + } + return list; + }).get(); + } catch (Exception e) { + throw new RuntimeException("查询实体失败, Function#" + role.getId(), e); + } } - + public void saveRoleFunctions(EmployeeRoleVo role, List functions) { + try { + async("saveRoleFunctions", new Object[]{role.getId(), functions.stream().mapToInt(FunctionVo::getId).toArray()}, new Object[]{Integer.class, Integer[].class}).handle((response, ex) -> { + if (ex != null) { + throw new RuntimeException("远程方法+saveRoleFunctions+调用失败", ex); + } + return null; + }).get(); + } catch (Exception e) { + throw new RuntimeException("保存角色的功能失败, 角色#" + role.getId(), e); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/VendorEntityService.java b/client/src/main/java/com/ecep/contract/service/VendorEntityService.java index 50f4469..e5bc8ce 100644 --- a/client/src/main/java/com/ecep/contract/service/VendorEntityService.java +++ b/client/src/main/java/com/ecep/contract/service/VendorEntityService.java @@ -12,7 +12,7 @@ import com.ecep.contract.vm.CompanyVendorEntityViewModel; @Service public class VendorEntityService extends QueryService { - + /** * 根据供应商ID查询关联实体列表 */ @@ -20,14 +20,14 @@ public class VendorEntityService extends QueryService findByVendor(VendorVo vendor) { return findByVendorId(vendor.getId()); } - + /** * 根据供应商ID创建新的CompanyVendorEntity实例 */ @@ -43,10 +43,18 @@ public class VendorEntityService extends QueryService fromEntities = findByVendor(from); + for (VendorEntityVo fromEntity : fromEntities) { + fromEntity.setVendorId(to.getId()); + save(fromEntity); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/VendorFileService.java b/client/src/main/java/com/ecep/contract/service/VendorFileService.java index 0e3a794..91390d1 100644 --- a/client/src/main/java/com/ecep/contract/service/VendorFileService.java +++ b/client/src/main/java/com/ecep/contract/service/VendorFileService.java @@ -97,33 +97,13 @@ public class VendorFileService extends QueryService files = findAllByVendorAndType(companyVendor, VendorFileType.EvaluationForm); - if (files == null || files.isEmpty()) { - holder.error("未见供应商评价"); - return; - } - // 检索 验证日期最近一年内的有效评价表,日期宽限7天 - LocalDate begin = verifyDate.plusYears(-1); - VendorFileVo vendorFile = files.stream() - .filter(v -> v.getSignDate() != null && v.isValid()) - .filter(v -> MyDateTimeUtils.dateValidFilter(v.getSignDate(), begin, verifyDate, 7)) - .findFirst().orElse(null); - if (vendorFile == null) { - // 检索最后一个有效评价表 - VendorFileVo latestFile = files.stream() - .filter(v -> v.getSignDate() != null && v.isValid()) - .max(Comparator.comparing(VendorFileVo::getSignDate)) - .orElse(null); + } - if (latestFile == null) { - holder.error("未匹配的供应商评价"); - return; - } - // 提示评价表已过期 - holder.error("供应商评价已过期:" + latestFile.getSignDate() + ", 检测日期:" + verifyDate); - } + public List findAllByVendor(VendorVo vendor) { + return findAll(ParamUtils.builder() + .equals("vendor", vendor.getId()) + .build(), Pageable.unpaged()).getContent(); } public List findAllByVendorAndType(VendorVo vendor, VendorFileType type) { @@ -132,4 +112,12 @@ public class VendorFileService extends QueryService fromFiles = findAllByVendor(from); + for (VendorFileVo fromFile : fromFiles) { + fromFile.setVendorId(to.getId()); + save(fromFile); + } + } } diff --git a/client/src/main/java/com/ecep/contract/service/VendorGroupService.java b/client/src/main/java/com/ecep/contract/service/VendorGroupService.java index 494d60c..b49d7e9 100644 --- a/client/src/main/java/com/ecep/contract/service/VendorGroupService.java +++ b/client/src/main/java/com/ecep/contract/service/VendorGroupService.java @@ -1,9 +1,11 @@ package com.ecep.contract.service; +import com.ecep.contract.converter.VendorGroupStringConverter; import com.ecep.contract.model.VendorGroup; import com.ecep.contract.util.ParamUtils; import com.ecep.contract.vm.VendorGroupViewModel; import com.ecep.contract.vo.VendorGroupVo; +import javafx.util.StringConverter; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -15,6 +17,8 @@ import org.springframework.stereotype.Service; @Service @CacheConfig(cacheNames = "vendor-group") public class VendorGroupService extends QueryService { + + private VendorGroupStringConverter stringConverter = new VendorGroupStringConverter(this); @Cacheable(key = "#id") @Override public VendorGroupVo findById(Integer id) { @@ -53,4 +57,9 @@ public class VendorGroupService extends QueryService page = findAll(ParamUtils.equal("company", company.getId()), Pageable.ofSize(1)); - if (page.isEmpty()) { - return null; - } - return page.getContent().getFirst(); + return findOneByProperty("company", company.getId()); } public void verify(ContractVo contract, MessageHolder holder) { @@ -189,4 +181,32 @@ public class VendorService extends QueryService findAllByCompany(CompanyVo company) { + return findAll(ParamUtils.builder().equals("company", company.getId()).build(), Pageable.unpaged()); + } + + public void mergeTo(CompanyVo from, CompanyVo to) { + List list = findAllByCompany(from).getContent(); + for (CloudYuVo item : list) { + item.setCompanyId(to.getId()); + save(item); + } + } } diff --git a/client/src/main/java/com/ecep/contract/task/CompanyMergeClientTasker.java b/client/src/main/java/com/ecep/contract/task/CompanyMergeClientTasker.java new file mode 100644 index 0000000..7bf9185 --- /dev/null +++ b/client/src/main/java/com/ecep/contract/task/CompanyMergeClientTasker.java @@ -0,0 +1,44 @@ +package com.ecep.contract.task; + +import com.ecep.contract.WebSocketClientTasker; +import com.ecep.contract.MessageHolder; +import com.ecep.contract.vo.CompanyVo; +import javafx.application.Platform; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; + +/** + * 公司合并客户端任务器 + * 通过WebSocket连接与服务器端通信,执行公司合并操作 + */ + +public class CompanyMergeClientTasker extends Tasker implements WebSocketClientTasker { + + @Getter + @Setter + private CompanyVo company; + + @Getter + @Setter + private List nameList; + + @Override + public String getTaskName() { + return "CompanyMergeTask"; + } + + @Override + public void updateProgress(long current, long total) { + super.updateProgress(current, total); + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + return callRemoteTask(holder, getLocale(), company.getId(), nameList.toArray()); + } +} \ No newline at end of file diff --git a/client/src/main/java/com/ecep/contract/task/ContractGroupSyncTask.java b/client/src/main/java/com/ecep/contract/task/ContractGroupSyncTask.java index f9eadd1..a2707ac 100644 --- a/client/src/main/java/com/ecep/contract/task/ContractGroupSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/ContractGroupSyncTask.java @@ -1,11 +1,21 @@ package com.ecep.contract.task; import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; -public class ContractGroupSyncTask extends Tasker{ +public class ContractGroupSyncTask extends Tasker implements WebSocketClientTasker { @Override - public Object execute(MessageHolder holder) { - return null; + public String getTaskName() { + return "ContractGroupSyncTask"; } + @Override + public Object execute(MessageHolder holder) { + return callRemoteTask(holder, getLocale()); + } + + @Override + public void updateProgress(long current, long total) { + super.updateProgress(current, total); + } } diff --git a/client/src/main/java/com/ecep/contract/task/ContractKindSyncTask.java b/client/src/main/java/com/ecep/contract/task/ContractKindSyncTask.java index 6cbe975..8944c2e 100644 --- a/client/src/main/java/com/ecep/contract/task/ContractKindSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/ContractKindSyncTask.java @@ -1,10 +1,23 @@ package com.ecep.contract.task; import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; + +public class ContractKindSyncTask extends Tasker implements WebSocketClientTasker { + public static final String TASK_NAME = "ContractKindSyncTask"; + + @Override + public String getTaskName() { + return TASK_NAME; + } + + @Override + public void updateProgress(long workDone, long max) { + super.updateProgress(workDone, max); + } -public class ContractKindSyncTask extends Tasker { @Override public Object execute(MessageHolder holder) { - return null; + return callRemoteTask(holder, getLocale()); } } diff --git a/client/src/main/java/com/ecep/contract/task/ContractSyncTask.java b/client/src/main/java/com/ecep/contract/task/ContractSyncTask.java index 1e5741a..5254f26 100644 --- a/client/src/main/java/com/ecep/contract/task/ContractSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/ContractSyncTask.java @@ -4,9 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ecep.contract.MessageHolder; -import com.ecep.contract.SpringApp; import com.ecep.contract.WebSocketClientTasker; -import com.ecep.contract.service.YongYouU8Service; /** * 合同同步任务 @@ -14,15 +12,6 @@ import com.ecep.contract.service.YongYouU8Service; public class ContractSyncTask extends Tasker implements WebSocketClientTasker { private static final Logger logger = LoggerFactory.getLogger(ContractSyncTask.class); - private YongYouU8Service yongYouU8Service; - - private YongYouU8Service getYongYouU8Service() { - if (yongYouU8Service == null) { - yongYouU8Service = SpringApp.getBean(YongYouU8Service.class); - } - return yongYouU8Service; - } - public String getTaskName() { return "ContractSyncTask"; } diff --git a/client/src/main/java/com/ecep/contract/task/ContractTypeSyncTask.java b/client/src/main/java/com/ecep/contract/task/ContractTypeSyncTask.java index 2bd736d..9cc8934 100644 --- a/client/src/main/java/com/ecep/contract/task/ContractTypeSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/ContractTypeSyncTask.java @@ -1,10 +1,27 @@ package com.ecep.contract.task; -import com.ecep.contract.MessageHolder; +import java.util.Locale; + +import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; + +/** + * 合同类型同步任务 + */ +public class ContractTypeSyncTask extends Tasker implements WebSocketClientTasker { + @Override + public String getTaskName() { + return "ContractTypeSyncTask"; + } + + @Override + public void updateProgress(long current, long total) { + super.updateProgress(current, total); + } -public class ContractTypeSyncTask extends Tasker { @Override public Object execute(MessageHolder holder) { - return null; + // 调用远程WebSocket任务 + return callRemoteTask(holder, Locale.getDefault()); } } diff --git a/client/src/main/java/com/ecep/contract/task/CustomerClassSyncTask.java b/client/src/main/java/com/ecep/contract/task/CustomerClassSyncTask.java index 79a224f..9600f94 100644 --- a/client/src/main/java/com/ecep/contract/task/CustomerClassSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/CustomerClassSyncTask.java @@ -1,13 +1,32 @@ package com.ecep.contract.task; -import com.ecep.contract.MessageHolder; +import java.util.Locale; -public class CustomerClassSyncTask extends Tasker { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; + +public class CustomerClassSyncTask extends Tasker implements WebSocketClientTasker { + public static final String TASK_NAME = "CustomerClassSyncTask"; + private static final Logger logger = LoggerFactory.getLogger(CustomerClassSyncTask.class); + + @Override + public String getTaskName() { + return TASK_NAME; + } @Override protected Object execute(MessageHolder holder) throws Exception { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + updateTitle("用友U8系统-同步客户分类"); + return callRemoteTask(holder, Locale.getDefault()); + } + + // 显式重写 updateProgress 以解决方法隐藏冲突 + @Override + public void updateProgress(long workDone, long max) { + super.updateProgress(workDone, max); } } diff --git a/client/src/main/java/com/ecep/contract/task/CustomerSyncTask.java b/client/src/main/java/com/ecep/contract/task/CustomerSyncTask.java index 7c73f0d..bb92b93 100644 --- a/client/src/main/java/com/ecep/contract/task/CustomerSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/CustomerSyncTask.java @@ -1,20 +1,36 @@ package com.ecep.contract.task; +import java.util.Locale; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; /** * 同步客户任务 */ -public class CustomerSyncTask extends Tasker { +public class CustomerSyncTask extends Tasker implements WebSocketClientTasker { + + public static final String TASK_NAME = "CustomerSyncTask"; private static final Logger logger = LoggerFactory.getLogger(CustomerSyncTask.class); + @Override + public String getTaskName() { + return TASK_NAME; + } + + // 显式重写 updateProgress 以解决方法隐藏冲突 + @Override + public void updateProgress(long workDone, long max) { + super.updateProgress(workDone, max); + } + @Override protected Object execute(MessageHolder holder) throws Exception { updateTitle("用友U8系统-同步客户"); - return null; + return callRemoteTask(holder, Locale.getDefault()); } } diff --git a/client/src/main/java/com/ecep/contract/task/VendorClassSyncTask.java b/client/src/main/java/com/ecep/contract/task/VendorClassSyncTask.java index 5c456fb..6eb8865 100644 --- a/client/src/main/java/com/ecep/contract/task/VendorClassSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/VendorClassSyncTask.java @@ -1,10 +1,28 @@ package com.ecep.contract.task; -import com.ecep.contract.MessageHolder; +import java.util.Locale; + +import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; + +public class VendorClassSyncTask extends Tasker implements WebSocketClientTasker { + public static final String TASK_NAME = "VendorClassSyncTask"; + + @Override + public String getTaskName() { + return TASK_NAME; + } + + @Override + public void updateProgress(long current, long total) { + // 直接使用父类的updateProgress方法 + super.updateProgress(current, total); + } -public class VendorClassSyncTask extends Tasker { @Override public Object execute(MessageHolder holder) { - return null; + // 使用callRemoteTask调用远程任务 + updateTitle("用友U8系统-同步供应商分类"); + return callRemoteTask(holder, Locale.getDefault()); } } diff --git a/client/src/main/java/com/ecep/contract/task/VendorSyncTask.java b/client/src/main/java/com/ecep/contract/task/VendorSyncTask.java index e81244f..850f1b5 100644 --- a/client/src/main/java/com/ecep/contract/task/VendorSyncTask.java +++ b/client/src/main/java/com/ecep/contract/task/VendorSyncTask.java @@ -4,16 +4,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ecep.contract.MessageHolder; +import com.ecep.contract.WebSocketClientTasker; /** * 供应商同步任务 */ -public class VendorSyncTask extends Tasker { +public class VendorSyncTask extends Tasker implements WebSocketClientTasker { private static final Logger logger = LoggerFactory.getLogger(VendorSyncTask.class); + private static final String TASK_NAME = "VendorSyncTask"; + + @Override + public String getTaskName() { + return TASK_NAME; + } + + @Override + public void updateProgress(long current, long total) { + super.updateProgress(current, total); + } @Override protected Object execute(MessageHolder holder) throws Exception { updateTitle("用友U8系统-同步供应商"); - return null; + return callRemoteTask(holder, getLocale()); } } diff --git a/client/src/main/resources/ui/cloud/rk_manager.fxml b/client/src/main/resources/ui/cloud/rk_manager.fxml index 86889ba..1d3f723 100644 --- a/client/src/main/resources/ui/cloud/rk_manager.fxml +++ b/client/src/main/resources/ui/cloud/rk_manager.fxml @@ -3,7 +3,7 @@ + fx:controller="com.ecep.contract.controller.cloud.rk.CloudRkManagerWindowController"> @@ -39,7 +39,6 @@ - diff --git a/client/src/main/resources/ui/employee/function.fxml b/client/src/main/resources/ui/employee/function.fxml index e25856a..435d4e3 100644 --- a/client/src/main/resources/ui/employee/function.fxml +++ b/client/src/main/resources/ui/employee/function.fxml @@ -7,7 +7,7 @@ + fx:controller="com.ecep.contract.controller.permission.EmployeeFunctionWindowController">
diff --git a/client/src/main/resources/ui/employee/functions-manager.fxml b/client/src/main/resources/ui/employee/functions-manager.fxml index 384d789..1c03859 100644 --- a/client/src/main/resources/ui/employee/functions-manager.fxml +++ b/client/src/main/resources/ui/employee/functions-manager.fxml @@ -10,7 +10,6 @@ - diff --git a/client/src/main/resources/ui/employee/role.fxml b/client/src/main/resources/ui/employee/role.fxml index 2caa473..e6b440a 100644 --- a/client/src/main/resources/ui/employee/role.fxml +++ b/client/src/main/resources/ui/employee/role.fxml @@ -23,7 +23,8 @@ - +
diff --git a/client/src/main/resources/ui/employee/roles-manager.fxml b/client/src/main/resources/ui/employee/roles-manager.fxml index 34082bf..57e0b46 100644 --- a/client/src/main/resources/ui/employee/roles-manager.fxml +++ b/client/src/main/resources/ui/employee/roles-manager.fxml @@ -9,7 +9,6 @@ - diff --git a/common/pom.xml b/common/pom.xml index 57ae604..0f26700 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,12 +6,12 @@ com.ecep.contract Contract-Manager - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT com.ecep.contract common - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT ${java.version} diff --git a/common/src/main/java/com/ecep/contract/model/EmployeeRole.java b/common/src/main/java/com/ecep/contract/model/EmployeeRole.java index 94210d8..4137239 100644 --- a/common/src/main/java/com/ecep/contract/model/EmployeeRole.java +++ b/common/src/main/java/com/ecep/contract/model/EmployeeRole.java @@ -1,27 +1,14 @@ package com.ecep.contract.model; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - import com.ecep.contract.util.HibernateProxyUtils; import com.ecep.contract.vo.EmployeeRoleVo; -import com.ecep.contract.vo.FunctionVo; import com.fasterxml.jackson.annotation.JsonIgnore; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.Table; +import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import java.util.Objects; + @Getter @Setter @jakarta.persistence.Entity @@ -56,12 +43,12 @@ public class EmployeeRole implements IdentityEntity, NamedEntity, Voable functions = new java.util.ArrayList<>(); - @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }) + @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name = "EMPLOYEE_ROLES", joinColumns = @JoinColumn(name = "ROLE_ID"), inverseJoinColumns = @JoinColumn(name = "EMPLOYEE_ID")) @JsonIgnore private java.util.List employees = new java.util.ArrayList<>(); @@ -93,22 +80,6 @@ public class EmployeeRole implements IdentityEntity, NamedEntity, Voable functionVos = new ArrayList<>(); - for (Function function : getFunctions()) { - if (function != null) { - FunctionVo functionVo = new FunctionVo(); - functionVo.setId(function.getId()); - functionVo.setName(function.getName()); - functionVo.setKey(function.getKey()); - functionVos.add(functionVo); - } - } - vo.setFunctions(functionVos); - } - return vo; } } diff --git a/common/src/main/java/com/ecep/contract/util/HttpJsonUtils.java b/common/src/main/java/com/ecep/contract/util/HttpJsonUtils.java index 80b28e1..c8995f9 100644 --- a/common/src/main/java/com/ecep/contract/util/HttpJsonUtils.java +++ b/common/src/main/java/com/ecep/contract/util/HttpJsonUtils.java @@ -12,7 +12,7 @@ import java.util.Map; import java.util.function.Consumer; public class HttpJsonUtils { - public static void post(String url, + public static void post(String url, Map headers, Consumer> data, Consumer consumer, ObjectMapper objectMapper, Proxy proxy) throws IOException { @@ -24,6 +24,10 @@ public class HttpJsonUtils { conn.usingProxy(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); + if (headers != null) { + headers.forEach(conn::setRequestProperty); + } + conn.setDoOutput(true); conn.setDoInput(true); PrintWriter writer = new PrintWriter(conn.getOutputStream()); @@ -37,12 +41,16 @@ public class HttpJsonUtils { } - public static void get(String url, Consumer consumer, ObjectMapper objectMapper, Proxy proxy) throws IOException { + public static void get(String url, Map headers, + Consumer consumer, ObjectMapper objectMapper, Proxy proxy) throws IOException { URI uri = URI.create(url); HttpURLConnection conn = (HttpURLConnection) (proxy == null ? uri.toURL().openConnection() : uri.toURL().openConnection(proxy)); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json, text/plain, */*"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"); + if (headers != null) { + headers.forEach(conn::setRequestProperty); + } try (InputStream is = conn.getInputStream()) { JsonNode json = objectMapper.readTree(is); consumer.accept(json); diff --git a/common/src/main/java/com/ecep/contract/vo/EmployeeRoleVo.java b/common/src/main/java/com/ecep/contract/vo/EmployeeRoleVo.java index 0331472..54030d9 100644 --- a/common/src/main/java/com/ecep/contract/vo/EmployeeRoleVo.java +++ b/common/src/main/java/com/ecep/contract/vo/EmployeeRoleVo.java @@ -18,6 +18,7 @@ public class EmployeeRoleVo implements IdentityEntity, NamedEntity, Serializable private boolean manager = false; private boolean active = true; private String description; - private List functions; + // 角色功能通过功能列表查询 + // private List functions; } \ No newline at end of file diff --git a/docs/task/tasker_implementation_guide.md b/docs/task/tasker_implementation_guide.md new file mode 100644 index 0000000..6c2bafe --- /dev/null +++ b/docs/task/tasker_implementation_guide.md @@ -0,0 +1,182 @@ +# Tasker实现规范指南 + +## 1. Tasker架构概述 + +Tasker是系统中用于执行异步任务的核心框架,主要用于处理耗时操作并提供实时进度反馈。Tasker架构包括: + +- **Tasker基类**:提供任务执行的基础框架和通信功能 +- **WebSocketServerTasker接口**:定义WebSocket通信任务的规范 +- **MessageHolder**:用于在任务执行过程中传递消息和更新进度 + +## 2. 服务器端Tasker实现规范 + +### 2.1 基本结构 + +服务器端Tasker实现应遵循以下结构: + +```java +@Data +@EqualsAndHashCode(callSuper = true) +public class YourTaskNameTask extends Tasker implements WebSocketServerTasker { + // 服务依赖 + @Setter + private YourService service; + + @Override + public void init(JsonNode argsNode) { + // 初始化任务标题 + updateTitle("任务标题"); + // 初始化参数 + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + // 实现业务逻辑 + // 使用holder.info()、holder.error()等方法反馈消息 + return null; + } +} +``` + +### 2.2 关键注意事项 + +1. **继承与实现**: + - 必须继承`Tasker`基类 + - 必须实现`WebSocketServerTasker`接口 + +2. **方法实现**: + - **不要重复实现**Tasker基类已提供的handler设置方法:`setMessageHandler`、`setTitleHandler`、`setPropertyHandler`、`setProgressHandler` + - 必须实现`init`方法,设置任务标题和初始化参数 + - 必须实现`execute`方法,实现具体业务逻辑 + +3. **进度和消息反馈**: + - 使用`updateProgress`方法提供进度更新 + - 使用`MessageHolder`的`info`、`error`、`debug`等方法提供消息反馈 + +4. **任务取消**: + - 在适当的地方检查`isCancelled()`状态,支持任务取消 + +5. **注册要求**: + - 任务类必须在`tasker_mapper.json`中注册 + +## 3. 客户端Tasker实现规范 + +客户端Tasker实现应遵循: + +```java +public class YourTaskNameTask extends Tasker { + @Override + protected Object execute(MessageHolder holder) throws Exception { + // 客户端任务逻辑 + return null; + } +} +``` + +## 4. 通信规范 + +### 4.1 命名规范 + +- 客户端和服务器端对应的Tasker类应使用**相同的类名** +- 示例:客户端的`ContractSyncAllTask`对应服务器端的`ContractSyncAllTask` + +### 4.2 消息传递 + +- 使用`MessageHolder`进行消息传递 +- 支持不同级别的消息:INFO、ERROR、DEBUG + +## 5. 最佳实践 + +1. **任务拆分**: + - 将大型任务拆分为多个小任务,提高可维护性 + +2. **异常处理**: + - 捕获并正确处理异常,使用`holder.error()`提供详细错误信息 + +3. **资源管理**: + - 确保及时释放资源,特别是在任务取消的情况下 + +4. **日志记录**: + - 使用`holder.debug()`记录调试信息 + - 使用`holder.info()`记录进度信息 + +5. **状态检查**: + - 定期检查`isCancelled()`状态,确保任务可以被及时取消 + +## 6. 代码示例 + +### 6.1 服务器端Tasker示例 + +```java +@Data +@EqualsAndHashCode(callSuper = true) +public class ExampleSyncTask extends Tasker implements WebSocketServerTasker { + @Setter + private ExampleService exampleService; + + @Override + public void init(JsonNode argsNode) { + updateTitle("示例同步任务"); + // 从argsNode初始化参数 + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + try { + // 获取总记录数 + long total = exampleService.getTotalCount(); + holder.info("开始同步数据,共" + total + "条记录"); + + // 分批处理 + long processed = 0; + List entities = exampleService.getAllEntities(); + + for (ExampleEntity entity : entities) { + // 检查是否取消 + if (isCancelled()) { + holder.info("任务已取消"); + return null; + } + + // 处理单个实体 + exampleService.processEntity(entity); + processed++; + + // 更新进度 + updateProgress(processed, total); + if (processed % 100 == 0) { + holder.info("已处理" + processed + "/" + total + "条记录"); + } + } + + holder.info("同步完成,共处理" + processed + "条记录"); + return processed; + } catch (Exception e) { + holder.error("同步失败: " + e.getMessage()); + throw e; + } + } +} +``` + +## 7. 常见错误和避免方法 + +1. **重复实现handler方法**: + - 错误:在子类中重复实现setMessageHandler等方法 + - 解决:删除子类中的这些方法,直接使用Tasker基类提供的实现 + +2. **缺少任务注册**: + - 错误:任务类没有在tasker_mapper.json中注册 + - 解决:确保所有服务器端Tasker类都在配置文件中正确注册 + +3. **类名不匹配**: + - 错误:客户端和服务器端Tasker类名不一致 + - 解决:确保两端使用相同的类名 + +4. **进度更新不正确**: + - 错误:进度计算错误或没有更新 + - 解决:正确计算和更新进度,提高用户体验 + +5. **资源泄漏**: + - 错误:未及时关闭数据库连接等资源 + - 解决:使用try-with-resources或在finally块中关闭资源 \ No newline at end of file diff --git a/docs/天眼查下载信用报告.md b/docs/天眼查下载信用报告.md index e69de29..47a2157 100644 --- a/docs/天眼查下载信用报告.md +++ b/docs/天眼查下载信用报告.md @@ -0,0 +1,58 @@ +# 天眼查企业信用报告下载操作指南 + +## 操作目标 +使用 playwright MCP服务在天眼查平台上查询并下载目标企业的各类信用报告,包括: +- 企业分析报告 +- 基础版企业信用报告 +- 专业版企业信用报告 +- 工商官网信息快照(图片格式) + +## 准备工作 +1. 确保已登录天眼查账户 +2. 确认浏览器下载路径设置为:`C:\Users\SQQ\Downloads\` +3. 确保网络连接稳定 + +## 操作步骤 + +### 1. 企业搜索 +- 页面导航到 `https://www.tianyancha.com/nsearch?key={企业名称}` +- 或者使用如下步骤: + - 在天眼查首页搜索框中输入目标企业全称 + - 点击搜索按钮或按回车键 + - 在搜索结果中点击目标企业进入详情页 + +### 2. 生成企业报告 +- 在企业详情页找到并点击"报告"按钮 +- 在弹出的报告选择框中,依次点击以下报告的"下载报告"按钮: + - 企业分析报告 + - 专业版企业信用报告 + - 基础版企业信用报告 +- 每个报告下载点击有效时,页面上将提示会"报告已生成" + +### 3. 下载PDF报告 +- 进入个人中心报告页面:https://www.tianyancha.com/usercenter/report +- 却换到 `报告下载` +- 等待对应的三个报告的状态变为"报告完成" +- 依次找到对应的报告行 + - 点击对应报告行的操作列中的下载图标(操作列里有4个图标,第二个图标是下载,需要鼠标点击才会出现下载界面,这个图标是一个 SVG 图片,DOM 对象上没有任何文本,不能通过文本定位到这个按钮),不要使用"在线预览"的段落 + - 出现 报告下载 窗口 + - 格式选中:选中PDF格式 + - 点击下载按钮, + - 关闭 报告下载 窗口 + - 不要使用"在线预览"的段落,那样会打开一个新页面,而不是下载文件 + - 不要使用 page.pdf 方法 + +### 4. 下载工商官网快照 +- 在企业详情页找到"工商信息"模块 +- 点击"工商官网快照"链接 +- 在新打开的页面中点击"下载图片"按钮 + +### 5. 移动文件 +- 将下载的PDF报告和图片文件移动至指定目录:`C:\Users\SQQ\Downloads\` + +## 注意事项 +1. 报告生成需要一定时间,请耐心等待 +2. 下载前确保报告状态为"报告完成" +3. 如果报告下载失败,可尝试重新点击下载按钮 +4. 工商官网快照为图片格式,其他报告为PDF格式 +5. 部分高级报告可能需要会员权限才能下载 \ No newline at end of file diff --git a/pom.xml b/pom.xml index f14dcb9..e0ea0e9 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.ecep.contract Contract-Manager - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT pom server diff --git a/server/pom.xml b/server/pom.xml index 15bcc0c..a4132cd 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -6,12 +6,12 @@ com.ecep.contract Contract-Manager - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT com.ecep.contract server - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT ${java.version} @@ -22,7 +22,7 @@ com.ecep.contract common - 0.0.134-SNAPSHOT + 0.0.135-SNAPSHOT org.springframework.boot diff --git a/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java b/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java index 29050ac..9a1bfcf 100644 --- a/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java +++ b/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import com.ecep.contract.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,10 +26,6 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import com.ecep.contract.BlackReasonType; -import com.ecep.contract.IEntityService; -import com.ecep.contract.QueryService; -import com.ecep.contract.SpringApp; import com.ecep.contract.cloud.CloudInfo; import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.ds.company.model.CompanyBlackReason; @@ -146,80 +143,6 @@ public class CloudRkService implements IEntityService, QueryService companyNames = new ArrayList<>(); - companyNames.add(company.getName()); - // fixed 平台API使用企业名称,可能记录的是曾用名 - companyOldNameRepository.findAllByCompanyId(company.getId()).forEach(oldName -> { - // 歧义的曾用名不采用 - if (oldName.getAmbiguity()) { - return; - } - companyNames.add(oldName.getName()); - }); - - List reasonList = new ArrayList<>(); - List dbReasons = companyBlackReasonRepository.findAllByCompany(company); - - companyNames.forEach(name -> { - String url = context.getUrl() + URLEncoder.encode(name, StandardCharsets.UTF_8); - try { - HttpJsonUtils.get(url, json -> { - if (!json.has("success") || !json.get("success").asBoolean()) { - System.out.println("json = " + json.toPrettyString()); - return; - } - - if (json.has("data")) { - JsonNode data = json.get("data"); - try { - if (data.has("blackReason")) { - for (JsonNode reason : data.get("blackReason")) { - toCompanyBlackReasonList(company, BlackReasonType.BLACK, reason, dbReasons, - reasonList, context.getObjectMapper()); - } - } - if (data.has("greyReason")) { - for (JsonNode reason : data.get("greyReason")) { - toCompanyBlackReasonList(company, BlackReasonType.GRAY, reason, dbReasons, - reasonList, context.getObjectMapper()); - } - } - } catch (Exception ex) { - logger.error("{} {},json = {}", company.getName(), ex.getMessage(), json, ex); - throw new RuntimeException(json.toString(), ex); - } - } - - // 保存JSON数据到公司目录 - String companyPath = company.getPath(); - if (StringUtils.hasText(companyPath)) { - File dir = new File(companyPath); - if (dir.exists()) { - File file = new File(dir, FileUtils.FILE_BLACK_LIST_JSON); - try { - objectMapper.writeValue(file, json); - } catch (IOException e) { - logger.warn("Unable Save BlackList to {}, company:{}", file, company.getName(), e); - } - } - } - }, context.getObjectMapper(), context.getSocksProxy()); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - - if (!reasonList.isEmpty()) { - companyBlackReasonRepository.saveAll(reasonList); - } - cloudRk.setCloudBlackListUpdated(LocalDateTime.now()); - } - private void toCompanyBlackReasonList( Company company, BlackReasonType type, JsonNode reason, List dbReasons, @@ -381,15 +304,19 @@ public class CloudRkService implements IEntityService, QueryService list = cloudRKRepository.findAllByCompanyId(from.getId()); + if (list.isEmpty()) { + holder.debug("No records to reset"); + return; + } for (CloudRk item : list) { item.setCompany(to); + holder.info("Reset #" + item.getId()); } cloudRKRepository.saveAll(list); } diff --git a/server/src/main/java/com/ecep/contract/cloud/rk/ctx/CloudRkCtx.java b/server/src/main/java/com/ecep/contract/cloud/rk/ctx/CloudRkCtx.java index 41c3e5a..5ae1e90 100644 --- a/server/src/main/java/com/ecep/contract/cloud/rk/ctx/CloudRkCtx.java +++ b/server/src/main/java/com/ecep/contract/cloud/rk/ctx/CloudRkCtx.java @@ -14,10 +14,7 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Supplier; @@ -73,7 +70,15 @@ public class CloudRkCtx extends AbstractCtx implements CloudRkContext { } public void post(String url, Consumer> data, Consumer consumer) throws IOException { - HttpJsonUtils.post(url, data, consumer, getObjectMapper(), getSocksProxy()); + Map headers = new HashMap<>(); + headers.put("token", "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0X2NvZGUiOiJDNEU0M0lQVCIsImx0cGFfdG9rZW4iOm51bGwsImV4cGlyZV"); + HttpJsonUtils.post(url, headers, data, consumer, getObjectMapper(), getSocksProxy()); + } + + public void get(String url, Consumer consumer) throws IOException { + Map headers = new HashMap<>(); + headers.put("token", "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0X2NvZGUiOiJDNEU0M0lQVCIsImx0cGFfdG9rZW4iOm51bGwsImV4cGlyZV"); + HttpJsonUtils.get(url, headers, consumer, getObjectMapper(), getSocksProxy()); } /** @@ -162,10 +167,10 @@ public class CloudRkCtx extends AbstractCtx implements CloudRkContext { for (String name : companyNames) { String url = api + URLEncoder.encode(name, StandardCharsets.UTF_8); try { - HttpJsonUtils.get(url, json -> { + get(url, json -> { applyBlackReason(json, company, cloudRk, reasonList, dbReasons, holder); saveJsonToFile(company, json, "black-" + name + ".json", holder); - }, getObjectMapper(), getSocksProxy()); + }); } catch (IOException e) { catchException(e, holder); } @@ -466,16 +471,15 @@ public class CloudRkCtx extends AbstractCtx implements CloudRkContext { Company company, CloudRk cloudRk, MessageHolder holder) throws IOException { String url = getConfService().getString(CloudRkService.KEY_ENT_FUZZY_URL); List results = new ArrayList<>(); - ObjectMapper objectMapper = getObjectMapper(); try { holder.debug("POST " + url); - HttpJsonUtils.post(url, data -> { + post(url, data -> { data.put("theKey", company.getName()); data.put("get", true); }, json -> { applyEnterpriseQuery(json, company, cloudRk, results, holder); saveJsonToFile(company, json, "fuzzy.json", holder); - }, objectMapper, getSocksProxy()); + }); } catch (IOException ex) { catchException(ex, holder); } diff --git a/server/src/main/java/com/ecep/contract/cloud/tyc/CloudTycService.java b/server/src/main/java/com/ecep/contract/cloud/tyc/CloudTycService.java index 8b8311e..cc0d068 100644 --- a/server/src/main/java/com/ecep/contract/cloud/tyc/CloudTycService.java +++ b/server/src/main/java/com/ecep/contract/cloud/tyc/CloudTycService.java @@ -5,7 +5,6 @@ import java.util.Optional; import java.util.stream.Collectors; import com.ecep.contract.QueryService; -import com.ecep.contract.ds.other.model.CloudRk; import com.ecep.contract.util.SpecificationUtils; import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; @@ -146,10 +145,15 @@ public class CloudTycService implements IEntityService, QueryService list = cloudTycRepository.findAllByCompanyId(from.getId()); + if (list.isEmpty()) { + holder.debug("No records to reset"); + return; + } for (CloudTyc item : list) { item.setCompany(to); + holder.info("Reset #" + item.getId()); } cloudTycRepository.saveAll(list); } diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/ContractGroupSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/ContractGroupSyncTask.java index 2a34dc5..dc78c31 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/ContractGroupSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/ContractGroupSyncTask.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; -import com.ecep.contract.vo.ContractGroupVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -13,28 +12,53 @@ import org.springframework.beans.BeansException; import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.ds.contract.service.ContractGroupService; -import com.ecep.contract.model.ContractGroup; +import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** - * 同步合同分组 + * 合同分组同步任务 + *

+ * 该类实现了从U8系统同步合同分组到本地系统的功能 + *

+ * 实现特点: + * 1. 继承Tasker基类获取通信框架和消息处理功能 + * 2. 实现WebSocketServerTasker接口以支持WebSocket通信 + * 3. 使用ContractGroupService处理具体的分组数据同步逻辑 + * 4. 提供进度反馈和状态更新 + *

+ * 工作流程: + * 1. init方法初始化任务标题 + * 2. execute方法读取U8系统中的所有合同分组 + * 3. 遍历并同步每个分组数据到本地 + * 4. 提供实时进度更新 + *

+ * 注意事项: + * 1. 不重复实现Tasker基类已提供的handler设置方法 + * 2. 使用MessageHolder进行消息反馈 + * 3. 在执行过程中检查cancelled状态以支持任务取消 + *

+ * 使用示例: + * 该任务通常由WebSocketServerTaskManager自动实例化和执行,通过WebSocket与客户端通信 */ -public class ContractGroupSyncTask extends Tasker { +public class ContractGroupSyncTask extends Tasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(ContractGroupSyncTask.class); @Setter private ContractGroupService contractGroupService; public ContractGroupSyncTask() { - updateTitle("用友U8系统-同步合同分组信息"); + // 初始化逻辑已移至init方法 } - ContractGroupService getContractGroupService() { - if (contractGroupService == null) { - contractGroupService = SpringApp.getBean(ContractGroupService.class); - } - return contractGroupService; + public ContractGroupService getContractGroupService() { + return getCachedBean(ContractGroupService.class); + } + + @Override + public void init(JsonNode argsNode) { + updateTitle("用友U8系统-同步合同分组信息"); } @Override @@ -44,7 +68,7 @@ public class ContractGroupSyncTask extends Tasker { service = SpringApp.getBean(YongYouU8Service.class); } catch (BeansException e) { logger.error("can't get bean of YongYouU8Service", e); - holder.error("can't get bean of YongYouU8Service"); + holder.info("can't get bean of YongYouU8Service"); return null; } @@ -53,7 +77,6 @@ public class ContractGroupSyncTask extends Tasker { List> list = service.queryAllContractGroup(); int size = list.size(); holder.debug("总共读取 CM_Group 数据 " + size + " 条"); - for (Map map : list) { if (isCancelled()) { holder.info("Cancelled"); @@ -64,7 +87,6 @@ public class ContractGroupSyncTask extends Tasker { // 更新进度 updateProgress(counter.incrementAndGet(), size); } - return null; } diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/ContractKindSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/ContractKindSyncTask.java index 0f4c13e..e62a701 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/ContractKindSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/ContractKindSyncTask.java @@ -13,22 +13,20 @@ import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.ds.contract.service.ContractKindService; import com.ecep.contract.model.ContractKind; +import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** * 同步合同分类 */ -public class ContractKindSyncTask extends Tasker { +public class ContractKindSyncTask extends Tasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(ContractKindSyncTask.class); @Setter private ContractKindService contractKindService; - public ContractKindSyncTask() { - updateTitle("用友U8系统-同步合同分类信息"); - } - ContractKindService getContractKindService() { if (contractKindService == null) { contractKindService = SpringApp.getBean(ContractKindService.class); @@ -36,35 +34,54 @@ public class ContractKindSyncTask extends Tasker { return contractKindService; } + @Override + public void init(JsonNode argsNode) { + // 初始化任务标题 + updateTitle("用友U8系统-同步合同分类信息"); + } + @Override protected Object execute(MessageHolder holder) throws Exception { + holder.info("开始执行合同类型同步任务..."); + updateProgress(0, 100); + YongYouU8Repository repository = null; try { repository = SpringApp.getBean(YongYouU8Repository.class); - } catch (BeansException e) { - logger.error("can't get bean of YongYouU8Repository", e); - holder.error("can't get bean of YongYouU8Repository"); - return null; - } - AtomicInteger counter = new AtomicInteger(0); - logger.info("读取 U8 系统 CM_Kind 数据表..."); - List> list = repository.queryAllContractKind(); - int size = list.size(); - holder.debug("总共读取 CM_Kind 数据 " + size + " 条"); + AtomicInteger counter = new AtomicInteger(0); + logger.info("正在读取U8系统中的合同分类数据..."); - for (Map map : list) { - if (isCancelled()) { - holder.info("Cancelled"); - return null; + List> list = repository.queryAllContractKind(); + int size = list.size(); + holder.debug("共读取到" + size + "条合同分类数据,开始同步..."); + + for (Map map : list) { + if (isCancelled()) { + holder.info("任务已取消"); + return null; + } + MessageHolder sub = holder.sub(counter.get() + "/" + size + ">"); + sync(map, sub); + // 更新进度 + int progress = (int) ((counter.incrementAndGet() * 100.0) / size); + updateProgress(counter.get(), size); + holder.info("同步进度: " + progress + "%"); } - MessageHolder sub = holder.sub(counter.get() + "/" + size + ">"); - sync(map, sub); - // 更新进度 - updateProgress(counter.incrementAndGet(), size); - } - return null; + holder.info("合同类型同步任务执行完成"); + updateProgress(100, 100); + + return null; + } catch (BeansException e) { + logger.error("无法获取YongYouU8Repository实例", e); + holder.error("无法获取YongYouU8Repository实例"); + throw e; + } catch (Exception e) { + logger.error("合同类型同步任务执行失败", e); + holder.error("合同类型同步任务执行失败: " + e.getMessage()); + throw e; + } } private void sync(Map map, MessageHolder holder) { diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/ContractTypeSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/ContractTypeSyncTask.java index 634f231..2870634 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/ContractTypeSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/ContractTypeSyncTask.java @@ -13,58 +13,75 @@ import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.ds.contract.service.ContractTypeService; import com.ecep.contract.model.ContractType; +import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** * 同步合同类型 */ -public class ContractTypeSyncTask extends Tasker { +public class ContractTypeSyncTask extends Tasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(ContractTypeSyncTask.class); @Setter private ContractTypeService contractTypeService; - public ContractTypeSyncTask() { + @Override + public void init(JsonNode argsNode) { + // 初始化任务标题 updateTitle("用友U8系统-同步合同类型信息"); } ContractTypeService getContractTypeService() { - if (contractTypeService == null) { - contractTypeService = SpringApp.getBean(ContractTypeService.class); - } - return contractTypeService; + return getCachedBean(ContractTypeService.class); } @Override protected Object execute(MessageHolder holder) throws Exception { + // 初始化消息 + holder.info("开始执行合同类型同步任务..."); + updateProgress(0, 100); + YongYouU8Repository repository = null; try { repository = SpringApp.getBean(YongYouU8Repository.class); - } catch (BeansException e) { - logger.error("can't get bean of YongYouU8Service", e); - holder.error("can't get bean of YongYouU8Service"); - return null; - } - AtomicInteger counter = new AtomicInteger(0); - logger.info("读取 U8 系统 CM_Type,CM_TypeClass 数据表..."); - List> list = repository.queryAllContractType(); - int size = list.size(); - holder.debug("总共读取 CM_Type,CM_TypeClass 数据 " + size + " 条"); + AtomicInteger counter = new AtomicInteger(0); + logger.info("正在读取U8系统中的合同类型数据..."); - for (Map map : list) { - if (isCancelled()) { - holder.info("Cancelled"); - return null; + List> list = repository.queryAllContractType(); + int size = list.size(); + holder.debug("共读取到" + size + "条合同类型数据,开始同步..."); + + for (Map map : list) { + if (isCancelled()) { + holder.info("任务已取消"); + return null; + } + + MessageHolder sub = holder.sub(counter.get() + "/" + size + "> "); + sync(map, sub); + + // 更新进度 + int progress = (int) ((counter.incrementAndGet() * 100.0) / size); + updateProgress(counter.get(), size); + sub.info("同步进度: " + progress + "%"); } - MessageHolder sub = holder.sub(counter.get() + "/" + size + ">"); - sync(map, sub); - // 更新进度 - updateProgress(counter.incrementAndGet(), size); - } - return null; + holder.info("合同类型同步任务执行完成"); + updateProgress(100, 100); + + return null; + } catch (BeansException e) { + logger.error("无法获取YongYouU8Repository实例", e); + holder.error("无法获取YongYouU8Repository实例"); + throw e; + } catch (Exception e) { + logger.error("合同类型同步任务执行失败", e); + holder.error("合同类型同步任务执行失败: " + e.getMessage()); + throw e; + } } private void sync(Map map, MessageHolder holder) { diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/CustomerClassSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/CustomerClassSyncTask.java index 33c20cd..938ba11 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/CustomerClassSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/CustomerClassSyncTask.java @@ -5,37 +5,38 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; -import com.ecep.contract.ds.customer.service.CustomerCatalogService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; +import com.ecep.contract.ds.customer.service.CustomerCatalogService; import com.ecep.contract.ds.customer.service.CustomerService; import com.ecep.contract.model.CustomerCatalog; +import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; import com.ecep.contract.vo.CustomerCatalogVo; - -import lombok.Setter; +import com.fasterxml.jackson.databind.JsonNode; /** * 同步客户分类 */ -public class CustomerClassSyncTask extends Tasker { +public class CustomerClassSyncTask extends Tasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(CustomerClassSyncTask.class); - @Setter - private CustomerService companyCustomerService; public CustomerClassSyncTask() { - updateTitle("用友U8系统-同步客户分类信息"); + } CustomerService getCompanyCustomerService() { - if (companyCustomerService == null) { - companyCustomerService = SpringApp.getBean(CustomerService.class); - } - return companyCustomerService; + return getCachedBean(CustomerService.class); + } + + @Override + public void init(JsonNode argsNode) { + // 初始化参数,当前任务不需要特殊参数 + updateTitle("用友U8系统-同步客户分类信息"); } @Override @@ -45,27 +46,29 @@ public class CustomerClassSyncTask extends Tasker { repository = SpringApp.getBean(YongYouU8Repository.class); } catch (BeansException e) { logger.error("can't get bean of YongYouU8Repository", e); - holder.error("can't get bean of YongYouU8Repository"); + holder.error("获取YongYouU8Repository失败: " + e.getMessage()); return null; } AtomicInteger counter = new AtomicInteger(0); - logger.info("读取 U8 系统 CustomerClass 数据表..."); + logger.info("开始读取U8系统客户分类数据..."); List> list = repository.queryAllCustomerClass(); int size = list.size(); - holder.debug("总共读取 CustomerClass 数据 " + size + " 条"); + holder.debug("总共读取到" + size + "条客户分类数据"); for (Map map : list) { if (isCancelled()) { - holder.info("Cancelled"); + holder.info("任务已取消"); return null; } MessageHolder sub = holder.sub(counter.get() + "/" + size + ">"); sync(map, sub); // 更新进度 - updateProgress(counter.incrementAndGet(), size); + int current = counter.incrementAndGet(); + updateProgress(current, size); } + holder.info("客户分类数据同步完成"); return null; } @@ -74,33 +77,40 @@ public class CustomerClassSyncTask extends Tasker { String code = (String) map.get("cCCCode"); String name = (String) map.get("cCCName"); + + holder.debug("处理客户分类:" + code + " - " + name); var catalogService = getCachedBean(CustomerCatalogService.class); - CustomerCatalogVo customerCatalog = catalogService.findByCode(code); - if (customerCatalog == null) { - CustomerCatalog v1 = new CustomerCatalog(); - v1.setCode(code); - v1.setName(name); - v1 = catalogService.save(v1); - holder.info("新建客户分类:" + code); - customerCatalog = v1.toVo(); - } + try { + CustomerCatalogVo customerCatalog = catalogService.findByCode(code); + if (customerCatalog == null) { + CustomerCatalog v1 = new CustomerCatalog(); + v1.setCode(code); + v1.setName(name); + v1 = catalogService.save(v1); + holder.info("新建客户分类:" + code); + customerCatalog = v1.toVo(); + } - if (!Objects.equals(customerCatalog.getCode(), code)) { - customerCatalog.setCode(code); - holder.info("客户分类代码:" + customerCatalog.getCode() + " -> " + code); - modified = true; - } - if (!Objects.equals(customerCatalog.getName(), name)) { - customerCatalog.setName(name); - holder.info("客户分类名称:" + customerCatalog.getName() + " -> " + name); - modified = true; - } + if (!Objects.equals(customerCatalog.getCode(), code)) { + customerCatalog.setCode(code); + holder.info("客户分类代码:" + customerCatalog.getCode() + " -> " + code); + modified = true; + } + if (!Objects.equals(customerCatalog.getName(), name)) { + customerCatalog.setName(name); + holder.info("客户分类名称:" + customerCatalog.getName() + " -> " + name); + modified = true; + } - if (modified) { - var v1 = catalogService.getById(customerCatalog.getId()); - catalogService.updateByVo(v1, customerCatalog); - catalogService.save(v1); + if (modified) { + var v1 = catalogService.getById(customerCatalog.getId()); + catalogService.updateByVo(v1, customerCatalog); + catalogService.save(v1); + } + } catch (Exception e) { + logger.error("同步客户分类失败: " + code, e); + holder.error("同步客户分类失败: " + e.getMessage()); } } } diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/CustomerSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/CustomerSyncTask.java index 6aa7d53..8b7dcf7 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/CustomerSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/CustomerSyncTask.java @@ -6,8 +6,6 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; -import com.ecep.contract.ds.customer.service.CompanyCustomerEntityService; -import com.ecep.contract.vo.CompanyCustomerEntityVo; import org.hibernate.Hibernate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,32 +16,44 @@ import com.ecep.contract.SpringApp; import com.ecep.contract.cloud.u8.ctx.CompanyCtx; import com.ecep.contract.cloud.u8.ctx.CustomerCtx; import com.ecep.contract.constant.CloudServiceConstant; +import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.ds.company.service.CompanyService; import com.ecep.contract.ds.contract.tasker.AbstContractRepairTasker; -import com.ecep.contract.ds.customer.service.CustomerService; -import com.ecep.contract.ds.other.model.CloudYu; -import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.ds.customer.model.CompanyCustomer; import com.ecep.contract.ds.customer.model.CompanyCustomerEntity; +import com.ecep.contract.ds.customer.service.CompanyCustomerEntityService; +import com.ecep.contract.ds.customer.service.CustomerService; +import com.ecep.contract.ds.other.model.CloudYu; +import com.ecep.contract.service.tasker.WebSocketServerTasker; +import com.ecep.contract.vo.CompanyCustomerEntityVo; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** * 同步客户任务 */ -public class CustomerSyncTask extends AbstContractRepairTasker { +public class CustomerSyncTask extends AbstContractRepairTasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(CustomerSyncTask.class); private YongYouU8Repository repository; private final CustomerCtx customerCtx = new CustomerCtx(); @Setter private YongYouU8Service yongYouU8Service; - public CustomerSyncTask() { + private CustomerService getCustomerService() { + return getCachedBean(CustomerService.class); + } + + @Override + public void init(JsonNode argsNode) { + // 初始化参数,当前任务不需要特殊参数 updateTitle("用友U8系统-同步客户"); } - private CustomerService getCompanyCustomerService() { - return customerCtx.getCompanyCustomerService(); + @Override + protected Object execute(MessageHolder holder) throws Exception { + repair(holder); + return null; } @Override @@ -106,7 +116,7 @@ public class CustomerSyncTask extends AbstContractRepairTasker { return; } if (!Hibernate.isInitialized(customer)) { - customer = getCompanyCustomerService().getById(customer.getId()); + customer = getCustomerService().getById(customer.getId()); } Company company = customer.getCompany(); if (company == null) { @@ -130,7 +140,7 @@ public class CustomerSyncTask extends AbstContractRepairTasker { return; } if (!Hibernate.isInitialized(customer)) { - customer = getCompanyCustomerService().getById(customer.getId()); + customer = getCustomerService().getById(customer.getId()); } Company company = customer.getCompany(); if (company == null) { diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/EmployeesSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/EmployeesSyncTask.java index f31f946..021c20c 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/EmployeesSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/EmployeesSyncTask.java @@ -15,7 +15,6 @@ import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.ds.other.service.DepartmentService; import com.ecep.contract.ds.other.service.EmployeeService; -import com.ecep.contract.model.Department; import com.ecep.contract.model.Employee; import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/VendorClassSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/VendorClassSyncTask.java index 66d1291..97c3722 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/VendorClassSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/VendorClassSyncTask.java @@ -13,19 +13,26 @@ import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.ds.vendor.repository.VendorClassRepository; import com.ecep.contract.model.VendorCatalog; +import com.ecep.contract.service.tasker.WebSocketServerTasker; import com.ecep.contract.ui.Tasker; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** * 同步供应商分类 */ -public class VendorClassSyncTask extends Tasker { +public class VendorClassSyncTask extends Tasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(VendorClassSyncTask.class); @Setter private VendorClassRepository vendorClassRepository; public VendorClassSyncTask() { + } + + @Override + public void init(JsonNode argsNode) { + // 初始化参数,当前任务不需要特殊参数 updateTitle("用友U8系统-同步供应商分类信息"); } @@ -43,7 +50,7 @@ public class VendorClassSyncTask extends Tasker { service = SpringApp.getBean(YongYouU8Service.class); } catch (BeansException e) { logger.error("can't get bean of YongYouU8Service", e); - holder.error("can't get bean of YongYouU8Service"); + holder.error("获取YongYouU8Service失败: " + e.getMessage()); return null; } @@ -51,7 +58,7 @@ public class VendorClassSyncTask extends Tasker { logger.info("读取 U8 系统 VendorClass 数据表..."); List> list = service.queryAllVendorClass(); int size = list.size(); - holder.debug("总共读取 VendorClass 数据 " + size + " 条"); + holder.debug("总共读取到" + size + "条供应商分类数据"); for (Map map : list) { if (isCancelled()) { @@ -64,6 +71,8 @@ public class VendorClassSyncTask extends Tasker { updateProgress(counter.incrementAndGet(), size); } + updateMessage("供应商分类数据同步完成"); + updateProperty("completed", true); return null; } @@ -73,27 +82,33 @@ public class VendorClassSyncTask extends Tasker { String code = (String) map.get("cVCCode"); String name = (String) map.get("cVCName"); - VendorClassRepository repository = getVendorClassRepository(); - VendorCatalog vendorCatalog = repository.findByCode(code).orElse(null); - if (vendorCatalog == null) { - vendorCatalog = new VendorCatalog(); - holder.info("新建供应商分类:" + code); - modified = true; - } + try { + VendorClassRepository repository = getVendorClassRepository(); + VendorCatalog vendorCatalog = repository.findByCode(code).orElse(null); + if (vendorCatalog == null) { + vendorCatalog = new VendorCatalog(); + holder.info("新建供应商分类:" + code); + modified = true; + } - if (!Objects.equals(vendorCatalog.getCode(), code)) { - vendorCatalog.setCode(code); - holder.info("供应商分类代码:" + vendorCatalog.getCode() + " -> " + code); - modified = true; - } - if (!Objects.equals(vendorCatalog.getName(), name)) { - vendorCatalog.setName(name); - holder.info("供应商分类名称:" + vendorCatalog.getName() + " -> " + name); - modified = true; - } + if (!Objects.equals(vendorCatalog.getCode(), code)) { + vendorCatalog.setCode(code); + holder.info("供应商分类代码:" + vendorCatalog.getCode() + " -> " + code); + modified = true; + } + if (!Objects.equals(vendorCatalog.getName(), name)) { + vendorCatalog.setName(name); + holder.info("供应商分类名称:" + vendorCatalog.getName() + " -> " + name); + modified = true; + } - if (modified) { - repository.save(vendorCatalog); + if (modified) { + repository.save(vendorCatalog); + } + } catch (Exception e) { + logger.error("同步供应商分类失败: " + code, e); + holder.error("同步供应商分类失败: " + e.getMessage()); + updateMessage("同步供应商分类失败: " + code + " - " + e.getMessage()); } } } diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/VendorSyncTask.java b/server/src/main/java/com/ecep/contract/cloud/u8/VendorSyncTask.java index 51f2c81..6b14b81 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/VendorSyncTask.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/VendorSyncTask.java @@ -3,6 +3,8 @@ package com.ecep.contract.cloud.u8; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Predicate; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -12,6 +14,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; +import com.ecep.contract.Message; import com.ecep.contract.MessageHolder; import com.ecep.contract.SpringApp; import com.ecep.contract.cloud.u8.ctx.CompanyCtx; @@ -25,17 +28,17 @@ import com.ecep.contract.ds.other.model.CloudYu; import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.ds.vendor.model.Vendor; import com.ecep.contract.ds.vendor.model.VendorEntity; +import com.ecep.contract.service.tasker.WebSocketServerTasker; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Setter; /** * 供应商同步任务 */ -public class VendorSyncTask extends AbstContractRepairTasker { +public class VendorSyncTask extends AbstContractRepairTasker implements WebSocketServerTasker { private static final Logger logger = LoggerFactory.getLogger(VendorSyncTask.class); private final VendorCtx vendorCtx = new VendorCtx(); - private VendorService vendorService; - private VendorEntityService vendorEntityService; private YongYouU8Repository repository; @Setter private YongYouU8Service yongYouU8Service; @@ -45,17 +48,22 @@ public class VendorSyncTask extends AbstContractRepairTasker { } private VendorService getVendorService() { - if (vendorService == null) { - vendorService = SpringApp.getBean(VendorService.class); - } - return vendorService; + return getCachedBean(VendorService.class); } private VendorEntityService getVendorEntityService() { - if (vendorEntityService == null) { - vendorEntityService = SpringApp.getBean(VendorEntityService.class); - } - return vendorEntityService; + return getCachedBean(VendorEntityService.class); + } + + @Override + public void init(JsonNode argsNode) { + // 初始化参数,当前任务不需要特殊参数 + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + repair(holder); + return null; } @Override diff --git a/server/src/main/java/com/ecep/contract/cloud/u8/YongYouU8Service.java b/server/src/main/java/com/ecep/contract/cloud/u8/YongYouU8Service.java index edbb04d..8501978 100644 --- a/server/src/main/java/com/ecep/contract/cloud/u8/YongYouU8Service.java +++ b/server/src/main/java/com/ecep/contract/cloud/u8/YongYouU8Service.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import com.ecep.contract.MessageHolder; import com.ecep.contract.util.SpecificationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -119,13 +120,13 @@ public class YongYouU8Service * @param cloudYu Cloud Yu 对象 * @return 更新的 Cloud Yu */ - @Caching(evict = { @CacheEvict(key = "#cloudYu.id") }) + @Caching(evict = {@CacheEvict(key = "#cloudYu.id")}) @Override public CloudYu save(CloudYu cloudYu) { return cloudYuRepository.save(cloudYu); } - @Caching(evict = { @CacheEvict(key = "#cloudYu.id") }) + @Caching(evict = {@CacheEvict(key = "#cloudYu.id")}) @Override public void delete(CloudYu vo) { CloudYu entity = cloudYuRepository.findById(vo.getId()).orElse(null); @@ -143,10 +144,15 @@ public class YongYouU8Service } } - public void resetTo(Company from, Company to) { + public void resetTo(Company from, Company to, MessageHolder holder) { List list = cloudYuRepository.findAllByCompanyId(from.getId()); + if (list.isEmpty()) { + holder.debug("No records to reset"); + return; + } for (CloudYu item : list) { item.setCompany(to); + holder.info("Reset #" + item.getId()); } cloudYuRepository.saveAll(list); } diff --git a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyContactService.java b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyContactService.java index a19be87..1ae678b 100644 --- a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyContactService.java +++ b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyContactService.java @@ -2,6 +2,7 @@ package com.ecep.contract.ds.company.service; import java.util.List; +import com.ecep.contract.MessageHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -51,15 +52,17 @@ public class CompanyContactService implements IEntityService, Qu return repository.save(contact); } - public void resetTo(Company from, Company to) { - // 曾用名 关联到 updater + public void resetTo(Company from, Company to, MessageHolder holder) { + // 联系人 关联到 updater List list = repository.findAllByCompany(from); if (list.isEmpty()) { + holder.info("无联系人"); return; } - for (CompanyContact oldName : list) { - oldName.setMemo(MyStringUtils.appendIfAbsent(oldName.getMemo(), "转自 " + from.getId())); - oldName.setCompany(to); + for (CompanyContact contact : list) { + contact.setMemo(MyStringUtils.appendIfAbsent(contact.getMemo(), "转自 " + from.getId())); + contact.setCompany(to); + holder.info("关联了 " + contact.getName() + " 至 " + to.getName()); } repository.saveAll(list); } diff --git a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyOldNameService.java b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyOldNameService.java index 98cb0b2..a4b94cd 100644 --- a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyOldNameService.java +++ b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyOldNameService.java @@ -4,6 +4,8 @@ import java.io.File; import java.time.LocalDate; import java.util.List; +import com.ecep.contract.Message; +import com.ecep.contract.MessageHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -163,17 +165,21 @@ public class CompanyOldNameService implements IEntityService, Qu * @param from from * @param to to */ - public void resetTo(Company from, Company to) { + public void resetTo(Company from, Company to, MessageHolder holder) { // 曾用名 关联到 to List list = companyOldNameRepository.findAllByCompanyId(from.getId()); if (list.isEmpty()) { + holder.info("无曾用名"); return; } for (CompanyOldName oldName : list) { oldName.setMemo(MyStringUtils.appendIfAbsent(oldName.getMemo(), "转自 " + from.getId())); oldName.setCompanyId(to.getId()); + holder.info("关联了 " + oldName.getName() + " 至 " + to.getName()); } companyOldNameRepository.saveAll(list); + + } public void deleteByCompany(Company company) { diff --git a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyService.java b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyService.java index ba023fe..0926aa2 100644 --- a/server/src/main/java/com/ecep/contract/ds/company/service/CompanyService.java +++ b/server/src/main/java/com/ecep/contract/ds/company/service/CompanyService.java @@ -10,6 +10,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import com.ecep.contract.ds.project.service.ProjectService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -92,6 +93,8 @@ public class CompanyService extends EntityService @Lazy @Autowired(required = false) private YongYouU8Service yongYouU8Service; + @Autowired + private ProjectService projectService; @Override protected CompanyRepository getRepository() { @@ -130,7 +133,7 @@ public class CompanyService extends EntityService protected Specification buildParameterSpecification(JsonNode paramsNode) { Specification spec = null; // field - spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name","uniscid", "abbName"); + spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name", "uniscid", "abbName"); return spec; } @@ -251,7 +254,7 @@ public class CompanyService extends EntityService // 合并重复的 for (Company company : removes) { try { - merge(company, updater); + merge(company, updater, null); } catch (Exception e) { logger.error("合并 {} -> {} 时发生错误:{}", company.toPrettyString(), updater.toPrettyString(), e.getMessage(), e); @@ -275,11 +278,11 @@ public class CompanyService extends EntityService * * 或者 把关联数据转移到其他公司 *
    - *
  • {@link VendorService#resetTo(Company, Company)}
  • - *
  • {@link CustomerService#resetTo(Company, Company)}
  • + *
  • {@link VendorService#resetTo(Company, Company, MessageHolder)}
  • + *
  • {@link CustomerService#mergeTo(Company, Company)}
  • *
  • {@link CompanyOldNameService#resetTo(Company, Company)}
  • *
  • {@link CompanyContactService#resetTo(Company, Company)}
  • - *
  • {@link ContractService#resetTo(Company, Company)}
  • + *
  • {@link ContractService#mergeTo(Company, Company, MessageHolder)}
  • *
  • {@link CompanyContactService#resetTo(Company, Company)}
  • *
*

@@ -323,20 +326,20 @@ public class CompanyService extends EntityService * *

*/ - public void merge(Company from, Company to) { - // cloudRkService.findById(from.getId()); - cloudRkService.resetTo(from, to); - cloudTycService.resetTo(from, to); - yongYouU8Service.resetTo(from, to); + public void merge(Company from, Company to, MessageHolder holder) { + cloudRkService.resetTo(from, to, holder); + cloudTycService.resetTo(from, to, holder); + yongYouU8Service.resetTo(from, to, holder); - companyOldNameService.resetTo(from, to); - companyContactService.resetTo(from, to); + companyOldNameService.resetTo(from, to, holder); + companyContactService.resetTo(from, to, holder); // 供应商和客户 - vendorService.resetTo(from, to); - companyCustomerService.resetTo(from, to); + vendorService.resetTo(from, to, holder); + companyCustomerService.mergeTo(from, to, holder); - contractService.resetTo(from, to); - companyContactService.resetTo(from, to); + contractService.mergeTo(from, to, holder); + + projectService.mergeTo(from, to, holder); repository.delete(from); if (logger.isInfoEnabled()) { logger.info("Merge {} to {}", from, to); @@ -428,7 +431,6 @@ public class CompanyService extends EntityService * * @param company 要验证的公司 * @param verifyDate 验证日期 - * @param status 状态输出 */ public boolean verifyEnterpriseStatus(Company company, LocalDate verifyDate, MessageHolder holder) { // 检查营业状态 @@ -506,7 +508,7 @@ public class CompanyService extends EntityService } public Predicate buildSearchPredicate(String searchText, Path root, CriteriaQuery query, - CriteriaBuilder builder) { + CriteriaBuilder builder) { return builder.or( builder.like(root.get("name"), "%" + searchText + "%"), builder.like(root.get("shortName"), "%" + searchText + "%"), diff --git a/server/src/main/java/com/ecep/contract/ds/company/tasker/CompanyMergeServerTasker.java b/server/src/main/java/com/ecep/contract/ds/company/tasker/CompanyMergeServerTasker.java new file mode 100644 index 0000000..541f786 --- /dev/null +++ b/server/src/main/java/com/ecep/contract/ds/company/tasker/CompanyMergeServerTasker.java @@ -0,0 +1,102 @@ +package com.ecep.contract.ds.company.tasker; + +import com.ecep.contract.MessageHolder; +import com.ecep.contract.ds.company.model.Company; +import com.ecep.contract.ds.company.service.CompanyService; +import com.ecep.contract.service.tasker.WebSocketServerTasker; +import com.ecep.contract.ui.Tasker; +import com.ecep.contract.vo.CompanyVo; +import com.fasterxml.jackson.databind.JsonNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +/** + * 公司合并任务 + * 用于通过WebSocket执行公司合并操作 + */ + +public class CompanyMergeServerTasker extends Tasker implements WebSocketServerTasker { + private static final Logger logger = LoggerFactory.getLogger(CompanyMergeServerTasker.class); + private CompanyVo company; + private List nameList; + + private CompanyService companyService; + + @Override + public void init(JsonNode argsNode) { + companyService = getBean(CompanyService.class); + // 从第一个参数获取公司ID并查找公司 + company = companyService.findById(argsNode.get(0).asInt()); + + // 从第二个参数获取名称列表 + JsonNode nameListNode = argsNode.get(1); + nameList = new ArrayList<>(); + + // 遍历JSON数组,将每个元素添加到nameList中 + if (nameListNode != null && nameListNode.isArray()) { + for (JsonNode nameNode : nameListNode) { + nameList.add(nameNode.asText()); + } + } + } + + @Override + protected Object execute(MessageHolder holder) throws Exception { + HashSet nameSet = new HashSet<>(nameList); + nameSet.add(company.getName()); + + int size = nameSet.size(); + int count = 1; + int merge = 0; + + + HashMap targetCompanyMap = new HashMap<>(); + for (String name : nameSet) { + MessageHolder sub = holder.sub(count + "/" + size + ":" + name + ">"); + if (name == null || name.isEmpty()) { + sub.warn("名称为空"); + continue; + } + + List list = companyService.findAllByName(name); + for (Company company : list) { + sub.debug("找到 " + company.getName()); + targetCompanyMap.put(company.getId(), company); + } + count++; + } + targetCompanyMap.remove(this.company.getId()); + + if (targetCompanyMap.isEmpty()) { + holder.warn("没有需要并户的公司"); + return null; + } + + // + Company targetCompany = companyService.getById(this.company.getId()); + for (Company company : targetCompanyMap.values()) { + try { + companyService.merge(company, targetCompany, holder); + holder.info("并户 " + company.getName() + "[" + company.getId() + "] 到当前公司"); + merge++; + } catch (Exception e) { + holder.warn("合并 " + company.getName() + " -> " + targetCompany.getName() + " 失败: " + e.getMessage()); + logger.warn("合并 {} -> {} 失败: {}", company.getName(), targetCompany.getName(), e.getMessage(), e); + } + } + + if (merge == 0) { + holder.info("没有需要并户的公司"); + } else { + holder.info("完成合并操作,共合并 " + merge + " 个公司"); + } + + updateTitle("公司合并任务完成"); + return null; + } +} \ No newline at end of file diff --git a/server/src/main/java/com/ecep/contract/ds/contract/service/ContractService.java b/server/src/main/java/com/ecep/contract/ds/contract/service/ContractService.java index c0673ec..cb4a52e 100644 --- a/server/src/main/java/com/ecep/contract/ds/contract/service/ContractService.java +++ b/server/src/main/java/com/ecep/contract/ds/contract/service/ContractService.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.ecep.contract.*; import com.ecep.contract.ds.contract.model.ContractItem; import com.ecep.contract.vo.ContractCatalogVo; import jakarta.persistence.criteria.Root; @@ -25,11 +26,6 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import com.ecep.contract.ContractPayWay; -import com.ecep.contract.EntityService; -import com.ecep.contract.IEntityService; -import com.ecep.contract.QueryService; -import com.ecep.contract.SpringApp; import com.ecep.contract.constant.ContractConstant; import com.ecep.contract.ds.company.service.CompanyService; import com.ecep.contract.ds.contract.repository.ContractRepository; @@ -420,14 +416,16 @@ public class ContractService extends EntityService list = contractRepository.findAllByCompanyId(from.getId()); if (list.isEmpty()) { + holder.info("没有合同需要处理"); return; } for (Contract contract : list) { contract.setDescription(MyStringUtils.appendIfAbsent(contract.getDescription(), "转自 " + from.getId())); contract.setCompany(to); + holder.info("Reset #" + contract.getId()); } contractRepository.saveAll(list); } diff --git a/server/src/main/java/com/ecep/contract/ds/customer/service/CustomerService.java b/server/src/main/java/com/ecep/contract/ds/customer/service/CustomerService.java index 085f1d4..05e737f 100644 --- a/server/src/main/java/com/ecep/contract/ds/customer/service/CustomerService.java +++ b/server/src/main/java/com/ecep/contract/ds/customer/service/CustomerService.java @@ -356,7 +356,7 @@ public class CustomerService extends CompanyBasicService * @param from 要转移的客户信息所属的公司 * @param to 要转移到的公司 */ - public void resetTo(Company from, Company to) { + public void mergeTo(Company from, Company to, MessageHolder holder) { // 这里使用Optional对象来处理可能为空的情况 Optional fromCustomer = repository.findByCompany(from); if (fromCustomer.isEmpty()) { @@ -373,7 +373,7 @@ public class CustomerService extends CompanyBasicService } // 把 fromCustomer 信息合并到 toCustomer - resetTo(fromCustomer.get(), toCustomer.get()); + mergeTo(fromCustomer.get(), toCustomer.get(), holder); } @@ -383,7 +383,7 @@ public class CustomerService extends CompanyBasicService * @param from 源客户对象 * @param to 目标客户对象 */ - public void resetTo(CompanyCustomer from, CompanyCustomer to) { + public void mergeTo(CompanyCustomer from, CompanyCustomer to, MessageHolder holder) { // file companyCustomerFileService.resetTo(from, to); // entity diff --git a/server/src/main/java/com/ecep/contract/ds/other/controller/EmployeeRoleController.java b/server/src/main/java/com/ecep/contract/ds/other/controller/EmployeeRoleController.java index dbc5d45..7361dac 100644 --- a/server/src/main/java/com/ecep/contract/ds/other/controller/EmployeeRoleController.java +++ b/server/src/main/java/com/ecep/contract/ds/other/controller/EmployeeRoleController.java @@ -2,6 +2,7 @@ package com.ecep.contract.ds.other.controller; import java.util.Map; +import com.ecep.contract.vo.FunctionVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -73,7 +74,7 @@ public class EmployeeRoleController { } @RequestMapping("/getFunctionsByRoleId") - public java.util.List getFunctionsByRoleId(Integer roleId) { + public java.util.List getFunctionsByRoleId(Integer roleId) { return employeeRoleService.getFunctionsByRoleId(roleId); } } \ No newline at end of file diff --git a/server/src/main/java/com/ecep/contract/ds/other/service/EmployeeRoleService.java b/server/src/main/java/com/ecep/contract/ds/other/service/EmployeeRoleService.java index 41b3457..981ba0d 100644 --- a/server/src/main/java/com/ecep/contract/ds/other/service/EmployeeRoleService.java +++ b/server/src/main/java/com/ecep/contract/ds/other/service/EmployeeRoleService.java @@ -3,6 +3,7 @@ package com.ecep.contract.ds.other.service; import java.util.List; import java.util.Optional; +import com.ecep.contract.vo.FunctionVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; @@ -36,6 +37,9 @@ public class EmployeeRoleService implements IEntityService, QueryS @Lazy @Autowired private EmployeeRoleRepository roleRepository; + @Lazy + @Autowired + private FunctionService functionService; @Cacheable(key = "#p0") public EmployeeRoleVo findById(Integer id) { @@ -95,15 +99,31 @@ public class EmployeeRoleService implements IEntityService, QueryS } @Transactional - public List getFunctionsByRoleId(int roleId) { + public List getFunctionsByRoleId(Integer roleId) { Optional optional = roleRepository.findById(roleId); if (optional.isPresent()) { EmployeeRole role = optional.get(); - return role.getFunctions().stream().toList(); + return role.getFunctions().stream().map(Function::toVo).toList(); } return null; } + @Transactional + public void saveRoleFunctions(Integer roleId, FunctionVo[] functions) { + Optional optional = roleRepository.findById(roleId); + if (optional.isEmpty()) { + throw new RuntimeException("角色不存在"); + } + EmployeeRole role = optional.get(); + java.util.List list = new java.util.ArrayList<>(); + for (FunctionVo functionVo : functions) { + Function func = functionService.getById(functionVo.getId()); + list.add(func); + } + role.setFunctions(list); + save(role); + } + @Override public void updateByVo(EmployeeRole role, EmployeeRoleVo vo) { role.setCode(vo.getCode()); diff --git a/server/src/main/java/com/ecep/contract/ds/project/repository/ProjectRepository.java b/server/src/main/java/com/ecep/contract/ds/project/repository/ProjectRepository.java index 4a75d06..2034a10 100644 --- a/server/src/main/java/com/ecep/contract/ds/project/repository/ProjectRepository.java +++ b/server/src/main/java/com/ecep/contract/ds/project/repository/ProjectRepository.java @@ -1,5 +1,6 @@ package com.ecep.contract.ds.project.repository; +import java.util.List; import java.util.Optional; import org.springframework.stereotype.Repository; @@ -30,4 +31,6 @@ public interface ProjectRepository extends Optional findByName(String name); Optional findByCode(String code); + + List findAllByCustomerId(Integer id); } diff --git a/server/src/main/java/com/ecep/contract/ds/project/service/ProjectService.java b/server/src/main/java/com/ecep/contract/ds/project/service/ProjectService.java index 7631738..e88c456 100644 --- a/server/src/main/java/com/ecep/contract/ds/project/service/ProjectService.java +++ b/server/src/main/java/com/ecep/contract/ds/project/service/ProjectService.java @@ -8,6 +8,8 @@ import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.ecep.contract.MessageHolder; +import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.vo.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -415,4 +417,16 @@ public class ProjectService } + public void mergeTo(Company from, Company to, MessageHolder holder) { + List list = projectRepository.findAllByCustomerId(from.getId()); + if (list.isEmpty()) { + holder.info("没有项目需要处理"); + return; + } + for (Project project : list) { + project.setCustomer(to); + holder.info("Reset #" + project.getId()); + } + projectRepository.saveAll(list); + } } diff --git a/server/src/main/java/com/ecep/contract/ds/vendor/converter/StringToVendorConverter.java b/server/src/main/java/com/ecep/contract/ds/vendor/converter/StringToVendorConverter.java new file mode 100644 index 0000000..9f0d01f --- /dev/null +++ b/server/src/main/java/com/ecep/contract/ds/vendor/converter/StringToVendorConverter.java @@ -0,0 +1,24 @@ +package com.ecep.contract.ds.vendor.converter; + +import org.springframework.lang.Nullable; + +import com.ecep.contract.ds.vendor.service.VendorService; +import com.ecep.contract.vo.VendorVo; + +public class StringToVendorConverter implements org.springframework.core.convert.converter.Converter { + + private VendorService service; + + public StringToVendorConverter(VendorService service) { + this.service = service; + } + + @Override + @Nullable + public VendorVo convert(String string) { + if (string == null || string.trim().isEmpty()) { + return null; + } + return service.findById(Integer.parseInt(string)); + } +} diff --git a/server/src/main/java/com/ecep/contract/ds/vendor/service/VendorService.java b/server/src/main/java/com/ecep/contract/ds/vendor/service/VendorService.java index 97d43a7..f863d24 100644 --- a/server/src/main/java/com/ecep/contract/ds/vendor/service/VendorService.java +++ b/server/src/main/java/com/ecep/contract/ds/vendor/service/VendorService.java @@ -31,6 +31,7 @@ import org.springframework.util.StringUtils; import com.ecep.contract.IEntityService; import com.ecep.contract.MessageHolder; import com.ecep.contract.QueryService; +import com.ecep.contract.SpringApp; import com.ecep.contract.VendorFileType; import com.ecep.contract.VendorType; import com.ecep.contract.constant.CompanyVendorConstant; @@ -44,6 +45,7 @@ import com.ecep.contract.ds.vendor.repository.VendorTypeLocalRepository; import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.model.CompanyBasicFile; import com.ecep.contract.ds.contract.model.Contract; +import com.ecep.contract.ds.vendor.converter.StringToVendorConverter; import com.ecep.contract.ds.vendor.model.Vendor; import com.ecep.contract.model.VendorCatalog; import com.ecep.contract.ds.vendor.model.VendorEntity; @@ -57,6 +59,7 @@ import com.ecep.contract.util.SpecificationUtils; import com.ecep.contract.vo.VendorVo; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.annotation.PostConstruct; import jakarta.persistence.criteria.Path; @Lazy @@ -89,6 +92,12 @@ public class VendorService extends CompanyBasicService @Autowired private VendorClassRepository vendorClassRepository; + @PostConstruct + public void init() { + SpringApp.getBean(org.springframework.core.convert.converter.ConverterRegistry.class) + .addConverter(new StringToVendorConverter(this)); + } + @Cacheable(key = "#p0") public VendorVo findById(Integer id) { return repository.findById(id).map(Vendor::toVo).orElse(null); @@ -226,7 +235,7 @@ public class VendorService extends CompanyBasicService @SuppressWarnings("unchecked") @Override protected > boolean fillFileAsDefaultType(F dbFile, File file, - Consumer status) { + Consumer status) { dbFile.setType((T) VendorFileType.General); fillFile(dbFile, file, null, status); vendorFileService.save((VendorFile) dbFile); @@ -236,7 +245,7 @@ public class VendorService extends CompanyBasicService @SuppressWarnings("unchecked") @Override protected > F fillFileType(File file, List fileList, - Consumer status) { + Consumer status) { VendorFile vendorFile = new VendorFile(); vendorFile.setType(VendorFileType.General); vendorFile.setFilePath(file.getAbsolutePath()); @@ -257,7 +266,7 @@ public class VendorService extends CompanyBasicService @Override protected > boolean fillFileAsEvaluationFile(F customerFile, File file, - List fileList, Consumer status) { + List fileList, Consumer status) { boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status); // 当评价表有日期,并且未设审核时 boolean valid = FileUtils.isArchiveFile(customerFile.getFilePath()) && customerFile.getSignDate() != null; @@ -428,7 +437,7 @@ public class VendorService extends CompanyBasicService return dir; } - public void resetTo(Company from, Company to) { + public void resetTo(Company from, Company to, MessageHolder holder) { Optional optional = repository.findByCompany(from); optional.ifPresent(companyVendor -> { companyVendor diff --git a/server/src/main/java/com/ecep/contract/service/WebSocketServerCallbackManager.java b/server/src/main/java/com/ecep/contract/service/WebSocketServerCallbackManager.java index 2050642..4e974bb 100644 --- a/server/src/main/java/com/ecep/contract/service/WebSocketServerCallbackManager.java +++ b/server/src/main/java/com/ecep/contract/service/WebSocketServerCallbackManager.java @@ -121,6 +121,7 @@ public class WebSocketServerCallbackManager { return method.invoke(service); } + // 第一个参数不是数组时 if (!argumentsNode.get(0).isArray()) { Class parameterType = Class.forName(argumentsNode.get(1).asText()); Object arg = objectMapper.treeToValue(argumentsNode.get(0), parameterType); diff --git a/server/src/main/java/com/ecep/contract/service/tasker/WebSocketServerTasker.java b/server/src/main/java/com/ecep/contract/service/tasker/WebSocketServerTasker.java index 81c3e81..09efdb0 100644 --- a/server/src/main/java/com/ecep/contract/service/tasker/WebSocketServerTasker.java +++ b/server/src/main/java/com/ecep/contract/service/tasker/WebSocketServerTasker.java @@ -13,12 +13,23 @@ import com.fasterxml.jackson.databind.JsonNode; * 定义了所有通过WebSocket与客户端通信的任务的通用方法 * 包括任务名称、初始化参数、设置会话、更新消息、更新标题、更新进度等操作 *

- * 所有通过WebSocket与客户端通信的任务类都应实现此接口, 文档参考 .trace/rules/server_task_rules.md - * tips:检查是否在 tasker_mapper.json 中注册 + * 所有通过WebSocket与客户端通信的任务类都应实现此接口, 通常应继承Tasker类并实现此接口 + *

+ * 实现注意事项: + * 1. 服务器端任务实现应继承Tasker基类,该基类已提供大部分通信功能的实现 + * 2. 不要在子类中重复实现setMessageHandler、setTitleHandler、setPropertyHandler和setProgressHandler方法,这些由Tasker基类提供 + * 3. 任务类应覆盖init方法以初始化参数和设置任务标题 + * 4. 任务类应覆盖execute方法实现具体业务逻辑,使用MessageHolder进行消息消息反馈 + * 5. 任务执行时使用updateProgress方法进行进度反馈 + * 6. 任务名称应与客户端对应的Tasker类名保持一致,以确保正确的通信映射 + *

+ * 注册要求: + * 任务类必须在tasker_mapper.json中注册,以确保WebSocketServerTaskManager能够正确识别和实例化 */ public interface WebSocketServerTasker extends Callable { /** * 初始化任务参数 + * 任务类应在init方法中解析argsNode, 初始化任务参数 * * @param argsNode 任务参数的JSON节点 */ diff --git a/server/src/main/java/com/ecep/contract/ui/Tasker.java b/server/src/main/java/com/ecep/contract/ui/Tasker.java index 0b080a5..e03f7a2 100644 --- a/server/src/main/java/com/ecep/contract/ui/Tasker.java +++ b/server/src/main/java/com/ecep/contract/ui/Tasker.java @@ -1,6 +1,5 @@ package com.ecep.contract.ui; -import java.util.HashMap; import java.util.Locale; import java.util.function.BiConsumer; import java.util.logging.Level; @@ -11,7 +10,6 @@ import org.springframework.beans.BeansException; import com.ecep.contract.Message; import com.ecep.contract.MessageHolder; -import com.ecep.contract.SpringApp; import com.ecep.contract.ds.company.service.CompanyService; import com.ecep.contract.ds.other.service.EmployeeService; import com.ecep.contract.ds.other.service.SysConfService; @@ -22,6 +20,27 @@ import com.ecep.contract.util.DefaultBeanContext; import lombok.Getter; import lombok.Setter; +/** + * Tasker基类,提供任务执行和通信的核心功能 + *

+ * 该基类实现了任务执行框架,提供了以下核心功能: + * 1. 任务执行环境(call方法) + * 2. 消息处理和进度更新机制 + * 3. Spring Bean获取和缓存功能 + * 4. 当前用户和本地化支持 + *

+ * 主要组件: + * - messageHandler: 处理消息通知 + * - titleHandler: 处理标题更新 + * - progressHandler: 处理进度更新 + * - propertyHandler: 处理属性更新 + *

+ * 使用说明: + * 1. 子类应继承此类并实现execute方法,实现具体业务逻辑 + * 2. 使用updateMessage、updateProgress等方法进行状态反馈 + * 3. 对于WebSocket任务,还应实现WebSocketServerTasker接口 + * 4. 子类通常不需要覆盖setter方法,这些由框架自动设置 + */ public abstract class Tasker implements java.util.concurrent.Callable, BeanContext { private static final Logger logger = LoggerFactory.getLogger(Tasker.class); @Setter diff --git a/server/src/main/resources/tasker_mapper.json b/server/src/main/resources/tasker_mapper.json index 6a7e534..7d48e86 100644 --- a/server/src/main/resources/tasker_mapper.json +++ b/server/src/main/resources/tasker_mapper.json @@ -20,7 +20,15 @@ "ContractRepairAllTask": "com.ecep.contract.ds.contract.tasker.ContractRepairAllTasker", "ContractFilesRebuildAllTasker": "com.ecep.contract.ds.contract.tasker.ContractFilesRebuildAllTasker", "ContractFilesRebuildTasker": "com.ecep.contract.ds.contract.tasker.ContractFilesRebuildTasker", - "EmployeesSyncTask": "com.ecep.contract.cloud.u8.EmployeesSyncTask" + "EmployeesSyncTask": "com.ecep.contract.cloud.u8.EmployeesSyncTask", + "CompanyMergeTask": "com.ecep.contract.ds.company.tasker.CompanyMergeServerTasker", + "VendorSyncTask": "com.ecep.contract.cloud.u8.VendorSyncTask", + "CustomerSyncTask": "com.ecep.contract.cloud.u8.CustomerSyncTask", + "CustomerClassSyncTask": "com.ecep.contract.cloud.u8.CustomerClassSyncTask", + "VendorClassSyncTask": "com.ecep.contract.cloud.u8.VendorClassSyncTask", + "ContractKindSyncTask": "com.ecep.contract.cloud.u8.ContractKindSyncTask", + "ContractTypeSyncTask": "com.ecep.contract.cloud.u8.ContractTypeSyncTask", + "ContractGroupSyncTask": "com.ecep.contract.cloud.u8.ContractGroupSyncTask" }, "descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用" } \ No newline at end of file