Compare commits
4 Commits
235269f86f
...
e761990ebf
| Author | SHA1 | Date | |
|---|---|---|---|
| e761990ebf | |||
| 1cb0edbd07 | |||
| dd49c3927a | |||
| 7d4961dae4 |
@@ -155,3 +155,69 @@ public class [业务]WindowController extends AbstEntityController<[Vo类型], [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 13. TableCell 工厂模式规范
|
||||||
|
|
||||||
|
### 13.1 工厂方法命名
|
||||||
|
|
||||||
|
- TableCell类应提供静态工厂方法`forTableColumn`,用于创建单元格工厂
|
||||||
|
- 方法命名必须为`forTableColumn`,保持一致性
|
||||||
|
|
||||||
|
### 13.2 工厂方法参数
|
||||||
|
|
||||||
|
- 工厂方法应接收服务层参数,如`BankService`
|
||||||
|
- 参数类型应与TableCell构造函数所需的服务类型一致
|
||||||
|
|
||||||
|
### 13.3 工厂方法返回类型
|
||||||
|
|
||||||
|
- 返回类型应为`Callback<javafx.scene.control.TableColumn<V, T>, javafx.scene.control.TableCell<V, T>>`
|
||||||
|
- 其中`V`为ViewModel类型,`T`为单元格值类型
|
||||||
|
|
||||||
|
### 13.4 工厂方法实现
|
||||||
|
|
||||||
|
- 在工厂方法内部,创建并返回TableCell实例
|
||||||
|
- 使用泛型参数确保类型安全
|
||||||
|
|
||||||
|
### 13.5 使用方式
|
||||||
|
|
||||||
|
- 在设置表格列的单元格工厂时,应调用TableCell的静态工厂方法
|
||||||
|
- 避免直接使用`new TableCell<>(service)`的方式创建实例
|
||||||
|
|
||||||
|
### 13.6 示例代码
|
||||||
|
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 银行单元格
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class BankTableCell<T> extends AsyncUpdateTableCell<T, Integer, BankVo> {
|
||||||
|
/**
|
||||||
|
* 创建单元格工厂
|
||||||
|
*
|
||||||
|
* @param bankService 银行服务
|
||||||
|
* @return 单元格工厂
|
||||||
|
*/
|
||||||
|
public static <V> Callback<javafx.scene.control.TableColumn<V, Integer>, javafx.scene.control.TableCell<V, Integer>> forTableColumn(
|
||||||
|
BankService bankService) {
|
||||||
|
return param -> new BankTableCell<V>(bankService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BankTableCell(BankService service) {
|
||||||
|
setService(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BankService getServiceBean() {
|
||||||
|
return getBean(BankService.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.7 使用示例
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 推荐方式:使用工厂方法
|
||||||
|
column.setCellFactory(BankTableCell.forTableColumn(getBankService()));
|
||||||
|
|
||||||
|
// 不推荐方式:直接实例化
|
||||||
|
// column.setCellFactory(param -> new BankTableCell<>(getBankService()));
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>Contract-Manager</artifactId>
|
<artifactId>Contract-Manager</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>client</artifactId>
|
<artifactId>client</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ public class WebSocketClientService {
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private long readTimeout = 30000;
|
private long readTimeout = 30000;
|
||||||
private String webSocketUrl = "ws://localhost:8080/ws";
|
|
||||||
private boolean isActive = false; // 标记连接是否活跃
|
private boolean isActive = false; // 标记连接是否活跃
|
||||||
private ScheduledFuture<?> heartbeatTask; // 心跳任务
|
private ScheduledFuture<?> heartbeatTask; // 心跳任务
|
||||||
private ScheduledFuture<?> reconnectFuture; // 修改类型为CompletableFuture<Void>
|
private ScheduledFuture<?> reconnectFuture; // 修改类型为CompletableFuture<Void>
|
||||||
@@ -248,6 +247,8 @@ public class WebSocketClientService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 构建WebSocket请求,包含认证信息
|
// 构建WebSocket请求,包含认证信息
|
||||||
|
var myProperties = SpringApp.getBean(MyProperties.class);
|
||||||
|
String webSocketUrl = "ws://" + myProperties.getServerHost() + ":" + myProperties.getServerPort() + "/ws";
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(webSocketUrl)
|
.url(webSocketUrl)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -53,32 +53,25 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
if (service instanceof QueryService<T, TV> queryService) {
|
if (service instanceof QueryService<T, TV> queryService) {
|
||||||
setStatus("读取...");
|
setStatus("读取...");
|
||||||
loadedFuture = queryService.asyncFindById(viewModel.getId().get());
|
loadedFuture = queryService.asyncFindById(viewModel.getId().get());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
||||||
|
return getViewModelService().findById(viewModel.getId().get());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
loadedFuture.thenAccept(entity -> {
|
loadedFuture.thenAccept(entity -> {
|
||||||
// fixed, bind change if new view model create
|
// fixed, bind change if new view model create
|
||||||
if (viewModel != null) {
|
if (viewModel != null) {
|
||||||
|
updateViewModel(entity);
|
||||||
viewModel.bindListener();
|
viewModel.bindListener();
|
||||||
}
|
}
|
||||||
setStatus();
|
setStatus();
|
||||||
// BaseViewModel.updateInFxApplicationThread(entity, viewModel);
|
|
||||||
});
|
});
|
||||||
loadedFuture.exceptionally(ex -> {
|
loadedFuture.exceptionally(ex -> {
|
||||||
handleException("载入失败,#" + viewModel.getId().get(), ex);
|
handleException("载入失败,#" + viewModel.getId().get(), ex);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
|
||||||
T entity = getViewModelService().findById(viewModel.getId().get());
|
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
setStatus();
|
|
||||||
viewModel.update(entity);
|
|
||||||
});
|
|
||||||
viewModel.bindListener();
|
|
||||||
return entity;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
registerTabSkins();
|
registerTabSkins();
|
||||||
if (saveBtn != null) {
|
if (saveBtn != null) {
|
||||||
@@ -89,6 +82,10 @@ public abstract class AbstEntityController<T extends IdentityEntity, TV extends
|
|||||||
installTabSkins();
|
installTabSkins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateViewModel(T entity) {
|
||||||
|
BaseViewModel.updateInFxApplicationThread(entity, viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
public T getEntity() {
|
public T getEntity() {
|
||||||
return getLoadedFuture().join();
|
return getLoadedFuture().join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ecep.contract.controller.bank;
|
package com.ecep.contract.controller.bank;
|
||||||
|
|
||||||
|
import org.controlsfx.control.SearchableComboBox;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
@@ -34,7 +35,6 @@ public class BankManagerWindowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onCreateNewAction(ActionEvent event) {
|
public void onCreateNewAction(ActionEvent event) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReBuildFilesAction(ActionEvent event) {
|
public void onReBuildFilesAction(ActionEvent event) {
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import com.ecep.contract.controller.AbstEntityController;
|
||||||
|
import com.ecep.contract.service.ViewModelService;
|
||||||
|
import com.ecep.contract.util.FxmlPath;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
@@ -50,10 +53,12 @@ import javafx.stage.Modality;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
@Component
|
@Component
|
||||||
public class CompanyCustomerEvaluationFormFileWindowController extends BaseController {
|
@FxmlPath("/ui/company/customer/customer_evaluation_form.fxml")
|
||||||
|
public class CompanyCustomerEvaluationFormFileWindowController extends AbstEntityController<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
|
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerEvaluationFormFileWindowController.class);
|
||||||
|
|
||||||
public static void show(CompanyCustomerEvaluationFormFileVo saved, Window window) {
|
public static void show(CompanyCustomerEvaluationFormFileVo saved, Window window) {
|
||||||
@@ -61,15 +66,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void show(CompanyCustomerEvaluationFormFileViewModel viewModel, Window window) {
|
public static void show(CompanyCustomerEvaluationFormFileViewModel viewModel, Window window) {
|
||||||
String key = viewModel.getClass().getName() + "-" + viewModel.getId().get();
|
show(CompanyCustomerEvaluationFormFileWindowController.class, viewModel, window);
|
||||||
if (toFront(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FxmlUtils.newLoaderAsyncWithRunLater("/ui/company/customer/customer_evaluation_form.fxml", null, loader -> {
|
|
||||||
CompanyCustomerEvaluationFormFileWindowController controller = loader.getController();
|
|
||||||
controller.viewModel = viewModel;
|
|
||||||
controller.show(loader, window, Modality.NONE, key);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -91,8 +88,6 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
public ScrollPane leftPane;
|
public ScrollPane leftPane;
|
||||||
public Label totalCreditScoreLabel;
|
public Label totalCreditScoreLabel;
|
||||||
|
|
||||||
private CompanyCustomerEvaluationFormFileViewModel viewModel;
|
|
||||||
|
|
||||||
private final SimpleStringProperty catalogProperty = new SimpleStringProperty("");
|
private final SimpleStringProperty catalogProperty = new SimpleStringProperty("");
|
||||||
private final SimpleStringProperty levelProperty = new SimpleStringProperty("");
|
private final SimpleStringProperty levelProperty = new SimpleStringProperty("");
|
||||||
private final SimpleIntegerProperty score1Property = new SimpleIntegerProperty(-1);
|
private final SimpleIntegerProperty score1Property = new SimpleIntegerProperty(-1);
|
||||||
@@ -105,7 +100,9 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
|
|
||||||
private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
|
private SimpleObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
private CompletableFuture<CompanyCustomerEvaluationFormFileVo> loadedFuture;
|
private SimpleStringProperty filePathProperty = new SimpleStringProperty();
|
||||||
|
private SimpleStringProperty editFilePathProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Autowired
|
@Autowired
|
||||||
private CompanyCustomerFileService companyCustomerFileService;
|
private CompanyCustomerFileService companyCustomerFileService;
|
||||||
@@ -131,24 +128,27 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
getTitle().set("客户评估表单");
|
getTitle().set("客户评估表单");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShown(WindowEvent windowEvent) {
|
protected void registerTabSkins() {
|
||||||
super.onShown(windowEvent);
|
initializePane();
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("onShown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initializePane();
|
@Override
|
||||||
|
protected void updateViewModel(CompanyCustomerEvaluationFormFileVo entity) {
|
||||||
loadedFuture = CompletableFuture.supplyAsync(() -> {
|
super.updateViewModel(entity);
|
||||||
int id = viewModel.getId().get();
|
CustomerFileVo file = companyCustomerFileService.findById(entity.getCustomerFile());
|
||||||
CustomerFileVo customerFile = companyCustomerFileService.findById(id);
|
Platform.runLater(() -> {
|
||||||
CompanyCustomerEvaluationFormFileVo formFile = evaluationFormFileService.findByCustomerFile(customerFile);
|
filePathProperty.set(file.getFilePath());
|
||||||
Platform.runLater(() -> update(formFile));
|
editFilePathProperty.set(file.getEditFilePath());
|
||||||
return formFile;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewModelService<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> getViewModelService() {
|
||||||
|
return evaluationFormFileService;
|
||||||
|
}
|
||||||
|
|
||||||
BiConsumer<ToggleGroup, String> stringRadioGroupUpdater = (group, newValue) -> {
|
BiConsumer<ToggleGroup, String> stringRadioGroupUpdater = (group, newValue) -> {
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
for (Toggle toggle : group.getToggles()) {
|
for (Toggle toggle : group.getToggles()) {
|
||||||
@@ -259,8 +259,8 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
|
|
||||||
private void initializePane() {
|
private void initializePane() {
|
||||||
idField.textProperty().bind(viewModel.getId().asString());
|
idField.textProperty().bind(viewModel.getId().asString());
|
||||||
// filePathField.textProperty().bind(viewModel.getFilePath());
|
filePathField.textProperty().bind(filePathProperty);
|
||||||
// editFilePathField.textProperty().bind(viewModel.getEditFilePath());
|
editFilePathField.textProperty().bind(editFilePathProperty);
|
||||||
// signDateField.valueProperty().bindBidirectional(viewModel.getSignDate());
|
// signDateField.valueProperty().bindBidirectional(viewModel.getSignDate());
|
||||||
// validField.selectedProperty().bindBidirectional(viewModel.getValid());
|
// validField.selectedProperty().bindBidirectional(viewModel.getValid());
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ public class CompanyCustomerEvaluationFormFileWindowController extends BaseContr
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
imageView.imageProperty().bind(viewModel.getFilePath().map(path -> {
|
imageView.imageProperty().bind(filePathProperty.map(path -> {
|
||||||
if (FileUtils.withExtensions(path, FileUtils.PDF)) {
|
if (FileUtils.withExtensions(path, FileUtils.PDF)) {
|
||||||
File pdfFile = new File(path);
|
File pdfFile = new File(path);
|
||||||
try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) {
|
try (PDDocument pdDocument = Loader.loadPDF(pdfFile)) {
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ public class ProjectTabSkinBase extends AbstProjectBasedTabSkin implements TabSk
|
|||||||
|
|
||||||
controller.saleTypeField.valueProperty().addListener((observable, oldValue, newValue) -> {
|
controller.saleTypeField.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
Predicate<DeliverySignMethodVo> predicate = p -> {
|
Predicate<DeliverySignMethodVo> predicate = p -> {
|
||||||
|
if (newValue == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return p == null || Objects.equals(p.getSaleTypeId(), newValue.getId());
|
return p == null || Objects.equals(p.getSaleTypeId(), newValue.getId());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -88,8 +88,23 @@ public class ProjectCostWindowController
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void registerTabSkins() {
|
protected void registerTabSkins() {
|
||||||
|
|
||||||
registerTabSkin(baseInfoTab, tab -> new ProjectCostTabSkinBase(this));
|
registerTabSkin(baseInfoTab, tab -> new ProjectCostTabSkinBase(this));
|
||||||
registerTabSkin(itemTab, tab -> new ProjectCostTabSkinItems(this));
|
registerTabSkin(itemTab, tab -> new ProjectCostTabSkinItems(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateViewModel(ProjectCostVo entity) {
|
||||||
|
super.updateViewModel(entity);
|
||||||
|
var tabPane = baseInfoTab.getTabPane();
|
||||||
|
if (entity.getVersion() > 0) {
|
||||||
|
if (!tabPane.getTabs().contains(approvalTab)) {
|
||||||
|
tabPane.getTabs().add(approvalTab);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tabPane.getTabs().remove(approvalTab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package com.ecep.contract.controller.project.quotation;
|
package com.ecep.contract.controller.project.quotation;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import com.ecep.contract.DesktopUtils;
|
import com.ecep.contract.DesktopUtils;
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
import com.ecep.contract.controller.customer.CompanyCustomerEvaluationFormFileWindowController;
|
import com.ecep.contract.controller.customer.CompanyCustomerEvaluationFormFileWindowController;
|
||||||
@@ -11,21 +7,13 @@ import com.ecep.contract.controller.tab.AbstEntityBasedTabSkin;
|
|||||||
import com.ecep.contract.controller.tab.TabSkin;
|
import com.ecep.contract.controller.tab.TabSkin;
|
||||||
import com.ecep.contract.converter.CompanyStringConverter;
|
import com.ecep.contract.converter.CompanyStringConverter;
|
||||||
import com.ecep.contract.converter.EmployeeStringConverter;
|
import com.ecep.contract.converter.EmployeeStringConverter;
|
||||||
import com.ecep.contract.model.Employee;
|
import com.ecep.contract.service.*;
|
||||||
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
|
|
||||||
import com.ecep.contract.service.CompanyCustomerFileService;
|
|
||||||
import com.ecep.contract.service.CustomerService;
|
|
||||||
import com.ecep.contract.service.CompanyService;
|
|
||||||
import com.ecep.contract.service.ProjectQuotationService;
|
|
||||||
import com.ecep.contract.service.ProjectService;
|
|
||||||
import com.ecep.contract.util.ProxyUtils;
|
import com.ecep.contract.util.ProxyUtils;
|
||||||
import com.ecep.contract.util.UITools;
|
import com.ecep.contract.util.UITools;
|
||||||
import com.ecep.contract.vm.ProjectQuotationViewModel;
|
import com.ecep.contract.vm.ProjectQuotationViewModel;
|
||||||
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
||||||
import com.ecep.contract.vo.CompanyVo;
|
|
||||||
import com.ecep.contract.vo.ProjectQuotationVo;
|
import com.ecep.contract.vo.ProjectQuotationVo;
|
||||||
import com.ecep.contract.vo.ProjectVo;
|
import com.ecep.contract.vo.ProjectVo;
|
||||||
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
@@ -37,36 +25,22 @@ import javafx.util.converter.LocalDateTimeStringConverter;
|
|||||||
import javafx.util.converter.NumberStringConverter;
|
import javafx.util.converter.NumberStringConverter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目报价单
|
||||||
|
*/
|
||||||
public class ProjectQuotationTabSkinBase
|
public class ProjectQuotationTabSkinBase
|
||||||
extends AbstEntityBasedTabSkin<ProjectQuotationWindowController, ProjectQuotationVo, ProjectQuotationViewModel>
|
extends AbstEntityBasedTabSkin<ProjectQuotationWindowController, ProjectQuotationVo, ProjectQuotationViewModel>
|
||||||
implements TabSkin {
|
implements TabSkin {
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private LocalDateTimeStringConverter localDateTimeStringConverter;
|
private LocalDateTimeStringConverter localDateTimeStringConverter;
|
||||||
@Setter
|
|
||||||
private ProjectQuotationService projectQuotationService;
|
|
||||||
@Setter
|
|
||||||
private LocalDateStringConverter localDateStringConverter;
|
|
||||||
@Setter
|
|
||||||
private EmployeeStringConverter employeeStringConverter;
|
|
||||||
@Setter
|
|
||||||
private CompanyStringConverter companyStringConverter;
|
|
||||||
@Setter
|
|
||||||
private CompanyService companyService;
|
|
||||||
@Setter
|
|
||||||
private CustomerService customerService;
|
|
||||||
@Setter
|
|
||||||
private CompanyCustomerFileService customerFileService;
|
|
||||||
@Setter
|
|
||||||
private CompanyCustomerEvaluationFormFileService evaluationFormFileService;
|
|
||||||
@Setter
|
|
||||||
private ProjectService projectService;
|
|
||||||
|
|
||||||
private ProjectService getProjectService() {
|
private ProjectService getProjectService() {
|
||||||
if (projectService == null) {
|
return getCachedBean(ProjectService.class);
|
||||||
projectService = getBean(ProjectService.class);
|
|
||||||
}
|
|
||||||
return projectService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectQuotationTabSkinBase(ProjectQuotationWindowController controller) {
|
public ProjectQuotationTabSkinBase(ProjectQuotationWindowController controller) {
|
||||||
@@ -133,7 +107,10 @@ public class ProjectQuotationTabSkinBase
|
|||||||
new NumberStringConverter(getLocale()));
|
new NumberStringConverter(getLocale()));
|
||||||
|
|
||||||
UITools.autoCompletion(controller.evaluationFileField, viewModel.getEvaluationFile(),
|
UITools.autoCompletion(controller.evaluationFileField, viewModel.getEvaluationFile(),
|
||||||
getEvaluationFormFileService());
|
getEvaluationFormFileService(), getEvaluationFormFileService().getStringConverter(), searchText -> {
|
||||||
|
var project = getProjectService().findById(getEntity().getProject());
|
||||||
|
return getEvaluationFormFileService().searchByCompany(project.getCustomerId(), searchText);
|
||||||
|
});
|
||||||
|
|
||||||
controller.authorizationFileField.textProperty().bind(viewModel.getAuthorizationFile().map(File::getName));
|
controller.authorizationFileField.textProperty().bind(viewModel.getAuthorizationFile().map(File::getName));
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,9 @@ public class CompanyTabSkinBankAccount
|
|||||||
bankAccountSearchBtn.setOnAction(this::onTableRefreshAction);
|
bankAccountSearchBtn.setOnAction(this::onTableRefreshAction);
|
||||||
|
|
||||||
bankAccountTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
bankAccountTable_idColumn.setCellValueFactory(param -> param.getValue().getId());
|
||||||
|
|
||||||
bankAccountTable_bankColumn.setCellValueFactory(param -> param.getValue().getBankId());
|
bankAccountTable_bankColumn.setCellValueFactory(param -> param.getValue().getBankId());
|
||||||
bankAccountTable_bankColumn.setCellFactory(param -> new BankTableCell<>(getBankService()));
|
bankAccountTable_bankColumn.setCellFactory(BankTableCell.forTableColumn(getBankService()));
|
||||||
|
|
||||||
bankAccountTable_openingBankColumn.setCellValueFactory(param -> param.getValue().getOpeningBank());
|
bankAccountTable_openingBankColumn.setCellValueFactory(param -> param.getValue().getOpeningBank());
|
||||||
bankAccountTable_accountColumn.setCellValueFactory(param -> param.getValue().getAccount());
|
bankAccountTable_accountColumn.setCellValueFactory(param -> param.getValue().getAccount());
|
||||||
|
|||||||
@@ -164,18 +164,18 @@ public class CompanyTabSkinContract
|
|||||||
contractTable_startDateColumn.setCellValueFactory(param -> param.getValue().getStartDate());
|
contractTable_startDateColumn.setCellValueFactory(param -> param.getValue().getStartDate());
|
||||||
contractTable_endDateColumn.setCellValueFactory(param -> param.getValue().getEndDate());
|
contractTable_endDateColumn.setCellValueFactory(param -> param.getValue().getEndDate());
|
||||||
contractTable_setupPersonColumn.setCellValueFactory(param -> param.getValue().getSetupPerson());
|
contractTable_setupPersonColumn.setCellValueFactory(param -> param.getValue().getSetupPerson());
|
||||||
contractTable_setupPersonColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
|
contractTable_setupPersonColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
|
||||||
contractTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
|
contractTable_setupDateColumn.setCellValueFactory(param -> param.getValue().getSetupDate());
|
||||||
// contractTable_setupDateColumn.setSortable(true);
|
// contractTable_setupDateColumn.setSortable(true);
|
||||||
// contractTable_setupDateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
// contractTable_setupDateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||||
contractTable_inurePersonColumn.setCellValueFactory(param -> param.getValue().getInurePerson());
|
contractTable_inurePersonColumn.setCellValueFactory(param -> param.getValue().getInurePerson());
|
||||||
contractTable_inurePersonColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
|
contractTable_inurePersonColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
|
||||||
contractTable_inureDateColumn.setCellValueFactory(param -> param.getValue().getInureDate());
|
contractTable_inureDateColumn.setCellValueFactory(param -> param.getValue().getInureDate());
|
||||||
|
|
||||||
contractTable_varyPersonColumn.setCellValueFactory(param -> param.getValue().getVaryPerson());
|
contractTable_varyPersonColumn.setCellValueFactory(param -> param.getValue().getVaryPerson());
|
||||||
contractTable_varyPersonColumn.setCellFactory(param -> new EmployeeTableCell<>(getEmployeeService()));
|
contractTable_varyPersonColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
|
||||||
|
|
||||||
contractTable_varyDateColumn.setCellValueFactory(param -> param.getValue().getVaryDate());
|
contractTable_varyDateColumn.setCellValueFactory(param -> param.getValue().getVaryDate());
|
||||||
contractTable_createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
|
contractTable_createdColumn.setCellValueFactory(param -> param.getValue().getCreated());
|
||||||
|
|||||||
@@ -1,20 +1,39 @@
|
|||||||
package com.ecep.contract.controller.table.cell;
|
package com.ecep.contract.controller.table.cell;
|
||||||
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.service.BankService;
|
import com.ecep.contract.service.BankService;
|
||||||
import com.ecep.contract.vo.BankVo;
|
import com.ecep.contract.vo.BankVo;
|
||||||
|
import javafx.util.Callback;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import static com.ecep.contract.SpringApp.getBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 银行单元格
|
||||||
|
*/
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class BankTableCell<T> extends AsyncUpdateTableCell<T, Integer, BankVo> {
|
public class BankTableCell<T> extends AsyncUpdateTableCell<T, Integer, BankVo> {
|
||||||
|
/**
|
||||||
|
* 创建单元格工厂
|
||||||
|
*
|
||||||
|
* @param bankService 银行服务
|
||||||
|
* @return 单元格工厂
|
||||||
|
*/
|
||||||
|
public static <V> Callback<javafx.scene.control.TableColumn<V, Integer>, javafx.scene.control.TableCell<V, Integer>> forTableColumn(
|
||||||
|
BankService bankService) {
|
||||||
|
return param -> new BankTableCell<V>(bankService);
|
||||||
|
}
|
||||||
|
|
||||||
public BankTableCell(BankService service) {
|
public BankTableCell(BankService service) {
|
||||||
setService(service);
|
setService(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BankService getServiceBean() {
|
protected BankService getServiceBean() {
|
||||||
return SpringApp.getBean(BankService.class);
|
return getBean(BankService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(BankVo entity) {
|
||||||
|
return getService().getStringConverter().toString(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.ecep.contract.converter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import com.ecep.contract.SpringApp;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerEvaluationFormFileService;
|
||||||
|
import com.ecep.contract.service.CompanyCustomerFileService;
|
||||||
|
import com.ecep.contract.vo.CompanyCustomerEvaluationFormFileVo;
|
||||||
|
import com.ecep.contract.vo.CustomerFileVo;
|
||||||
|
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompanyCustomerEvaluationFormFileVo的StringConverter实现,用于JavaFX控件中的显示和转换
|
||||||
|
*/
|
||||||
|
public class CompanyCustomerEvaluationFormFileStringConverter
|
||||||
|
extends StringConverter<CompanyCustomerEvaluationFormFileVo> {
|
||||||
|
private final CompanyCustomerEvaluationFormFileService service;
|
||||||
|
private CompanyCustomerFileService customerFileService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param service CompanyCustomerEvaluationFormFileService实例
|
||||||
|
*/
|
||||||
|
public CompanyCustomerEvaluationFormFileStringConverter(CompanyCustomerEvaluationFormFileService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompanyCustomerFileService getCustomerFileService() {
|
||||||
|
if (customerFileService == null) {
|
||||||
|
customerFileService = SpringApp.getBean(CompanyCustomerFileService.class);
|
||||||
|
}
|
||||||
|
return customerFileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将CompanyCustomerEvaluationFormFileVo对象转换为字符串
|
||||||
|
*
|
||||||
|
* @param formFile CompanyCustomerEvaluationFormFileVo对象
|
||||||
|
* @return 转换后的字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString(CompanyCustomerEvaluationFormFileVo formFile) {
|
||||||
|
if (formFile == null || formFile.getCustomerFile() == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomerFileVo customerFile = getCustomerFileService().findById(formFile.getCustomerFile());
|
||||||
|
if (customerFile == null || !StringUtils.hasText(customerFile.getFilePath())) {
|
||||||
|
return "#" + formFile.getCustomerFile();
|
||||||
|
}
|
||||||
|
File file = new File(customerFile.getFilePath());
|
||||||
|
return file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串转换为CompanyCustomerEvaluationFormFileVo对象
|
||||||
|
*
|
||||||
|
* @param string 字符串
|
||||||
|
* @return 转换后的CompanyCustomerEvaluationFormFileVo对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CompanyCustomerEvaluationFormFileVo fromString(String string) {
|
||||||
|
// 由于文件名称可能不唯一,这里返回null
|
||||||
|
// 实际使用时应该通过ID或其他唯一标识来查找
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.ecep.contract.converter;
|
||||||
|
|
||||||
|
import com.ecep.contract.service.DeliverySignMethodService;
|
||||||
|
import com.ecep.contract.vo.DeliverySignMethodVo;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeliverySignMethodVo的StringConverter实现,用于JavaFX控件中的显示和转换
|
||||||
|
*/
|
||||||
|
public class DeliverySignMethodStringConverter extends StringConverter<DeliverySignMethodVo> {
|
||||||
|
private final DeliverySignMethodService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param service DeliverySignMethodService实例
|
||||||
|
*/
|
||||||
|
public DeliverySignMethodStringConverter(DeliverySignMethodService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将DeliverySignMethodVo对象转换为字符串
|
||||||
|
*
|
||||||
|
* @param method DeliverySignMethodVo对象
|
||||||
|
* @return 转换后的字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString(DeliverySignMethodVo method) {
|
||||||
|
return method == null ? "" : method.getCode() + " " + method.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串转换为DeliverySignMethodVo对象
|
||||||
|
*
|
||||||
|
* @param string 字符串
|
||||||
|
* @return 转换后的DeliverySignMethodVo对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DeliverySignMethodVo fromString(String string) {
|
||||||
|
if (string == null || string.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 查找所有方法,然后通过名称匹配
|
||||||
|
return service.findByName(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,12 @@ public class ProjectTypeStringConverter extends StringConverter<ProjectTypeVo> {
|
|||||||
/**
|
/**
|
||||||
* 将ProjectTypeVo对象转换为字符串
|
* 将ProjectTypeVo对象转换为字符串
|
||||||
*
|
*
|
||||||
* @param object ProjectTypeVo对象
|
* @param type ProjectTypeVo对象
|
||||||
* @return 转换后的字符串
|
* @return 转换后的字符串
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString(ProjectTypeVo object) {
|
public String toString(ProjectTypeVo type) {
|
||||||
return object == null ? "" : object.getName();
|
return type == null ? "" : type.getCode() + " " + type.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ecep.contract.service;
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
import com.ecep.contract.converter.CompanyCustomerEvaluationFormFileStringConverter;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ import java.util.List;
|
|||||||
public class CompanyCustomerEvaluationFormFileService
|
public class CompanyCustomerEvaluationFormFileService
|
||||||
extends QueryService<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> {
|
extends QueryService<CompanyCustomerEvaluationFormFileVo, CompanyCustomerEvaluationFormFileViewModel> {
|
||||||
|
|
||||||
|
private final StringConverter<CompanyCustomerEvaluationFormFileVo> stringConverter = new CompanyCustomerEvaluationFormFileStringConverter(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据ID查找客户评估表文件
|
* 根据ID查找客户评估表文件
|
||||||
*/
|
*/
|
||||||
@@ -39,6 +43,12 @@ public class CompanyCustomerEvaluationFormFileService
|
|||||||
return page.getFirst();
|
return page.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<CompanyCustomerEvaluationFormFileVo> searchByCompany(Integer companyId, String searchText) {
|
||||||
|
ParamUtils.Builder params = getSpecification(searchText);
|
||||||
|
params.equals("company", companyId);
|
||||||
|
return findAll(params.build(), Pageable.ofSize(10)).getContent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存客户评估表文件
|
* 保存客户评估表文件
|
||||||
*/
|
*/
|
||||||
@@ -46,4 +56,10 @@ public class CompanyCustomerEvaluationFormFileService
|
|||||||
return super.save(formFile);
|
return super.save(formFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringConverter<CompanyCustomerEvaluationFormFileVo> getStringConverter() {
|
||||||
|
return stringConverter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ecep.contract.service;
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.cache.annotation.CacheConfig;
|
import org.springframework.cache.annotation.CacheConfig;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
@@ -7,16 +9,18 @@ import org.springframework.data.domain.Page;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.ecep.contract.converter.DeliverySignMethodStringConverter;
|
||||||
import com.ecep.contract.util.ParamUtils;
|
import com.ecep.contract.util.ParamUtils;
|
||||||
import com.ecep.contract.vm.DeliverySignMethodViewModel;
|
import com.ecep.contract.vm.DeliverySignMethodViewModel;
|
||||||
import com.ecep.contract.vo.DeliverySignMethodVo;
|
import com.ecep.contract.vo.DeliverySignMethodVo;
|
||||||
import com.ecep.contract.vo.ProjectSaleTypeVo;
|
import com.ecep.contract.vo.ProjectSaleTypeVo;
|
||||||
|
|
||||||
import java.util.List;
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@CacheConfig(cacheNames = "delivery-sign-method")
|
@CacheConfig(cacheNames = "delivery-sign-method")
|
||||||
public class DeliverySignMethodService extends QueryService<DeliverySignMethodVo, DeliverySignMethodViewModel> {
|
public class DeliverySignMethodService extends QueryService<DeliverySignMethodVo, DeliverySignMethodViewModel> {
|
||||||
|
private final StringConverter<DeliverySignMethodVo> stringConverter = new DeliverySignMethodStringConverter(this);
|
||||||
|
|
||||||
@Cacheable(key = "#id")
|
@Cacheable(key = "#id")
|
||||||
@Override
|
@Override
|
||||||
@@ -24,6 +28,14 @@ public class DeliverySignMethodService extends QueryService<DeliverySignMethodVo
|
|||||||
return super.findById(id);
|
return super.findById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeliverySignMethodVo findByCode(String code) {
|
||||||
|
return findOneByProperty("code", code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeliverySignMethodVo findByName(String name) {
|
||||||
|
return findOneByProperty("name", name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据销售类型和编码查询发货方式
|
* 根据销售类型和编码查询发货方式
|
||||||
*
|
*
|
||||||
@@ -32,15 +44,16 @@ public class DeliverySignMethodService extends QueryService<DeliverySignMethodVo
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public DeliverySignMethodVo findBySaleTypeAndCode(ProjectSaleTypeVo saleType, String code) {
|
public DeliverySignMethodVo findBySaleTypeAndCode(ProjectSaleTypeVo saleType, String code) {
|
||||||
Page<DeliverySignMethodVo> page = findAll(ParamUtils.builder()
|
var builder = ParamUtils.builder();
|
||||||
.equals("saleType", saleType.getId()).build(), Pageable.unpaged());
|
builder.equals("saleType", saleType.getId());
|
||||||
|
builder.equals("code", code);
|
||||||
|
Page<DeliverySignMethodVo> page = findAll(builder.build(), Pageable.unpaged());
|
||||||
if (page.isEmpty()) {
|
if (page.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return page.stream().filter(v -> v.getCode().equals(code)).findFirst().orElse(null);
|
return page.stream().findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Cacheable(key = "'all'")
|
@Cacheable(key = "'all'")
|
||||||
@Override
|
@Override
|
||||||
public List<DeliverySignMethodVo> findAll() {
|
public List<DeliverySignMethodVo> findAll() {
|
||||||
@@ -58,4 +71,9 @@ public class DeliverySignMethodService extends QueryService<DeliverySignMethodVo
|
|||||||
public void delete(DeliverySignMethodVo entity) {
|
public void delete(DeliverySignMethodVo entity) {
|
||||||
super.delete(entity);
|
super.delete(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringConverter<DeliverySignMethodVo> getStringConverter() {
|
||||||
|
return stringConverter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,9 @@ public class ProjectIndustryService extends QueryService<ProjectIndustryVo, Proj
|
|||||||
super.delete(entity);
|
super.delete(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public StringConverter<ProjectIndustryVo> getStringConverter() {
|
||||||
|
return stringConverter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ import javafx.util.StringConverter;
|
|||||||
@Service
|
@Service
|
||||||
@CacheConfig(cacheNames = "project-type")
|
@CacheConfig(cacheNames = "project-type")
|
||||||
public class ProjectTypeService extends QueryService<ProjectTypeVo, ProjectTypeViewModel> {
|
public class ProjectTypeService extends QueryService<ProjectTypeVo, ProjectTypeViewModel> {
|
||||||
private final StringConverter<ProjectTypeVo> stringConverter;
|
private final StringConverter<ProjectTypeVo> stringConverter = new ProjectTypeStringConverter(this);
|
||||||
|
|
||||||
public ProjectTypeService() {
|
|
||||||
this.stringConverter = new ProjectTypeStringConverter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Cacheable(key = "#p0")
|
@Cacheable(key = "#p0")
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -250,10 +250,16 @@ public class UITools {
|
|||||||
|
|
||||||
public static <T extends IdentityEntity, TV extends IdentityViewModel<T>> AutoCompletionBinding<T> autoCompletion(
|
public static <T extends IdentityEntity, TV extends IdentityViewModel<T>> AutoCompletionBinding<T> autoCompletion(
|
||||||
TextField textField, ObjectProperty<Integer> idProperty, QueryService<T, TV> queryService) {
|
TextField textField, ObjectProperty<Integer> idProperty, QueryService<T, TV> queryService) {
|
||||||
Integer id = idProperty.get();
|
|
||||||
StringConverter<T> converter = queryService.getStringConverter();
|
StringConverter<T> converter = queryService.getStringConverter();
|
||||||
|
|
||||||
|
return autoCompletion(textField, idProperty, queryService, converter, queryService::search);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends IdentityEntity, TV extends IdentityViewModel<T>> AutoCompletionBinding<T> autoCompletion(
|
||||||
|
TextField textField, ObjectProperty<Integer> idProperty, QueryService<T, TV> service, StringConverter<T> converter, Function<String, List<T>> searcher) {
|
||||||
|
Integer id = idProperty.get();
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
T entity = queryService.findById(id);
|
T entity = service.findById(id);
|
||||||
// 先赋值
|
// 先赋值
|
||||||
textField.textProperty().set(converter.toString(entity));
|
textField.textProperty().set(converter.toString(entity));
|
||||||
}
|
}
|
||||||
@@ -264,7 +270,7 @@ public class UITools {
|
|||||||
textField.textProperty().set(null);
|
textField.textProperty().set(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
T newEntity = queryService.findById(newValue);
|
T newEntity = service.findById(newValue);
|
||||||
textField.textProperty().set(converter.toString(newEntity));
|
textField.textProperty().set(converter.toString(newEntity));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -274,7 +280,7 @@ public class UITools {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return queryService.search(p.getUserText());
|
return searcher.apply(p.getUserText());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
textField.setText(e.getMessage());
|
textField.setText(e.getMessage());
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class CompanyCustomerEvaluationFormFileViewModel extends IdentityViewMode
|
|||||||
private SimpleIntegerProperty score5 = new SimpleIntegerProperty(0);
|
private SimpleIntegerProperty score5 = new SimpleIntegerProperty(0);
|
||||||
private SimpleIntegerProperty scoreTemplateVersion = new SimpleIntegerProperty(1);
|
private SimpleIntegerProperty scoreTemplateVersion = new SimpleIntegerProperty(1);
|
||||||
|
|
||||||
private SimpleStringProperty filePath = new SimpleStringProperty();
|
|
||||||
|
|
||||||
public static CompanyCustomerEvaluationFormFileViewModel from(CompanyCustomerEvaluationFormFileVo vo) {
|
public static CompanyCustomerEvaluationFormFileViewModel from(CompanyCustomerEvaluationFormFileVo vo) {
|
||||||
CompanyCustomerEvaluationFormFileViewModel viewModel = new CompanyCustomerEvaluationFormFileViewModel();
|
CompanyCustomerEvaluationFormFileViewModel viewModel = new CompanyCustomerEvaluationFormFileViewModel();
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.ecep.contract.vm;
|
package com.ecep.contract.vm;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import com.ecep.contract.util.NumberUtils;
|
import com.ecep.contract.util.NumberUtils;
|
||||||
import com.ecep.contract.vo.ContractInvoiceVo;
|
import com.ecep.contract.vo.ContractInvoiceVo;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -20,6 +22,11 @@ public class ContractInvoiceViewModel extends IdentityViewModel<ContractInvoiceV
|
|||||||
* 关联的合同对象, Contract
|
* 关联的合同对象, Contract
|
||||||
*/
|
*/
|
||||||
private final SimpleObjectProperty<Integer> contract = new SimpleObjectProperty<>();
|
private final SimpleObjectProperty<Integer> contract = new SimpleObjectProperty<>();
|
||||||
|
/**
|
||||||
|
* 关联的合同项目对象, ContractItem
|
||||||
|
*/
|
||||||
|
private final SimpleObjectProperty<Integer> contractItem = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关联的发票对象, Invoice
|
* 关联的发票对象, Invoice
|
||||||
*/
|
*/
|
||||||
@@ -46,7 +53,7 @@ public class ContractInvoiceViewModel extends IdentityViewModel<ContractInvoiceV
|
|||||||
invoice.set(v.getInvoiceId());
|
invoice.set(v.getInvoiceId());
|
||||||
amount.set(v.getAmount() != null ? v.getAmount() : 0.0);
|
amount.set(v.getAmount() != null ? v.getAmount() : 0.0);
|
||||||
remark.set(v.getRemark());
|
remark.set(v.getRemark());
|
||||||
|
createDate.set(v.getSetupDate());
|
||||||
updateDate.set(v.getUpdateDate());
|
updateDate.set(v.getUpdateDate());
|
||||||
creator.set(v.getSetupPersonId());
|
creator.set(v.getSetupPersonId());
|
||||||
updater.set(v.getUpdatePersonId());
|
updater.set(v.getUpdatePersonId());
|
||||||
@@ -85,23 +92,27 @@ public class ContractInvoiceViewModel extends IdentityViewModel<ContractInvoiceV
|
|||||||
v.setRemark(remark.get());
|
v.setRemark(remark.get());
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
if (!Objects.equals(creator.get(), v.getSetupPersonId())) {
|
|
||||||
v.setSetupPersonId(creator.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(updater.get(), v.getUpdatePersonId())) {
|
|
||||||
v.setUpdatePersonId(updater.get());
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
if (!Objects.equals(createDate.get(), v.getSetupDate())) {
|
if (!Objects.equals(createDate.get(), v.getSetupDate())) {
|
||||||
v.setSetupDate(createDate.get());
|
v.setSetupDate(createDate.get());
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Objects.equals(updateDate.get(), v.getUpdateDate())) {
|
if (!Objects.equals(updateDate.get(), v.getUpdateDate())) {
|
||||||
v.setUpdateDate(updateDate.get());
|
v.setUpdateDate(updateDate.get());
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(creator.get(), v.getSetupPersonId())) {
|
||||||
|
v.setSetupPersonId(creator.get());
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(updater.get(), v.getUpdatePersonId())) {
|
||||||
|
v.setUpdatePersonId(updater.get());
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
client/src/main/resources/application.properties
Normal file
1
client/src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# my.downloadsPath = C:\\Users\\SQQ\\Downloads\\
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
<TableColumn fx:id="contractTable_codeColumn" prefWidth="85.0" text="合同编码" />
|
<TableColumn fx:id="contractTable_codeColumn" prefWidth="85.0" text="合同编码" />
|
||||||
<TableColumn fx:id="contractTable_nameColumn" maxWidth="1.7976931348623157E308" minWidth="80.0" prefWidth="200.0" text="名称" />
|
<TableColumn fx:id="contractTable_nameColumn" maxWidth="1.7976931348623157E308" minWidth="80.0" prefWidth="200.0" text="名称" />
|
||||||
<TableColumn fx:id="contractTable_stateColumn" minWidth="30.0" prefWidth="45.0" text="状态" visible="false" />
|
<TableColumn fx:id="contractTable_stateColumn" minWidth="30.0" prefWidth="45.0" text="状态" visible="false" />
|
||||||
<TableColumn fx:id="contractTable_groupColumn" prefWidth="75.0" text="分组" />
|
<TableColumn fx:id="contractTable_groupColumn" prefWidth="75.0" text="分组" visible="false" />
|
||||||
<TableColumn fx:id="contractTable_typeColumn" prefWidth="110.0" text="类型" />
|
<TableColumn fx:id="contractTable_typeColumn" prefWidth="110.0" text="类型" visible="false" />
|
||||||
<TableColumn fx:id="contractTable_kindColumn" prefWidth="100.0" text="性质" />
|
<TableColumn fx:id="contractTable_kindColumn" prefWidth="100.0" text="性质" visible="false" />
|
||||||
<TableColumn fx:id="contractTable_parentCodeColumn" prefWidth="90.0" text="主合同号" visible="false" />
|
<TableColumn fx:id="contractTable_parentCodeColumn" prefWidth="90.0" text="主合同号" visible="false" />
|
||||||
<TableColumn fx:id="contractTable_setupDateColumn" prefWidth="75.0" sortType="DESCENDING" text="提交日期" />
|
<TableColumn fx:id="contractTable_setupDateColumn" prefWidth="75.0" sortType="DESCENDING" text="提交日期" />
|
||||||
<TableColumn fx:id="contractTable_inureDateColumn" prefWidth="75.0" text="生效日期" />
|
<TableColumn fx:id="contractTable_inureDateColumn" prefWidth="75.0" text="生效日期" />
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ fx:controller="com.ecep.contract.controller.customer.CompanyCustomerEvaluationFo
|
|||||||
<Label text="★★★★≤200分,★★★≤150分,★★≤100分,★≤60分" />
|
<Label text="★★★★≤200分,★★★≤150分,★★≤100分,★≤60分" />
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
<Button mnemonicParsing="false" onAction="#onSaveAction" text="保存" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="10" />
|
<Button mnemonicParsing="false" text="保存" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="10" />
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets right="5.0" />
|
<Insets right="5.0" />
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<?import javafx.scene.paint.Color?>
|
<?import javafx.scene.paint.Color?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<BorderPane fx:id="root" maxHeight="900" maxWidth="1024" minHeight="300" minWidth="200" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.ds.company.controller.contact.CompanyContactWindowController">
|
<BorderPane fx:id="root" maxHeight="900" maxWidth="1024" minHeight="300" minWidth="200" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.controller.company.CompanyContactWindowController">
|
||||||
<center>
|
<center>
|
||||||
<TabPane fx:id="tabPane" tabClosingPolicy="UNAVAILABLE" tabMaxWidth="100.0" tabMinWidth="40.0">
|
<TabPane fx:id="tabPane" tabClosingPolicy="UNAVAILABLE" tabMaxWidth="100.0" tabMinWidth="40.0">
|
||||||
<tabs>
|
<tabs>
|
||||||
|
|||||||
@@ -1,13 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.ContextMenu?>
|
||||||
|
<?import javafx.scene.control.DatePicker?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.MenuItem?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.Tab?>
|
||||||
|
<?import javafx.scene.control.TabPane?>
|
||||||
|
<?import javafx.scene.control.TableColumn?>
|
||||||
|
<?import javafx.scene.control.TableView?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.control.ToolBar?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.paint.Color?>
|
<?import javafx.scene.paint.Color?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
<BorderPane fx:id="root" prefHeight="680.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/22"
|
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
<BorderPane fx:id="root" prefHeight="680.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ecep.contract.controller.project.ProjectWindowController">
|
||||||
fx:controller="com.ecep.contract.controller.project.ProjectWindowController">
|
|
||||||
<top>
|
<top>
|
||||||
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
<items>
|
<items>
|
||||||
@@ -22,150 +42,97 @@
|
|||||||
<tabs>
|
<tabs>
|
||||||
<Tab fx:id="baseInfoTab" text="基本信息">
|
<Tab fx:id="baseInfoTab" text="基本信息">
|
||||||
<content>
|
<content>
|
||||||
<ScrollPane fitToWidth="true" maxHeight="1.7976931348623157E308"
|
<ScrollPane fitToWidth="true" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="150.0" pannable="true">
|
||||||
maxWidth="1.7976931348623157E308" minWidth="150.0" pannable="true">
|
|
||||||
<content>
|
<content>
|
||||||
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
|
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="600.0" minWidth="150.0">
|
||||||
minHeight="600.0" minWidth="150.0">
|
|
||||||
<children>
|
<children>
|
||||||
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
|
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="150.0" VBox.vgrow="NEVER">
|
||||||
minWidth="150.0" VBox.vgrow="NEVER">
|
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="200.0"
|
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="200.0" minWidth="80.0" prefWidth="120.0" />
|
||||||
minWidth="80.0" prefWidth="120.0"/>
|
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="50.0" prefWidth="180.0" />
|
||||||
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308"
|
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="200.0" minWidth="80.0" prefWidth="120.0" />
|
||||||
minWidth="50.0" prefWidth="180.0"/>
|
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="50.0" prefWidth="180.0" />
|
||||||
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="200.0"
|
|
||||||
minWidth="80.0" prefWidth="120.0"/>
|
|
||||||
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308"
|
|
||||||
minWidth="50.0" prefWidth="180.0"/>
|
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" minHeight="80.0" prefHeight="80.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0" vgrow="NEVER" />
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
<RowConstraints fillHeight="false" maxHeight="1.7976931348623157E308" minHeight="30.0" prefHeight="42.0" vgrow="NEVER" />
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="80.0" prefHeight="80.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" minHeight="30.0" prefHeight="30.0"
|
|
||||||
vgrow="NEVER"/>
|
|
||||||
<RowConstraints fillHeight="false" maxHeight="1.7976931348623157E308"
|
|
||||||
minHeight="30.0" prefHeight="42.0" vgrow="NEVER"/>
|
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Label text="项目名称" />
|
<Label text="项目名称" />
|
||||||
<TextField fx:id="nameField" GridPane.columnIndex="1"
|
<TextField fx:id="nameField" GridPane.columnIndex="1" GridPane.columnSpan="3" />
|
||||||
GridPane.columnSpan="3"/>
|
<HBox alignment="CENTER_RIGHT" spacing="5.0" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="12">
|
||||||
<HBox alignment="CENTER_RIGHT" spacing="5.0" GridPane.columnIndex="1"
|
|
||||||
GridPane.columnSpan="3" GridPane.rowIndex="13">
|
|
||||||
<children>
|
<children>
|
||||||
|
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label fx:id="versionLabel" text="\@Version" GridPane.rowIndex="13"/>
|
<Label fx:id="versionLabel" text="\@Version" GridPane.rowIndex="12" />
|
||||||
<Label text="项目编码" GridPane.rowIndex="1" />
|
<Label text="项目编码" GridPane.rowIndex="1" />
|
||||||
<TextField fx:id="codeField" GridPane.columnIndex="1"
|
<TextField fx:id="codeField" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
GridPane.rowIndex="1"/>
|
<Label text="创建日期" GridPane.rowIndex="11" />
|
||||||
<Label text="创建日期" GridPane.rowIndex="12"/>
|
|
||||||
<Label text="项目类型" GridPane.rowIndex="5" />
|
<Label text="项目类型" GridPane.rowIndex="5" />
|
||||||
<DatePicker fx:id="createdField" GridPane.columnIndex="1"
|
<DatePicker fx:id="createdField" GridPane.columnIndex="1" GridPane.rowIndex="11" />
|
||||||
GridPane.rowIndex="12"/>
|
<Label text="其他" GridPane.columnIndex="2" GridPane.rowIndex="2" />
|
||||||
<Label text="投标/报价" GridPane.columnIndex="2" GridPane.rowIndex="2"/>
|
|
||||||
<Label text="项目地址" GridPane.rowIndex="8" />
|
<Label text="项目地址" GridPane.rowIndex="8" />
|
||||||
<TextField fx:id="addressField" GridPane.columnIndex="1"
|
<TextField fx:id="addressField" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="8" />
|
||||||
GridPane.columnSpan="3" GridPane.rowIndex="8"/>
|
|
||||||
<Label text="备注" GridPane.rowIndex="10" />
|
<Label text="备注" GridPane.rowIndex="10" />
|
||||||
<TextArea fx:id="descriptionField" GridPane.columnIndex="1"
|
<TextArea fx:id="descriptionField" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="10">
|
||||||
GridPane.columnSpan="3" GridPane.rowIndex="10">
|
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets bottom="3.0" top="3.0" />
|
<Insets bottom="3.0" top="3.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</TextArea>
|
</TextArea>
|
||||||
<HBox spacing="12.0" GridPane.columnIndex="3" GridPane.rowIndex="2">
|
<HBox spacing="12.0" GridPane.columnIndex="3" GridPane.rowIndex="2">
|
||||||
<children>
|
<children>
|
||||||
<CheckBox fx:id="useBidField" mnemonicParsing="false"
|
<CheckBox fx:id="useBidField" mnemonicParsing="false" text="投标" />
|
||||||
text="投标"/>
|
<CheckBox fx:id="useOfferField" mnemonicParsing="false" text="报价" />
|
||||||
<CheckBox fx:id="useOfferField" mnemonicParsing="false"
|
<CheckBox fx:id="standardPayWayField" mnemonicParsing="false" text="标准付款方式" />
|
||||||
text="报价"/>
|
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label text="付款方式" GridPane.columnIndex="2" GridPane.rowIndex="4"/>
|
|
||||||
<CheckBox fx:id="standardPayWayField" mnemonicParsing="false"
|
|
||||||
text="标准付款方式" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="4"/>
|
|
||||||
<Label text="产品用途类型" GridPane.rowIndex="7" />
|
<Label text="产品用途类型" GridPane.rowIndex="7" />
|
||||||
<ComboBox fx:id="industryField" prefWidth="150.0"
|
<ComboBox fx:id="industryField" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="6"/>
|
|
||||||
<Label text="销售类型" GridPane.rowIndex="2" />
|
<Label text="销售类型" GridPane.rowIndex="2" />
|
||||||
<Label text="最终用户行业类型" GridPane.rowIndex="6" />
|
<Label text="最终用户行业类型" GridPane.rowIndex="6" />
|
||||||
<Label text="预计开工时间" GridPane.rowIndex="9"/>
|
<Label text="预计开工" GridPane.columnIndex="2" GridPane.rowIndex="5" />
|
||||||
<Label text="预计完工时间" GridPane.columnIndex="2"
|
<Label text="预计完工" GridPane.columnIndex="2" GridPane.rowIndex="6" />
|
||||||
GridPane.rowIndex="9"/>
|
|
||||||
<Label text="交货签收方式" GridPane.rowIndex="3" />
|
<Label text="交货签收方式" GridPane.rowIndex="3" />
|
||||||
<Label text="产品类型" GridPane.rowIndex="4" />
|
<Label text="产品类型" GridPane.rowIndex="4" />
|
||||||
<HBox GridPane.columnIndex="1" GridPane.rowIndex="2">
|
<HBox GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||||
<children>
|
<children>
|
||||||
<ComboBox fx:id="saleTypeField" minWidth="100.0"
|
<ComboBox fx:id="saleTypeField" minWidth="100.0" prefWidth="100.0" />
|
||||||
prefWidth="100.0"/>
|
<TextField fx:id="codeYearField" alignment="CENTER_RIGHT" layoutX="160.0" layoutY="10.0" maxWidth="30.0" minWidth="30.0" prefWidth="30.0" promptText="00" />
|
||||||
<TextField fx:id="codeYearField" alignment="CENTER_RIGHT"
|
<TextField fx:id="codeSequenceNumberField" alignment="CENTER_RIGHT" maxWidth="38.0" minWidth="38.0" prefWidth="38.0" promptText="000" />
|
||||||
layoutX="160.0" layoutY="10.0" maxWidth="30.0"
|
<Button mnemonicParsing="false" onAction="#onGetNextCodeSNAction" text="+" />
|
||||||
minWidth="30.0" prefWidth="30.0" promptText="00"/>
|
</children>
|
||||||
<TextField fx:id="codeSequenceNumberField"
|
</HBox>
|
||||||
alignment="CENTER_RIGHT" maxWidth="38.0"
|
<ComboBox fx:id="projectTypeField" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
minWidth="38.0" prefWidth="38.0" promptText="000"/>
|
<ComboBox fx:id="productTypeField" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
<Button mnemonicParsing="false"
|
<ComboBox fx:id="deliverySignMethodField" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
onAction="#onGetNextCodeSNAction" text="+"/>
|
<ComboBox fx:id="productUsageField" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||||
|
<Label text="申请人" GridPane.rowIndex="9" />
|
||||||
|
<Label text="审核人" GridPane.columnIndex="2" GridPane.rowIndex="9" />
|
||||||
|
<TextField fx:id="applicantField" GridPane.columnIndex="1" GridPane.rowIndex="9" />
|
||||||
|
<TextField fx:id="authorizerField" GridPane.columnIndex="3" GridPane.rowIndex="9" />
|
||||||
|
<DatePicker fx:id="plannedStartTimeField" layoutX="130.0" layoutY="424.0" GridPane.columnIndex="3" GridPane.rowIndex="5" />
|
||||||
|
<DatePicker fx:id="plannedCompletionTimeField" layoutX="130.0" layoutY="254.0" GridPane.columnIndex="3" GridPane.rowIndex="6" />
|
||||||
|
<Label text="项目金额" GridPane.columnIndex="2" GridPane.rowIndex="1" />
|
||||||
|
<TextField fx:id="amountField" GridPane.columnIndex="3" GridPane.rowIndex="1" />
|
||||||
|
<Label text="工程项目" GridPane.columnIndex="2" GridPane.rowIndex="4" />
|
||||||
|
<HBox spacing="12.0" GridPane.columnIndex="3" GridPane.rowIndex="4">
|
||||||
|
<children>
|
||||||
|
<CheckBox fx:id="investigationField" mnemonicParsing="false" text="现场勘察" />
|
||||||
|
<CheckBox fx:id="investigationField1" mnemonicParsing="false" text="项目交底" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<ComboBox fx:id="projectTypeField" prefWidth="150.0"
|
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="5"/>
|
|
||||||
<ComboBox fx:id="productTypeField" prefWidth="150.0"
|
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
|
||||||
<ComboBox fx:id="deliverySignMethodField" prefWidth="150.0"
|
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
|
||||||
<ComboBox fx:id="productUsageField" prefWidth="150.0"
|
|
||||||
GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
|
||||||
<Label text="申请人" GridPane.rowIndex="11"/>
|
|
||||||
<Label text="审核人" GridPane.columnIndex="2" GridPane.rowIndex="11"/>
|
|
||||||
<TextField fx:id="applicantField" GridPane.columnIndex="1"
|
|
||||||
GridPane.rowIndex="11"/>
|
|
||||||
<TextField fx:id="authorizerField" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="11"/>
|
|
||||||
<DatePicker fx:id="plannedStartTimeField" layoutX="130.0"
|
|
||||||
layoutY="424.0" GridPane.columnIndex="1"
|
|
||||||
GridPane.rowIndex="9"/>
|
|
||||||
<DatePicker fx:id="plannedCompletionTimeField" layoutX="130.0"
|
|
||||||
layoutY="254.0" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="9"/>
|
|
||||||
<Label text="项目金额" GridPane.columnIndex="2" GridPane.rowIndex="3"/>
|
|
||||||
<TextField fx:id="amountField" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="3"/>
|
|
||||||
<Label text="现场勘察" GridPane.columnIndex="2" GridPane.rowIndex="5"/>
|
|
||||||
<CheckBox fx:id="investigationField" mnemonicParsing="false"
|
|
||||||
text="现场勘察" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="5"/>
|
|
||||||
<Label text="项目交底" GridPane.columnIndex="2" GridPane.rowIndex="6"/>
|
|
||||||
<CheckBox fx:id="investigationField1" mnemonicParsing="false"
|
|
||||||
text="项目交底" GridPane.columnIndex="3"
|
|
||||||
GridPane.rowIndex="6"/>
|
|
||||||
</children>
|
</children>
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||||
@@ -212,25 +179,19 @@
|
|||||||
<Insets bottom="10.0" left="7.0" right="10.0" top="10.0" />
|
<Insets bottom="10.0" left="7.0" right="10.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</HBox>
|
</HBox>
|
||||||
<TableView fx:id="fileTable" layoutX="48.0" layoutY="26.0" prefHeight="487.0"
|
<TableView fx:id="fileTable" layoutX="48.0" layoutY="26.0" prefHeight="487.0" prefWidth="900.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="42.0">
|
||||||
prefWidth="900.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0"
|
|
||||||
AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="42.0">
|
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn fx:id="fileTable_idColumn" prefWidth="75.0" text="ID" />
|
<TableColumn fx:id="fileTable_idColumn" prefWidth="75.0" text="ID" />
|
||||||
<TableColumn fx:id="fileTable_typeColumn" text="类型" />
|
<TableColumn fx:id="fileTable_typeColumn" text="类型" />
|
||||||
<TableColumn fx:id="fileTable_filePathColumn" prefWidth="300.0" text="文件" />
|
<TableColumn fx:id="fileTable_filePathColumn" prefWidth="300.0" text="文件" />
|
||||||
<TableColumn fx:id="fileTable_applyDateColumn" prefWidth="150.0"
|
<TableColumn fx:id="fileTable_applyDateColumn" prefWidth="150.0" text="生效日期" />
|
||||||
text="生效日期"/>
|
<TableColumn fx:id="fileTable_descriptionColumn" prefWidth="200.0" text="有效日期" />
|
||||||
<TableColumn fx:id="fileTable_descriptionColumn" prefWidth="200.0"
|
|
||||||
text="有效日期"/>
|
|
||||||
</columns>
|
</columns>
|
||||||
<contextMenu>
|
<contextMenu>
|
||||||
<ContextMenu onShowing="#onFileTableContextMenuShowing">
|
<ContextMenu onShowing="#onFileTableContextMenuShowing">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#onFileTableRefreshAction"
|
<MenuItem mnemonicParsing="false" onAction="#onFileTableRefreshAction" text="刷新" />
|
||||||
text="刷新"/>
|
<MenuItem mnemonicParsing="false" onAction="#onFileTableDeleteAction" text="删除" />
|
||||||
<MenuItem mnemonicParsing="false" onAction="#onFileTableDeleteAction"
|
|
||||||
text="删除"/>
|
|
||||||
</items>
|
</items>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</contextMenu>
|
</contextMenu>
|
||||||
@@ -245,8 +206,7 @@
|
|||||||
<bottom>
|
<bottom>
|
||||||
<HBox id="HBox" alignment="CENTER_LEFT" spacing="5.0">
|
<HBox id="HBox" alignment="CENTER_LEFT" spacing="5.0">
|
||||||
<children>
|
<children>
|
||||||
<Label fx:id="leftStatusLabel" maxHeight="1.7976931348623157E308" text="Left status"
|
<Label fx:id="leftStatusLabel" maxHeight="1.7976931348623157E308" text="Left status" HBox.hgrow="ALWAYS">
|
||||||
HBox.hgrow="ALWAYS">
|
|
||||||
<font>
|
<font>
|
||||||
<Font size="11.0" fx:id="x3" />
|
<Font size="11.0" fx:id="x3" />
|
||||||
</font>
|
</font>
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>Contract-Manager</artifactId>
|
<artifactId>Contract-Manager</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ecep.contract.vo;
|
package com.ecep.contract.vo;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import com.ecep.contract.model.IdentityEntity;
|
import com.ecep.contract.model.IdentityEntity;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#Contract Manager \u5E94\u7528\u7A0B\u5E8F\u914D\u7F6E
|
#Contract Manager \u5E94\u7528\u7A0B\u5E8F\u914D\u7F6E
|
||||||
#Sat Sep 27 00:34:02 CST 2025
|
#Sat Sep 27 00:34:02 CST 2025
|
||||||
|
#server.host=cms.ecctrl.com
|
||||||
|
#server.port=80
|
||||||
server.host=127.0.0.1
|
server.host=127.0.0.1
|
||||||
server.port=8080
|
server.port=8080
|
||||||
user.name=qiqing.song
|
user.name=qiqing.song
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -10,7 +10,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>Contract-Manager</artifactId>
|
<artifactId>Contract-Manager</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<modules>
|
<modules>
|
||||||
<module>server</module>
|
<module>server</module>
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>Contract-Manager</artifactId>
|
<artifactId>Contract-Manager</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>server</artifactId>
|
<artifactId>server</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ecep.contract</groupId>
|
<groupId>com.ecep.contract</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>0.0.126-SNAPSHOT</version>
|
<version>0.0.134-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -93,6 +93,12 @@
|
|||||||
<artifactId>mssql-jdbc</artifactId>
|
<artifactId>mssql-jdbc</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- SMBJ library for SMB/CIFS file operations -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hierynomus</groupId>
|
||||||
|
<artifactId>smbj</artifactId>
|
||||||
|
<version>0.11.5</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ecep.contract.cloud.u8.ctx;
|
package com.ecep.contract.cloud.u8.ctx;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -50,6 +51,7 @@ import com.ecep.contract.ds.vendor.model.Vendor;
|
|||||||
import com.ecep.contract.ds.vendor.model.VendorEntity;
|
import com.ecep.contract.ds.vendor.model.VendorEntity;
|
||||||
import com.ecep.contract.ds.vendor.service.VendorEntityService;
|
import com.ecep.contract.ds.vendor.service.VendorEntityService;
|
||||||
import com.ecep.contract.ds.vendor.service.VendorService;
|
import com.ecep.contract.ds.vendor.service.VendorService;
|
||||||
|
import com.ecep.contract.service.SmbFileService;
|
||||||
import com.ecep.contract.util.BeanContext;
|
import com.ecep.contract.util.BeanContext;
|
||||||
import com.ecep.contract.util.FileUtils;
|
import com.ecep.contract.util.FileUtils;
|
||||||
import com.ecep.contract.util.NumberUtils;
|
import com.ecep.contract.util.NumberUtils;
|
||||||
@@ -63,6 +65,7 @@ import com.ecep.contract.vo.CustomerVo;
|
|||||||
import com.ecep.contract.vo.ProjectSaleTypeVo;
|
import com.ecep.contract.vo.ProjectSaleTypeVo;
|
||||||
import com.ecep.contract.vo.ProjectVo;
|
import com.ecep.contract.vo.ProjectVo;
|
||||||
import com.ecep.contract.vo.VendorEntityVo;
|
import com.ecep.contract.vo.VendorEntityVo;
|
||||||
|
import com.hierynomus.smbj.common.SmbPath;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -995,13 +998,20 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateContractPath(ContractVo contract, MessageHolder holder) {
|
public boolean updateContractPath(ContractVo contract, MessageHolder holder) {
|
||||||
// 如果合同路径存在
|
// 如果设置了合同路径,检查路径是否存在
|
||||||
if (CompanyFileUtils.exists(contract.getPath())) {
|
SmbFileService smbFileService = getCachedBean(SmbFileService.class);
|
||||||
File dir = new File(contract.getPath());
|
if (StringUtils.hasText(contract.getPath())) {
|
||||||
if (dir.exists()) {
|
var smbPath = SmbPath.parse(contract.getPath());
|
||||||
|
try {
|
||||||
|
if (smbFileService.exists(smbPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
holder.warn("检查合同路径 " + smbPath + " 异常:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试创建合同路径
|
// 尝试创建合同路径
|
||||||
holder.debug("合同目录不存在,尝试创建");
|
holder.debug("合同目录不存在,尝试创建");
|
||||||
|
|
||||||
@@ -1022,15 +1032,17 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File contractPath = new File(parent.getPath());
|
File contractPath = new File(parent.getPath());
|
||||||
if (!contractPath.exists()) {
|
if (!smbFileService.exists(contractPath)) {
|
||||||
holder.debug("父合同 " + parentCode + " 目录不存在 " + parent.getPath());
|
holder.debug("父合同 " + parentCode + " 目录不存在 " + parent.getPath());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String contractDirName = contract.getCode();
|
String contractDirName = contract.getCode();
|
||||||
File path = new File(contractPath, contractDirName);
|
File path = new File(contractPath, contractDirName);
|
||||||
if (!path.exists()) {
|
if (!smbFileService.exists(path)) {
|
||||||
if (!path.mkdir()) {
|
try {
|
||||||
holder.warn("创建目录失败 = " + path.getAbsolutePath());
|
smbFileService.mkdir(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
holder.warn("创建目录失败 = " + path.getAbsolutePath() + ", 异常: " + e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1049,9 +1061,12 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
File dir = new File(saleType.getPath());
|
File dir = new File(saleType.getPath());
|
||||||
if (saleType.isStoreByYear()) {
|
if (saleType.isStoreByYear()) {
|
||||||
dir = new File(dir, "20" + projectVo.getCodeYear());
|
dir = new File(dir, "20" + projectVo.getCodeYear());
|
||||||
if (!dir.exists()) {
|
if (!smbFileService.exists(dir)) {
|
||||||
if (dir.mkdir()) {
|
try {
|
||||||
holder.info("新建目录 " + dir.getAbsolutePath());
|
smbFileService.mkdir(dir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
holder.warn("创建目录失败 = " + dir.getAbsolutePath() + ", 异常: " + e.getMessage());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1074,10 +1089,11 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
|
|
||||||
String contractDirName = contract.getCode() + "-" + FileUtils.escapeFileName(contract.getName());
|
String contractDirName = contract.getCode() + "-" + FileUtils.escapeFileName(contract.getName());
|
||||||
File path = new File(contractPath, contractDirName);
|
File path = new File(contractPath, contractDirName);
|
||||||
if (!path.exists()) {
|
if (!smbFileService.exists(path)) {
|
||||||
if (!path.mkdir()) {
|
try {
|
||||||
//
|
smbFileService.mkdir(path);
|
||||||
holder.warn("创建目录失败 = " + path.getAbsolutePath());
|
} catch (IOException e) {
|
||||||
|
holder.warn("创建目录失败 = " + path.getAbsolutePath() + ", 异常: " + e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1086,6 +1102,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateContractAmount(ContractVo contract, MessageHolder holder) {
|
public boolean updateContractAmount(ContractVo contract, MessageHolder holder) {
|
||||||
@@ -1190,8 +1207,10 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
if (!StringUtils.hasText(contractPath)) {
|
if (!StringUtils.hasText(contractPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
SmbFileService smbFileService = getCachedBean(SmbFileService.class);
|
||||||
|
// 检查合同目录是否存在
|
||||||
File dir = new File(contractPath);
|
File dir = new File(contractPath);
|
||||||
if (!dir.exists()) {
|
if (!smbFileService.exists(dir)) {
|
||||||
holder.warn("合同目录不存在:" + contractPath);
|
holder.warn("合同目录不存在:" + contractPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1228,11 +1247,11 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 遍历合同目录下的文件,如果未创建,创建
|
// 遍历合同目录下的文件,如果未创建,创建
|
||||||
File[] files = dir.listFiles();
|
try {
|
||||||
if (files != null) {
|
List<File> files = smbFileService.listFiles(dir);
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
// 只处理文件
|
// 只处理文件
|
||||||
if (!file.isFile()) {
|
if (!smbFileService.isFile(file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String fileName = file.getName();
|
String fileName = file.getName();
|
||||||
@@ -1247,6 +1266,9 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
|||||||
syncContractFile(contractFile, file, holder);
|
syncContractFile(contractFile, file, holder);
|
||||||
retrieveFiles.add(contractFile);
|
retrieveFiles.add(contractFile);
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
holder.error("遍历合同目录下的文件失败:" + contractPath + ",错误:" + e.getMessage());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retrieveFiles.isEmpty()) {
|
if (retrieveFiles.isEmpty()) {
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ public class CustomerCtx extends AbstractYongYouU8Ctx {
|
|||||||
public CompanyCustomerEntityVo updateCustomerEntityDetailByCode(CompanyCustomerEntityVo item, String unitCode,
|
public CompanyCustomerEntityVo updateCustomerEntityDetailByCode(CompanyCustomerEntityVo item, String unitCode,
|
||||||
MessageHolder holder) {
|
MessageHolder holder) {
|
||||||
CompanyCustomerEntity v0 = getCustomerEntityService().getById(item.getId());
|
CompanyCustomerEntity v0 = getCustomerEntityService().getById(item.getId());
|
||||||
if (applyEntityDetail(v0, repository.findCustomerByCusCode(unitCode), holder)) {
|
initializeRepository(holder);
|
||||||
|
if (applyEntityDetail(v0, getRepository().findCustomerByCusCode(unitCode), holder)) {
|
||||||
item = save(v0).toVo();
|
item = save(v0).toVo();
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
@@ -252,8 +253,10 @@ public class CustomerCtx extends AbstractYongYouU8Ctx {
|
|||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContractCtx ctx = getContractCtx();
|
||||||
|
ctx.initializeRepository(holder);
|
||||||
for (CompanyCustomerEntity entity : entities) {
|
for (CompanyCustomerEntity entity : entities) {
|
||||||
if (getContractCtx().syncByCustomerEntity(companyCustomer, entity, holder)) {
|
if (ctx.syncByCustomerEntity(companyCustomer, entity, holder)) {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class SalesOrderCtx extends AbstractYongYouU8Ctx {
|
|||||||
Collectors.toMap(SalesOrderItem::getCode, item -> item))));
|
Collectors.toMap(SalesOrderItem::getCode, item -> item))));
|
||||||
|
|
||||||
// 查询 U8 数据库
|
// 查询 U8 数据库
|
||||||
List<Map<String, Object>> ds = repository.findAllSalesOrderItemByContractCode(contract.getCode());
|
List<Map<String, Object>> ds = getRepository().findAllSalesOrderItemByContractCode(contract.getCode());
|
||||||
holder.debug("查找到 " + ds.size() + " 条销售订单条目记录在 " + CloudServiceConstant.U8_NAME);
|
holder.debug("查找到 " + ds.size() + " 条销售订单条目记录在 " + CloudServiceConstant.U8_NAME);
|
||||||
|
|
||||||
Map<SalesOrder, List<SalesOrderItem>> updateMap = new HashMap<>();
|
Map<SalesOrder, List<SalesOrderItem>> updateMap = new HashMap<>();
|
||||||
@@ -202,7 +202,7 @@ public class SalesOrderCtx extends AbstractYongYouU8Ctx {
|
|||||||
|
|
||||||
for (SalesOrder order : updateMap.keySet()) {
|
for (SalesOrder order : updateMap.keySet()) {
|
||||||
holder.debug("销售订单 #" + order.getCode());
|
holder.debug("销售订单 #" + order.getCode());
|
||||||
if (applySalesOrderDetail(order, repository.querySalesOrderDetail(order.getCode()), holder)) {
|
if (applySalesOrderDetail(order, getRepository().querySalesOrderDetail(order.getCode()), holder)) {
|
||||||
saleOrdersService.save(order);
|
saleOrdersService.save(order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ public class VendorCtx extends AbstractYongYouU8Ctx {
|
|||||||
*/
|
*/
|
||||||
public VendorEntityVo updateVendorEntityDetailByCode(VendorEntityVo item, String unitCode, MessageHolder holder) {
|
public VendorEntityVo updateVendorEntityDetailByCode(VendorEntityVo item, String unitCode, MessageHolder holder) {
|
||||||
VendorEntity v1 = getCompanyVendorEntityService().getById(item.getId());
|
VendorEntity v1 = getCompanyVendorEntityService().getById(item.getId());
|
||||||
if (applyEntityDetail(v1, repository.findVendorByVendCode(unitCode), holder)) {
|
initializeRepository(holder);
|
||||||
|
if (applyEntityDetail(v1, getRepository().findVendorByVendCode(unitCode), holder)) {
|
||||||
item = save(v1).toVo();
|
item = save(v1).toVo();
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
@@ -248,8 +249,10 @@ public class VendorCtx extends AbstractYongYouU8Ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 同步供应商关联的合同
|
// 同步供应商关联的合同
|
||||||
|
ContractCtx ctx = getContractCtx();
|
||||||
|
ctx.initializeRepository(holder);
|
||||||
for (VendorEntity entity : entities) {
|
for (VendorEntity entity : entities) {
|
||||||
if (getContractCtx().syncByVendorEntity(vendor, entity, holder)) {
|
if (ctx.syncByVendorEntity(vendor, entity, holder)) {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
server/src/main/java/com/ecep/contract/config/SmbConfig.java
Normal file
30
server/src/main/java/com/ecep/contract/config/SmbConfig.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ecep.contract.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.hierynomus.smbj.SMBClient;
|
||||||
|
import com.hierynomus.smbj.auth.NtlmAuthenticator;
|
||||||
|
|
||||||
|
@Configuration()
|
||||||
|
public class SmbConfig {
|
||||||
|
|
||||||
|
@Value("${smb.server.username}")
|
||||||
|
@Getter
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Value("${smb.server.password}")
|
||||||
|
@Getter
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SMBClient smbClient() {
|
||||||
|
var smbConfig = com.hierynomus.smbj.SmbConfig.builder()
|
||||||
|
.withMultiProtocolNegotiate(true).withSigningRequired(true)
|
||||||
|
// .withAuthenticators(new NtlmAuthenticator(username, password))
|
||||||
|
.build();
|
||||||
|
return new SMBClient(smbConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,4 +34,7 @@ public class WebSocketConfig implements WebSocketConfigurer {
|
|||||||
new HttpSessionHandshakeInterceptor(List.of("JSESSIONID", "loginHistoryId", "employeeId")))
|
new HttpSessionHandshakeInterceptor(List.of("JSESSIONID", "loginHistoryId", "employeeId")))
|
||||||
.setAllowedOrigins("*");
|
.setAllowedOrigins("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ecep.contract.ds.company;
|
package com.ecep.contract.ds.company;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@@ -9,12 +10,17 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import com.ecep.contract.constant.CompanyConstant;
|
import com.ecep.contract.constant.CompanyConstant;
|
||||||
import com.ecep.contract.util.FileUtils;
|
import com.ecep.contract.util.FileUtils;
|
||||||
|
import com.hierynomus.smbj.common.SmbPath;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.ecep.contract.CompanyFileType;
|
import com.ecep.contract.CompanyFileType;
|
||||||
import com.ecep.contract.MyDateTimeUtils;
|
import com.ecep.contract.MyDateTimeUtils;
|
||||||
|
import com.ecep.contract.SpringApp;
|
||||||
import com.ecep.contract.constant.CloudServiceConstant;
|
import com.ecep.contract.constant.CloudServiceConstant;
|
||||||
import com.ecep.contract.ds.company.model.CompanyFile;
|
import com.ecep.contract.ds.company.model.CompanyFile;
|
||||||
|
import com.ecep.contract.service.SmbFileService;
|
||||||
|
|
||||||
public class CompanyFileUtils {
|
public class CompanyFileUtils {
|
||||||
|
|
||||||
@@ -130,10 +136,24 @@ public class CompanyFileUtils {
|
|||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean exists(SmbPath path) throws IOException {
|
||||||
|
return SpringApp.getBean(SmbFileService.class).exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean exists(String path) {
|
public static boolean exists(String path) {
|
||||||
if (!StringUtils.hasText(path)) {
|
if (!StringUtils.hasText(path)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path.startsWith("\\\\")) {
|
||||||
|
var smbPath = SmbPath.parse(path);
|
||||||
|
try {
|
||||||
|
return SpringApp.getBean(SmbFileService.class).exists(smbPath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
return file.exists();
|
return file.exists();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,7 +285,9 @@ public abstract class AbstContractRepairTasker extends Tasker<Object> {
|
|||||||
*/
|
*/
|
||||||
private void syncSaleOrder(ContractVo contract, MessageHolder holder) {
|
private void syncSaleOrder(ContractVo contract, MessageHolder holder) {
|
||||||
SalesOrderCtx ctx = getSalesOrderCtx();
|
SalesOrderCtx ctx = getSalesOrderCtx();
|
||||||
|
ctx.initializeRepository(holder);
|
||||||
if (ctx.getRepository() == null) {
|
if (ctx.getRepository() == null) {
|
||||||
|
holder.warn("未启用 " + CloudServiceConstant.U8_NAME + " 服务");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<SalesOrder> orders = ctx.syncByContract(contract, holder);
|
List<SalesOrder> orders = ctx.syncByContract(contract, holder);
|
||||||
@@ -293,6 +295,11 @@ public abstract class AbstContractRepairTasker extends Tasker<Object> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SalesBillVoucherCtx voucherCtx = getSalesBillVoucherCtx();
|
SalesBillVoucherCtx voucherCtx = getSalesBillVoucherCtx();
|
||||||
|
voucherCtx.initializeRepository(holder);
|
||||||
|
if (voucherCtx.getRepository() == null) {
|
||||||
|
holder.warn("未启用 " + CloudServiceConstant.U8_NAME + " 服务");
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (SalesOrder order : orders) {
|
for (SalesOrder order : orders) {
|
||||||
voucherCtx.syncBySalesOrder(order, holder);
|
voucherCtx.syncBySalesOrder(order, holder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.ecep.contract.ds.customer.service;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Path;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -58,9 +59,14 @@ public class CompanyCustomerEvaluationFormFileService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (root, query, builder) -> {
|
return (root, query, builder) -> {
|
||||||
|
var customerFile = root.get("customerFile");
|
||||||
return builder.or(
|
return builder.or(
|
||||||
builder.like(root.get("catalog"), "%" + searchText + "%"),
|
builder.like(root.get("catalog"), "%" + searchText + "%"),
|
||||||
builder.like(root.get("level"), "%" + searchText + "%"));
|
builder.like(root.get("level"), "%" + searchText + "%"),
|
||||||
|
builder.and(
|
||||||
|
customerFile.isNotNull(),
|
||||||
|
builder.like(customerFile.get("filePath"), "%" + searchText + "%")
|
||||||
|
));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,10 +100,12 @@ public class CompanyCustomerEvaluationFormFileService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (paramsNode.has("customer")) {
|
if (paramsNode.has("customer")) {
|
||||||
CompanyCustomer customer = new CompanyCustomer();
|
|
||||||
customer.setId(paramsNode.get("customer").asInt());
|
|
||||||
spec = SpecificationUtils.and(spec,
|
spec = SpecificationUtils.and(spec,
|
||||||
(root, query, builder) -> builder.equal(root.get("customerFile").get("customer"), customer));
|
(root, query, builder) -> builder.equal(root.get("customerFile").get("customer").get("id"), paramsNode.get("customer").asInt()));
|
||||||
|
}
|
||||||
|
if (paramsNode.has("company")) {
|
||||||
|
spec = SpecificationUtils.and(spec,
|
||||||
|
(root, query, builder) -> builder.equal(root.get("customerFile").get("customer").get("company").get("id"), paramsNode.get("company").asInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
spec = SpecificationUtils.andParam(spec, paramsNode, "customerFile");
|
spec = SpecificationUtils.andParam(spec, paramsNode, "customerFile");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.ecep.contract.ds.project.service;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.ecep.contract.util.SpecificationUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheConfig;
|
import org.springframework.cache.annotation.CacheConfig;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
@@ -63,7 +64,7 @@ public class DeliverySignMethodService
|
|||||||
spec = getSpecification(paramsNode.get("searchText").asText());
|
spec = getSpecification(paramsNode.get("searchText").asText());
|
||||||
}
|
}
|
||||||
// field
|
// field
|
||||||
// spec = SpecificationUtils.andParam(spec, paramsNode, "company");
|
spec = SpecificationUtils.andParam(spec, paramsNode, "saleType");
|
||||||
Page<DeliverySignMethod> page = findAll(spec, pageable);
|
Page<DeliverySignMethod> page = findAll(spec, pageable);
|
||||||
return page.map(DeliverySignMethod::toVo);
|
return page.map(DeliverySignMethod::toVo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,493 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.ecep.contract.SpringApp;
|
||||||
|
import com.ecep.contract.config.SmbConfig;
|
||||||
|
import com.hierynomus.msdtyp.AccessMask;
|
||||||
|
import com.hierynomus.mserref.NtStatus;
|
||||||
|
import com.hierynomus.msfscc.FileAttributes;
|
||||||
|
import com.hierynomus.msfscc.fileinformation.FileAllInformation;
|
||||||
|
import com.hierynomus.mssmb2.SMB2CreateDisposition;
|
||||||
|
import com.hierynomus.mssmb2.SMB2CreateOptions;
|
||||||
|
import com.hierynomus.mssmb2.SMB2ShareAccess;
|
||||||
|
import com.hierynomus.mssmb2.SMBApiException;
|
||||||
|
import com.hierynomus.smbj.SMBClient;
|
||||||
|
import com.hierynomus.smbj.auth.AuthenticationContext;
|
||||||
|
import com.hierynomus.smbj.common.SmbPath;
|
||||||
|
import com.hierynomus.smbj.share.DiskShare;
|
||||||
|
import com.hierynomus.smbj.share.File;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMB文件服务类,提供SMB/CIFS协议的文件操作功能
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class SmbFileService implements DisposableBean {
|
||||||
|
private final SMBClient client;
|
||||||
|
private AuthenticationContext authContext;
|
||||||
|
private final SmbConfig smbConfig;
|
||||||
|
private final ReentrantLock authLock = new ReentrantLock();
|
||||||
|
|
||||||
|
// 连接空闲超时时间:3分钟
|
||||||
|
private static final long CONNECTION_IDLE_TIMEOUT_MS = 3 * 60 * 1000;
|
||||||
|
|
||||||
|
// 连接信息内部类,用于存储连接和最后使用时间
|
||||||
|
private static class ConnectionInfo {
|
||||||
|
private final com.hierynomus.smbj.connection.Connection connection;
|
||||||
|
private volatile long lastUsedTimestamp;
|
||||||
|
|
||||||
|
public ConnectionInfo(com.hierynomus.smbj.connection.Connection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.lastUsedTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.hierynomus.smbj.connection.Connection getConnection() {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastUsedTimestamp() {
|
||||||
|
return lastUsedTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLastUsedTimestamp() {
|
||||||
|
this.lastUsedTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIdle(long timeoutMs) {
|
||||||
|
return (System.currentTimeMillis() - lastUsedTimestamp) > timeoutMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接池,使用ConcurrentHashMap确保线程安全
|
||||||
|
private final Map<String, ConnectionInfo> connectionPool = new ConcurrentHashMap<>();
|
||||||
|
// 连接池锁,用于同步连接的创建和关闭
|
||||||
|
private final ReentrantLock connectionPoolLock = new ReentrantLock();
|
||||||
|
// 定时清理线程池
|
||||||
|
private final ScheduledExecutorService cleanupScheduler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,注入SMB客户端和配置,初始化定时清理任务
|
||||||
|
*
|
||||||
|
* @param smbClient SMB客户端实例
|
||||||
|
*/
|
||||||
|
public SmbFileService(@Autowired SMBClient smbClient, @Autowired ScheduledExecutorService executor) {
|
||||||
|
this.client = smbClient;
|
||||||
|
this.smbConfig = SpringApp.getBean(SmbConfig.class);
|
||||||
|
|
||||||
|
// 初始化定时清理任务,每30秒运行一次
|
||||||
|
this.cleanupScheduler = executor;
|
||||||
|
|
||||||
|
// 启动定时清理任务,延迟1分钟后开始,每30秒执行一次
|
||||||
|
this.cleanupScheduler.scheduleAtFixedRate(this::cleanupIdleConnections, 1, 30, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取认证上下文,线程安全实现
|
||||||
|
*
|
||||||
|
* @param host 主机名
|
||||||
|
* @return 认证上下文
|
||||||
|
*/
|
||||||
|
private AuthenticationContext getAuthenticationContext(String host) {
|
||||||
|
// 双重检查锁定模式,确保线程安全
|
||||||
|
if (authContext == null) {
|
||||||
|
authLock.lock();
|
||||||
|
try {
|
||||||
|
if (authContext == null) {
|
||||||
|
log.debug("Creating new AuthenticationContext for host: {}", host);
|
||||||
|
authContext = new AuthenticationContext(
|
||||||
|
smbConfig.getUsername(),
|
||||||
|
smbConfig.getPassword().toCharArray(),
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
authLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return authContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SMB操作的通用方法,简化连接和会话的创建
|
||||||
|
*
|
||||||
|
* @param smbPath SMB路径
|
||||||
|
* @param operation 要执行的操作
|
||||||
|
* @param <T> 操作返回类型
|
||||||
|
* @return 操作结果
|
||||||
|
* @throws IOException 如果操作失败
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 从连接池获取或创建连接
|
||||||
|
*
|
||||||
|
* @param hostname 主机名
|
||||||
|
* @return SMB连接
|
||||||
|
* @throws IOException 如果创建连接失败
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 从连接池获取或创建连接
|
||||||
|
*
|
||||||
|
* @param hostname 主机名
|
||||||
|
* @return SMB连接
|
||||||
|
* @throws IOException 如果创建连接失败
|
||||||
|
*/
|
||||||
|
private com.hierynomus.smbj.connection.Connection getConnection(String hostname) throws IOException {
|
||||||
|
// 首先检查连接池是否已有该主机的连接
|
||||||
|
ConnectionInfo connectionInfo = connectionPool.get(hostname);
|
||||||
|
com.hierynomus.smbj.connection.Connection connection = null;
|
||||||
|
|
||||||
|
// 如果连接存在且有效,则更新最后使用时间并返回
|
||||||
|
if (connectionInfo != null) {
|
||||||
|
connection = connectionInfo.getConnection();
|
||||||
|
if (connection != null && connection.isConnected()) {
|
||||||
|
// 更新连接的最后使用时间
|
||||||
|
connectionInfo.updateLastUsedTimestamp();
|
||||||
|
log.debug("Reusing SMB connection for host: {}", hostname);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果连接不存在或已关闭,则创建新连接
|
||||||
|
connectionPoolLock.lock();
|
||||||
|
try {
|
||||||
|
// 双重检查锁定模式
|
||||||
|
connectionInfo = connectionPool.get(hostname);
|
||||||
|
if (connectionInfo != null) {
|
||||||
|
connection = connectionInfo.getConnection();
|
||||||
|
if (connection != null && connection.isConnected()) {
|
||||||
|
connectionInfo.updateLastUsedTimestamp();
|
||||||
|
log.debug("Reusing SMB connection for host: {}", hostname);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
// 如果连接已失效,从池中移除
|
||||||
|
connectionPool.remove(hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新连接
|
||||||
|
log.debug("Creating new SMB connection for host: {}", hostname);
|
||||||
|
connection = client.connect(hostname);
|
||||||
|
connectionInfo = new ConnectionInfo(connection);
|
||||||
|
connectionPool.put(hostname, connectionInfo);
|
||||||
|
} finally {
|
||||||
|
connectionPoolLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理空闲连接的定时任务
|
||||||
|
*/
|
||||||
|
private void cleanupIdleConnections() {
|
||||||
|
log.debug("Running idle connections cleanup task");
|
||||||
|
|
||||||
|
// 创建要移除的连接列表,避免在迭代时修改Map
|
||||||
|
List<String> idleHostnames = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
// 查找所有空闲连接
|
||||||
|
for (Map.Entry<String, ConnectionInfo> entry : connectionPool.entrySet()) {
|
||||||
|
String hostname = entry.getKey();
|
||||||
|
ConnectionInfo connectionInfo = entry.getValue();
|
||||||
|
|
||||||
|
// 检查连接是否空闲超时
|
||||||
|
if (connectionInfo != null && connectionInfo.isIdle(CONNECTION_IDLE_TIMEOUT_MS)) {
|
||||||
|
idleHostnames.add(hostname);
|
||||||
|
log.debug("Found idle connection for host: {}, will be closed", hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭并移除空闲连接
|
||||||
|
if (!idleHostnames.isEmpty()) {
|
||||||
|
connectionPoolLock.lock();
|
||||||
|
try {
|
||||||
|
for (String hostname : idleHostnames) {
|
||||||
|
ConnectionInfo connectionInfo = connectionPool.get(hostname);
|
||||||
|
if (connectionInfo != null) {
|
||||||
|
try {
|
||||||
|
log.debug("Closing idle connection for host: {}", hostname);
|
||||||
|
connectionInfo.getConnection().close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error closing idle connection for host: {}", hostname, e);
|
||||||
|
}
|
||||||
|
connectionPool.remove(hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
connectionPoolLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Idle connections cleanup completed, closed {} connections", idleHostnames.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SMB操作的通用方法,使用连接池
|
||||||
|
*
|
||||||
|
* @param smbPath SMB路径
|
||||||
|
* @param operation 要执行的操作
|
||||||
|
* @param <T> 操作返回类型
|
||||||
|
* @return 操作结果
|
||||||
|
* @throws IOException 如果操作失败
|
||||||
|
*/
|
||||||
|
private <T> T executeSmbOperation(SmbPath smbPath, SmbOperation<T> operation) throws IOException {
|
||||||
|
String hostname = smbPath.getHostname();
|
||||||
|
com.hierynomus.smbj.connection.Connection connection = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从连接池获取连接
|
||||||
|
connection = getConnection(hostname);
|
||||||
|
|
||||||
|
// 使用获取的连接进行身份验证
|
||||||
|
var session = connection.authenticate(getAuthenticationContext(hostname));
|
||||||
|
|
||||||
|
try (var share = (DiskShare) session.connectShare(smbPath.getShareName())) {
|
||||||
|
return operation.execute(share, smbPath.getPath());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 如果操作失败且连接存在,检查连接状态
|
||||||
|
if (connection != null && !connection.isConnected()) {
|
||||||
|
// 从连接池移除失效的连接
|
||||||
|
connectionPool.remove(hostname);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到SMB服务器
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param fileContent 文件内容字节数组
|
||||||
|
* @throws IOException 如果上传失败
|
||||||
|
*/
|
||||||
|
public void uploadFile(String filePath, byte[] fileContent) throws IOException {
|
||||||
|
Objects.requireNonNull(filePath, "File path cannot be null");
|
||||||
|
Objects.requireNonNull(fileContent, "File content cannot be null");
|
||||||
|
|
||||||
|
log.debug("Uploading file: {} with size: {} bytes", filePath, fileContent.length);
|
||||||
|
|
||||||
|
var smbPath = SmbPath.parse(filePath);
|
||||||
|
|
||||||
|
executeSmbOperation(smbPath, (share, path) -> {
|
||||||
|
// 创建目录(如果不存在)
|
||||||
|
String directoryPath = path.substring(0, path.lastIndexOf('/'));
|
||||||
|
if (!share.folderExists(directoryPath)) {
|
||||||
|
share.mkdir(directoryPath);
|
||||||
|
log.debug("Created directory: {}", directoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件内容
|
||||||
|
try (File smbFile = share.openFile(smbPath.getPath(),
|
||||||
|
EnumSet.of(AccessMask.GENERIC_WRITE),
|
||||||
|
EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL),
|
||||||
|
EnumSet.of(SMB2ShareAccess.FILE_SHARE_WRITE),
|
||||||
|
SMB2CreateDisposition.FILE_CREATE,
|
||||||
|
EnumSet.noneOf(SMB2CreateOptions.class))) {
|
||||||
|
try (OutputStream out = smbFile.getOutputStream()) {
|
||||||
|
out.write(fileContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("Successfully uploaded file: {}", filePath);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查SMB路径是否存在
|
||||||
|
*
|
||||||
|
* @param smbPath SMB路径
|
||||||
|
* @return 如果路径存在则返回true
|
||||||
|
* @throws IOException 如果检查失败
|
||||||
|
*/
|
||||||
|
public boolean exists(SmbPath smbPath) throws IOException {
|
||||||
|
return executeSmbOperation(smbPath, (share, path) -> {
|
||||||
|
try {
|
||||||
|
FileAllInformation info = share.getFileInformation(path);
|
||||||
|
if (info.getStandardInformation().isDirectory()) {
|
||||||
|
return share.folderExists(path);
|
||||||
|
}
|
||||||
|
return share.fileExists(path);
|
||||||
|
} catch (SMBApiException e) {
|
||||||
|
if (e.getStatus().equals(NtStatus.STATUS_OBJECT_NAME_NOT_FOUND)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.error("Error checking if path exists: {}", path, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否存在(基于File对象)
|
||||||
|
*
|
||||||
|
* @param file 文件对象
|
||||||
|
* @return 如果文件存在则返回true
|
||||||
|
*/
|
||||||
|
public boolean exists(java.io.File file) {
|
||||||
|
Objects.requireNonNull(file, "File cannot be null");
|
||||||
|
try {
|
||||||
|
var smbPath = SmbPath.parse(file.getAbsolutePath());
|
||||||
|
return exists(smbPath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error checking if file exists: {}", file.getAbsolutePath(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建目录
|
||||||
|
*
|
||||||
|
* @param path 要创建的目录路径
|
||||||
|
* @throws IOException 如果创建失败
|
||||||
|
*/
|
||||||
|
public void mkdir(java.io.File path) throws IOException {
|
||||||
|
Objects.requireNonNull(path, "Path cannot be null");
|
||||||
|
var smbPath = SmbPath.parse(path.getAbsolutePath());
|
||||||
|
|
||||||
|
executeSmbOperation(smbPath, (share, smbFilePath) -> {
|
||||||
|
if (!share.folderExists(smbFilePath)) {
|
||||||
|
share.mkdir(smbFilePath);
|
||||||
|
log.debug("Created directory: {}", smbFilePath);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出目录中的文件(不包括子目录)
|
||||||
|
*
|
||||||
|
* @param dir 目录对象
|
||||||
|
* @return 文件列表
|
||||||
|
* @throws IOException 如果列出失败
|
||||||
|
*/
|
||||||
|
public List<java.io.File> listFiles(java.io.File dir) throws IOException {
|
||||||
|
Objects.requireNonNull(dir, "Directory cannot be null");
|
||||||
|
var smbPath = SmbPath.parse(dir.getAbsolutePath());
|
||||||
|
|
||||||
|
return executeSmbOperation(smbPath, (share, path) -> {
|
||||||
|
try {
|
||||||
|
FileAllInformation info = share.getFileInformation(path);
|
||||||
|
if (info.getStandardInformation().isDirectory() && share.folderExists(path)) {
|
||||||
|
var files = share.list(path);
|
||||||
|
return files.stream()
|
||||||
|
.filter(f -> !f.getFileName().startsWith(".") && !f.getFileName().equals(".."))
|
||||||
|
.filter(f -> {
|
||||||
|
try {
|
||||||
|
String fullPath = path + "\\" + f.getFileName();
|
||||||
|
return !share.getFileInformation(fullPath).getStandardInformation().isDirectory();
|
||||||
|
} catch (SMBApiException e) {
|
||||||
|
log.warn("Error checking file type for: {}", f.getFileName(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(f -> new java.io.File(dir, f.getFileName()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
} catch (SMBApiException e) {
|
||||||
|
log.error("Error listing files in directory: {}", path, e);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为文件(非目录)
|
||||||
|
* 修复:之前的实现逻辑错误,现在正确返回是否为文件
|
||||||
|
*
|
||||||
|
* @param file 文件对象
|
||||||
|
* @return 如果是文件(非目录)则返回true
|
||||||
|
* @throws IOException 如果检查失败
|
||||||
|
*/
|
||||||
|
public boolean isFile(java.io.File file) throws IOException {
|
||||||
|
Objects.requireNonNull(file, "File cannot be null");
|
||||||
|
var smbPath = SmbPath.parse(file.getAbsolutePath());
|
||||||
|
|
||||||
|
return executeSmbOperation(smbPath, (share, path) -> {
|
||||||
|
FileAllInformation fileInformation = share.getFileInformation(path);
|
||||||
|
// 修复:返回是否不是目录,即是否为文件
|
||||||
|
return !fileInformation.getStandardInformation().isDirectory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为目录
|
||||||
|
*
|
||||||
|
* @param file 文件对象
|
||||||
|
* @return 如果是目录则返回true
|
||||||
|
* @throws IOException 如果检查失败
|
||||||
|
*/
|
||||||
|
public boolean isDirectory(java.io.File file) throws IOException {
|
||||||
|
Objects.requireNonNull(file, "File cannot be null");
|
||||||
|
var smbPath = SmbPath.parse(file.getAbsolutePath());
|
||||||
|
|
||||||
|
return executeSmbOperation(smbPath, (share, path) -> {
|
||||||
|
FileAllInformation fileInformation = share.getFileInformation(path);
|
||||||
|
return fileInformation.getStandardInformation().isDirectory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() throws Exception {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭并清理所有连接资源
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
log.debug("Shutting down SMB connection pool");
|
||||||
|
|
||||||
|
// 关闭定时清理任务
|
||||||
|
try {
|
||||||
|
cleanupScheduler.shutdown();
|
||||||
|
if (!cleanupScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
cleanupScheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
cleanupScheduler.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭所有连接
|
||||||
|
connectionPoolLock.lock();
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, ConnectionInfo> entry : connectionPool.entrySet()) {
|
||||||
|
try {
|
||||||
|
log.debug("Closing connection to host: {}", entry.getKey());
|
||||||
|
entry.getValue().getConnection().close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error closing connection to host: {}", entry.getKey(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectionPool.clear();
|
||||||
|
|
||||||
|
// 关闭SMB客户端
|
||||||
|
client.close();
|
||||||
|
} finally {
|
||||||
|
connectionPoolLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMB操作接口,用于执行具体的SMB操作
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface SmbOperation<T> {
|
||||||
|
T execute(DiskShare share, String path) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,9 +25,21 @@ spring.jpa.show-sql=false
|
|||||||
spring.data.jpa.repositories.enabled=true
|
spring.data.jpa.repositories.enabled=true
|
||||||
|
|
||||||
spring.lifecycle.timeout-per-shutdown-phase=10s
|
spring.lifecycle.timeout-per-shutdown-phase=10s
|
||||||
# my.downloadsPath = C:\\Users\\SQQ\\Downloads\\
|
|
||||||
spring.data.redis.host=10.84.209.229
|
spring.data.redis.client-type=lettuce
|
||||||
|
spring.data.redis.host=10.84.210.110
|
||||||
|
spring.data.redis.port=6379
|
||||||
|
spring.data.redis.username=default
|
||||||
|
spring.data.redis.password=redis_NFYa7z
|
||||||
spring.data.redis.database=3
|
spring.data.redis.database=3
|
||||||
|
spring.data.redis.repositories.enabled=false
|
||||||
|
|
||||||
|
# Redis连接池和容错配置
|
||||||
|
spring.data.redis.lettuce.pool.max-active=8
|
||||||
|
spring.data.redis.lettuce.pool.max-wait=10000
|
||||||
|
spring.data.redis.lettuce.pool.max-idle=8
|
||||||
|
spring.data.redis.lettuce.pool.min-idle=0
|
||||||
|
spring.data.redis.timeout=60000
|
||||||
|
|
||||||
logging.level.org.hibernate.tool.hbm2ddl=DEBUG
|
logging.level.org.hibernate.tool.hbm2ddl=DEBUG
|
||||||
|
|
||||||
@@ -53,3 +65,6 @@ spring.cache.redis.cache-null-values=true
|
|||||||
server.error.whitelabel.enabled=false
|
server.error.whitelabel.enabled=false
|
||||||
# 设置错误处理路径,确保404等错误能被全局异常处理器捕获
|
# 设置错误处理路径,确保404等错误能被全局异常处理器捕获
|
||||||
spring.web.resources.add-mappings=true
|
spring.web.resources.add-mappings=true
|
||||||
|
|
||||||
|
smb.server.username=qiqing.song
|
||||||
|
smb.server.password=huez8310
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.ecep.contract.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis连接测试类
|
||||||
|
* 用于测试基于application.properties中配置的Redis连接参数是否能正常连接Redis服务器
|
||||||
|
*/
|
||||||
|
@SpringJUnitConfig
|
||||||
|
@SpringBootTest
|
||||||
|
public class RedisConnectionTest {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(RedisConnectionTest.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisConnectionFactory redisConnectionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试Redis连接是否正常
|
||||||
|
* 验证StringRedisTemplate是否成功注入,并且能够执行基本的Redis操作
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRedisConnection() {
|
||||||
|
logger.info("开始测试Redis连接...");
|
||||||
|
|
||||||
|
// 验证RedisConnectionFactory是否成功注入
|
||||||
|
assertNotNull(redisConnectionFactory, "RedisConnectionFactory注入失败");
|
||||||
|
logger.info("RedisConnectionFactory注入成功");
|
||||||
|
|
||||||
|
// 验证StringRedisTemplate是否成功注入
|
||||||
|
assertNotNull(stringRedisTemplate, "StringRedisTemplate注入失败");
|
||||||
|
logger.info("StringRedisTemplate注入成功");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 尝试获取原生连接以验证连接是否正常
|
||||||
|
logger.info("尝试获取Redis原生连接...");
|
||||||
|
redisConnectionFactory.getConnection().close();
|
||||||
|
logger.info("成功获取并关闭Redis原生连接");
|
||||||
|
|
||||||
|
// 执行一个简单的Redis操作来验证连接是否正常
|
||||||
|
String testKey = "test:connection";
|
||||||
|
String testValue = "connection-test-value";
|
||||||
|
|
||||||
|
logger.info("执行Redis写操作,key: {}", testKey);
|
||||||
|
// 写入测试数据
|
||||||
|
stringRedisTemplate.opsForValue().set(testKey, testValue);
|
||||||
|
logger.info("Redis写操作成功");
|
||||||
|
|
||||||
|
logger.info("执行Redis读操作,key: {}", testKey);
|
||||||
|
// 读取测试数据并验证
|
||||||
|
String retrievedValue = stringRedisTemplate.opsForValue().get(testKey);
|
||||||
|
logger.info("Redis读操作成功,获取到值: {}", retrievedValue);
|
||||||
|
|
||||||
|
assertTrue(testValue.equals(retrievedValue), "Redis写入和读取的值不匹配");
|
||||||
|
logger.info("Redis读写值验证成功");
|
||||||
|
|
||||||
|
// 清理测试数据
|
||||||
|
stringRedisTemplate.delete(testKey);
|
||||||
|
logger.info("Redis删除操作成功");
|
||||||
|
|
||||||
|
logger.info("Redis连接测试成功!能够正常执行set、get和delete操作");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 记录详细的异常信息
|
||||||
|
logger.error("Redis连接测试失败: {}", e.getMessage(), e);
|
||||||
|
// 打印异常堆栈以方便调试
|
||||||
|
e.printStackTrace();
|
||||||
|
// 测试失败,但提供详细信息
|
||||||
|
assertTrue(false, "Redis连接测试失败: " + e.getMessage() + ", 请检查Redis服务器是否运行以及配置是否正确");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试Redis连接池状态
|
||||||
|
* 验证Redis连接池是否正常工作
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRedisConnectionPool() {
|
||||||
|
logger.info("开始测试Redis连接池...");
|
||||||
|
|
||||||
|
assertNotNull(stringRedisTemplate, "StringRedisTemplate注入失败");
|
||||||
|
logger.info("StringRedisTemplate注入成功");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 执行多次Redis操作来测试连接池
|
||||||
|
logger.info("执行多次Redis操作以测试连接池...");
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
String testKey = "test:pool:" + i;
|
||||||
|
String testValue = "pool-test-value:" + i;
|
||||||
|
|
||||||
|
logger.info("第{}次测试 - 执行Redis写操作,key: {}", i, testKey);
|
||||||
|
// 写入数据
|
||||||
|
stringRedisTemplate.opsForValue().set(testKey, testValue);
|
||||||
|
|
||||||
|
logger.info("第{}次测试 - 执行Redis读操作,key: {}", i, testKey);
|
||||||
|
// 读取并验证
|
||||||
|
String retrievedValue = stringRedisTemplate.opsForValue().get(testKey);
|
||||||
|
logger.info("第{}次测试 - 获取到值: {}", i, retrievedValue);
|
||||||
|
|
||||||
|
assertTrue(testValue.equals(retrievedValue), "Redis连接池测试失败,第" + i + "次操作出错");
|
||||||
|
logger.info("第{}次测试 - 值验证成功", i);
|
||||||
|
|
||||||
|
// 清理数据
|
||||||
|
stringRedisTemplate.delete(testKey);
|
||||||
|
logger.info("第{}次测试 - 删除操作成功", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Redis连接池测试成功!能够正常处理多个连接请求");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Redis连接池测试失败: {}", e.getMessage(), e);
|
||||||
|
e.printStackTrace();
|
||||||
|
assertTrue(false, "Redis连接池测试失败: " + e.getMessage() + ", 请检查Redis连接池配置和服务器状态");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user