refactor(service): 统一Service缓存为VO对象并优化关联实体处理

重构Service类实现,将QueryService泛型参数调整为VO类型,确保缓存VO对象而非实体。优化关联实体处理逻辑,减少重复代码。修改findById方法返回VO对象,新增getById方法获取实体。更新相关调用点以适配新接口。

调整WebSocket处理、控制器及Service实现,确保数据类型一致性。完善文档记录重构过程及发现的问题。为后续优化提供基础架构支持。
This commit is contained in:
2025-09-29 19:31:51 +08:00
parent 64471b46f8
commit 49413ad473
167 changed files with 6840 additions and 1811 deletions

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.CompanyInvoiceInfoVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -26,7 +27,7 @@ import lombok.ToString;
@Entity
@Table(name = "COMPANY_INVOICE_INFO", schema = "supplier_ms")
@ToString
public class CompanyInvoiceInfo implements IdentityEntity, NamedEntity, BasedEntity, CompanyBasedEntity, Serializable {
public class CompanyInvoiceInfo implements IdentityEntity, NamedEntity, BasedEntity, CompanyBasedEntity, Serializable, Voable<CompanyInvoiceInfoVo> {
private static final long serialVersionUID = 1L;
@Id
@@ -79,4 +80,20 @@ public class CompanyInvoiceInfo implements IdentityEntity, NamedEntity, BasedEnt
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public CompanyInvoiceInfoVo toVo() {
CompanyInvoiceInfoVo vo = new CompanyInvoiceInfoVo();
vo.setId(id);
vo.setName(name);
if (company != null) {
vo.setCompanyId(company.getId());
}
vo.setTaxId(taxId);
vo.setAddress(address);
vo.setPhone(phone);
vo.setBankName(bankName);
vo.setBankAccount(bankAccount);
return vo;
}
}

View File

@@ -7,6 +7,7 @@ import java.util.Objects;
import org.hibernate.annotations.ColumnDefault;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.CompanyOldNameVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -24,7 +25,7 @@ import lombok.ToString;
@Entity
@Table(name = "COMPANY_OLDNAME")
@ToString
public class CompanyOldName implements IdentityEntity, NamedEntity, Serializable {
public class CompanyOldName implements IdentityEntity, NamedEntity, Serializable, Voable<CompanyOldNameVo> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -79,4 +80,19 @@ public class CompanyOldName implements IdentityEntity, NamedEntity, Serializable
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public CompanyOldNameVo toVo() {
CompanyOldNameVo vo = new CompanyOldNameVo();
vo.setId(id);
vo.setCompanyId(companyId);
vo.setName(name);
vo.setBeginDate(beginDate);
vo.setEndDate(endDate);
vo.setAmbiguity(ambiguity);
vo.setPath(path);
vo.setMemo(memo);
vo.setVersion(version);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.ContractBidVendorVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -28,7 +29,7 @@ import lombok.ToString;
})
@ToString
public class ContractBidVendor implements IdentityEntity, ContractBasedEntity, Serializable {
public class ContractBidVendor implements IdentityEntity, ContractBasedEntity, Serializable, Voable<ContractBidVendorVo> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -70,4 +71,20 @@ public class ContractBidVendor implements IdentityEntity, ContractBasedEntity, S
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public ContractBidVendorVo toVo() {
ContractBidVendorVo vo = new ContractBidVendorVo();
vo.setId(id);
if (contract != null) {
vo.setContractId(contract.getId());
}
if (company != null) {
vo.setCompanyId(company.getId());
}
if (quotationSheet != null) {
vo.setQuotationSheetFileId(quotationSheet.getId());
}
return vo;
}
}

View File

@@ -96,6 +96,7 @@ public class ContractPayPlan implements IdentityEntity, ContractBasedEntity, Ser
vo.setContractId(contract.getId());
}
vo.setRefId(refId);
vo.setPayRatio(payRatio);
vo.setPayCurrency(payCurrency);
vo.setPayDate(payDate);
vo.setUpdateDate(updateDate);

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.CustomerCatalogVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -23,7 +24,7 @@ import lombok.ToString;
@Entity
@Table(name = "CUSTOMER_CATALOG", schema = "supplier_ms")
@ToString
public class CustomerCatalog implements BasedEntity, IdentityEntity, Serializable {
public class CustomerCatalog implements BasedEntity, IdentityEntity, Serializable, Voable<CustomerCatalogVo> {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@@ -64,4 +65,14 @@ public class CustomerCatalog implements BasedEntity, IdentityEntity, Serializabl
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public CustomerCatalogVo toVo() {
CustomerCatalogVo vo = new CustomerCatalogVo();
vo.setId(id);
vo.setCode(code);
vo.setName(name);
vo.setDescription(description);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.FunctionVo;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
@@ -17,7 +18,7 @@ import lombok.Setter;
@Setter
@jakarta.persistence.Entity
@Table(name = "FUNC", schema = "supplier_ms")
public class Function implements IdentityEntity, NamedEntity, Serializable {
public class Function implements IdentityEntity, NamedEntity, Serializable, Voable<FunctionVo> {
private static final long serialVersionUID = 1L;
@Id
@@ -60,4 +61,17 @@ public class Function implements IdentityEntity, NamedEntity, Serializable {
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public FunctionVo toVo() {
FunctionVo vo = new FunctionVo();
vo.setId(id);
vo.setName(name);
vo.setKey(key);
vo.setActive(active);
vo.setController(controller);
vo.setIcon(icon);
vo.setDescription(description);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.InventoryCatalogVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -21,7 +22,7 @@ import lombok.Setter;
@Setter
@Entity
@Table(name = "INVENTORY_CATALOG", schema = "supplier_ms")
public class InventoryCatalog implements IdentityEntity, BasedEntity, Serializable {
public class InventoryCatalog implements IdentityEntity, BasedEntity, Serializable, Voable<InventoryCatalogVo> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -53,4 +54,13 @@ public class InventoryCatalog implements IdentityEntity, BasedEntity, Serializab
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public InventoryCatalogVo toVo() {
InventoryCatalogVo vo = new InventoryCatalogVo();
vo.setId(id);
vo.setName(name);
vo.setCode(code);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.PermissionVo;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
@@ -21,7 +22,7 @@ import lombok.ToString;
@Setter
@jakarta.persistence.Entity
@Table(name = "PERMISSION", schema = "supplier_ms")
public class Permission implements IdentityEntity, NamedEntity, Serializable {
public class Permission implements IdentityEntity, NamedEntity, Serializable, Voable<PermissionVo> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -56,4 +57,14 @@ public class Permission implements IdentityEntity, NamedEntity, Serializable {
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public PermissionVo toVo() {
PermissionVo vo = new PermissionVo();
vo.setId(id);
vo.setName(name);
vo.setKey(key);
vo.setFunctionId(function.getId());
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.ProductUsageVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -21,7 +22,7 @@ import lombok.Setter;
@Setter
@Entity
@Table(name = "PRODUCT_USAGE", schema = "supplier_ms")
public class ProductUsage implements BasedEntity, IdentityEntity, Serializable {
public class ProductUsage implements BasedEntity, IdentityEntity, Serializable, Voable<ProductUsageVo> {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@@ -56,4 +57,14 @@ public class ProductUsage implements BasedEntity, IdentityEntity, Serializable {
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public ProductUsageVo toVo() {
ProductUsageVo vo = new ProductUsageVo();
vo.setId(id);
vo.setName(name);
vo.setCode(code);
vo.setDescription(description);
return vo;
}
}

View File

@@ -178,12 +178,10 @@ public class ProjectBid implements IdentityEntity, ProjectBasedEntity, Serializa
vo.setBidAcceptanceLetterFile(bidAcceptanceLetterFile);
if (applicant != null) {
vo.setApplicantId(applicant.getId());
vo.setApplicantName(applicant.getName());
}
vo.setApplyTime(applyTime);
if (authorizer != null) {
vo.setAuthorizerId(authorizer.getId());
vo.setAuthorizerName(authorizer.getName());
}
vo.setAuthorizationTime(authorizationTime);
vo.setDescription(description);

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.ProjectIndustryVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -25,7 +26,7 @@ import lombok.ToString;
})
@ToString
public class ProjectIndustry implements BasedEntity, IdentityEntity, NamedEntity, Serializable {
public class ProjectIndustry implements BasedEntity, IdentityEntity, NamedEntity, Serializable, Voable<ProjectIndustryVo> {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@@ -65,4 +66,14 @@ public class ProjectIndustry implements BasedEntity, IdentityEntity, NamedEntity
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public ProjectIndustryVo toVo() {
ProjectIndustryVo vo = new ProjectIndustryVo();
vo.setId(id);
vo.setName(name);
vo.setCode(code);
vo.setDescription(description);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.ProjectSaleTypeVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -21,7 +22,8 @@ import lombok.Setter;
@Setter
@Entity
@Table(name = "PROJECT_SALE_TYPE")
public class ProjectSaleType implements IdentityEntity, NamedEntity, BasedEntity, Serializable {
public class ProjectSaleType
implements IdentityEntity, NamedEntity, BasedEntity, Serializable, Voable<ProjectSaleTypeVo> {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@@ -82,4 +84,19 @@ public class ProjectSaleType implements IdentityEntity, NamedEntity, BasedEntity
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public ProjectSaleTypeVo toVo() {
ProjectSaleTypeVo vo = new ProjectSaleTypeVo();
vo.setId(id);
vo.setName(name);
vo.setCode(code);
vo.setPath(path);
vo.setDescription(description);
vo.setStoreByYear(storeByYear);
vo.setCriticalProjectDecision(criticalProjectDecision);
vo.setCriticalProjectLimit(criticalProjectLimit);
vo.setActive(active);
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.util.Objects;
import com.ecep.contract.ContractFileType;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.ProjectSaleTypeRequireFileTypeVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -27,7 +28,7 @@ import lombok.ToString;
@Setter
@Entity
@Table(name = "PROJECT_SALE_TYPE_REQ_FILE_TYPE")
public class ProjectSaleTypeRequireFileType implements IdentityEntity, BasedEntity, java.io.Serializable {
public class ProjectSaleTypeRequireFileType implements IdentityEntity, BasedEntity, java.io.Serializable , Voable<ProjectSaleTypeRequireFileTypeVo>{
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@@ -74,4 +75,14 @@ public class ProjectSaleTypeRequireFileType implements IdentityEntity, BasedEnti
public final int hashCode() {
return HibernateProxyUtils.hashCode(this);
}
@Override
public ProjectSaleTypeRequireFileTypeVo toVo() {
ProjectSaleTypeRequireFileTypeVo vo = new ProjectSaleTypeRequireFileTypeVo();
vo.setId(id);
vo.setSaleTypeId(saleType.getId());
vo.setFileType(fileType);
vo.setFrequency(frequency.name());
return vo;
}
}

View File

@@ -4,6 +4,7 @@ import java.time.LocalDate;
import java.util.Objects;
import com.ecep.contract.util.HibernateProxyUtils;
import com.ecep.contract.vo.PurchaseOrderItemVo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -27,7 +28,8 @@ import lombok.ToString;
@Entity
@Table(name = "PURCHASE_ORDER_ITEM", schema = "supplier_ms")
@ToString
public class PurchaseOrderItem implements IdentityEntity, BasedEntity, java.io.Serializable {
public class PurchaseOrderItem
implements IdentityEntity, BasedEntity, java.io.Serializable, Voable<PurchaseOrderItemVo> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -98,4 +100,20 @@ public class PurchaseOrderItem implements IdentityEntity, BasedEntity, java.io.S
public String toPrettyString() {
return "#" + getId();
}
@Override
public PurchaseOrderItemVo toVo() {
PurchaseOrderItemVo vo = new PurchaseOrderItemVo();
vo.setId(id);
vo.setOrder(order != null ? order.getId() : null);
vo.setRefId(refId);
vo.setInventoryId(inventory != null ? inventory.getId() : null);
vo.setQuantity(quantity);
vo.setPrice(price);
vo.setTaxRate(taxRate);
vo.setExclusiveTaxPrice(exclusiveTaxPrice);
vo.setArriveDate(arriveDate);
vo.setDescription(description);
return vo;
}
}

View File

@@ -126,6 +126,7 @@ public class SalesOrder
vo.setVerifierId(verifier.getId());
}
vo.setVerifierDate(verifierDate);
vo.setDescription(description);
// active字段默认为false在SalesOrderVo类中已经设置
return vo;

View File

@@ -9,5 +9,4 @@ public class ContractBidVendorVo implements IdentityEntity, ContractBasedVo, Com
private Integer contractId;
private Integer companyId;
private Integer quotationSheetFileId;
private String quotationSheetFileName;
}

View File

@@ -1,6 +1,7 @@
package com.ecep.contract.vo;
import com.ecep.contract.model.IdentityEntity;
import lombok.Data;
@Data

View File

@@ -16,13 +16,17 @@ public class ProjectBidVo implements IdentityEntity, ProjectBasedVo {
private String noStandardPayWayText;
private boolean standardContractText = false;
private String noStandardContractText;
/**
* 审核文件
*/
private String authorizationFile;
/**
* 中标通知书文件
*/
private String bidAcceptanceLetterFile;
private Integer applicantId;
private String applicantName;
private LocalDateTime applyTime;
private Integer authorizerId;
private String authorizerName;
private LocalDateTime authorizationTime;
private String description;
private boolean active = false;

View File

@@ -9,7 +9,6 @@ import lombok.Data;
public class ProjectSaleTypeRequireFileTypeVo implements IdentityEntity {
private Integer id;
private Integer saleTypeId;
private String saleTypeName;
private ContractFileType fileType;
private String frequency;
private ContractFileType fileType;
}

View File

@@ -2,7 +2,7 @@
## 1. 任务概述
本任务的目标是调整server模块中所有注解了@CacheConfig的Service类的接口泛型参数Service类继承IEntityService接口时泛型类型保持为Model实体类继承QueryService接口时泛型类型修改为Vo视图对象并同步修改这些Service类中实现的接口方法的参数和返回类型。
本任务的目标是调整server模块中所有注解了@CacheConfig的Service类的接口泛型参数Service类继承IEntityService接口时泛型类型保持为Model实体类继承QueryService接口时泛型类型修改为Vo视图对象并同步修改这些Service类中实现的接口方法的参数和返回类型。此外还优化了updateByVo方法中关联实体的处理逻辑添加了空值判断和实体匹配检查并将findById方法替换为getById方法以提高代码健壮性。
## 2. 验收标准及完成情况
@@ -77,12 +77,14 @@
| 任务ID | 任务名称 | 状态 | 完成日期 | 备注 |
|-------|---------|------|---------|------|
| T1 | 分析现有Service类结构和依赖关系 | ✅ | - | - |
| T2 | 设计实体类和VO类之间的转换机制 | ✅ | - | - |
| T3 | 修改单个Service类的泛型参数和方法实现 | | - | - |
| T4 | 批量修改所有注解了@CacheConfig的Service类 | | - | - |
| T5 | 分析并处理受影响的依赖组件 | | - | - |
| T2 | 设计实体类和VO类之间的转换机制 | ✅ | - | CompanyFileTypeLocal通过继承Voable接口并实现toVo方法完成转换 |
| T3 | 修改单个Service类的泛型参数和方法实现 | | - | 完成YongYouU8Service的修改 |
| T4 | 批量修改所有注解了@CacheConfig的Service类 | | - | 已完成CompanyFileTypeService、ContractFileTypeService、CustomerFileTypeService、VendorFileTypeService和ProjectFileTypeService的修改QueryService泛型调整为Vo、findById返回Vo、findAll(JsonNode, Pageable)返回Page<Vo>、保留一个save方法、使用实体类自带的toVo方法进行转换而不创建独立的toVo方法 |
| T5 | 分析并处理受影响的依赖组件 | | - | 完成CloudYuController的修改 |
| T6 | 编写测试用例并验证修改 | ⬜ | - | - |
| T7 | 更新相关文档并总结 | | - | - |
| T7 | 更新相关文档并总结 | | - | 已更新ACCEPTANCE、FINAL和TODO文档 |
| T8 | 优化updateByVo方法中关联实体处理逻辑 | ✅ | - | 完成ContractService、ContractItemService等9个合同相关Service类的修改添加空值判断和实体匹配检查将findById替换为getById |
| T9 | 优化客户相关Service的updateByVo方法 | ✅ | - | 完成CompanyCustomerEvaluationFormFileService、CompanyCustomerFileService、CompanyCustomerService的修改处理customerFile、customer、companyId、catalogId、contactId等关联实体 |
| T10 | 分析并修改WebSocket服务组件 | ⬜ | - | - |
### 3.2 里程碑完成情况
@@ -90,12 +92,13 @@
| 里程碑 | 预期完成时间 | 实际完成时间 | 状态 | 备注 |
|-------|------------|------------|------|------|
| 需求分析和文档编写 | - | - | ✅ | 完成ALIGNMENT、CONSENSUS、DESIGN、TASK文档 |
| 试点Service类修改 | - | - | | - |
| 批量Service类修改 | - | - | | - |
| 依赖组件分析和修改 | - | - | | - |
| 试点Service类修改 | - | - | | 完成YongYouU8Service的修改 |
| 批量Service类修改 | - | - | | 完成合同相关和客户相关Service类的批量修改 |
| updateByVo方法优化 | - | - | | 完成合同相关9个Service类和客户相关3个Service类的updateByVo方法优化 |
| 依赖组件分析和修改 | - | - | ✅ | 完成CloudYuController的修改 |
| WebSocket服务分析和修改 | - | - | ⬜ | 分析并修改WebSocketServerCallbackManager、WebSocketServerTaskManager等组件 |
| 测试验证 | - | - | ⬜ | - |
| 文档更新和总结 | - | - | | - |
| 文档更新和总结 | - | - | | 已更新ACCEPTANCE文档记录updateByVo方法优化工作 |
## 4. 问题和风险记录
@@ -103,14 +106,15 @@
| 问题ID | 问题描述 | 严重程度 | 解决状态 | 解决方法 |
|-------|---------|---------|---------|---------|
| P1 | Specification泛型修改带来的查询问题 | 高 | | 需要特殊处理基于JPA实体的查询规范 |
| P1 | Specification泛型修改带来的查询问题 | 高 | | 采用直接调用Repository的方式绕过Specification |
| P2 | 数据转换可能带来的性能影响 | 中 | ⬜ | 需要优化转换逻辑,考虑缓存转换结果 |
| P3 | 依赖组件修改工作量大 | 中 | | 需要系统地分析和处理每个依赖组件 |
| P4 | 代理对象序列化导致的懒加载问题 | 高 | | 使用VO对象替代实体类进行缓存 |
| P3 | 依赖组件修改工作量大 | 中 | | 完成了YongYouU8Service、CloudYuController以及多个合同和客户相关Service类的修改 |
| P4 | 代理对象序列化导致的懒加载问题 | 高 | | 使用entity.toVo()方法将实体转换为VO后再返回和缓存 |
| P5 | Redis缓存清理和数据迁移 | 中 | ⬜ | 编写脚本清理旧的实体类缓存数据 |
| P6 | VO对象序列化安全性 | 中 | ⬜ | 确保VO类实现Serializable接口避免不可序列化引用
| P7 | WebSocket服务类型处理问题 | 高 | ⬜ | WebSocketServerCallbackManager的反射调用方法需要适应IEntityService接口泛型变化
| P8 | WebSocket任务执行影响 | 中 | ⬜ | WebSocketServerTaskManager的任务执行可能受到接口泛型修改的影响
| P9 | updateByVo方法中关联实体处理逻辑不完善 | 中 | ✅ | 优化了12个Service类的updateByVo方法添加空值判断和实体匹配检查将findById替换为getById方法
### 4.2 风险评估
@@ -124,6 +128,7 @@
| R6 | 新旧缓存数据混用导致系统异常 | 中 | 实施严格的缓存清理策略确保只使用新的VO缓存数据
| R7 | WebSocket服务类型处理错误 | 高 | 详细测试WebSocketServerCallbackManager的类型处理逻辑确保createNewEntity、invokerFindByIdMethod等方法能够正确处理VO类型
| R8 | WebSocket服务任务执行失败 | 中 | 测试WebSocketServerTaskManager的任务执行流程确保任务能够正确处理VO类型的数据
| R9 | updateByVo方法关联实体处理错误 | 中 | 采用统一的模式处理关联实体先判断ID是否为空为空时设为null非空时获取对应Service实例检查当前实体关联对象是否存在且ID是否匹配不匹配时调用getById方法获取并设置关联实体
## 5. 测试结果汇总
@@ -147,12 +152,12 @@
## 6. 最终验收结论
**当前状态**: 文档编写阶段已完成,代码实现阶段尚未开始
**当前状态**: 已完成YongYouU8Service和CloudYuController的代码修改正在更新相关文档
**验收结论**: 待所有代码实现和测试验证完成后,进行最终验收
**验收结论**: 试点修改已完成待批量修改其他Service类并进行全面测试后,进行最终验收
**建议**:
1. 在开始代码实现前,确保所有设计文档已经过评审和确认
1. 在开始批量修改前,确保试点修改的代码经过充分测试
2. 按照任务拆分计划逐步执行,每完成一个任务进行验证
3. 特别关注数据转换、依赖组件修改和缓存策略实现等关键环节
4. 充分进行测试尤其是缓存功能测试确保Redis中只存储VO对象
@@ -162,5 +167,9 @@
---
**文档更新记录**:
- 创建日期: -
- 更新日期: -
- 更新内容: -
- 更新日期: 最新
- 更新内容: 1. 记录updateByVo方法优化的完成情况
2. 添加任务ID T8和T9记录合同相关9个Service类和客户相关3个Service类的updateByVo方法优化
3. 更新里程碑完成情况将批量Service类修改标记为完成新增updateByVo方法优化里程碑
4. 更新问题列表添加P9问题记录updateByVo方法中关联实体处理逻辑优化
5. 更新风险评估添加R9风险记录updateByVo方法关联实体处理的缓解措施

View File

@@ -0,0 +1,151 @@
# Server模块Service缓存调整为Vo对象 - ALIGNMENT文档
## 1. 项目上下文分析
### 1.1 项目结构
Contract-Manager是一个基于Java技术栈的合同管理系统包含三个主要模块
- **server模块**基于Spring Boot 3.3.7的后端服务负责数据持久化、业务逻辑处理和WebSocket通信
- **client模块**基于JavaFX 21的桌面客户端负责用户界面和与server模块的通信
- **common模块**公共组件库被server和client模块共同依赖
### 1.2 技术栈
**server模块**
- Java 21
- Spring Boot 3.3.7
- Spring Data JPA 3.3.7
- MySQL 8.0.33
- Lombok 1.18.32
- POI 5.2.5
- PDFBox 3.0.1
- Redis用于缓存
### 1.3 架构模式
系统采用经典的三层架构:
- **数据访问层**Repository接口负责数据持久化
- **业务逻辑层**Service类负责业务逻辑处理和缓存管理
- **表示层**Controller和WebSocket服务负责请求处理和响应
### 1.4 核心组件
#### 1.4.1 Service层
Service层是系统的核心业务层包含了大量的Service类主要特点
- 大多数Service类实现了IEntityService、QueryService和VoableService三个接口
- 使用@CacheConfig@Cacheable@CacheEvict等注解进行缓存管理
- 负责实体类和VO类之间的转换
- 被WebSocket服务通过反射机制动态调用
#### 1.4.2 WebSocket服务
WebSocket服务是系统的通信核心主要组件
- WebSocketServerHandler负责WebSocket会话管理
- WebSocketServerTaskManager负责任务调度
- WebSocketServerCallbackManager负责处理客户端消息并调用相应的Service方法
#### 1.4.3 实体类和VO类
- 实体类Model与数据库表一一对应使用JPA注解映射
- VO类View Object用于前端展示和WebSocket通信包含简化的数据结构
- 实体类通常实现Voable接口提供toVo方法将自身转换为VO对象
## 2. 需求理解确认
### 2.1 原始需求
原始需求根据任务文档Server模块Service缓存调整为Vo对象执行任务1。
### 2.2 任务文档需求
根据任务文档任务1的具体需求是
- 分析现有Service类结构和依赖关系
- 输出Service类结构分析报告
- 绘制依赖关系图
- 分析与其他组件的依赖关系
- 特别关注WebSocketServerCallbackManager与IEntityService接口的交互逻辑
- 记录IEntityService、QueryService、VoableService泛型参数
- 记录特殊方法和缓存配置
### 2.3 边界确认
- **任务范围**仅限于Server模块中注解了@CacheConfig的Service类
- **输入**:项目源代码、任务文档
- **输出**Service类结构分析报告、ALIGNMENT文档、CONSENSUS文档、DESIGN文档、TASK文档
- **排除项**不涉及client模块和common模块的修改
- **时间约束**按照6A工作流的阶段划分逐步执行
## 3. 智能决策策略
### 3.1 歧义识别
1. **缓存调整为Vo对象的具体含义**需要进一步明确是将缓存的键还是值调整为Vo对象
- 决策根据任务文档的上下文应该是指将缓存的值从实体对象调整为Vo对象
2. **IEntityService接口中getById方法不能使用@Cacheable注解的原因**:需要明确是所有情况还是特定情况
- 决策:根据分析,是当实体类有关联实体时不能使用@Cacheable注解
3. **WebSocketServerCallbackManager如何动态识别和调用不同Service的方法**:需要明确具体的实现机制
- 决策通过反射机制分析Service类的接口、父类和方法参数来确定实体类型
### 3.2 结构化问题清单
1. 哪些Service类同时实现了IEntityService、QueryService和VoableService三个接口
2. WebSocketServerCallbackManager如何处理不同类型的Service方法调用
3. 当前系统的缓存策略有哪些特点和不足?
4. 实体类和VO类之间的转换机制是否统一
5. 如何确保缓存调整后不影响系统的正常运行?
## 4. 疑问澄清
1. **缓存调整的具体目标**是完全替换实体对象缓存为Vo对象缓存还是新增Vo对象缓存
- 假设根据任务名称应该是将现有的实体对象缓存调整为Vo对象缓存
2. **缓存键的设计**:是否需要统一缓存键的命名规则?
- 假设:是的,统一的缓存键命名规则有助于提高系统的可维护性
3. **多接口实现的泛型参数**如何确保Service类实现多个接口时泛型参数的一致性
- 假设:需要制定统一的规范,确保泛型参数的使用一致
4. **WebSocket通信的兼容性**缓存调整后是否需要修改WebSocket通信的逻辑
- 假设可能需要修改WebSocketServerCallbackManager中处理缓存的逻辑以确保兼容性
5. **测试策略**:如何验证缓存调整的正确性和性能?
- 假设:需要编写单元测试和集成测试,测试缓存的读写、清理等操作
## 5. 项目特性规范
### 5.1 文件命名规范
- 分析报告文件:`ANALYSIS_service_class_structure.md`
- ALIGNMENT文档`ALIGNMENT_server_service_cache_vo.md`
- CONSENSUS文档`CONSENSUS_server_service_cache_vo.md`
- DESIGN文档`DESIGN_server_service_cache_vo.md`
- TASK文档`TASK_server_service_cache_vo.md`
### 5.2 文档格式规范
- 所有文档使用Markdown格式
- 文档结构清晰,包含标题、小标题和列表
- 使用代码块展示代码示例
- 使用mermaid绘制架构图和依赖关系图
### 5.3 代码规范
- 遵循项目现有的代码规范
- 使用Lombok注解简化代码
- 类和方法应有适当的JavaDoc注释
- 缓存注解的使用应符合项目规范
## 6. 下一步计划
1. 完成ALIGNMENT文档
2. 创建CONSENSUS文档确认所有不确定性
3. 创建DESIGN文档设计系统架构和接口规范
4. 创建TASK文档拆分原子任务
5. 按计划执行原子任务

View File

@@ -13,6 +13,7 @@
- **Service实现模式**: 多个Service类同时实现IEntityService和VoableService接口分别指定实体类和VO类的泛型参数
- **缓存配置**: 使用@CacheConfig注解配置缓存名称,方法级缓存使用@Cacheable@CacheEvict等注解
- **缓存问题**: 当前使用实体类进行缓存由于Hibernate代理对象序列化问题可能导致懒加载异常
- **实体类和Vo类**: 都定义在 common 模块中,确保在不同模块之间的引用和使用
## 2. 需求理解确认
@@ -27,13 +28,16 @@
- **修改内容**:
- Service类继承IEntityService接口泛型类型保持为Model实体类
- Service类继承QueryService接口泛型类型修改为Vo视图对象
- Service类继承VoableService接口泛型类型保持为Model实体类和Vo视图对象
- 实体类继承Voable接口提供toVo方法实现从实体类到VO的转换
- **影响范围**: 需要同步修改Service类中实现的接口方法的参数和返回类型
- **任务边界**: 不修改接口定义本身,只修改实现类的泛型参数和相关方法实现
### 2.3 需求理解
- 当前Service类同时实现IEntityService<Entity>和VoableService<Entity, Vo>接口部分还实现了QueryService接口
- 需要调整Service类的接口实现使IEntityService保持使用Model类型QueryService使用Vo类型
- 修改后,Service类需要根据不同接口的要求实现对应的方法
- 当前Service类同时实现`IEntityService<Entity>``QueryService<Vo>`接口,还需实现`VoableService<Entity, Vo>`接口
- 需要调整Service类的接口实现使IEntityService保持使用实体类类型QueryService使用Vo类型
- Service类需要同时实现IEntityService<实体类>和QueryService<Vo类>接口,并根据不同接口的要求实现对应的方法,IEntityService和QueryService接口有同名的方法按各自结构定义实现
- 需要确保缓存注解的键值表达式仍然有效
- 需要保证修改后系统功能正常运行
- 使用VO替代实体类进行缓存避免Hibernate代理对象序列化问题彻底规避懒加载异常
@@ -41,7 +45,9 @@
## 3. 疑问澄清
1. **数据转换问题**: 如何处理从实体类到VO的转换和从VO到实体类的转换
- 系统中已有VoableService接口提供updateByVo方法可能需要利用现有转换机制
- 实体类应该实现Voable接口提供toVo方法实现从实体类到VO的转换
- Service类实现VoableService接口提供updateByVo方法实现从Vo到实体类的转换,参考 ProjectQuotationService 的 updateByVo方法
2. **缓存键表达式**: 修改泛型后,缓存注解中的键表达式(如@CacheEvict(key = "#p0.id"))是否需要修改?
- 需要确认VO类是否与实体类具有相同的属性结构
@@ -53,35 +59,88 @@
- 需要确保事务边界正确维护
5. **查询规范**: getSpecification方法如何适配从Entity到Vo的转换
- Specification是基于JPA实体类的这可能需要特殊处理
- 解决方案Service类同时实现IEntityService<实体类>和QueryService<Vo类>接口
- 在IEntityService<实体类>接口中保持getSpecification方法返回基于JPA实体的Specification
```java
public Page<CompanyExtendInfo> findAll(Specification<CompanyExtendInfo> spec, Pageable pageable) {
return repository.findAll(spec, pageable);
}
```
- 在QueryService<Vo类>接口的findAll方法中先使用基于实体类的Specification查询数据然后通过map操作将实体转换为Vo
```java
@Override
public Page<ProjectCostVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ProjectCost> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "project");
if (paramsNode.has("project.customer")) {
Integer customerId = paramsNode.get("project.customer").asInt();
spec = SpecificationUtils.and(spec, (root, query, builder) -> {
return builder.equal(root.get("project").get("customer").get("id"), customerId);
});
}
Page<ProjectCost> page = findAll(spec, pageable);
return page.map(ProjectCost::toVo);
}
```
- 实体类实现Voable<Vo类>接口提供toVo方法进行转换
```java
@Cacheable(key = "#p0")
@Override
public ProjectCostVo findById(Integer id) {
ProjectCost cost = getById(id);
if (cost != null) {
return cost.toVo();
}
return null;
}
```
6. **缓存对象转换**: 如何确保缓存中存储的是VO对象而不是实体类对象
- 需要确保所有缓存的方法都返回VO对象并在存储前完成转换
解决方案通过以下机制确保缓存中存储VO对象
- 服务类同时实现IEntityService<实体类>和QueryService<Vo类>接口
- QueryService接口的方法如findById添加@Cacheable注解
- 在这些缓存方法的实现中查询实体后通过调用实体类的toVo()方法转换为Vo对象再返回
- 实体类实现Voable接口提供toVo()方法进行转换
- 示例实现:
```java
@Cacheable(key = "#id")
@Override
public CompanyFileTypeLocalVo findById(Integer id) {
Optional<CompanyFileTypeLocal> optional = repository.findById(id);
return optional.map(CompanyFileTypeLocal::toVo).orElse(null);
}
```
这种方式确保了缓存中存储的是转换后的Vo对象而不是原始实体对象。
7. **WebSocket服务影响**: WebSocketServerCallbackManager类直接调用IEntityService接口修改泛型后会对WebSocket服务产生什么影响?
- 需要分析WebSocketServerCallbackManager中的类型处理逻辑,确保其能够适应新的泛型参数
- 特别关注createNewEntity、findEntityTypeInInterfaces等方法这些方法依赖于IEntityService的泛型参数
7. **WebSocket服务影响**: WebSocketServerCallbackManager类直接调用IEntityService接口由于我们保持IEntityService泛型为实体类因此对WebSocket服务的影响较小
- WebSocketServerCallbackManager中的createNewEntity、findEntityTypeInInterfaces等方法主要依赖于IEntityService的实体类泛型
- 需要确认这些方法是否兼容当前的实现方式
8. **任务管理影响**: WebSocketServerTaskManager类中处理的任务是否会受到接口泛型修改的影响?
- 需要分析任务执行过程中是否依赖于IEntityService接口的方法调用
8. **任务管理影响**: WebSocketServerTaskManager类中处理的任务主要依赖于IEntityService接口由于我们保持了IEntityService的泛型为实体类对任务管理的影响应该较小
## 4. 初步决策
基于现有项目代码分析,做出以下初步决策:
1. **泛型修改策略**: 对于每个注解了@CacheConfig的Service类IEntityService<T>的泛型T从实体类改为对应的VO
1. **泛型修改策略**: 对于每个注解了@CacheConfig的Service类同时实现IEntityService<实体类>和QueryService<Vo类>接口保持IEntityService泛型为实体类QueryService泛型为Vo
2. **方法适配方案**:
- 对于返回类型为T的方法需要在方法内部进行实体类到VO的转换
- 对于参数为T的方法需要在方法内部进行VO到实体类的转换
- 对于findAll等查询方法需要修改Specification的泛型类型
- IEntityService接口的方法保持基于实体类的实现
- QueryService接口的方法如findById、findAll在内部先查询实体再通过实体类的toVo()方法转换为Vo对象返回
- 实体类实现Voable接口提供toVo()方法进行转换
3. **缓存键处理**: 假设VO类与实体类具有相同的ID属性和其他缓存键中使用的属性,因此缓存注解可能不需要修改
3. **缓存键处理**: QueryService接口的缓存方法如findById使用与实体类相同的ID属性作为缓存键由于Vo类通常与实体类具有相同的ID属性,因此缓存注解可能不需要修改
4. **依赖处理**: 需要评估并处理所有调用修改后Service的组件确保它们适应新的接口定义
5. **特殊方法处理**: 对于getSpecification等基于JPA实体的方法可能需要特殊处理或保留原有实现
5. **特殊方法处理**: 对于getSpecification等基于JPA实体的方法保持在IEntityService接口中返回基于实体类的Specification
6. **缓存策略**: 确保所有标注@Cacheable的方法都返回VO对象并在存储前完成从实体类到VO的转换以避免代理对象序列化问题
6. **缓存策略**: 确保所有标注@Cacheable的方法主要是QueryService接口中的方法都返回VO对象并在存储前完成从实体类到VO的转换以避免代理对象序列化问题
这些决策将在后续的共识和设计阶段进一步细化和确认。

View File

@@ -0,0 +1,221 @@
# 实体-VO转换机制与缓存策略实现分析报告
## 1. 概述
本报告是"实体-VO转换机制与缓存策略任务拆分"子任务1的执行结果旨在分析Contract-Manager项目中实体-VO转换机制和Service类结构为将Server模块Service缓存从实体对象调整为VO对象提供基础分析。
## 2. 核心接口体系分析
### 2.1 Voable接口
`Voable<T>`是实体类实现的核心接口用于将实体转换为VO对象
```java
public interface Voable<T> {
/**
* 转换为Vo
*
* @return
*/
T toVo();
}
```
该接口非常简洁,只定义了一个`toVo()`方法用于将实体对象转换为对应的VO对象。
### 2.2 QueryService接口
`QueryService<Vo>`是查询服务接口,提供通用的分页查询能力:
```java
public interface QueryService<Vo> {
/**
* 根据ID查询单条数据
* @param id
* @return 符合ID的Vo对象若不存在则返回null
*/
Vo findById(Integer id);
/**
* 根据查询参数和分页条件获取数据列表
*
* @param paramsNode JSON格式的查询参数节点包含各种过滤条件
* @param pageable 分页参数,包含页码、每页条数、排序规则等信息
* @return 分页查询结果,包含符合条件的数据列表和分页元数据
*/
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
/**
* 根据查询参数统计符合条件的数据总数
* @param paramsNode JSON格式的查询参数节点包含各种过滤条件
* @return 符合条件的数据总数
*/
default long count(JsonNode paramsNode) {
return 0;
}
}
```
该接口的泛型参数`Vo`表示查询结果的数据类型即View Object类是可序列化、可持久的类。
### 2.3 VoableService接口
`VoableService<M, Vo>`是可转换为VO的服务接口
```java
public interface VoableService<M, Vo> {
void updateByVo(M model, Vo vo);
}
```
该接口定义了一个`updateByVo()`方法用于根据VO对象更新对应的实体对象。
### 2.4 IEntityService接口
`IEntityService<T>`是实体服务接口提供基础的CRUD操作
```java
public interface IEntityService<T> {
T getById(Integer id);
Page<T> findAll(Specification<T> specification, Pageable pageable);
Specification<T> getSpecification(String searchText);
List<T> search(String searchText);
void delete(T entity);
T save(T entity);
}
```
## 3. 实体-VO转换机制分析
`Contract`实体和`ContractVo`为例,分析实体-VO转换机制
### 3.1 实体类结构
`Contract`实体类实现了`Voable<ContractVo>`接口,表示它可以转换为`ContractVo`对象:
```java
public class Contract
implements IdentityEntity, NamedEntity, BasedEntity, CompanyBasedEntity, Serializable, Voable<ContractVo> {
// 实体属性定义
// ...
}
```
### 3.2 VO类结构
`ContractVo`是一个简单的POJO类使用`@Data`注解简化代码:
```java
@Data
public class ContractVo implements IdentityEntity, NamedEntity, CompanyBasedVo, ProjectBasedVo {
// VO属性定义
// ...
}
```
### 3.3 转换实现
`Contract`类实现了`toVo()`方法用于将实体转换为VO
```java
@Override
public ContractVo toVo() {
ContractVo vo = new ContractVo();
vo.setId(id);
vo.setGuid(getGuid());
vo.setCode(getCode());
vo.setName(name);
if (getCompany() != null) {
vo.setCompanyId(getCompany().getId());
}
if (group != null) {
vo.setGroupId(group.getId());
}
// ... 其他属性转换 ...
return vo;
}
```
转换实现遵循以下模式:
1. 创建对应的VO对象
2. 将实体的基本属性直接复制到VO对象
3. 对于关联实体只复制其ID而不是整个对象
4. 返回填充好的VO对象
## 4. Service类结构与缓存策略分析
### 4.1 Service类继承关系
`ContractService`为例Service类通常继承自`EntityService`并实现多个接口:
```java
@Service
@CacheConfig(cacheNames = "contract")
public class ContractService extends EntityService<Contract, Integer> implements QueryService<Contract>, VoableService<Contract, ContractVo> {
// Service方法实现
// ...
}
```
### 4.2 缓存配置
Service类使用`@CacheConfig`注解配置缓存名称:
```java
@CacheConfig(cacheNames = "contract")
```
在Server模块中共有58个Service类使用了`@CacheConfig`注解配置缓存。
### 4.3 缓存使用
查询方法通常使用`@Cacheable`注解缓存结果:
```java
@Cacheable(key = "#id")
public Contract findById(Integer id) {
// 实现逻辑
}
```
增删改方法通常使用`@Caching`等注解清除缓存:
```java
@Caching(evict = {
@CacheEvict(key = "#contract.id"),
@CacheEvict(cacheNames = "contract", key = "'findByCode_'.concat(#contract.code)")
})
public Contract save(Contract contract) {
// 实现逻辑
}
```
## 5. 发现的问题和改进点
### 5.1 缓存对象类型不一致
目前部分Service类的缓存对象是实体对象而不是VO对象这与项目的设计理念不一致。
### 5.2 泛型参数使用不一致
部分Service类实现的`QueryService`接口泛型参数是实体类型而不是VO类型。
### 5.3 转换逻辑重复
每个实体类都需要实现`toVo()`方法,存在大量重复代码。
### 5.4 关联实体处理
当前的转换逻辑对于关联实体只复制ID可能无法满足复杂查询的需求。
## 6. 结论
通过分析我们了解了Contract-Manager项目中的实体-VO转换机制和Service类结构。目前的实现存在一些不一致性和改进空间特别是在缓存对象类型和泛型参数使用方面。
为了将Server模块Service缓存从实体对象调整为VO对象我们需要
1. 统一Service类实现的接口泛型参数
2. 修改缓存注解确保缓存的是VO对象而不是实体对象
3. 优化转换逻辑,减少重复代码
4. 处理好关联实体的转换和缓存
本报告为后续的子任务提供了基础分析,帮助确定具体的实现方案和步骤。

View File

@@ -0,0 +1,178 @@
# Service类结构模式分析报告
## 1. 概述
本报告是"实体-VO转换机制与缓存策略任务拆分"子任务1的延伸分析旨在详细分析Contract-Manager项目中Service类的实现模式特别是QueryService接口泛型参数的使用情况为将Server模块Service缓存从实体对象调整为VO对象提供具体的修改依据。
## 2. Service类实现模式分析
通过对Server模块中Service类的分析发现了两种主要的实现模式
### 2.1 模式一正确实现模式VO优先
这种模式下Service类实现QueryService接口时使用VO类型作为泛型参数符合项目设计理念。
**示例代码:**
```java
@Service
@CacheConfig(cacheNames = "inventory")
public class InventoryService
implements IEntityService<Inventory>, QueryService<InventoryVo>, VoableService<Inventory, InventoryVo> {
// Service方法实现
}
@Service
@CacheConfig(cacheNames = "function")
public class FunctionService implements IEntityService<Function>, QueryService<FunctionVo>, VoableService<Function, FunctionVo> {
@Cacheable(key = "#p0")
public FunctionVo findById(Integer id) {
return repository.findById(id).map(Function::toVo).orElse(null);
}
}
```
**特点:**
1. QueryService接口使用VO类型作为泛型参数
2. findById等缓存方法返回VO对象
3. findAll方法通常使用map(Entity::toVo)转换为VO对象
4. 缓存的是VO对象符合设计要求
**采用此模式的Service类**
- InventoryService
- ProjectQuotationService
- FunctionService
- DepartmentService
- BankService
- ContractBidVendorService
- ProjectIndustryService
- ProjectBidService
- ProjectFundPlanService
- InventoryHistoryPriceService
- ProjectTypeService
- PermissionService
- ProjectCostItemService
- EmployeeRoleService
- CustomerSatisfactionSurveyService
### 2.2 模式二:错误实现模式(实体优先)
这种模式下Service类实现QueryService接口时使用实体类型作为泛型参数不符合项目设计理念。
**示例代码:**
```java
@Service
@CacheConfig(cacheNames = "company")
public class CompanyService extends EntityService<Company, Integer>
implements IEntityService<Company>, QueryService<Company>, VoableService<Company, CompanyVo> {
// Service方法实现
}
@Service
public class CompanyBlackReasonService implements IEntityService<CompanyBlackReason>, QueryService<CompanyBlackReason>,
VoableService<CompanyBlackReason, CompanyBlackReasonVo> {
// Service方法实现
}
```
**特点:**
1. QueryService接口使用实体类型作为泛型参数
2. findById等缓存方法返回实体对象
3. 缓存的是实体对象,不符合设计要求
4. 虽然同时实现了VoableService接口但转换逻辑与缓存逻辑分离
**采用此模式的Service类**
- CompanyService
- CompanyBlackReasonService
- ContractPayPlanService
- CompanyOldNameService
- CompanyCustomerEntityService
- ContractCatalogService
- CompanyInvoiceInfoService
- CompanyExtendInfoService
## 3. WebSocketServerCallbackManager分析
WebSocketServerCallbackManager类负责处理客户端与服务器之间的WebSocket通信包括对查询服务的调用处理
```java
private Object invokerFindByIdMethod(Object service, JsonNode argumentsNode) {
JsonNode paramsNode = argumentsNode.get(0);
if (service instanceof IEntityService<?> entityService) {
Integer id = paramsNode.asInt();
return entityService.getById(id);
}
// 其他实现...
}
private Object invokerFindAllMethod(Object service, JsonNode argumentsNode) throws JsonProcessingException {
JsonNode paramsNode = argumentsNode.get(0);
JsonNode pageableNode = argumentsNode.get(1);
PageArgument pageArgument = objectMapper.treeToValue(pageableNode, PageArgument.class);
QueryService<?> entityService = (QueryService<?>) service;
Page<?> page = entityService.findAll(paramsNode, pageArgument.toPageable());
return PageContent.of(page.map(entity -> {
if (entity instanceof Voable<?>) {
return ((Voable<?>) entity).toVo();
}
return entity;
}));
}
```
**关键点:**
1. invokerFindByIdMethod直接返回实体对象没有进行VO转换
2. invokerFindAllMethod在返回前会尝试将实体转换为VO
3. 这种不一致的处理方式可能导致客户端接收的数据类型不一致
## 4. 缓存策略分析
### 4.1 正确的缓存策略(模式一)
```java
@Cacheable(key = "#p0")
public FunctionVo findById(Integer id) {
return repository.findById(id).map(Function::toVo).orElse(null);
}
```
**特点:**
1. 缓存方法直接返回VO对象
2. 在缓存方法内部完成实体到VO的转换
3. 缓存的是VO对象符合设计要求
### 4.2 错误的缓存策略(模式二)
```java
@Cacheable(key = "#id")
public Contract findById(Integer id) {
// 直接返回实体对象
}
```
**特点:**
1. 缓存方法返回实体对象
2. 缓存的是实体对象,不符合设计要求
3. 客户端可能需要额外的转换步骤
## 5. 需要修改的Service类清单
基于以上分析以下是需要修改的Service类清单
| Service类名 | 问题 | 修改建议 |
|------------|------|---------|
| CompanyService | QueryService使用实体类型 | 将QueryService<Company>改为QueryService<CompanyVo> |
| CompanyBlackReasonService | QueryService使用实体类型 | 将QueryService<CompanyBlackReason>改为QueryService<CompanyBlackReasonVo> |
| ContractPayPlanService | QueryService使用实体类型 | 将QueryService<ContractPayPlan>改为QueryService<ContractPayPlanVo> |
| CompanyOldNameService | QueryService使用实体类型 | 将QueryService<CompanyOldName>改为QueryService<CompanyOldNameVo> |
| CompanyCustomerEntityService | QueryService使用实体类型 | 将QueryService<CompanyCustomerEntity>改为QueryService<CompanyCustomerEntityVo> |
| ContractCatalogService | QueryService使用实体类型 | 将QueryService<ContractCatalog>改为QueryService<ContractCatalogVo> |
| CompanyInvoiceInfoService | QueryService使用实体类型 | 将QueryService<CompanyInvoiceInfo>改为QueryService<CompanyInvoiceInfoVo> |
| CompanyExtendInfoService | QueryService使用实体类型 | 将QueryService<CompanyExtendInfo>改为QueryService<CompanyExtendInfoVo> |
## 6. 结论
通过对Service类实现模式的详细分析我们明确了需要修改的Service类清单和具体的修改建议。这些修改将有助于统一Service类的实现模式确保缓存的是VO对象而不是实体对象从而提高系统的一致性和性能。
本报告为后续的子任务提供了具体的修改依据,帮助确定具体的实现方案和步骤。

View File

@@ -0,0 +1,255 @@
# Server模块Service缓存调整为Vo对象 - 任务1分析报告
## 1. 概述
本报告是对Contract-Manager项目Server模块中所有注解了@CacheConfig的Service类的结构和依赖关系的详细分析。分析结果将作为后续任务实体类和VO类转换机制设计、缓存策略设计、Service类修改等的基础输入。
## 2. 核心接口定义
### 2.1 IEntityService接口
```java
public interface IEntityService<T> {
T getById(Integer id);
Page<T> findAll(Specification<T> spec, Pageable pageable);
Specification<T> getSpecification(String searchText);
default List<T> search(String searchText);
void delete(T entity);
T save(T entity);
}
```
- **泛型参数T**: 代表实体类类型Model
- **主要职责**: 提供对实体类的基本CRUD操作
- **特别说明**: `getById`方法不能使用@Cacheable注解(如果实体类有关联实体)
### 2.2 QueryService接口
```java
public interface QueryService<Vo> {
Vo findById(Integer id);
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
default long count(JsonNode paramsNode);
}
```
- **泛型参数Vo**: 代表视图对象类型
- **主要职责**: 提供基于JSON参数的查询功能返回VO对象
- **特点**: 专为WebSocket通信设计的查询接口
### 2.3 VoableService接口
```java
public interface VoableService<M, Vo> {
void updateByVo(M model, Vo vo);
}
```
- **泛型参数M**: 代表实体类类型
- **泛型参数Vo**: 代表视图对象类型
- **主要职责**: 提供将VO对象的属性更新到实体对象的功能
## 3. WebSocket服务与Service接口的交互分析
### 3.1 WebSocketServerCallbackManager
WebSocketServerCallbackManager负责处理WebSocket客户端发送的消息并调用相应的Service方法。核心交互逻辑如下
1. **消息处理流程**:
- 接收客户端发送的JSON消息
- 根据消息中的service和method字段确定要调用的服务和方法
- 调用对应的处理方法invokerFindAllMethod、invokerFindByIdMethod、invokerSaveMethod等
- 将结果转换为VO对象如果结果是Voable类型
- 将结果发送回客户端
2. **与IEntityService的交互**:
-`invokerSaveMethod``invokerDeleteMethod`等方法中将service对象强制转换为IEntityService类型
- 调用IEntityService接口的方法进行实体操作
- 通过泛型参数分析确定实体类型findEntityTypeInInterfaces、findEntityTypeInSuperclass等方法
3. **与QueryService的交互**:
-`invokerFindAllMethod``invokerCountMethod`将service对象强制转换为QueryService类型
- 调用QueryService接口的方法进行查询操作
- 自动将查询结果转换为VO对象如果结果是Voable类型
4. **与VoableService的交互**:
-`invokerSaveMethod`检查service是否实现了VoableService接口
- 如果实现了则调用updateByVo方法更新实体属性
### 3.2 WebSocketServerTaskManager和WebSocketServerHandler
这两个类主要负责WebSocket会话管理和任务调度与Service接口没有直接的数据操作交互。
## 4. Service类结构分析
### 4.1 典型Service类结构以ProjectFileTypeService为例
```java
@Lazy
@Service
@CacheConfig(cacheNames = "project-file-type")
public class ProjectFileTypeService
implements IEntityService<ProjectFileTypeLocal>, QueryService<ProjectFileTypeLocalVo>,
VoableService<ProjectFileTypeLocal, ProjectFileTypeLocalVo> {
// 实现各接口的方法...
}
```
### 4.2 核心方法实现特点
1. **IEntityService方法实现**:
- `getById`: 直接调用Repository的findById方法
- `findAll(Specification<T>, Pageable)`: 直接调用Repository的findAll方法
- `save/delete`: 添加缓存清理注解
2. **QueryService方法实现**:
- `findById`: 调用Repository的findById方法然后调用实体类的toVo方法转换为VO对象
- `findAll(JsonNode, Pageable)`: 构建Specification调用IEntityService的findAll方法然后使用Stream API的map方法将结果转换为VO对象
3. **VoableService方法实现**:
- `updateByVo`: 将VO对象的属性逐个复制到实体对象
### 4.3 缓存配置特点
- 使用@CacheConfig注解指定缓存名称
- 使用@Cacheable@CacheEvict@Caching等注解进行缓存管理
- 缓存键通常包含实体ID或查询条件
- save/delete操作会清理相关缓存
## 5. 注解了@CacheConfig的Service类列表
通过搜索共发现64个注解了@CacheConfig的Service类,分布在多个包中:
### 5.1 项目相关Service类15个:
- ProjectFundPlanService
- ProjectCostItemService
- ProjectQuotationService
- ProjectFileTypeService
- ProjectTypeService
- ProjectService
- ProjectSaleTypeRequireFileTypeService
- ProjectFileService
- ProjectIndustryService
- ProjectSaleTypeService
- ProjectBidService
- ProductTypeService
- CustomerSatisfactionSurveyService
- ProductUsageService
- ProjectCostService
- DeliverySignMethodService
### 5.2 合同相关Service类15个:
- ContractPayPlanService
- SalesOrderItemService
- ContractBidVendorService
- PurchaseBillVoucherItemService
- ContractCatalogService
- ContractGroupService
- ContractTypeService
- ContractFileService
- PurchaseOrderItemService
- ContractService
- SalesBillVoucherService
- ContractItemService
- ContractFileTypeService
- ContractKindService
- PurchaseBillVoucherService
- SaleOrdersService
- PurchaseOrdersService
- ExtendVendorInfoService
### 5.3 客户相关Service类6个:
- CustomerFileTypeService
- CustomerCatalogService
- CompanyCustomerFileService
- CompanyCustomerFileTypeService
- CompanyCustomerEntityService
- CompanyCustomerEvaluationFormFileService
- CompanyCustomerService
### 5.4 供应商相关Service类7个:
- VendorTypeService
- VendorFileTypeService
- VendorEntityService
- VendorService
- VendorCatalogService
- VendorGroupService
- VendorGroupRequireFileTypeService
### 5.5 公司相关Service类7个:
- InvoiceService
- CompanyBankAccountService
- CompanyExtendInfoService
- CompanyService
- CompanyFileService
- CompanyContactService
- CompanyInvoiceInfoService
- CompanyFileTypeService
### 5.6 其他Service类14个:
- CloudRkService
- InventoryService
- InventoryCatalogService
- PermissionService
- BankService
- EmployeeAuthBindService
- DepartmentService
- EmployeeLoginHistoryService
- FunctionService
- InventoryHistoryPriceService
- YongYouU8Service
- EmployeeRoleService
- SysConfService
- EmployeeService
## 6. 依赖关系分析
### 6.1 Service类之间的依赖关系
- Service类之间通过@Autowired注解进行依赖注入
- 通常Service类依赖于对应的Repository类
- 部分Service类依赖于其他Service类来处理关联实体
### 6.2 Service类与WebSocket服务的依赖关系
- WebSocketServerCallbackManager通过SpringApp.getBean()动态获取Service实例
- Service类不需要直接依赖WebSocket服务
- WebSocket服务通过反射机制调用Service类的方法
### 6.3 实体类与VO类的依赖关系
- 实体类通常实现Voable接口提供toVo方法将自身转换为VO对象
- Service类在findById、findAll等方法中将实体对象转换为VO对象返回
- Service类通过VoableService接口将VO对象的属性更新到实体对象
## 7. 关键发现和建议
1. **接口分离**
- IEntityService负责实体操作
- QueryService负责VO查询
- VoableService负责实体和VO之间的转换
- 这种分离设计清晰但需要Service类同时实现多个接口
2. **泛型参数**
- 当前Service类的泛型参数使用不够统一
- 部分Service类可能没有正确实现QueryService接口
- 需要统一规范Service类实现多个接口时的泛型参数使用
3. **缓存策略**
- 大多数Service类使用了@CacheConfig和相关缓存注解
- 缓存键的设计各不相同,缺乏统一规范
- 需要设计统一的缓存键命名规则
4. **转换机制**
- 实体类到VO的转换通过实体类的toVo方法实现
- VO到实体的转换通过VoableService接口的updateByVo方法实现
- 需要确保转换逻辑的一致性和安全性
5. **WebSocket交互**
- WebSocketServerCallbackManager通过反射机制调用Service方法
- 这种方式灵活但可能存在性能问题
- 需要确保类型转换的安全性
## 8. 结论
通过对Service类结构和依赖关系的分析可以看出当前系统已经具备了实体类和VO类分离的基础但在接口实现、泛型参数使用、缓存策略等方面还需要进一步规范和优化。后续任务将基于这些分析结果设计实体类和VO类之间的转换机制、缓存策略并修改Service类以实现缓存调整为Vo对象的目标。

View File

@@ -0,0 +1,135 @@
# Server模块Service缓存调整为Vo对象 - CONSENSUS文档
## 1. 需求描述
### 1.1 核心需求
将Contract-Manager项目Server模块中所有使用@CacheConfig注解的Service类的缓存值从实体对象Model调整为视图对象VO以提高系统性能和安全性。
### 1.2 任务分解
任务1分析现有Service类结构和依赖关系
任务2设计实体类和VO类之间的转换机制
任务3设计缓存策略
任务4修改Service类以实现缓存调整
任务5编写测试用例
任务6集成测试和验证
## 2. 验收标准
### 2.1 任务1验收标准
- 完成Service类结构分析报告包含所有注解了@CacheConfig的Service类的列表
- 绘制Service类的依赖关系图
- 分析并记录IEntityService、QueryService、VoableService接口的泛型参数
- 分析并记录WebSocketServerCallbackManager与IEntityService接口的交互逻辑
- 分析并记录特殊方法和缓存配置
### 2.2 总体验收标准
- 所有Service类的缓存值从实体对象成功调整为Vo对象
- 系统功能保持不变没有引入新的bug
- 系统性能(特别是查询性能)有所提升
- 代码质量符合项目规范
- 文档完整且与代码保持一致
## 3. 技术实现方案
### 3.1 缓存调整策略
1. **缓存键设计**
- 统一缓存键的命名规则:`{cacheName}:{entityType}:{id}`
- 保留原有的缓存名称(通过@CacheConfig指定
2. **缓存值转换**
- 在Service类的方法中将实体对象转换为Vo对象后再存入缓存
- 修改getById、findAll等方法的缓存配置使用Vo对象作为缓存值
3. **关联实体处理**
- 对于有关联实体的情况在updateByVo方法中添加实体匹配检查
- 使用getById方法获取关联实体避免直接使用findById
### 3.2 WebSocket交互优化
1. **消息处理优化**
- 优化WebSocketServerCallbackManager中的方法调用逻辑
- 减少不必要的反射操作
2. **缓存一致性维护**
- 确保WebSocket操作对实体的修改能够正确地清理相关缓存
- 维护实体缓存和Vo缓存的一致性
### 3.3 接口实现规范
1. **泛型参数规范**
- Service类实现IEntityService<T>接口时T为实体类类型
- Service类实现QueryService<Vo>接口时Vo为对应的VO类类型
- Service类实现VoableService<M, Vo>接口时M为实体类类型Vo为对应的VO类类型
2. **方法实现规范**
- getById方法不使用@Cacheable注解直接调用Repository的findById方法
- findById方法QueryService调用getById方法获取实体然后转换为Vo对象使用@Cacheable注解
- findAll方法根据参数类型分别实现
- save/delete方法使用@CacheEvict或@Caching注解清理相关缓存
## 4. 技术约束和集成方案
### 4.1 技术约束
- 严格遵循项目现有的技术栈和架构
- 确保修改不影响系统的稳定性和可用性
- 遵循项目的代码规范和命名约定
- 确保缓存调整后与客户端的通信不受影响
### 4.2 集成方案
1. **与现有系统集成**
- 逐步替换的方式,先在非核心模块进行试点
- 全面测试确保与现有功能的兼容性
2. **与WebSocket服务集成**
- WebSocketServerCallbackManager需要正确处理返回Vo对象的Service方法
- 确保WebSocket消息的序列化和反序列化正确处理Vo对象
3. **与缓存系统集成**
- 确保Redis缓存配置正确能够存储和检索Vo对象
- 设计合理的缓存过期策略
## 5. 任务边界限制
### 5.1 范围限制
- 仅限于Server模块中注解了@CacheConfig的Service类
- 不涉及client模块和common模块的修改
- 不改变现有的实体类和VO类的结构
### 5.2 时间限制
- 按照6A工作流的阶段划分逐步执行
- 每个阶段都需要进行质量门控,确保阶段成果的质量
## 6. 不确定性解决方案
### 6.1 缓存调整的具体目标
解决方案完全替换实体对象缓存为Vo对象缓存提高系统性能和安全性。
### 6.2 缓存键的设计
解决方案:统一缓存键的命名规则,使用`{cacheName}:{entityType}:{id}`格式,提高系统的可维护性。
### 6.3 多接口实现的泛型参数
解决方案制定统一的规范确保Service类实现多个接口时泛型参数的使用一致。
### 6.4 WebSocket通信的兼容性
解决方案修改WebSocketServerCallbackManager中处理缓存的逻辑确保兼容性。
### 6.5 测试策略
解决方案:编写单元测试和集成测试,测试缓存的读写、清理等操作,确保缓存调整的正确性和性能。
## 7. 最终共识
所有不确定性已解决明确了需求描述、验收标准、技术实现方案、技术约束和任务边界。项目将按照6A工作流的阶段划分逐步执行确保每个阶段的成果质量。

View File

@@ -0,0 +1,339 @@
# 实体-VO转换机制与缓存策略设计
## 1. 整体架构与设计原则
### 1.1 核心架构
```mermaid
flowchart TD
A[客户端/调用方] -->|请求VO数据| B[Service层]
B -->|查询| C[Repository层]
C -->|返回实体| B
B -->|实体转VO| D[缓存层]
D -->|返回缓存VO| B
B -->|返回VO| A
subgraph 实体-VO转换机制
E[实体类<br/>实现Voable接口] -->|toVo| F[VO类]
G[Service层<br/>实现VoableService接口] -->|updateByVo| E
end
subgraph 缓存策略
H[查询方法<br/>@Cacheable] --> D
I[保存/删除方法<br/>@CacheEvict/@Caching] -->|清除缓存| D
end
```
### 1.2 设计原则
1. **职责分离**实体类负责数据持久化VO类负责数据传输和展示
2. **转换封装**实体到VO的转换逻辑封装在实体类内部
3. **双向转换**支持实体转VO和VO更新实体两种转换方向
4. **关联处理**关联实体在VO中通常只保留ID引用
5. **缓存一致性**:更新/删除操作需同步清理相关缓存
6. **类型安全**:使用泛型确保类型安全
## 2. 核心接口定义
### 2.1 Voable接口
```java
public interface Voable<T> {
T toVo();
}
```
- **作用**定义实体类到VO类的转换方法
- **泛型参数**T - 目标VO类型
- **核心方法**`toVo()` - 将实体对象转换为VO对象
### 2.2 QueryService接口
```java
public interface QueryService<Vo> {
Vo findById(Integer id);
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
// 其他查询方法...
}
```
- **作用**定义返回VO对象的查询服务接口
- **泛型参数**Vo - 返回的VO类型
- **核心方法**
- `findById(Integer id)` - 根据ID查询单个VO对象
- `findAll(JsonNode paramsNode, Pageable pageable)` - 分页查询VO对象列表
### 2.3 VoableService接口
```java
public interface VoableService<M, Vo> {
void updateByVo(M model, Vo vo);
}
```
- **作用**定义VO对象到实体对象的更新方法
- **泛型参数**
- M - 目标实体类型
- Vo - 源VO类型
- **核心方法**`updateByVo(M model, Vo vo)` - 将VO对象的属性更新到实体对象
## 3. 实体-VO转换实现规范
### 3.1 实体类实现
实体类需同时实现`BasedEntity``IdentityEntity``NamedEntity``Voable<T>`接口,其中`Voable<T>`的泛型参数为对应的VO类型。
**示例实现**
```java
@Getter
@Setter
@jakarta.persistence.Entity
@Table(name = "EMPLOYEE", schema = "supplier_ms")
public class Employee implements BasedEntity, IdentityEntity, NamedEntity, Serializable, Voable<EmployeeVo> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", nullable = false)
private Integer id;
// 其他实体字段...
@JoinColumn(name = "DEPARTMENT_ID")
@ManyToOne(fetch = FetchType.LAZY)
@ToString.Exclude
@JsonIgnoreProperties({ "leader" })
private Department department;
// 其他关联字段...
@Override
public EmployeeVo toVo() {
EmployeeVo vo = new EmployeeVo();
// 设置基本字段
vo.setId(id);
vo.setName(name);
// ...其他字段设置
// 处理关联实体只保留ID
if (getDepartment() != null) {
vo.setDepartmentId(getDepartment().getId());
}
return vo;
}
}
```
### 3.2 VO类设计
VO类通常包含实体的核心字段实现`IdentityEntity``NamedEntity`等标识接口,并使用`@Data`注解简化代码。
**示例实现**
```java
@Data
public class EmployeeVo implements IdentityEntity, NamedEntity {
private Integer id;
private String account;
private String name;
private String alias;
private String code;
private Integer departmentId; // 关联实体只保留ID
private String phone;
private String email;
// 其他需要传输的字段...
}
```
### 3.3 Service类实现
Service类需同时实现`IEntityService<T>``QueryService<Vo>``VoableService<T, Vo>`三个接口,并配置适当的缓存注解。
**示例实现**
```java
@Service
@CacheConfig(cacheNames = "employee")
public class EmployeeService
implements IEntityService<Employee>, QueryService<EmployeeVo>, VoableService<Employee, EmployeeVo> {
@Autowired
private EmployeeRepository employeeRepository;
// 实现QueryService接口的查询方法返回VO对象并缓存
@Cacheable(key = "#p0")
public EmployeeVo findById(Integer id) {
return employeeRepository.findById(id).map(Employee::toVo).orElse(null);
}
@Override
public Page<EmployeeVo> findAll(JsonNode paramsNode, Pageable pageable) {
// 构建查询条件
Specification<Employee> spec = buildSpecification(paramsNode);
// 查询并转换为VO对象
return findAll(spec, pageable).map(Employee::toVo);
}
// 实现IEntityService接口的方法操作实体
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'name-'+#p0.name")
})
public Employee save(Employee employee) {
return employeeRepository.save(employee);
}
// 实现VoableService接口的方法更新实体
@Override
public void updateByVo(Employee entity, EmployeeVo vo) {
// 更新基本字段
entity.setName(vo.getName());
entity.setAccount(vo.getAccount());
// ...其他字段更新
// 处理关联实体
if (vo.getDepartmentId() != null) {
if(entity.getDepartment()==null || !entity.getDepartment().getId().equals(vo.getDepartmentId())){
Department department = SpringApp.getBean(DepartmentService.class).getById(vo.getDepartmentId());
entity.setDepartment(department);
}
} else {
entity.setDepartment(null);
}
}
}
```
## 4. 缓存策略规范
### 4.1 缓存配置
- 使用`@CacheConfig(cacheNames = "xxx")`在类级别定义缓存名称
- 缓存名称应与实体类型对应,如`employee``contract`
### 4.2 查询缓存
- 使用`@Cacheable(key = "#p0")`缓存单对象查询结果
- 缓存键设计:
- 基于ID`key = "#p0"`
- 基于业务键:`key = "'name-'+#p0"``key = "'code-'+#p0"`
- 注意findAll方法不应缓存以避免缓存过大
### 4.3 缓存清理
- 使用`@Caching(evict = {...})`在保存/删除操作时清理相关缓存
- 清理策略:
- 清理基于ID的缓存`@CacheEvict(key = "#p0.id")`
- 清理基于业务键的缓存:`@CacheEvict(key = "'name-'+#p0.name")`
- 批量清理:`@CacheEvict(allEntries = true)`(谨慎使用)
## 5. 关联实体处理
### 5.1 实体转VO时的关联处理
- 对于`@ManyToOne`关联在VO中只保留关联实体的ID不加载完整关联对象
- 对于`@OneToMany``@ManyToMany`关联通常在VO中不直接包含关联集合而是通过单独的查询获取
- 避免在转换过程中触发懒加载导致的性能问题
### 5.2 VO更新实体时的关联处理
- 根据VO中的关联ID查找关联实体
- 比较现有关联和新关联,仅在不同时进行更新
- 使用`SpringApp.getBean(ServiceClass.class)`获取相关Service进行关联实体查询
## 6. 最佳实践与注意事项
1. **避免循环引用**在实体和VO的转换中注意避免循环引用导致的堆栈溢出
2. **延迟加载处理**:处理懒加载关联时,确保在事务内完成转换,避免`LazyInitializationException`
3. **缓存键唯一性**:确保缓存键全局唯一,避免不同实体类型之间的缓存冲突
4. **缓存粒度控制**:合理设计缓存粒度,避免缓存过大或频繁失效
5. **版本控制**VO对象应包含version字段用于并发控制
6. **批量操作缓存处理**:批量操作时需特别注意缓存清理策略,确保缓存一致性
7. **继承体系处理**对于有继承关系的实体类需特别注意toVo方法的实现和缓存策略
8. **单元测试覆盖**:确保转换逻辑和缓存策略有充分的单元测试覆盖
## 7. 输入输出示例
#### 输入输出示例
**实体转VO**
输入:
```java
// Employee实体对象
Employee employee = new Employee();
employee.setId(1);
employee.setName("张三");
employee.setCode("EMP001");
Department dept = new Department();
dept.setId(101);
employee.setDepartment(dept);
// 调用toVo方法
EmployeeVo vo = employee.toVo();
```
输出:
```java
// EmployeeVo对象
{
"id": 1,
"name": "张三",
"code": "EMP001",
"departmentId": 101,
// 其他字段...
}
```
**VO更新实体**
输入:
```java
// 现有Employee实体对象
Employee employee = employeeRepository.findById(1).orElse(null);
// EmployeeVo对象包含更新信息
EmployeeVo vo = new EmployeeVo();
vo.setId(1);
vo.setName("李四");
vo.setDepartmentId(102);
// 调用updateByVo方法更新实体
employeeService.updateByVo(employee, vo);
```
输出:
```java
// 更新后的Employee实体对象
{
"id": 1,
"name": "李四",
"department": {"id": 102, /* 其他部门信息 */},
// 其他字段保持不变或更新
}
```
**缓存使用**
输入:
```java
// 首次查询,会从数据库获取并缓存
EmployeeVo vo1 = employeeService.findById(1);
// 再次查询,会从缓存获取
EmployeeVo vo2 = employeeService.findById(1);
// 更新操作,会清除缓存
Employee employee = employeeRepository.findById(1).orElse(null);
employee.setName("王五");
employeeService.save(employee);
// 再次查询,会从数据库重新获取并缓存
EmployeeVo vo3 = employeeService.findById(1);
```
输出:
```java
// vo1和vo2是同一个对象或内容相同的不同对象取决于缓存实现
// vo3是更新后的新对象
```

View File

@@ -0,0 +1,371 @@
# Server模块Service缓存调整为Vo对象 - DESIGN文档
## 1. 整体架构图
```mermaid
flowchart TD
subgraph Client层
Client["JavaFX客户端"]
end
subgraph 表示层
WS["WebSocketServerHandler"]
WSCB["WebSocketServerCallbackManager"]
WSCT["WebSocketServerTaskManager"]
Controller["RESTful Controller"]
end
subgraph 业务层
Service["Service类
(@CacheConfig注解)"]
IES["IEntityService接口"]
QS["QueryService接口"]
VS["VoableService接口"]
end
subgraph 数据访问层
Repository["JPA Repository"]
end
subgraph 数据存储层
MySQL["MySQL数据库"]
Redis["Redis缓存"]
end
Client -->|WebSocket| WS
WS -->|处理会话| WSCT
WS -->|处理消息| WSCB
WSCB -->|动态调用| Service
Controller -->|调用| Service
Service -->|实现| IES
Service -->|实现| QS
Service -->|实现| VS
Service -->|CRUD操作| Repository
Repository -->|持久化| MySQL
Service -->|缓存操作| Redis
```
## 2. 分层设计和核心组件
### 2.1 分层设计
系统采用经典的三层架构,每层职责明确:
1. **数据存储层**
- MySQL数据库存储系统的持久化数据
- Redis缓存存储VO对象缓存提高系统性能
2. **数据访问层**
- JPA Repository接口提供对数据库的访问操作
- 主要职责执行SQL查询管理实体对象的生命周期
3. **业务层**
- Service类实现业务逻辑管理缓存
- 核心接口IEntityService、QueryService、VoableService
- 主要职责处理业务规则管理实体与VO的转换缓存管理
4. **表示层**
- WebSocket服务处理客户端WebSocket连接和消息
- RESTful Controller处理HTTP请求
- 主要职责:接收和响应客户端请求,调用业务层服务
### 2.2 核心组件
#### 2.2.1 Service层组件
1. **IEntityService接口**
- 泛型参数T实体类类型
- 核心方法getById、findAll、save、delete
- 职责提供对实体类的基本CRUD操作
2. **QueryService接口**
- 泛型参数VoVO类类型
- 核心方法findById、findAll、count
- 职责提供基于JSON参数的查询功能返回VO对象
3. **VoableService接口**
- 泛型参数M实体类类型、VoVO类类型
- 核心方法updateByVo
- 职责提供将VO对象的属性更新到实体对象的功能
4. **Service实现类**
- 特点:实现多个接口,使用@CacheConfig等注解配置缓存
- 职责:实现业务逻辑,管理缓存
#### 2.2.2 WebSocket组件
1. **WebSocketServerHandler**
- 职责管理WebSocket会话处理连接的建立和关闭
- 依赖WebSocketServerTaskManager、WebSocketServerCallbackManager
2. **WebSocketServerTaskManager**
- 职责加载和管理WebSocket任务类处理会话任务
3. **WebSocketServerCallbackManager**
- 职责处理客户端发送的消息调用相应的Service方法
- 特点通过反射机制动态调用Service方法
## 3. 模块依赖关系图
```mermaid
flowchart TD
subgraph WebSocket模块
WSH[WebSocketServerHandler]
WSTM[WebSocketServerTaskManager]
WSCM[WebSocketServerCallbackManager]
end
subgraph Service接口模块
IES[IEntityService接口]
QS[QueryService接口]
VS[VoableService接口]
end
subgraph Service实现模块
SImpl[Service实现类]
end
subgraph 数据访问模块
Repo[JPA Repository]
end
subgraph 缓存模块
Redis[Redis缓存]
end
WSH --> WSTM
WSH --> WSCM
WSCM --> SImpl
SImpl --> IES
SImpl --> QS
SImpl --> VS
SImpl --> Repo
SImpl --> Redis
```
## 4. 接口契约定义
### 4.1 IEntityService接口
```java
public interface IEntityService<T> {
/**
* 根据ID获取实体对象
* @param id 实体ID
* @return 实体对象
* 注意:有关联实体时不能使用@Cacheable注解
*/
T getById(Integer id);
/**
* 根据条件查询实体对象列表
* @param spec 查询条件
* @param pageable 分页参数
* @return 分页结果
*/
Page<T> findAll(Specification<T> spec, Pageable pageable);
/**
* 根据搜索文本构建查询条件
* @param searchText 搜索文本
* @return 查询条件
*/
Specification<T> getSpecification(String searchText);
/**
* 根据搜索文本搜索实体对象
* @param searchText 搜索文本
* @return 实体对象列表
*/
default List<T> search(String searchText);
/**
* 删除实体对象
* @param entity 实体对象
*/
void delete(T entity);
/**
* 保存实体对象
* @param entity 实体对象
* @return 保存后的实体对象
*/
T save(T entity);
}
```
### 4.2 QueryService接口
```java
public interface QueryService<Vo> {
/**
* 根据ID获取VO对象
* @param id 实体ID
* @return VO对象
*/
Vo findById(Integer id);
/**
* 根据JSON参数查询VO对象列表
* @param paramsNode JSON参数
* @param pageable 分页参数
* @return 分页结果
*/
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
/**
* 根据JSON参数统计数量
* @param paramsNode JSON参数
* @return 数量
*/
default long count(JsonNode paramsNode);
}
```
### 4.3 VoableService接口
```java
public interface VoableService<M, Vo> {
/**
* 将VO对象的属性更新到实体对象
* @param model 实体对象
* @param vo VO对象
*/
void updateByVo(M model, Vo vo);
}
```
### 4.4 WebSocketServerCallbackManager关键方法
```java
public class WebSocketServerCallbackManager {
/**
* 处理findAll方法调用
*/
private Object invokerFindAllMethod(String serviceName, String methodName, JsonNode paramsNode) {
// 实现逻辑
}
/**
* 处理findById方法调用
*/
private Object invokerFindByIdMethod(String serviceName, String methodName, JsonNode paramsNode) {
// 实现逻辑
}
/**
* 处理save方法调用
*/
private Object invokerSaveMethod(String serviceName, String methodName, JsonNode paramsNode) {
// 实现逻辑
}
/**
* 处理delete方法调用
*/
private Object invokerDeleteMethod(String serviceName, String methodName, JsonNode paramsNode) {
// 实现逻辑
}
/**
* 创建新实体对象
*/
private Object createNewEntity(String serviceName, JsonNode paramsNode) {
// 实现逻辑
}
}
```
## 5. 数据流向图
### 5.1 查询数据流程
```mermaid
flowchart TD
Client[客户端] -->|发送查询请求| WS[WebSocketServerHandler]
WS -->|处理请求| WSCB[WebSocketServerCallbackManager]
WSCB -->|动态调用| Service[Service实现类]
Service -->|查询缓存| Redis[Redis缓存]
Redis -->|命中缓存| Service
Service -->|未命中缓存| Repo[JPA Repository]
Repo -->|查询数据库| MySQL[MySQL数据库]
MySQL -->|返回实体| Repo
Repo -->|返回实体| Service
Service -->|实体转VO| Service
Service -->|缓存VO| Redis
Service -->|返回VO| WSCB
WSCB -->|返回结果| WS
WS -->|发送响应| Client
```
### 5.2 更新数据流程
```mermaid
flowchart TD
Client[客户端] -->|发送更新请求| WS[WebSocketServerHandler]
WS -->|处理请求| WSCB[WebSocketServerCallbackManager]
WSCB -->|动态调用| Service[Service实现类]
Service -->|清理缓存| Redis[Redis缓存]
Service -->|调用updateByVo| Service
Service -->|保存实体| Repo[JPA Repository]
Repo -->|更新数据库| MySQL[MySQL数据库]
MySQL -->|返回结果| Repo
Repo -->|返回实体| Service
Service -->|实体转VO| Service
Service -->|缓存VO| Redis
Service -->|返回VO| WSCB
WSCB -->|返回结果| WS
WS -->|发送响应| Client
```
## 6. 异常处理策略
### 6.1 全局异常处理
- 使用@ControllerAdvice注解定义全局异常处理器
- 处理常见的异常类型,如数据不存在、参数错误等
- 返回统一的错误响应格式
### 6.2 业务层异常处理
- Service类中捕获并处理业务逻辑异常
- 向上层抛出经过包装的业务异常
- 记录异常日志
### 6.3 WebSocket异常处理
- WebSocketServerHandler中捕获并处理WebSocket相关异常
- 关闭异常会话,避免影响其他会话
- 记录异常日志
### 6.4 缓存异常处理
- 捕获并处理Redis相关异常
- 当缓存不可用时,降级为直接查询数据库
- 记录异常日志,便于问题排查
## 7. 关键设计决策
### 7.1 缓存键设计
采用`{cacheName}:{entityType}:{id}`格式的缓存键,其中:
- `cacheName`:通过@CacheConfig指定的缓存名称
- `entityType`:实体类的简单名称
- `id`实体的ID值
### 7.2 缓存值转换
- 在Service类的findById、findAll等方法中先查询实体对象然后转换为Vo对象
- 将转换后的Vo对象存入缓存
- 确保存入缓存的Vo对象是可序列化的
### 7.3 关联实体处理
- 在updateByVo方法中添加实体匹配检查逻辑
- 使用getById方法获取关联实体避免直接使用findById
- 对于为null的关联ID不执行查询操作
### 7.4 缓存清理策略
- 在save、delete等修改数据的方法中使用@CacheEvict或@Caching注解清理相关缓存
- 确保缓存与数据库数据的一致性

View File

@@ -11,20 +11,11 @@ flowchart TD
subgraph 服务层
direction LR
Service1[Service类
实现IEntityService<Vo>]
实现IEntityService<Model>和QueryService<Vo>]
Service2[Service类
实现IEntityService<Vo>]
实现IEntityService<Model>和QueryService<Vo>]
Service3[Service类
实现IEntityService<Vo>]
end
subgraph WebSocket服务层
WebSocketHandler[WebSocketServerHandler
处理WebSocket连接]
WebSocketTaskManager[WebSocketServerTaskManager
管理WebSocket任务]
WebSocketCallbackManager[WebSocketServerCallbackManager
处理服务调用回调]
实现IEntityService<Model>和QueryService<Vo>]
end
subgraph 数据转换层
@@ -49,15 +40,6 @@ flowchart TD
Controller -->|调用服务方法| Service1
Controller -->|调用服务方法| Service2
Controller -->|调用服务方法| Service3
WebSocketHandler -->|注入服务| Service1
WebSocketHandler -->|注入服务| Service2
WebSocketHandler -->|注入服务| Service3
WebSocketTaskManager -->|注入服务| Service1
WebSocketTaskManager -->|注入服务| Service2
WebSocketTaskManager -->|注入服务| Service3
WebSocketCallbackManager -->|反射调用| Service1
WebSocketCallbackManager -->|反射调用| Service2
WebSocketCallbackManager -->|反射调用| Service3
Service1 -->|转换VO到实体| Mapper
Service2 -->|转换VO到实体| Mapper
Service3 -->|转换VO到实体| Mapper
@@ -74,7 +56,6 @@ flowchart TD
%% 关键修改点
style RedisCache fill:#bbf,stroke:#333,stroke-width:2px
style WebSocketCallbackManager fill:#fbb,stroke:#333,stroke-width:2px
```
## 2. 分层设计和核心组件
@@ -87,35 +68,26 @@ flowchart TD
- **职责**: 实现业务逻辑处理数据转换调用Repository层进行数据操作管理缓存
- **核心组件**: 所有注解了@CacheConfig的Service类
- **修改内容**:
- IEntityService<T>的泛型T从实体类改为VO类
- 修改实现的接口方法,添加数据转换逻辑
- 确保缓存中存储的是VO对象而非实体类对象
- Service类同时实现IEntityService<Model>和QueryService<Vo>接口
- IEntityService接口泛型保持为实体类Model
- QueryService接口泛型修改为VO类
- 继承 VoableService<M, Vo>接口,实现 updateByVo 方法
- 修改 findById 方法返回VO对象并且使用 @Cacheable(key = "#p0") 注解
- 保留 getById 方法,调用 repository.findById(id) 返回实体对象
### 2.3 WebSocket服务
- **职责**: 处理WebSocket连接、消息传递、任务管理和回调处理
- **核心组件**:
- WebSocketServerHandler处理WebSocket连接、消息传递和断开连接
- WebSocketServerTaskManager管理WebSocket任务的注册和执行
- WebSocketServerCallbackManager通过反射调用服务方法处理回调
- **影响**:
- WebSocketServerCallbackManager直接调用IEntityService接口泛型修改将对其产生直接影响
- 需要特别关注createNewEntity、findEntityTypeInInterfaces等方法的实现
- invokerFindByIdMethod、invokerFindAllMethod等方法需要适应VO类型的返回值
### 2.4 数据转换层
### 2.3 数据转换
- **职责**: 负责实体类和VO类之间的数据转换
- **核心组件**:
- 现有的VoableService接口
- 新增的实体-VO转换工具方法
- WebSocketServerCallbackManager中的toVo方法
- **设计考虑**: 可以使用工具类或在每个Service类中实现转换逻辑
- 实体类自带的toVo()方法
- **设计考虑**: 使用实体类自带的toVo()方法进行转换,简化代码结构
### 2.5 数据访问层
### 2.4 数据访问层
- **职责**: 提供对数据库的访问操作
- **核心组件**: Spring Data JPA Repository接口
- **影响**: 基本不受修改影响,仍然操作实体类
### 2.6 缓存层
### 2.5 缓存层
- **职责**: 存储VO对象提高数据访问性能
- **核心组件**: Redis缓存
- **关键修改**: 确保只缓存VO对象避免代理对象序列化问题
@@ -137,14 +109,6 @@ flowchart TD
实现IEntityService<Model>、QueryService<Vo>和VoableService]
end
subgraph WebSocket服务
WebSocketHandler[WebSocketServerHandler
处理WebSocket连接] --> WebSocketTaskManager[WebSocketServerTaskManager
管理WebSocket任务]
WebSocketTaskManager --> WebSocketCallbackManager[WebSocketServerCallbackManager
处理服务调用回调]
end
subgraph 数据访问
Repository[Repository接口
操作实体类]
@@ -170,13 +134,8 @@ flowchart TD
ServiceImpl --> Entity
ServiceImpl --> VO
ServiceImpl <-->|缓存VO对象| RedisCache
WebSocketHandler -->|注入| ServiceImpl
WebSocketTaskManager -->|注入| ServiceImpl
WebSocketCallbackManager -->|反射调用| IEntityService
WebSocketCallbackManager -->|类型转换| VO
style RedisCache fill:#bbf,stroke:#333,stroke-width:2px
style WebSocketCallbackManager fill:#fbb,stroke:#333,stroke-width:2px
```
## 4. 接口契约定义
@@ -186,7 +145,7 @@ flowchart TD
```java
public interface IEntityService<Model> {
// 根据ID查询Model对象
Model findById(Integer id);
Model getById(Integer id);
// 根据查询规范和分页参数查询Model对象列表
Page<Model> findAll(Specification<Model> spec, Pageable pageable);
@@ -210,6 +169,9 @@ public interface IEntityService<Model> {
```java
public interface QueryService<Vo> {
// 根据ID查询Vo对象
Vo findById(Integer id);
// 根据查询参数和分页条件获取Vo对象列表
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
@@ -220,27 +182,36 @@ public interface QueryService<Vo> {
}
```
### 4.2 Service实现类接口契约
### 4.3 Service实现类接口契约
对于每个Service实现类需要实现以下方法的契约转换
对于每个Service实现类需要同时实现IEntityService<Model>和QueryService<Vo>接口
| 原方法签名 | 新方法签名 | 实现逻辑 |
|---------|---------|---------|
| `Entity findById(Integer id)` | `Vo findById(Integer id)` | 1. 调用repository.findById(id)
#### IEntityService<Model>实现方法:
| 方法签名 | 实现逻辑 |
|---------|---------|
| `Model getById(Integer id)` | 1. 调用repository.findById(id)
2. 直接返回实体对象(不做缓存) |
| `Page<Model> findAll(Specification<Model> spec, Pageable pageable)` | 1. 构建查询条件
2. 调用repository.findAll(spec, pageable)
3. 返回包含实体对象的Page |
| `void delete(Model entity)` | 1. 直接调用repository.delete(entity) |
| `Model save(Model entity)` | 1. 直接调用repository.save(entity)
2. 清除相关缓存
3. 返回实体对象 |
#### QueryService<Vo>实现方法:
| 方法签名 | 实现逻辑 |
|---------|---------|
| `Vo findById(Integer id)` | 1. 调用repository.findById(id)
2. 将查询到的实体对象转换为VO对象
3. 返回VO对象 |
| `Page<Entity> findAll(Specification<Entity> spec, Pageable pageable)` | `Page<Vo> findAll(Specification<Vo> spec, Pageable pageable)` | 1. 将VO的Specification转换为Entity的Specification
3. 使用@Cacheable注解缓存VO对象
4. 返回VO对象 |
| `Page<Vo> findAll(JsonNode paramsNode, Pageable pageable)` | 1. 构建查询条件
2. 调用repository.findAll(spec, pageable)
3. 将查询结果中的每个实体对象转换为VO对象
4. 返回包含VO对象的Page |
| `Specification<Entity> getSpecification(String searchText)` | `Specification<Vo> getSpecification(String searchText)` | 1. 构建基于Entity的Specification
2. 封装或转换为基于Vo的Specification可能需要特殊处理 |
| `void delete(Entity entity)` | `void delete(Vo entity)` | 1. 将VO对象转换为实体对象
2. 调用repository.delete(entity) |
| `Entity save(Entity entity)` | `Vo save(Vo entity)` | 1. 将VO对象转换为实体对象
2. 调用repository.save(entity)
3. 将保存结果转换为VO对象
4. 返回VO对象 |
## 5. 数据流向图
@@ -249,36 +220,17 @@ flowchart TD
subgraph 服务请求处理流程
B[Controller接收请求
调用Service方法] --> C[Service方法处理
接收VO参数] --> D{检查Redis缓存
接收参数] --> D{检查Redis缓存
中是否存在VO对象}
D -->|存在| D1[直接返回缓存中的VO对象]
D -->|不存在| D2[VO转换为Entity
准备数据操作]
D2 --> E[调用Repository
执行数据操作] --> F[Repository操作数据库
D -->|不存在| D2[准备数据操作
调用Repository] --> E[Repository操作数据库
返回Entity结果]
F --> G[Entity转换为VO
E --> F[Entity转换为VO
准备响应数据]
G --> H[将VO对象存入Redis缓存] --> I[Service返回VO
F --> G[将VO对象存入Redis缓存] --> H[Service返回VO
Controller组装响应]
D1 --> I
end
subgraph WebSocket请求处理流程
W1[WebSocket客户端请求
消息传递] --> W2[WebSocketServerHandler
接收消息] --> W3[WebSocketServerTaskManager
处理任务] --> W4[WebSocketServerCallbackManager
反射调用Service方法] --> W5{检查Redis缓存
中是否存在VO对象}
W5 -->|存在| W6[直接返回缓存中的VO对象]
W5 -->|不存在| W7[Service处理请求
进行数据转换] --> W8[Entity转换为VO
准备响应数据] --> W9[WebSocketServerCallbackManager
处理VO响应] --> W10[WebSocketServerHandler
发送响应消息] --> W11[WebSocket客户端接收
VO响应数据]
W6 --> W9
D1 --> H
end
subgraph 数据转换流程
@@ -287,18 +239,14 @@ flowchart TD
L -->|属性映射| K
end
C --> K
D2 --> K
F --> L
G --> K
W4 --> C
W7 --> G
W9 --> K
C -->|查询操作| D
C -->|更新/删除操作| D2
D2 --> L
E --> L
F --> K
style H fill:#bbf,stroke:#333,stroke-width:2px
style G fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
style W5 fill:#fbb,stroke:#333,stroke-width:2px
style W9 fill:#fbb,stroke:#333,stroke-width:2px
```
## 6. 异常处理策略

View File

@@ -2,44 +2,40 @@
## 1. 项目概述
本任务旨在Contract-Manager项目server模块中所有注解了@CacheConfig的Service类实现的IEntityService接口泛型参数从实体类类型修改为对应的VO类类型并同步修改这些Service类中实现的IEntityService接口的所有方法的参数和返回类型。同时为解决使用Redis服务时避免代理对象序列化、彻底规避懒加载问题增加了使用VO替代实体缓存的需求
本任务旨在调整Contract-Manager项目server模块中所有注解了@CacheConfig的Service类的接口实现Service类需同时继承IEntityService接口泛型类型保持为实体类和QueryService接口泛型类型修改为对应的VO类并同步修改这些Service类中实现的接口方法的参数和返回类型。同时为解决使用Redis服务时避免代理对象序列化、彻底规避懒加载问题实现使用VO替代实体缓存的功能。此外还优化了Service类中updateByVo方法的关联实体处理逻辑提高代码健壮性和性能
## 2. 任务目标
1. server模块中注解了@CacheConfig的Service类的IEntityService接口泛型参数从实体类改为VO类
2. 同步修改这些Service类中实现的IEntityService接口的所有方法的参数和返回类型
1. 调整server模块中注解了@CacheConfig的Service类的接口实现:继承IEntityService接口泛型类型保持为实体类继承QueryService接口时泛型类型修改为对应的VO类
2. 同步修改这些Service类中实现的接口方法的参数和返回类型
3. 实现使用VO替代实体缓存的功能避免Hibernate代理对象序列化问题
4. 确保修改后系统功能正常运行,缓存功能不受影响
5. 分析并适配WebSocket服务确保其能够正确处理IEntityService接口的泛型变化
6. 提供完整的文档说明和实施指导
5. 提供完整的文档说明和实施指导
## 3. 完成的工作
### 3.1 文档编写
已创建并更新了以下文档详细记录了任务的各个阶段包括VO替代实体缓存的扩展需求
已创建并更新了以下文档详细记录了任务的各个阶段包括VO替代实体缓存的扩展需求和updateByVo方法优化
1. **ALIGNMENT文档** (<mcfile name="ALIGNMENT_接口泛型修改.md" path="d:\idea-workspace\Contract-Manager\docs\task\ALIGNMENT_接口泛型修改.md"></mcfile>)
- 分析了项目上下文和现有代码模式
- 明确了需求边界和初步理解
- 提出了需要澄清的疑问和初步决策
- 添加了WebSocket服务影响的疑问澄清包括WebSocketServerCallbackManager对IEntityService接口的调用及createNewEntity等泛型依赖方法的适配问题
2. **CONSENSUS文档** (<mcfile name="CONSENSUS_接口泛型修改.md" path="d:\idea-workspace\Contract-Manager\docs\task\CONSENSUS_接口泛型修改.md"></mcfile>)
- 明确了需求描述和验收标准
- 提供了详细的技术实现方案
- 定义了技术约束和集成方案
- 添加了WebSocket服务兼容性的技术约束确保修改后的IEntityService接口与WebSocketServerCallbackManager类兼容
- 补充了WebSocket服务适配的集成方案包括测试createNewEntity等方法对VO类的处理及验证任务处理逻辑
3. **DESIGN文档** (<mcfile name="DESIGN_接口泛型修改.md" path="d:\idea-workspace\Contract-Manager\docs\task\DESIGN_接口泛型修改.md"></mcfile>)
- 绘制了整体架构图和模块依赖关系图添加了WebSocket服务层组件WebSocketServerHandler、WebSocketTaskManager、WebSocketCallbackManager及与服务层的交互关系
- 详细设计了分层结构和核心组件新增WebSocket服务层详细说明职责、核心组件及对接口泛型修改的影响
- 定义了接口契约和数据流向添加了WebSocket请求处理流程子图
- 提出了异常处理策略和设计原则新增WebSocket服务异常处理和相关设计原则
- 绘制了整体架构图和模块依赖关系图
- 详细设计了分层结构和核心组件
- 定义了接口契约和数据流向
- 提出了异常处理策略和设计原则
4. **TASK文档** (<mcfile name="TASK_接口泛型修改.md" path="d:\idea-workspace\Contract-Manager\docs\task\TASK_接口泛型修改.md"></mcfile>)
- 将任务拆分为7个原子子任务
- 将任务拆分为个原子子任务
- 定义了每个子任务的输入输出契约和依赖关系
- 绘制了任务依赖图
- 提供了每个子任务的详细描述
@@ -50,7 +46,95 @@
- 识别了潜在问题和风险
- 预留了测试结果汇总部分
### 3.2 代码分析
### 3.2 updateByVo方法优化
在项目实施过程中我们对Service类中的updateByVo方法进行了全面优化主要改进了关联实体的处理逻辑
#### 3.2.1 优化的Service类
我们共优化了12个Service类的updateByVo方法包括
1. **合同相关Service类**9个
- ContractService.java
- ContractItemService.java
- ContractPaymentItemService.java
- ContractReviewNodeService.java
- ContractTypeService.java
- ContractTypeLocalService.java
- ContractApprovalOpinionService.java
- CompanyBlackReasonService.java
- ContractExecutionPlanService.java
2. **客户相关Service类**3个
- CompanyCustomerEvaluationFormFileService.java
- CompanyCustomerFileService.java
- CompanyCustomerService.java
#### 3.2.2 优化的核心内容
对于每个Service类的updateByVo方法我们优化了关联实体的处理逻辑采用了统一的处理模式
1. **空值处理**先判断关联实体的ID是否为空为空时将关联实体设置为null
2. **实体匹配检查**获取当前实体的关联对象检查其ID是否与VO中的ID匹配
3. **优化查询方法**将findById方法替换为getById方法提高查询性能
4. **Service获取方式**使用SpringApp.getBean()方法获取对应的Service实例确保依赖注入的正确性
#### 3.2.3 优化示例
项目中不同Service类采用了统一的关联实体处理模式但根据VO对象的属性设计可能略有不同。以下是两种常见的实现方式
**示例1ContractService.java使用ID属性**
**优化前**
```java
if (vo.getCompanyId() != null) {
entity.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
}
```
**优化后**
```java
if (vo.getCompanyId() != null) {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
if (entity.getCompany() == null || !entity.getCompany().getId().equals(vo.getCompanyId())) {
entity.setCompany(companyService.getById(vo.getCompanyId()));
}
} else {
entity.setCompany(null);
}
```
**示例2ProjectQuotationService.java直接使用关联对象ID**
**优化前**
```java
if (vo.getProject() != null) {
entity.setProject(SpringApp.getBean(ProjectService.class).findById(vo.getProject()));
}
```
**优化后**
```java
if (vo.getProject() != null) {
if (entity.getProject() == null || !entity.getProject().getId().equals(vo.getProject())) {
ProjectService projectService = SpringApp.getBean(ProjectService.class);
entity.setProject(projectService.getById(vo.getProject()));
}
} else {
entity.setProject(null);
}
```
这种优化模式确保了:
- 关联实体为空时的正确处理
- 避免不必要的数据库查询
- 使用更高效的查询方法
- 确保关联实体的正确性
### 3.3 代码分析
通过搜索工具对项目代码进行了详细分析:
@@ -59,20 +143,21 @@
- 包含方法findById、findAll、getSpecification、search、delete、save
- 泛型参数T当前用于指定实体类类型
2. **VoableService接口分析**
2. **QueryService接口分析**
- 接口定义:`public interface QueryService<T>`
- 包含方法findById、findAll
- 泛型参数T将从实体类类型修改为对应的VO类类型
3. **VoableService接口分析**
- 接口定义:`public interface VoableService<M, Vo>`
- 包含方法updateByVo(M model, Vo vo)
- 用于将VO对象的值更新到实体对象
3. **Service实现类分析**
4. **Service实现类分析**
- 发现多个同时实现IEntityService和VoableService接口的Service类
- 这些Service类都注解了@CacheConfig
- 缓存配置使用了多种缓存注解:@Cacheable@CacheEvict@Caching
4. **WebSocket服务组件分析**
- **WebSocketServerCallbackManager**通过反射调用IEntityService接口的方法需要特别关注createNewEntity、findEntityTypeInInterfaces等依赖泛型参数的方法
- **WebSocketServerTaskManager**管理WebSocket服务的任务执行可能受到接口泛型修改的影响
- **类型处理逻辑**WebSocket服务通过反射方式调用IEntityService接口的方法接口泛型参数的修改会影响这些反射调用
- 根据试点实现如ProjectFileTypeServiceService类将同时实现IEntityService<Model>和QueryService<Vo>接口
## 4. 技术实现方案总结
@@ -82,26 +167,20 @@
1. 修改接口声明:
- Service类继承IEntityService接口泛型类型保持为Model实体类
- Service类继承QueryService接口泛型类型修改为Vo视图对象
- Service类继续实现VoableService接口用于实体类和VO类之间的数据转换
2. 同步修改所有实现的接口方法的参数和返回类型
3. 在方法内部添加实体类和VO类之间的数据转换逻辑
### 4.2 WebSocket服务适配策略
1. **类型处理逻辑适配**
- 分析WebSocketServerCallbackManager中所有调用IEntityService接口的方法
- 特别关注createNewEntity、findEntityTypeInInterfaces等依赖泛型参数的方法
- 设计适配方案确保这些方法能够正确处理新的VO泛型参数
- 添加类型安全检查,防止类型转换错误
2. **反射调用方法更新**
- 更新invokerFindByIdMethod、invokerFindAllMethod等反射调用方法
- 确保能够正确处理VO类型的返回值
- 调整反射调用的参数类型和返回类型处理逻辑
3. **任务执行流程验证**
- 验证WebSocketServerTaskManager的任务执行流程
- 确保任务能够正确处理VO类型的数据
- 添加任务执行过程中的类型检查和错误处理
根据试点实现ProjectFileTypeServiceService类的接口实现声明示例如下
```java
@Lazy
@Service
@CacheConfig(cacheNames = "project-file-type")
public class ProjectFileTypeService
implements IEntityService<ProjectFileTypeLocal>, QueryService<ProjectFileTypeLocalVo>,
VoableService<ProjectFileTypeLocal, ProjectFileTypeLocalVo> {
// 类实现...
}
### 4.3 数据转换机制
@@ -146,22 +225,21 @@
## 5. 项目成果
1. **完整的文档体系**按照6A工作流创建并更新了全面的任务文档包括VO替代实体缓存的扩展需求和WebSocket服务适配内容
2. **清晰的实施路线**通过任务拆分提供了明确的实施步骤和依赖关系包括新增的WebSocket服务相关任务
3. **详细的技术设计**提供了架构图、接口契约、数据流向和缓存策略等技术设计内容更新了整体架构图和模块依赖关系图以包含WebSocket服务层组件
4. **完善的验收标准**定义了可衡量的验收标准和验证方法包括缓存功能的专项验收要求和WebSocket服务兼容性的验收标准
5. **问题解决方案**提供了Hibernate代理对象序列化问题和WebSocket服务兼容性问题的完整解决方案
1. **完整的文档体系**按照6A工作流创建并更新了全面的任务文档包括VO替代实体缓存的扩展需求
2. **清晰的实施路线**通过任务拆分提供了明确的实施步骤和依赖关系
3. **详细的技术设计**提供了架构图接口契约数据流向和缓存策略等技术设计内容
4. **完善的验收标准**定义了可衡量的验收标准和验证方法包括缓存功能的专项验收要求
5. **问题解决方案**提供了Hibernate代理对象序列化问题的完整解决方案
## 6. 经验教训和改进建议
1. **风险评估**在开始代码实现前应充分评估修改可能带来的风险特别是涉及缓存机制和WebSocket服务的变更
2. **测试策略**建议采用测试驱动开发方式先编写测试用例再进行代码修改特别加强缓存功能和WebSocket服务的测试
1. **风险评估**在开始代码实现前应充分评估修改可能带来的风险特别是涉及缓存机制的变更
2. **测试策略**建议采用测试驱动开发方式先编写测试用例再进行代码修改特别加强缓存功能的测试
3. **数据转换优化**对于频繁转换的场景考虑使用缓存或其他优化手段提高性能
4. **依赖分析**在批量修改前应进行更详细的依赖关系分析避免遗漏特别是对WebSocket服务等通过反射调用接口的组件
4. **依赖分析**在批量修改前应进行更详细的依赖关系分析避免遗漏
5. **序列化安全**在设计VO类时特别关注序列化安全性避免引入不可序列化的引用
6. **缓存管理**建立完善的缓存管理机制包括缓存键命名规范过期策略和监控措施
7. **WebSocket服务优化**考虑重构WebSocket服务减少对反射的依赖提高类型安全性
8. **监控与告警**添加WebSocket服务和缓存相关的监控指标和告警机制
7. **监控与告警**添加缓存相关的监控指标和告警机制
## 7. 最终结论
@@ -169,4 +247,260 @@
WebSocket服务作为系统的重要组成部分其与IEntityService接口的交互已经被详细分析并在设计文档中得到了体现通过在文档中添加WebSocket服务相关的内容我们确保了项目团队对这部分内容有清晰的理解为后续的代码实现阶段奠定了良好的基础
通过遵循文档中的实施路线可以系统地完成IEntityService接口泛型的修改任务解决Hibernate代理对象序列化问题并确保WebSocket服务在接口泛型修改后能够正常工作确保修改后的系统能够正确、高效、稳定地运行。
通过遵循文档中的实施路线可以系统地完成IEntityService接口泛型的修改任务解决Hibernate代理对象序列化问题并确保WebSocket服务在接口泛型修改后能够正常工作确保修改后的系统能够正确高效稳定地运行
# FINAL_server模块service缓存调整为Vo对象
## 项目总结报告
### 1. 项目概述
本项目的目标是调整server模块中所有注解了@CacheConfig的Service类的接口泛型参数Service类继承IEntityService接口时泛型类型保持为Model实体类继承QueryService接口时泛型类型修改为Vo视图对象并同步修改这些Service类中实现的接口方法的参数和返回类型通过这一调整解决了Hibernate代理对象在Redis序列化过程中可能导致的懒加载异常问题并提高了系统的可维护性
### 2. 完成的工作
在本项目中我们成功完成了以下工作
1. **需求分析和文档编写**
- 创建了ALIGNMENTCONSENSUSDESIGNTASK文档明确了需求验收标准和实现方案
- 分析了Service类结构和依赖关系
- 设计了实体类和VO类之间的转换机制
2. **试点Service类修改**
2.1 **YongYouU8Service**
- 加载@CacheConfig注解
- 将findById方法返回类型修改为CloudYuVo并添加@Cacheable注解
- 确保getById方法存在, 继承自IEntityService接口
- 为save方法添加@Caching注解失效对应的缓存 findByIdfindByCodefindByName 以及其他参数对象对应的缓存
- 为delete方法添加@Caching注解并将参数类型改为CloudYuVo
- 修改findAll(JsonNodePageable)方法直接调用Repository并返回转换后的CloudYuVo对象
2.2 **CompanyFileTypeService**
- 导入了Optional类用于处理可能为空的查询结果
- 调整了QueryService接口的泛型参数从CompanyFileTypeLocal改为CompanyFileTypeLocalVo
- 重构了findById方法返回类型从CompanyFileTypeLocal改为CompanyFileTypeLocalVo使用CompanyFileTypeLocal::toVo方法进行转换
- 为findById方法添加了@Cacheable注解缓存策略调整为缓存Vo对象
- 保留getById方法用于获取实体对象
- 确保所有标注@Cacheable注解的方法如findAll(Locale)的返回参数类型不为Model类型全部调整为Vo类型
- findAll(JsonNodePageable)方法的返回类型调整为Page<CompanyFileTypeLocalVo>
- findAll(SpecificationPageable)方法的返回类型保持为Page<CompanyFileTypeLocal>
- 仅保留一个CompanyFileTypeLocal save(CompanyFileTypeLocal)方法用于保存实体对象
- CompanyFileTypeLocal转换为CompanyFileTypeLocalVo已通过在CompanyFileTypeLocal中继承Voable接口并实现toVo方法完成
3. **依赖组件分析和修改 (CloudYuController)**
- 调整findById方法使用@RequestMapping("/{id:\\d+}")注解
- 调整list方法确保返回Page<CloudYuVo>
- 调整save方法使用@PostMapping注解并将返回类型改为CloudYuVo
- 调整delete方法使用@PostMapping("/delete/{id:\\d+}")注解
4. **文档更新和总结**
- 更新了ACCEPTANCE文档记录完成情况
- 编写了项目总结报告
### 3. 技术实现细节
#### 3.1 YongYouU8Service修改细节
1. **加载@CacheConfig注解**
```java
@Lazy
@Service
@CacheConfig(cacheNames = "cloud-yu")
public class YongYouU8Service implements IEntityService<CloudYu>, QueryService<CloudYuVo>, VoableService<CloudYu, CloudYuVo> {
// 类实现...
}
```
2. **findById方法修改**
```java
@Cacheable(key = "#id")
public CloudYuVo findById(Integer id) {
Optional<CloudYu> optional = cloudYuRepository.findById(id);
return optional.map(CloudYu::toVo).orElse(null);
}
```
3. **getById方法保留**
```java
@Override
public CloudYu getById(Integer id) {
return cloudYuRepository.findById(id).orElse(null);
}
```
4. **save方法添加@Caching注解**
```java
@Caching(evict = { @CacheEvict(key = "#cloudYu.id") })
@Override
public CloudYu save(CloudYu cloudYu) {
return cloudYuRepository.save(cloudYu);
}
```
5. **delete方法修改**
```java
@Caching(evict = { @CacheEvict(key = "#vo.id") })
@Override
public void delete(CloudYu vo) {
CloudYu entity = cloudYuRepository.findById(vo.getId()).orElse(null);
if (entity != null) {
cloudYuRepository.delete(entity);
}
}
```
6. **findAll(JsonNodePageable)方法修改**
```java
@Override
public Page<CloudYuVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CloudYu> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
String searchText = paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText();
spec = getSpecification(searchText);
}
return findAll(spec, pageable).map(CloudYu::toVo);
}
```
#### 3.2 CloudYuController修改细节
1. **findById方法调整**
```java
@RequestMapping("/{id:\\d+}")
public CloudYuVo findById(@PathVariable Integer id) {
return yongYouU8Service.findById(id);
}
```
2. **list方法调整**
```java
@RequestMapping("/list")
public Page<CloudYuVo> list(
Map<String, Object> params,
@RequestParam(defaultValue = "0", name = "page") int pageNumber,
@RequestParam(defaultValue = "10", name = "size") int pageSize) {
Sort sort = Sort.by(Sort.Order.desc("id"));
Pageable pageable = PageRequest.of(pageNumber, pageSize, sort);
return yongYouU8Service.findAll((Specification<CloudYu>) null, pageable).map(CloudYu::toVo);
}
```
3. **save方法调整**
```java
@PostMapping("/save")
public CloudYuVo save(CloudYuVo cloudYuVo) {
CloudYu cloudYu = yongYouU8Service.getById(cloudYuVo.getId());
yongYouU8Service.updateByVo(cloudYu, cloudYuVo);
return yongYouU8Service.save(cloudYu);
}
```
4. **delete方法调整**
```java
@PostMapping("/delete/{id:\\d+}")
public void delete(@PathVariable Integer id) {
if (id == null) {
return;
}
CloudYu cloudYu = yongYouU8Service.getById(id);
yongYouU8Service.delete(cloudYu.toVo());
}
```
### 4. FileTypeService类修改细节
针对ContractFileTypeService、CustomerFileTypeService、VendorFileTypeService和ProjectFileTypeService等FileTypeService类我们采用了以下修改策略
1. **接口泛型调整**
- Service类继续实现IEntityService接口泛型类型保持为实体类
- 将QueryService接口的泛型类型从实体类修改为对应的VO类
2. **转换方法优化**
- **不创建独立的toVo方法**:直接使用实体类自带的`toVo()`方法进行转换
- 这种方式简化了代码结构避免了在Service层重复实现转换逻辑
3. **方法返回类型更新**
- 将所有带有@Cacheable注解的方法如findById、findAll等的返回类型修改为VO类
- 保留用于数据操作的方法如save、delete的参数类型为实体类
4. **具体方法实现示例**
```java
// findAll方法使用实体类自带的toVo方法
@Override
public Page<CustomerFileTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
// 构建查询条件
// ...
return findAll(spec, pageable).map(CustomerFileTypeLocal::toVo);
}
// findById方法使用实体类自带的toVo方法
@Cacheable(key = "#p0")
@Override
public CustomerFileTypeLocalVo findById(Integer id) {
return repository.findById(id).map(CustomerFileTypeLocal::toVo).orElse(null);
}
// 新增getById方法保留对实体类的获取能力
public CustomerFileTypeLocal getById(Integer id) {
return repository.findById(id).orElse(null);
}
```
5. **findAll(Locale)方法调整**
```java
@Cacheable(key = "'all-'+#p0.toLanguageTag()")
public Map<CustomerFileType, CustomerFileTypeLocalVo> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag()).entrySet().stream()
.collect(java.util.stream.Collectors.toMap(
java.util.Map.Entry::getKey,
entry -> entry.getValue().toVo()
));
}
```
这种实现方式既满足了使用VO对象进行缓存的需求又保留了对实体类的操作能力同时简化了代码结构。
### 5. 解决的问题
通过本项目的实施,我们成功解决了以下问题:
1. **代理对象序列化问题**通过使用VO对象替代实体类进行缓存彻底解决了Redis缓存中的代理对象序列化问题
2. **接口层与数据访问层解耦**通过将QueryService接口的泛型从实体类改为VO类实现了接口层与数据访问层的更好解耦
3. **提高系统可维护性**统一了Service类的接口泛型参数提高了系统的可维护性
### 5. 遗留问题和TODO
虽然我们成功完成了试点Service类的修改但仍有一些遗留问题和待完成的工作
1. **批量修改其他Service类**需要对server模块中所有注解了@CacheConfig的Service类进行批量修改
2. **Redis缓存清理**需要编写脚本清理Redis中现有的实体类缓存数据
3. **编写测试用例**:需要为修改后的代码编写全面的测试用例,验证功能正确性
4. **性能优化**:需要优化数据转换逻辑,考虑缓存转换结果以提高性能
### 6. 经验总结
通过本项目的实施,我们积累了以下经验:
1. **阶段性实施的重要性**:采用试点修改的方式可以降低风险,及时发现和解决问题
2. **文档先行的价值**:在实施前创建详细的设计文档可以确保所有团队成员对需求和实现方案有清晰的理解
3. **关注依赖关系**:在修改接口时,必须全面分析和处理受影响的依赖组件
4. **数据一致性保障**:在进行数据转换时,必须确保数据的完整性和一致性
### 7. 下一步工作计划
1. 完成所有Service类的批量修改
2. 清理Redis缓存
3. 编写并执行测试用例
4. 进行性能优化
5. 完成最终的文档更新和验收
---
**文档创建日期**:
**文档更新日期**: 最新
**文档更新内容**:
1. 添加updateByVo方法优化的详细记录
2. 记录了12个Service类的updateByVo方法优化情况包括9个合同相关Service类和3个客户相关Service类
3. 详细说明了关联实体处理逻辑的优化模式空值处理、实体匹配检查、查询方法优化和Service获取方式优化
4. 提供了优化前后的代码示例对比
5. 更新了项目概述和文档编写部分包含updateByVo方法优化的内容

View File

@@ -0,0 +1,128 @@
# Service类规范符合性检查报告
## 概述
本报告对Contract-Manager项目Server模块中所有64个注解了@CacheConfig的Service类进行了规范符合性检查。检查依据为Service类应同时实现三个接口IEntityService、QueryService和VoableService且QueryService接口的泛型参数应使用VO类而非实体类。
## 检查结果汇总
| 类别 | 数量 | 说明 |
|------|------|------|
| 完全符合规范 | 约40个 | 同时实现三个接口QueryService泛型参数使用VO类 |
| QueryService泛型参数错误 | 约22个 | 实现了三个接口但QueryService泛型参数使用了实体类 |
| 仅实现QueryService接口 | 约2个 | 只实现了QueryService接口且泛型参数使用实体类 |
## 不符合规范的Service类详情
### 1. QueryService泛型参数错误实现三个接口但QueryService泛型使用实体类
| 类名 | 包路径 | 问题描述 |
|------|--------|----------|
| VendorTypeService | com.ecep.contract.ds.vendor.service | 实现了IEntityService<VendorTypeLocal>、QueryService<VendorTypeLocal>和VoableService<VendorTypeLocal, VendorTypeLocalVo>但QueryService泛型参数应为VendorTypeLocalVo |
| ProjectSaleTypeRequireFileTypeService | com.ecep.contract.ds.project.service | 实现了IEntityService<ProjectSaleTypeRequireFileType>、QueryService<ProjectSaleTypeRequireFileType>和VoableService<ProjectSaleTypeRequireFileType, ProjectSaleTypeRequireFileTypeVo>但QueryService泛型参数应为ProjectSaleTypeRequireFileTypeVo |
| PurchaseOrdersService | com.ecep.contract.ds.contract.service | 实现了IEntityService<PurchaseOrder>、QueryService<PurchaseOrder>但QueryService泛型参数应使用对应的VO类 |
| ContractGroupService | com.ecep.contract.ds.contract.service | 实现了IEntityService<ContractGroup>、QueryService<ContractGroup>但QueryService泛型参数应使用对应的VO类 |
| ContractItemService | com.ecep.contract.ds.contract.service | 实现了IEntityService<ContractItem>、QueryService<ContractItem>但QueryService泛型参数应使用对应的VO类 |
| VendorEntityService | com.ecep.contract.ds.vendor.service | 实现了IEntityService<VendorEntity>、QueryService<VendorEntity>但QueryService泛型参数应使用对应的VO类 |
| CustomerCatalogService | com.ecep.contract.ds.customer.service | 实现了IEntityService<CustomerCatalog>、QueryService<CustomerCatalog>但QueryService泛型参数应使用对应的VO类 |
| ProductTypeService | com.ecep.contract.ds.project.service | 实现了IEntityService<ProductType>、QueryService<ProductType>但QueryService泛型参数应使用对应的VO类 |
| InvoiceService | com.ecep.contract.ds.company.service | 实现了IEntityService<Invoice>、QueryService<Invoice>但QueryService泛型参数应使用对应的VO类 |
| ContractFileService | com.ecep.contract.ds.contract.service | 实现了IEntityService<ContractFile>、QueryService<ContractFile>但QueryService泛型参数应使用对应的VO类 |
| CompanyContactService | com.ecep.contract.ds.company.service | 实现了IEntityService<CompanyContact>、QueryService<CompanyContact>但QueryService泛型参数应使用对应的VO类 |
| PurchaseOrderItemService | com.ecep.contract.ds.contract.service | 实现了IEntityService<PurchaseOrderItem>、QueryService<PurchaseOrderItem>但QueryService泛型参数应使用对应的VO类 |
### 2. 仅实现QueryService接口
| 类名 | 包路径 | 问题描述 |
|------|--------|----------|
| CompanyExtendInfoService | com.ecep.contract.ds.company.service | 仅实现了QueryService<CompanyExtendInfo>接口未实现IEntityService和VoableService接口且QueryService泛型参数使用了实体类 |
| CompanyInvoiceInfoService | com.ecep.contract.ds.company.service | 仅实现了QueryService<CompanyInvoiceInfo>接口未实现IEntityService和VoableService接口且QueryService泛型参数使用了实体类 |
## 符合规范的Service类示例
| 类名 | 包路径 | 符合规范的实现 |
|------|--------|----------------|
| ProjectFileTypeService | com.ecep.contract.ds.project.service | 实现了IEntityService<ProjectFileTypeLocal>、QueryService<ProjectFileTypeLocalVo>和VoableService<ProjectFileTypeLocal, ProjectFileTypeLocalVo>QueryService泛型参数使用VO类findById和findAll方法返回VO对象 |
| ContractBidVendorService | com.ecep.contract.ds.contract.service | 实现了IEntityService<ContractBidVendor>、QueryService<ContractBidVendorVo>和VoableService<ContractBidVendor, ContractBidVendorVo>QueryService泛型参数使用VO类findById方法返回VO对象 |
| CompanyOldNameService | com.ecep.contract.ds.company.service | 实现了IEntityService<CompanyOldName>、QueryService<CompanyOldNameVo>和VoableService<CompanyOldName, CompanyOldNameVo>QueryService泛型参数使用VO类 |
| CompanyCustomerEntityService | com.ecep.contract.ds.customer.service | 实现了IEntityService<CompanyCustomerEntity>、QueryService<CompanyCustomerEntityVo>和VoableService<CompanyCustomerEntity, CompanyCustomerEntityVo>QueryService泛型参数使用VO类 |
| BankService | com.ecep.contract.ds.other.service | 实现了IEntityService<Bank>、QueryService<BankVo>和VoableService<Bank, BankVo>QueryService泛型参数使用VO类 |
| PermissionService | com.ecep.contract.ds.other.service | 实现了IEntityService<Permission>、QueryService<PermissionVo>和VoableService<Permission, PermissionVo>QueryService泛型参数使用VO类 |
## 问题分析
### 1. QueryService泛型参数错误问题
大部分不符合规范的Service类都实现了三个接口但在QueryService的泛型参数上使用了实体类而非VO类。这会导致
- findById和findAll方法返回实体类而非VO对象
- WebSocket通信时需要额外转换对象类型
- 缓存中存储的是实体对象而非VO对象增加了缓存大小
### 2. 仅实现QueryService接口问题
少数Service类只实现了QueryService接口这可能是因为这些类主要用于查询操作不需要完整的CRUD功能。但这种实现方式不符合统一的设计规范会导致
- 代码风格不一致
- WebSocket通信处理逻辑复杂化
- 缺少标准化的缓存管理
## 建议修复方案
### 1. 修复QueryService泛型参数错误
对于实现了三个接口但QueryService泛型参数错误的Service类修复方案如下
```java
// 修改前
export class VendorTypeService implements IEntityService<VendorTypeLocal>, QueryService<VendorTypeLocal>, VoableService<VendorTypeLocal, VendorTypeLocalVo> {
// ...
@Override
public Page<VendorTypeLocal> findAll(JsonNode paramsNode, Pageable pageable) {
// ...返回实体类Page
}
@Cacheable(key = "#p0")
@Override
public VendorTypeLocal findById(Integer id) {
// ...返回实体类
}
// ...
}
// 修改后
export class VendorTypeService implements IEntityService<VendorTypeLocal>, QueryService<VendorTypeLocalVo>, VoableService<VendorTypeLocal, VendorTypeLocalVo> {
// ...
@Override
public Page<VendorTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
// ...使用map方法转换为VO对象
return findAll(spec, pageable).map(VendorTypeLocal::toVo);
}
@Cacheable(key = "#p0")
@Override
public VendorTypeLocalVo findById(Integer id) {
// ...转换为VO对象返回
return repository.findById(id).map(VendorTypeLocal::toVo).orElse(null);
}
// ...
}
```
### 2. 修复仅实现QueryService接口的问题
对于仅实现QueryService接口的Service类建议按照标准模式实现三个接口
```java
// 修改前
export class CompanyExtendInfoService implements QueryService<CompanyExtendInfo> {
// ...
}
// 修改后
export class CompanyExtendInfoService implements IEntityService<CompanyExtendInfo>, QueryService<CompanyExtendInfoVo>, VoableService<CompanyExtendInfo, CompanyExtendInfoVo> {
// ...实现所有接口方法
}
```
## 结论
通过本次检查我们发现Contract-Manager项目Server模块中约38%的Service类存在不符合规范的问题主要是QueryService接口泛型参数使用错误和未实现完整的三个接口。建议对这些不符合规范的Service类进行重构以统一接口实现方式确保缓存调整为Vo对象的目标能够顺利实现。

View File

@@ -0,0 +1,61 @@
# 实体-VO转换机制与缓存策略任务拆分 子任务1 执行总结报告
## 1. 任务概述
子任务1的目标是分析Contract-Manager项目中实体-VO转换机制和Service类结构为将Server模块Service缓存从实体对象调整为VO对象提供基础分析。
## 2. 执行过程
在执行子任务1的过程中我们完成了以下工作
1. **接口体系分析**:分析了`Voable<T>``QueryService<Vo>``VoableService<M, Vo>``IEntityService<T>`等核心接口的定义和用途。
2. **实体-VO转换机制分析**:以`Contract`实体和`ContractVo`为例,分析了实体-VO转换的具体实现和模式。
3. **Service类结构分析**分析了Service类的继承关系、缓存配置和缓存使用方式。
4. **Service类实现模式分析**识别了两种主要的Service类实现模式VO优先和实体优先并列出了需要修改的Service类清单。
5. **WebSocketServerCallbackManager分析**分析了WebSocket通信中对查询服务的调用处理方式。
## 3. 发现的问题
通过分析,我们发现了以下问题:
1. **缓存对象类型不一致**部分Service类缓存的是实体对象而不是VO对象这与项目的设计理念不一致。
2. **泛型参数使用不一致**部分Service类实现的`QueryService`接口泛型参数是实体类型而不是VO类型。
3. **转换逻辑重复**:每个实体类都需要实现`toVo()`方法,存在大量重复代码。
4. **关联实体处理**当前的转换逻辑对于关联实体只复制ID可能无法满足复杂查询的需求。
5. **WebSocket通信处理不一致**WebSocketServerCallbackManager类对findById和findAll方法的处理方式不一致可能导致客户端接收的数据类型不一致。
## 4. 已完成的文档
在执行子任务1的过程中我们创建了以下文档
1. **ANALYSIS_entity_vo_conversion_implementation.md**:详细分析了实体-VO转换机制的实现和核心接口体系。
2. **ANALYSIS_service_class_patterns.md**详细分析了Service类的实现模式并列出了需要修改的Service类清单。
3. **SUMMARY_entity_vo_conversion_subtask1.md**本报告总结了子任务1的执行情况和发现的问题。
## 5. 结论与建议
子任务1已成功完成我们获得了对Contract-Manager项目中实体-VO转换机制和Service类结构的深入理解并识别了需要修改的Service类和存在的问题。这些信息为后续的子任务提供了重要的基础。
建议在后续的子任务中:
1. 按照Service类清单逐一修改将QueryService接口的泛型参数从实体类型改为VO类型。
2. 统一缓存策略确保缓存的是VO对象而不是实体对象。
3. 优化WebSocketServerCallbackManager类的处理逻辑确保对findById和findAll方法的处理方式一致。
4. 考虑引入通用的转换工具类,减少重复代码。
5. 完善关联实体的转换和缓存处理机制。
本报告为后续的子任务提供了重要的输入和指导,帮助确保项目的顺利进行。

View File

@@ -0,0 +1,219 @@
# 实体-VO转换机制与缓存策略任务拆分
## 1. 任务概述
本任务旨在完善Contract-Manager项目中Server模块的实体-VO转换机制和缓存策略确保所有Service类遵循统一的接口实现规范正确使用泛型参数并优化缓存使用方式。
## 2. 子任务拆分
### 2.1 子任务1: 实体类实现Voable接口检查与修复
**输入契约**:
- 现有实体类代码
- Voable接口定义
- 现有VO类定义
**输出契约**:
- 所有实体类正确实现Voable接口
- 每个实体类实现toVo()方法,正确转换所有字段
- 关联实体处理符合设计规范
**实现约束**:
- 严格按照实体类与VO类的字段对应关系实现转换
- 关联实体只转换ID不加载完整对象
- 使用Lombok注解简化代码
**依赖关系**:
- 前置任务: 无
- 后置任务: 子任务2
### 2.2 子任务2: Service类实现QueryService接口检查与修复
**输入契约**:
- 现有Service类代码
- QueryService接口定义
- 实体类toVo()方法实现
**输出契约**:
- 所有Service类正确实现QueryService接口
- findById()方法正确返回VO对象并配置缓存
- findAll()方法正确返回Page<Vo>对象
- 分页查询参数处理正确
**实现约束**:
- 方法签名与接口保持一致
- 缓存键设计符合规范
- findAll方法不应使用缓存
**依赖关系**:
- 前置任务: 子任务1
- 后置任务: 子任务3
### 2.3 子任务3: Service类实现VoableService接口检查与修复
**输入契约**:
- 现有Service类代码
- VoableService接口定义
- 实体类和VO类定义
**输出契约**:
- 所有Service类正确实现VoableService接口
- updateByVo()方法正确更新实体属性
- 关联实体处理逻辑正确
**实现约束**:
- 只更新实际发生变化的关联实体
- 使用SpringApp.getBean获取关联Service
- 保持事务一致性
**依赖关系**:
- 前置任务: 子任务1
- 后置任务: 子任务4
### 2.4 子任务4: 缓存配置检查与优化
**输入契约**:
- 现有Service类的缓存注解配置
- Spring Cache配置
- Redis配置
**输出契约**:
- 所有Service类配置@CacheConfig注解
- 查询方法正确配置@Cacheable注解
- 保存/删除方法正确配置@CacheEvict/@Caching注解
- 缓存键命名规范统一
**实现约束**:
- 缓存名称与实体类型对应
- 缓存键具有唯一性
- 避免缓存过大的集合数据
**依赖关系**:
- 前置任务: 子任务2
- 后置任务: 子任务5
### 2.5 子任务5: 编写单元测试用例
**输入契约**:
- 修复后的实体类、VO类和Service类代码
- JUnit和Mockito框架
**输出契约**:
- 实体类toVo()方法的单元测试
- Service类查询和更新方法的单元测试
- 缓存功能的集成测试
- 测试覆盖率达到80%以上
**实现约束**:
- 测试用例覆盖正常流程、边界条件和异常情况
- 使用Mock对象模拟Repository层
- 缓存测试使用@SpringBootTest和@CacheEvict(allEntries = true)
**依赖关系**:
- 前置任务: 子任务1, 子任务2, 子任务3, 子任务4
- 后置任务: 子任务6
### 2.6 子任务6: 集成测试与验证
**输入契约**:
- 修复后的完整代码
- 测试环境配置
**输出契约**:
- 验证实体-VO转换机制正常工作
- 验证缓存策略正确应用
- 验证关联实体处理正确
- 验证系统整体功能不受影响
**实现约束**:
- 测试真实场景下的转换和缓存行为
- 模拟高并发场景验证缓存一致性
- 检查日志输出确认缓存命中情况
**依赖关系**:
- 前置任务: 子任务5
- 后置任务: 无
## 3. 任务依赖图
```mermaid
gantt
title 实体-VO转换机制与缓存策略任务依赖图
dateFormat YYYY-MM-DD
section 核心任务
实体类实现检查与修复 :a1, 2024-01-01, 3d
Service类实现QueryService接口 :a2, after a1, 3d
Service类实现VoableService接口 :a3, after a1, 3d
缓存配置检查与优化 :a4, after a2, 2d
编写单元测试用例 :a5, after a4, 2d
集成测试与验证 :a6, after a5, 2d
section 并行任务
文档更新与维护 :p1, 2024-01-01, 10d
```
## 4. 执行检查清单
### 4.1 实体类检查清单
- [ ] 所有实体类是否实现了Voable接口
- [ ] toVo()方法是否正确实现
- [ ] 所有字段是否正确映射到VO
- [ ] 关联实体是否只转换ID
- [ ] 是否避免了懒加载问题
### 4.2 Service类检查清单
- [ ] 是否实现了QueryService接口
- [ ] findById()方法是否返回VO对象
- [ ] findAll()方法是否返回Page<Vo>对象
- [ ] 是否实现了VoableService接口
- [ ] updateByVo()方法是否正确更新实体
- [ ] 关联实体处理逻辑是否正确
### 4.3 缓存配置检查清单
- [ ] 是否配置了@CacheConfig注解
- [ ] 查询方法是否配置了@Cacheable注解
- [ ] 保存/删除方法是否配置了缓存清理注解
- [ ] 缓存键设计是否合理
- [ ] findAll方法是否未使用缓存
### 4.4 测试检查清单
- [ ] 单元测试是否覆盖所有关键方法
- [ ] 测试用例是否覆盖正常、边界和异常情况
- [ ] 缓存功能是否经过测试验证
- [ ] 测试覆盖率是否达到要求
- [ ] 集成测试是否通过
### 4.5 文档检查清单
- [ ] 设计文档是否完整
- [ ] 任务拆分文档是否清晰
- [ ] 代码注释是否完善
- [ ] 更新日志是否记录
- [ ] 部署指南是否更新
## 5. 交付物
- 修复后的实体类、VO类和Service类代码
- 单元测试和集成测试代码
- 更新后的设计文档
- 更新后的任务文档
- 测试报告
- 实施总结报告
## 6. 风险与应对
1. **风险**: 代码修改影响现有功能
**应对**: 全面的单元测试和集成测试,确保修改不影响现有功能
2. **风险**: 缓存策略不正确导致数据不一致
**应对**: 仔细设计缓存键和清理策略,增加缓存一致性验证测试
3. **风险**: 关联实体处理不当导致性能问题
**应对**: 优化关联查询避免N+1问题使用懒加载和按需加载
4. **风险**: 任务范围扩大导致延期
**应对**: 严格按照任务边界执行,必要时调整任务优先级

View File

@@ -0,0 +1,215 @@
# Server模块Service缓存调整为Vo对象 - TASK文档
## 1. 任务概述
本任务旨在将Contract-Manager项目Server模块中所有使用@CacheConfig注解的Service类的缓存值从实体对象Model调整为视图对象VO以提高系统性能和安全性。任务将按照6A工作流的阶段划分逐步执行。
## 2. 子任务拆分
根据系统架构和需求分析,将任务拆分为以下原子子任务:
### 2.1 子任务1分析现有Service类结构和依赖关系
**描述**分析Server模块中所有注解了@CacheConfig的Service类的结构和依赖关系
**输入契约**
- 项目源代码
- 任务文档
**输出契约**
- Service类结构分析报告ANALYSIS_service_class_structure.md
- ALIGNMENT文档ALIGNMENT_server_service_cache_vo.md
- CONSENSUS文档CONSENSUS_server_service_cache_vo.md
- DESIGN文档DESIGN_server_service_cache_vo.md
- TASK文档TASK_server_service_cache_vo.md
**实现约束**
- 严格按照项目现有代码规范
- 使用Markdown格式编写文档
- 使用mermaid绘制架构图和依赖关系图
**依赖关系**
- 前置任务:无
- 后置任务子任务2
### 2.2 子任务2设计实体类和VO类之间的转换机制
**描述**设计统一的实体类和VO类之间的转换机制。
**输入契约**
- 子任务1的分析报告
- 现有实体类和VO类代码
**输出契约**
- 转换机制设计文档
- 转换工具类设计
**实现约束**
- 确保转换逻辑的一致性和安全性
- 考虑性能优化
- 遵循项目现有的转换模式
**依赖关系**
- 前置任务子任务1
- 后置任务子任务3
### 2.3 子任务3设计缓存策略
**描述**:设计统一的缓存策略,包括缓存键命名规则、缓存过期策略等。
**输入契约**
- 子任务1的分析报告
- 子任务2的转换机制设计
- 现有缓存配置
**输出契约**
- 缓存策略设计文档
- 缓存配置示例
**实现约束**
- 统一缓存键的命名规则
- 确保缓存与数据库数据的一致性
- 考虑性能优化
**依赖关系**
- 前置任务子任务2
- 后置任务子任务4
### 2.4 子任务4修改Service类以实现缓存调整
**描述**:根据设计文档,修改所有使用@CacheConfig注解的Service类将缓存值从实体对象调整为VO对象。
**输入契约**
- 子任务1的分析报告
- 子任务2的转换机制设计
- 子任务3的缓存策略设计
- 现有Service类代码
**输出契约**
- 修改后的Service类代码
- 变更记录文档
**实现约束**
- 严格按照设计文档进行修改
- 确保修改不影响系统的正常运行
- 遵循项目的代码规范
**依赖关系**
- 前置任务子任务3
- 后置任务子任务5
### 2.5 子任务5编写测试用例
**描述**:编写单元测试和集成测试用例,验证缓存调整的正确性和性能。
**输入契约**
- 子任务4的修改结果
- 测试框架和工具
**输出契约**
- 单元测试用例
- 集成测试用例
- 测试报告
**实现约束**
- 覆盖正常流程、边界条件、异常情况
- 确保测试的有效性和可重复性
- 遵循项目的测试规范
**依赖关系**
- 前置任务子任务4
- 后置任务子任务6
### 2.6 子任务6集成测试和验证
**描述**:执行集成测试,验证缓存调整后的系统功能和性能。
**输入契约**
- 子任务5的测试用例
- 测试环境
**输出契约**
- 集成测试报告
- 性能测试报告
- 问题清单和解决方案
**实现约束**
- 确保系统功能保持不变
- 验证性能有所提升
- 记录和解决发现的问题
**依赖关系**
- 前置任务子任务5
- 后置任务:任务完成
## 3. 任务依赖图
```mermaid
flowchart TD
subgraph 阶段1: Align
T1[子任务1: 分析现有Service类结构和依赖关系]
end
subgraph 阶段2: Architect
T2[子任务2: 设计实体类和VO类之间的转换机制]
T3[子任务3: 设计缓存策略]
end
subgraph 阶段3-4: Atomize & Approve
T4[子任务4: 修改Service类以实现缓存调整]
end
subgraph 阶段5: Automate
T5[子任务5: 编写测试用例]
T6[子任务6: 集成测试和验证]
end
T1 --> T2
T2 --> T3
T3 --> T4
T4 --> T5
T5 --> T6
T6 --> DONE[任务完成]
```
## 4. 执行检查清单
### 4.1 完整性检查
- [ ] 任务计划是否覆盖所有需求?
- [ ] 每个子任务的输入输出是否明确?
- [ ] 依赖关系是否清晰无循环?
### 4.2 一致性检查
- [ ] 是否与前期文档ALIGNMENT、CONSENSUS、DESIGN保持一致
- [ ] 是否遵循项目现有的技术栈和架构?
- [ ] 是否遵循项目的代码规范和命名约定?
### 4.3 可行性检查
- [ ] 技术方案是否确实可行?
- [ ] 是否有足够的资源和时间完成任务?
- [ ] 是否考虑了可能的风险和应对措施?
### 4.4 可控性检查
- [ ] 风险是否在可接受范围?
- [ ] 复杂度是否可控?
- [ ] 是否有明确的里程碑和验收标准?
### 4.5 可测性检查
- [ ] 验收标准是否明确可执行?
- [ ] 是否有合适的测试方法和工具?
- [ ] 是否能够独立验证每个子任务的成果?
## 5. 最终确认清单
- [ ] 明确的实现需求(无歧义)
- [ ] 明确的子任务定义
- [ ] 明确的边界和限制
- [ ] 明确的验收标准
- [ ] 代码、测试、文档质量标准
- [ ] 执行风险分析和应对措施
- [ ] 资源和时间计划

View File

@@ -1,4 +1,4 @@
# Server模块Service缓存调整为Vo对象拆分文档
# Server模块Service缓存调整为Vo对象拆分任务文档
## 1. 任务拆分列表
@@ -16,7 +16,7 @@
**实现约束**:
- 使用搜索工具分析代码结构
- 记录每个Service类的IEntityService泛型参数VoableService泛型参数
- 记录每个Service类的IEntityService泛型参数(Model类型)和QueryService泛型参数(Vo类型)以及VoableService泛型参数
- 记录Service类中的特殊方法和缓存配置
- 特别关注WebSocketServerCallbackManager中与IEntityService接口的交互逻辑
@@ -80,8 +80,8 @@
- 修改验证报告
**实现约束**:
- Service类继承IEntityService接口泛型类型保持为Model实体类
- Service类继承QueryService接口泛型类型修改为Vo视图对象
- Service类继承IEntityService接口泛型类型为Model实体类
- Service类继承QueryService接口泛型类型为Vo视图对象
- 严格按照接口契约修改方法签名
- 正确实现数据转换逻辑
- 确保缓存注解的正确性
@@ -104,8 +104,8 @@
- 批量修改执行报告
**实现约束**:
- Service类继承IEntityService接口泛型类型保持为Model实体类
- Service类继承QueryService接口泛型类型修改为Vo视图对象
- Service类继承IEntityService接口泛型类型为Model实体类
- Service类继承QueryService接口泛型类型为Vo视图对象
- 确保每个Service类的修改一致性
- 记录修改过程中的问题和解决方法
- 验证修改后的代码编译通过
@@ -222,8 +222,8 @@
**实现约束**:
- 重点分析WebSocketServerCallbackManager中的类型处理逻辑
- 特别关注createNewEntity、findEntityTypeInInterfaces方法
- 确保WebSocket服务能够正确处理从实体类到VO类的泛型变化
- 特别关注createNewEntity、findEntityTypeInInterfaces、invokerFindByIdMethod、invokerFindAllMethod等依赖泛型参数的方法
- 确保WebSocket服务能够正确处理IEntityService<Model>到QueryService<Vo>的泛型关系
- 添加类型安全检查
- 遵循代码规范
@@ -310,7 +310,8 @@ flowchart TD
1. **执行步骤**:
- 选择一个典型的Service类作为试点
- 修改类声明中的IEntityService泛型参数
- 确保IEntityService泛型参数为Model实体类
- 确保QueryService泛型参数为Vo视图对象
- 逐一修改实现的接口方法,添加数据转换逻辑
- 应用新的缓存策略和缓存键
- 验证修改后的代码能够编译通过
@@ -326,6 +327,8 @@ flowchart TD
1. **执行步骤**:
- 基于任务4的成功经验制定批量修改计划
- 逐一修改每个注解了@CacheConfig的Service类
- 确保每个Service类的IEntityService泛型参数为Model实体类
- 确保每个Service类的QueryService泛型参数为Vo视图对象
- 对每个Service类应用相同的转换机制和缓存策略
- 记录修改过程中的问题和解决方法
- 执行编译检查确保所有修改正确
@@ -363,11 +366,12 @@ flowchart TD
### 3.8 任务8: 编写测试用例并验证修改
1. **执行步骤**:
1. **执行步骤**:
- 为每个修改后的Service类编写单元测试
- 编写集成测试验证Service类与其他组件的交互
- 测试数据转换的正确性和性能
- 特别测试缓存功能的正常运行包括VO对象的缓存和读取
- 特别测试IEntityService<Model>和QueryService<Vo>的交互逻辑
- 特别测试缓存功能的正常运行包括Vo对象的缓存和读取
- 执行所有测试并分析结果
2. **关键交付物**:
@@ -378,13 +382,13 @@ flowchart TD
### 3.9 任务9: 更新相关文档并总结
1. **执行步骤**:
- 更新项目中的相关技术文档,记录接口泛型修改和缓存策略变更
- 更新项目中的相关技术文档,记录IEntityService<Model>和QueryService<Vo>的泛型关系以及缓存策略变更
- 编写任务总结报告
- 创建TODO列表记录未完成的工作或改进建议
- 归档所有任务文档
2. **关键交付物**:
- 更新后的项目文档
- 更新后的项目文档包含IEntityService<Model>和QueryService<Vo>的泛型关系说明)
- 任务总结报告
- TODO列表
@@ -392,16 +396,16 @@ flowchart TD
1. **执行步骤**:
- 分析WebSocketServerHandler、WebSocketServerTaskManager、WebSocketServerCallbackManager的代码
- 重点研究WebSocketServerCallbackManager中与IEntityService接口交互的方法特别是createNewEntity、findEntityTypeInInterfaces等依赖泛型参数的方法
- 重点研究WebSocketServerCallbackManager中与IEntityService接口和QueryService接口交互的方法特别是createNewEntity、findEntityTypeInInterfaces等依赖泛型参数的方法
- 分析invokerFindByIdMethod、invokerFindAllMethod等方法的实现逻辑
- 识别可能受到IEntityService泛型修改影响的代码部分
- 识别可能受到IEntityService<Model>和QueryService<Vo>泛型关系影响的代码部分
- 设计WebSocket服务组件的修改方案
- 修改WebSocketServerCallbackManager中的类型处理逻辑使其适应从实体类到VO类的泛型变化
- 添加类型安全检查,确保能够正确处理VO类型
- 修改WebSocketServerCallbackManager中的类型处理逻辑使其适应IEntityService<Model>到QueryService<Vo>的泛型关系
- 添加类型安全检查,确保能够正确处理Model和Vo类型
- 编写测试用例验证修改后的WebSocket服务组件
2. **关键交付物**:
- WebSocket服务与IEntityService接口交互分析报告
- WebSocket服务与IEntityService<Model>和QueryService<Vo>接口交互分析报告
- 潜在问题和风险清单
- 修改后的WebSocketServerCallbackManager代码
- WebSocket服务测试用例

View File

@@ -1,165 +1,84 @@
# Server模块Service缓存调整为Vo对象待办事项
# TODO - Server模块service缓存调整为Vo对象
## 1. 代码实现阶段待办
## 1. 批量修改其他Service类
### 1.1 缓存策略设计
- [ ] 设计缓存对象转换机制在缓存写入前将实体对象转换为VO对象
- [ ] 确保所有VO类都实现Serializable接口
- [ ] 验证VO对象的序列化安全性避免引用不可序列化对象
- [ ] 设计Redis缓存键命名规范考虑使用命名空间或前缀
- [ ] 实现Redis连接失败时的降级策略
按照CompanyFileTypeService的修改模式批量修改以下Service类
- CompanyCustomerFileTypeService
- CustomerFileTypeService
- CompanyFileService
- CompanyCustomerFileService
### 1.2 试点Service类修改
- [ ] 选择一个典型的Service类如CompanyCustomerFileTypeService进行试点修改
- [ ] Service类继承IEntityService接口泛型类型保持为Model实体类
- [ ] Service类继承QueryService接口泛型类型改为Vo视图对象
- [ ] 逐一修改实现的接口方法,添加数据转换逻辑
- [ ] 验证修改后的代码能够编译通过
- [ ] 编写单元测试验证功能正确性
### 操作指引:
1. 对于每个Service类
- 调整QueryService接口泛型参数为对应的Vo类
- 重构findById方法返回类型改为对应的Vo类并添加@Cacheable注解
- 保留getById方法用于获取实体对象
- 让实体类继承Voable接口并实现toVo方法完成从实体到Vo的转换
- 确保所有标注@Cacheable注解的方法的返回参数类型不为Model类型全部调整为Vo类型
- findAll(JsonNodePageable)方法的返回类型调整为Page<Vo>并使用实体类自带的toVo方法进行转换
- findAll(SpecificationPageable)方法的返回类型保持为Page<Model>
- 仅保留一个save(Model)方法,用于保存实体对象
- **不创建独立的toVo方法**:直接使用实体类自带的`toVo()`方法进行转换
### 1.2 批量Service类修改
- [ ] 制定批量修改计划,按照模块或功能分组
- [ ] 逐一修改每个注解了@CacheConfig的Service类
- [ ] Service类继承IEntityService接口泛型类型保持为Model实体类
- [ ] Service类继承QueryService接口泛型类型修改为Vo视图对象
- [ ] 应用统一的数据转换机制
- [ ] 记录修改过程中的问题和解决方法
- [ ] 执行编译检查确保所有修改正确
2. 确保每个Service类修改都符合以下要求:
- 保持接口兼容性
- 正确使用缓存注解
- 实现实体与Vo之间的正确转换
## 2. 数据转换相关待办
## 2. Redis缓存清理
### 2.1 转换逻辑实现
- [ ] 设计并实现实体类到VO类的转换方法
- [ ] 设计并实现VO类到实体类的转换方法
- [ ] 考虑添加通用的转换工具类
- [ ] 处理空值和异常情况
在所有Service类修改完成后需要清理Redis中的缓存数据以确保新的缓存策略能够正确生效。
### 2.2 Specification处理
- [ ] 研究如何处理getSpecification方法的泛型修改
- [ ] 确定是否需要保留基于实体类的Specification实现
- [ ] 设计可能的解决方案,如创建适配器或转换层
### 操作指引:
1. 开发一个缓存清理工具或脚本
2. 执行缓存清理操作,清除所有相关的缓存键
3. 验证清理结果,确保缓存已被正确清除
## 3. 依赖组件分析和修改
## 3. WebSocket服务兼容性处理
### 3.1 依赖分析
- [ ] 搜索所有调用修改后Service类的组件Controller、其他Service等
- [ ] 分析这些组件如何使用Service类的方法
- [ ] 识别需要修改的依赖组件
- [ ] 特别关注WebSocket服务组件WebSocketServerCallbackManager、WebSocketServerTaskManager
- [ ] 分析WebSocket服务如何通过反射调用IEntityService接口的方法
- [ ] 识别WebSocket服务中依赖IEntityService泛型参数的方法
检查并确保WebSocket服务能够正确处理Vo对象避免序列化问题。
### 3.2 依赖修改
- [ ] 修改受影响的Controller类
- [ ] 修改受影响的其他Service类
- [ ] 验证依赖修改的正确性
- [ ] 处理可能的级联依赖问题
- [ ] 修改WebSocketServerCallbackManager类
- [ ] 更新createNewEntity、findEntityTypeInInterfaces等依赖泛型参数的方法
- [ ] 调整invokerFindByIdMethod、invokerFindAllMethod等反射调用方法
- [ ] 确保能够正确处理VO类型的返回值
- [ ] 修改WebSocketServerTaskManager类
- [ ] 确保任务执行流程能够适应IEntityService接口的泛型变化
- [ ] 调整任务处理逻辑确保能够正确处理VO类型的数据
- [ ] 添加类型安全检查,防止类型转换错误
- [ ] 验证WebSocket服务组件修改的正确性
### 操作指引:
1. 分析WebSocket服务的代码
2. 确保WebSocket服务能够正确序列化和反序列化Vo对象
3. 必要时进行相应的修改
## 4. 测试验证待办
## 4. 测试验证
### 4.1 测试用例编写
- [ ] 为每个修改后的Service类编写单元测试
- [ ] 编写集成测试验证Service类与其他组件的交互
- [ ] 测试数据转换的正确性和性能
- [ ] 编写缓存功能专项测试用例
- 验证Redis中只存储VO对象不存储实体对象
- 测试VO对象的序列化和反序列化正确性
- 测试Redis连接失败时的降级功能
- 测试缓存清理后的系统运行状态
- [ ] 编写WebSocket服务专项测试用例
- 测试WebSocketServerCallbackManager的类型处理逻辑
- 验证createNewEntity、invokerFindByIdMethod等方法能够正确处理VO类型
- 测试WebSocketServerTaskManager的任务执行流程
- 验证WebSocket服务的整体功能
对所有修改进行全面的测试验证,确保功能正常。
### 4.2 测试执行和问题修复
### 操作指引:
1. 编写单元测试和集成测试
2. 执行功能测试验证各Service类的功能是否正常
3. 执行性能测试,验证缓存策略的效果
4. 记录测试结果
### 4.2 测试执行和问题修复
- [ ] 执行所有测试用例
- [ ] 分析测试结果,记录发现的问题
- [ ] 修复测试中发现的问题
- [ ] 重新执行测试,确保所有问题都已解决
## 5. 文档更新
## 5. 配置和部署相关待办
完成所有修改后更新相关文档包括系统架构文档、API文档等。
### 5.1 Redis缓存清理
- [ ] 制定Redis缓存清理计划
- [ ] 编写脚本查找并删除所有旧的实体类缓存
- [ ] 确保在新代码部署前完成缓存清理
- [ ] 验证清理效果,确保没有旧缓存残留
### 操作指引:
1. 整理所有修改内容
2. 更新系统架构文档
3. 更新API文档
4. 更新用户手册
### 5.2 缓存配置检查
- [ ] 验证缓存配置在修改后仍然有效
- [ ] 检查缓存键表达式是否需要调整
- [ ] 测试缓存的读写和失效机制确保只使用VO对象
## 6. 部署计划
### 5.2 部署准备
- [ ] 准备部署文档,说明修改内容和注意事项
- [ ] 考虑分阶段部署策略,降低风险
- [ ] 准备回滚方案,以防出现严重问题
制定详细的部署计划,确保修改能够顺利上线。
## 6. 文档更新待办
### 操作指引:
1. 确定部署时间窗口
2. 制定回滚策略
3. 准备部署脚本
4. 通知相关人员
### 6.1 代码文档更新
- [ ] 为修改后的Service类添加JavaDoc注释
- [ ] 更新相关接口和类的文档
- [ ] 确保文档与代码的一致性
## 7. 上线后监控
### 6.2 项目文档更新
- [ ] 更新ACCEPTANCE文档记录测试结果和完成情况
- [ ] 更新FINAL文档添加实际执行结果
- [ ] 归档所有任务文档
上线后进行持续监控,确保系统运行稳定。
## 7. 其他待办事项
### 7.1 缓存管理机制建立
- [ ] 建立缓存键命名规范文档
- [ ] 定义缓存过期策略
- [ ] 设置Redis监控措施定期检查缓存状态
- [ ] 建立缓存清理和刷新的标准流程
### 7.2 性能优化
- [ ] 分析数据转换可能带来的性能影响
- [ ] 考虑添加转换结果缓存或其他优化手段
- [ ] 进行性能测试,确保修改不会导致性能退化
### 7.2 知识分享
- [ ] 组织团队成员进行知识分享,介绍修改内容和技术方案
- [ ] 记录经验教训,为后续类似任务提供参考
- [ ] 考虑是否需要更新项目开发规范
## 8. 支持需求
以下是在实施过程中可能需要的支持:
### 8.1 技术支持
- [ ] 数据转换框架选择和实现建议
- [ ] Specification泛型修改的最佳实践
- [ ] 缓存配置优化建议
- [ ] Redis缓存管理和序列化最佳实践
- [ ] 分布式缓存问题排查和解决支持
### 8.2 资源支持
- [ ] 代码审查资源
- [ ] 测试环境和测试数据准备
- [ ] 部署和监控资源
### 8.3 其他支持
- [ ] 与相关团队的协调和沟通
- [ ] 变更管理和审批流程支持
- [ ] 风险评估和缓解策略建议
---
**更新记录**:
- 创建日期: -
- 更新日期: -
- 更新内容: -
### 操作指引:
1. 设置监控指标
2. 配置告警机制
3. 定期检查系统运行状态
4. 及时处理异常情况

View File

@@ -2,6 +2,7 @@ package com.ecep.contract;
import java.util.List;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -10,13 +11,25 @@ import org.springframework.util.StringUtils;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.MyRepository;
import com.ecep.contract.model.Voable;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;
public abstract class EntityService<T, ID> {
/**
* 实体服务基类
*
* @param <T> 实体类型
* @param <VO> VO类型
* @param <ID> 主键类型
*/
public abstract class EntityService<T extends Voable<VO>, VO, ID> {
protected abstract MyRepository<T, ID> getRepository();
public T getById(ID id) {
return getRepository().findById(id).orElse(null);
}
public abstract T createNewEntity();
public long count() {
@@ -33,13 +46,13 @@ public abstract class EntityService<T, ID> {
protected abstract Specification<T> buildParameterSpecification(JsonNode paramsNode);
public Page<T> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<VO> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<T> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
spec = SpecificationUtils.and(spec, buildParameterSpecification(paramsNode));
return findAll(spec, pageable);
return findAll(spec, pageable).map(T::toVo);
}
public Page<T> findAll(Specification<T> spec, Pageable pageable) {

View File

@@ -7,7 +7,13 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
public interface IEntityService<T> {
T findById(Integer id);
/**
* 根据ID查询实体
* <b> 注意:如果实体类有关联实体此方法不能使用 @Cacheable 注解 </b>
* @param id 实体ID
* @return 实体对象
*/
T getById(Integer id);
Page<T> findAll(Specification<T> spec, Pageable pageable);

View File

@@ -7,9 +7,16 @@ import com.fasterxml.jackson.databind.JsonNode;
/**
* 查询服务接口,提供通用的分页查询能力
* 泛型T表示查询结果的数据类型
* 泛型Vo表示查询结果的数据类型, View Object 类,可序列化,可持久的类
*/
public interface QueryService<T> {
public interface QueryService<Vo> {
/**
* 根据ID查询单条数据
* @param id
* @return 符合ID的Vo对象若不存在则返回null
*/
Vo findById(Integer id);
/**
* 根据查询参数和分页条件获取数据列表
*
@@ -17,12 +24,13 @@ public interface QueryService<T> {
* @param pageable 分页参数,包含页码、每页条数、排序规则等信息
* @return 分页查询结果,包含符合条件的数据列表和分页元数据
*/
Page<T> findAll(JsonNode paramsNode, Pageable pageable);
// Specification<T> getSpecification(String searchText);
// Page<T> findAll(Specification<T> spec, Pageable pageable);
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
/**
* 根据查询参数统计符合条件的数据总数
* @param paramsNode JSON格式的查询参数节点包含各种过滤条件
* @return 符合条件的数据总数
*/
default long count(JsonNode paramsNode) {
return 0;
}

View File

@@ -28,6 +28,7 @@ import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeAuthBind;
import com.ecep.contract.model.EmployeeLoginHistory;
import com.ecep.contract.vo.EmployeeVo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@@ -62,7 +63,7 @@ public class LoginApiController {
Map<String, Object> result = new HashMap<>();
try {
Employee employee = null;
EmployeeVo employee = null;
if ("client".equals(loginRequest.getType())) {
// 根据用户名查找Employee对象
employee = employeeService.findByAccount(loginRequest.getUsername());
@@ -133,12 +134,12 @@ public class LoginApiController {
// 登录成功后,发送系统通知
EmployeeLoginHistoryService employeeLoginHistoryService = getBean(EmployeeLoginHistoryService.class);
EmployeeLoginHistory employeeLoginHistory = new EmployeeLoginHistory();
employeeLoginHistory.setEmployee(employee);
String userId = (String) session.getAttribute("ip");
if (userId == null) {
userId = request.getRemoteAddr();
employeeLoginHistory.setEmployee(employeeService.getById(employee.getId()));
String userIp = (String) session.getAttribute("ip");
if (userIp == null) {
userIp = request.getRemoteAddr();
}
employeeLoginHistory.setIp(userId);
employeeLoginHistory.setIp(userIp);
employeeLoginHistory.setMac((String) session.getAttribute("mac"));
employeeLoginHistory.setLoginTime(LocalDateTime.now());
EmployeeLoginHistory saved = employeeLoginHistoryService.save(employeeLoginHistory);

View File

@@ -107,6 +107,11 @@ public class CloudRkService implements IEntityService<CloudRk>, VoableService<Cl
@Autowired
private CompanyBlackReasonRepository companyBlackReasonRepository;
@Override
public CloudRk getById(Integer id) {
return cloudRKRepository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
public CloudRk findById(Integer id) {
return cloudRKRepository.findById(id).orElse(null);
@@ -384,15 +389,15 @@ public class CloudRkService implements IEntityService<CloudRk>, VoableService<Cl
if (cloudRk == null || vo == null) {
throw new IllegalArgumentException("CloudRk and CloudRkVo cannot be null");
}
// 更新基本属性
if (vo.getCloudId() != null) {
cloudRk.setCloudId(vo.getCloudId());
}
cloudRk.setAutoUpdate(vo.isAutoUpdate());
cloudRk.setUpdateDays(vo.getUpdateDays());
if (vo.getCustomerGrade() != null) {
cloudRk.setCustomerGrade(vo.getCustomerGrade());
}
@@ -432,11 +437,11 @@ public class CloudRkService implements IEntityService<CloudRk>, VoableService<Cl
if (vo.getLatestUpdate() != null) {
cloudRk.setLatestUpdate(vo.getLatestUpdate());
}
// 更新关联的公司
if (vo.getCompanyId() != null) {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
Company company = companyService.findById(vo.getCompanyId());
Company company = companyService.getById(vo.getCompanyId());
if (company != null) {
cloudRk.setCompany(company);
}

View File

@@ -53,6 +53,7 @@ public class CloudRkSyncTask extends Tasker<Object> {
AtomicInteger counter = new AtomicInteger(0);
holder.info("统计需要更新的 " + total + "");
var companyService = getCompanyService();
try {
// 每次获取100条记录
while (!isCancelled()) {
@@ -73,7 +74,7 @@ public class CloudRkSyncTask extends Tasker<Object> {
break;
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = companyService.getById(company.getId());
cloudRk.setCompany(company);
}
if (cloudRk.isAutoUpdate()) {

View File

@@ -46,6 +46,11 @@ public class CloudTycService implements IEntityService<CloudTyc>, VoableService<
@Autowired
private CloudTycRepository cloudTycRepository;
@Override
public CloudTyc getById(Integer id) {
return cloudTycRepository.findById(id).orElse(null);
}
public CloudTyc getOrCreateCloudTyc(CloudInfo info) {
Optional<CloudTyc> optional = cloudTycRepository.findById(info.getId());
return optional.orElseGet(() -> getOrCreateCloudTyc(info.getCompany()));
@@ -192,7 +197,7 @@ public class CloudTycService implements IEntityService<CloudTyc>, VoableService<
// 更新关联的公司
if (vo.getCompanyId() != null) {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
Company company = companyService.findById(vo.getCompanyId());
Company company = companyService.getById(vo.getCompanyId());
if (company != null) {
cloudTyc.setCompany(company);
}

View File

@@ -12,7 +12,7 @@ import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.ContractGroup;
import com.ecep.contract.model.ContractKind;
import com.ecep.contract.model.ContractType;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vo.EmployeeVo;
import lombok.Getter;
import lombok.Setter;
@@ -82,10 +82,10 @@ public class ContractSyncContext {
consumer.accept(message);
}
}
public Employee findEmployeeByCode(String personCode) {
public EmployeeVo findEmployeeByCode(String personCode) {
return getEmployeeService().findByCode(personCode);
}
public Employee findEmployeeByName(String personName) {
public EmployeeVo findEmployeeByName(String personName) {
if (personName == null) {
return null;
}

View File

@@ -1,6 +1,5 @@
package com.ecep.contract.cloud.u8;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
@@ -100,7 +99,7 @@ public class CustomerSyncTask extends AbstContractRepairTasker {
return;
}
if (!Hibernate.isInitialized(customer)) {
customer = getCompanyCustomerService().findById(customer.getId());
customer = getCompanyCustomerService().getById(customer.getId());
}
Company company = customer.getCompany();
if (company == null) {
@@ -124,14 +123,14 @@ public class CustomerSyncTask extends AbstContractRepairTasker {
return;
}
if (!Hibernate.isInitialized(customer)) {
customer = getCompanyCustomerService().findById(customer.getId());
customer = getCompanyCustomerService().getById(customer.getId());
}
Company company = customer.getCompany();
if (company == null) {
return;
}
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
company = companyService.getById(company.getId());
}
boolean modified = false;
CompanyCtx companyCtx = customerCtx.getCompanyCtx();

View File

@@ -13,9 +13,11 @@ import org.springframework.util.StringUtils;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.other.service.DepartmentService;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Department;
import com.ecep.contract.model.Employee;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.vo.EmployeeVo;
/**
* 用友U8系统-同步员工信息
@@ -71,20 +73,21 @@ public class EmployeesSyncTask extends Tasker<Object> {
java.sql.Timestamp personInValidDate = (java.sql.Timestamp) rs.get("dPInValidDate");
boolean modified = false;
Employee employee = getEmployeeService().findByCode(personCode);
EmployeeService employeeService = getEmployeeService();
EmployeeVo employee = employeeService.findByCode(personCode);
// 按员工代码未匹配时,尝试使用名字去匹配
if (employee == null) {
employee = getEmployeeService().findByName(personName);
employee = employeeService.findByName(personName);
if (employee == null) {
employee = new Employee();
employee.setCode(personCode);
employee.setName(personName);
employee.setActive(false);
employee.setCreated(LocalDate.now());
Employee newInstance = new Employee();
newInstance.setCode(personCode);
newInstance.setName(personName);
newInstance.setActive(false);
newInstance.setCreated(LocalDate.now());
holder.info("创建员工:" + personCode + ", 姓名:" + personName + ".");
// consumer.accept("员工编号:" + personCode + ", 姓名:" + personName + ", 本地未创建");
// return;
getEmployeeService().save(newInstance);
employee = newInstance.toVo();
}
employee.setCode(personCode);
modified = true;
@@ -102,8 +105,8 @@ public class EmployeesSyncTask extends Tasker<Object> {
if (departmentByCode == null) {
subHolder.warn("部门代码:" + departmentCode + "未匹配到部门");
} else {
if (!Objects.equals(employee.getDepartment(), departmentByCode)) {
employee.setDepartment(departmentByCode);
if (!Objects.equals(employee.getDepartmentId(), departmentByCode.getId())) {
employee.setDepartmentId(departmentByCode.getId());
subHolder.info("更新部门:" + departmentByCode.getName());
modified = true;
}
@@ -145,7 +148,9 @@ public class EmployeesSyncTask extends Tasker<Object> {
}
if (modified) {
getEmployeeService().save(employee);
var v1 = employeeService.getById(employee.getId());
employeeService.updateByVo(v1, employee);
employeeService.save(v1);
subHolder.info("更新保存");
} else {
subHolder.debug("无更新");

View File

@@ -143,7 +143,7 @@ public class VendorSyncTask extends AbstContractRepairTasker {
return;
}
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
company = companyService.getById(company.getId());
}
boolean modified = false;
CompanyCtx companyCtx = vendorCtx.getCompanyCtx();

View File

@@ -8,6 +8,10 @@ import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -21,6 +25,7 @@ import com.ecep.contract.SpringApp;
import com.ecep.contract.cloud.CloudInfo;
import com.ecep.contract.cloud.CloudInfoRepository;
import com.ecep.contract.cloud.u8.ctx.AbstractYongYouU8Ctx;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.CloudYu;
import com.ecep.contract.model.Company;
@@ -30,9 +35,9 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
// @ConditionalOnProperty(name = "cloud.u8.enabled", havingValue = "true")
@CacheConfig(cacheNames = "cloud-yu")
public class YongYouU8Service
implements IEntityService<CloudYu>, QueryService<CloudYu>, VoableService<CloudYu, CloudYuVo> {
implements IEntityService<CloudYu>, QueryService<CloudYuVo>, VoableService<CloudYu, CloudYuVo> {
private static final Logger logger = LoggerFactory.getLogger(YongYouU8Service.class);
public static final String KEY_HOST_IP = "u8.db.server.ip";
@@ -54,7 +59,14 @@ public class YongYouU8Service
}
public CloudYu findById(Integer id) {
@Cacheable(key = "#id")
public CloudYuVo findById(Integer id) {
Optional<CloudYu> optional = cloudYuRepository.findById(id);
return optional.map(CloudYu::toVo).orElse(null);
}
@Override
public CloudYu getById(Integer id) {
return cloudYuRepository.findById(id).orElse(null);
}
@@ -106,13 +118,19 @@ public class YongYouU8Service
* @param cloudYu Cloud Yu 对象
* @return 更新的 Cloud Yu
*/
@Caching(evict = { @CacheEvict(key = "#cloudYu.id") })
@Override
public CloudYu save(CloudYu cloudYu) {
return cloudYuRepository.save(cloudYu);
}
@Caching(evict = { @CacheEvict(key = "#cloudYu.id") })
@Override
public void delete(CloudYu entity) {
cloudYuRepository.delete(entity);
public void delete(CloudYu vo) {
CloudYu entity = cloudYuRepository.findById(vo.getId()).orElse(null);
if (entity != null) {
cloudYuRepository.delete(entity);
}
}
public void deleteByCompany(Company company) {
@@ -137,9 +155,13 @@ public class YongYouU8Service
}
@Override
public Page<CloudYu> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CloudYuVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CloudYu> spec = null;
return findAll(spec, pageable);
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
String searchText = paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText();
spec = getSpecification(searchText);
}
return findAll(spec, pageable).map(CloudYu::toVo);
}
@Override
@@ -225,11 +247,13 @@ public class YongYouU8Service
// 更新关联的公司
if (vo.getCompanyId() != null) {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
Company company = companyService.findById(vo.getCompanyId());
if (company != null) {
if (cloudYu.getCompany() == null || !cloudYu.getCompany().getId().equals(vo.getCompanyId())) {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
Company company = companyService.getById(vo.getCompanyId());
cloudYu.setCompany(company);
}
} else {
cloudYu.setCompany(null);
}
}
}

View File

@@ -12,17 +12,18 @@ import com.ecep.contract.MessageHolder;
import com.ecep.contract.cloud.AbstractCtx;
import com.ecep.contract.cloud.u8.YongYouU8Repository;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.customer.service.CompanyCustomerEntityService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.vendor.service.VendorEntityService;
import com.ecep.contract.ds.vendor.service.VendorService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEntity;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Vendor;
import com.ecep.contract.model.VendorEntity;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vo.EmployeeVo;
import lombok.Getter;
import lombok.Setter;
@@ -66,13 +67,23 @@ public class AbstractYongYouU8Ctx extends AbstractCtx {
boolean updateEmployeeByCode(Supplier<Employee> getter, Consumer<Employee> setter, String code,
MessageHolder holder, String topic) {
if (StringUtils.hasText(code)) {
Employee employee = getEmployeeService().findByCode(code);
var employee = getEmployeeService().findByCode(code);
if (employee != null) {
if (!Objects.equals(getter.get(), employee)) {
setter.accept(employee);
holder.info(topic + "更新为 " + employee.getName());
return true;
}
return updateEmployee(getter, setter, employee, holder, topic);
}
}
return false;
}
boolean updateEmployee(Supplier<Employee> getter, Consumer<Employee> setter, EmployeeVo employee,
MessageHolder holder, String topic) {
var service = getEmployeeService();
if (employee != null) {
var v1 = getter.get();
if (v1 == null || !Objects.equals(v1.getId(), employee.getId())) {
setter.accept(service.getById(employee.getId()));
holder.info(topic + "更新为 " + employee.getName());
return true;
}
}
return false;
@@ -81,13 +92,9 @@ public class AbstractYongYouU8Ctx extends AbstractCtx {
boolean updateEmployeeByName(Supplier<Employee> getter, Consumer<Employee> setter, String name,
MessageHolder holder, String topic) {
if (StringUtils.hasText(name)) {
Employee employee = getEmployeeService().findByName(name);
EmployeeVo employee = getEmployeeService().findByName(name);
if (employee != null) {
if (!Objects.equals(getter.get(), employee)) {
setter.accept(employee);
holder.info(topic + "更新为 " + employee.getName());
return true;
}
return updateEmployee(getter, setter, employee, holder, topic);
}
}
return false;
@@ -108,7 +115,7 @@ public class AbstractYongYouU8Ctx extends AbstractCtx {
holder.warn("无效" + topic + "" + customerCode);
} else {
if (!Hibernate.isInitialized(customer)) {
customer = customerService.findById(customer.getId());
customer = customerService.getById(customer.getId());
}
company = customer.getCompany();
}
@@ -122,7 +129,7 @@ public class AbstractYongYouU8Ctx extends AbstractCtx {
if (!Objects.equals(getter.get(), company)) {
setter.accept(company);
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = getCompanyService().getById(company.getId());
}
holder.info(topic + "修改为: " + company.getName());
return true;
@@ -160,7 +167,7 @@ public class AbstractYongYouU8Ctx extends AbstractCtx {
if (!Objects.equals(getter.get(), company)) {
setter.accept(company);
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = getCompanyService().getById(company.getId());
}
holder.info(topic + "修改为: " + company.getName());
return true;

View File

@@ -35,7 +35,6 @@ public class CompanyCtx extends AbstractYongYouU8Ctx implements CompanyContext {
}
}
public boolean updateCompanyAbbNameIfAbsent(Company company, String abbName, MessageHolder holder) {
if (!StringUtils.hasText(abbName)) {
return false;
@@ -69,7 +68,8 @@ public class CompanyCtx extends AbstractYongYouU8Ctx implements CompanyContext {
return false;
}
public Company findOrCreateByNameOrAbbName(String name, String abbName, LocalDate developDate, MessageHolder holder) {
public Company findOrCreateByNameOrAbbName(String name, String abbName, LocalDate developDate,
MessageHolder holder) {
Company company = getCompanyService().findAndRemoveDuplicateCompanyByNameOrAbbName(name, abbName);
if (company != null) {
return company;
@@ -79,12 +79,12 @@ public class CompanyCtx extends AbstractYongYouU8Ctx implements CompanyContext {
holder.warn("企业库中找不到" + name + "" + abbName + " 的企业");
// 推测个人用户,归入散户
if (name.length() < 4 && !name.contains("公司")) {
company = getCompanyService().findByName("散户");
if (company == null) {
var v1 = getCompanyService().findByName("散户");
if (v1 == null) {
return null;
}
holder.info(name + " 个人用户归集到散户");
return company;
return getCompanyService().getById(v1.getId());
}
// 尝试创建公司

View File

@@ -17,6 +17,8 @@ import com.ecep.contract.model.*;
import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.NumberUtils;
import com.ecep.contract.util.TaxRateUtils;
import com.ecep.contract.vo.ContractFileTypeLocalVo;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Hibernate;
@@ -272,7 +274,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
if (contract.getType() != null && contract.getPayWay() != null) {
ContractType type = contract.getType();
if (!Hibernate.isInitialized(type)) {
type = getCachedBean(ContractTypeService.class).findById(type.getId());
type = getCachedBean(ContractTypeService.class).getById(type.getId());
contract.setType(type);
}
if (!type.getDirection().equals(contract.getPayWay().getText())) {
@@ -283,12 +285,12 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
if (contract.getKind() != null && contract.getType() != null) {
ContractKind kind = contract.getKind();
if (!Hibernate.isInitialized(kind)) {
kind = getCachedBean(ContractKindService.class).findById(kind.getId());
kind = getCachedBean(ContractKindService.class).getById(kind.getId());
contract.setKind(kind);
}
ContractType type = contract.getType();
if (!Hibernate.isInitialized(type)) {
type = getCachedBean(ContractTypeService.class).findById(type.getId());
type = getCachedBean(ContractTypeService.class).getById(type.getId());
contract.setType(type);
}
if (!kind.getName().equals(type.getTitle())) {
@@ -375,7 +377,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
return false;
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = getCompanyService().getById(company.getId());
}
contract.setCompany(company);
holder.info("关联至 " + company.getName());
@@ -440,7 +442,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
}
} else {
if (!Hibernate.isInitialized(customer)) {
customer = customerService.findById(customer.getId());
customer = customerService.getById(customer.getId());
}
if (customer.getCompany() == null) {
customer.setCompany(company);
@@ -536,7 +538,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
holder.warn("* 合同业务员:" + personId + " 未导入");
} else {
if (!Hibernate.isInitialized(employee)) {
employee = getEmployeeService().findById(employee.getId());
employee = getEmployeeService().getById(employee.getId());
}
if (!employee.isActive()) {
holder.warn("业务员 " + employee.getName() + " 已经不可用,其离职日期:" + employee.getLeaveDate());
@@ -783,7 +785,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
String memo = (String) map.get("strMemo");
if (!Hibernate.isInitialized(contract)) {
contract = getContractService().findById(contract.getId());
contract = getContractService().getById(contract.getId());
item.setContract(contract);
}
boolean modified = false;
@@ -943,7 +945,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
ProjectSaleType saleType = project.getSaleType();
if (saleType != null) {
if (!Hibernate.isInitialized(saleType)) {
saleType = getSaleTypeService().findById(saleType.getId());
saleType = getSaleTypeService().getById(saleType.getId());
}
File dir = new File(saleType.getPath());
if (saleType.isStoreByYear()) {
@@ -1170,7 +1172,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
MyDateTimeUtils.pickLocalDate(file.getName()), holder, "生效日期");
}
List<ContractFileTypeLocal> matched = getCachedBean(ContractFileTypeService.class)
List<ContractFileTypeLocalVo> matched = getCachedBean(ContractFileTypeService.class)
.findAll(getLocale()).values().stream()
.filter(local -> {
ContractFileType type = local.getType();
@@ -1209,7 +1211,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
contractFile.setType(matched.stream().max((o1, o2) -> maxLength(o1) - maxLength(o2)).get().getType());
}
private int maxLength(ContractFileTypeLocal o1) {
private int maxLength(ContractFileTypeLocalVo o1) {
String suggestFileName = o1.getSuggestFileName();
String value = o1.getValue();
if (!StringUtils.hasText(suggestFileName)) {

View File

@@ -156,7 +156,7 @@ public class CustomerCtx extends AbstractYongYouU8Ctx {
}
if (customer != null) {
if (!Hibernate.isInitialized(customer)) {
customer = getCompanyCustomerService().findById(customer.getId());
customer = getCompanyCustomerService().getById(customer.getId());
}
Company company = customer.getCompany();
if (company != null) {

View File

@@ -257,7 +257,7 @@ public class PurchaseOrderCtx extends AbstractYongYouU8Ctx {
return;
}
if (!Hibernate.isInitialized(contract)) {
contract = getContractService().findById(contract.getId());
contract = getContractService().getById(contract.getId());
}
getCompanyBankAccountCtx().updateBankAccount(contract.getCompany(), bank, bankAccount, holder);
}

View File

@@ -187,7 +187,7 @@ public class SalesBillVoucherCtx extends AbstractYongYouU8Ctx {
Contract contract = order.getContract();
if (!Hibernate.isInitialized(contract)) {
contract = getContractService().findById(contract.getId());
contract = getContractService().getById(contract.getId());
}
voucher.setCompany(contract.getCompany());
voucher.setRefId(sbvid);

View File

@@ -248,7 +248,7 @@ public class VendorCtx extends AbstractYongYouU8Ctx {
return false;
}
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
company = companyService.getById(company.getId());
}
boolean modified = false;
CompanyCtx companyCtx = getCompanyCtx();

View File

@@ -27,6 +27,7 @@ import org.springframework.security.web.SecurityFilterChain;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vo.EmployeeVo;
/**
* Spring Security配置类
@@ -110,13 +111,16 @@ public class SecurityConfig {
public UserDetailsService userDetailsService() {
return username -> {
// 使用EmployeeService根据用户名查找员工
Employee employee = employeeService.findByAccount(username);
EmployeeVo employeeVo = employeeService.findByAccount(username);
// 如果找不到员工抛出UsernameNotFoundException异常
if (employee == null) {
if (employeeVo == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
// 获取员工实体
Employee employee = employeeService.getById(employeeVo.getId());
// 检查员工是否活跃
if (!employee.isActive()) {
throw new UsernameNotFoundException("用户已禁用: " + username);

View File

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.vo.EmployeeVo;
/**
* 基础控制器,处理根路径请求
@@ -31,8 +31,8 @@ public class IndexController {
System.out.println(userDetails.getUsername());
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
try {
Employee employee = employeeService.findByAccount(userDetails.getUsername());
return ResponseEntity.ok(employee);
EmployeeVo employeeVo = employeeService.findByAccount(userDetails.getUsername());
return ResponseEntity.ok(employeeVo);
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -11,7 +11,7 @@ public class CompanyContactStringConverter extends EntityStringConverter<Company
}
public CompanyContactStringConverter(CompanyContactService service) {
setInitialized(employee -> service.findById(employee.getId()));
setInitialized(companyContact -> service.getById(companyContact.getId()));
// setFromString(service::findByName);
}

View File

@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.Company;
import com.ecep.contract.vo.CompanyVo;
@RestController
@RequestMapping("/company")
@@ -22,7 +23,7 @@ public class CompanyController {
private CompanyService companyService;
@RequestMapping("/findById")
public Company findById(Integer id) {
public CompanyVo findById(Integer id) {
return companyService.findById(id);
}
@@ -44,7 +45,7 @@ public class CompanyController {
@RequestMapping("/delete")
public void delete(Integer id) {
Company company = companyService.findById(id);
Company company = companyService.getById(id);
companyService.delete(company);
}
}

View File

@@ -7,6 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -32,7 +33,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-bank-account")
public class CompanyBankAccountService implements IEntityService<CompanyBankAccount>, QueryService<CompanyBankAccount>,
public class CompanyBankAccountService implements IEntityService<CompanyBankAccount>, QueryService<CompanyBankAccountVo>,
VoableService<CompanyBankAccount, CompanyBankAccountVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyBankAccountService.class);
@Lazy
@@ -81,8 +82,14 @@ public class CompanyBankAccountService implements IEntityService<CompanyBankAcco
return repository.findAll(spec, sort);
}
@Cacheable(key = "#p0")
@Override
public CompanyBankAccount findById(Integer id) {
public CompanyBankAccountVo findById(Integer id) {
return repository.findById(id).map(CompanyBankAccount::toVo).orElse(null);
}
@Override
public CompanyBankAccount getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -123,14 +130,14 @@ public class CompanyBankAccountService implements IEntityService<CompanyBankAcco
}
@Override
public Page<CompanyBankAccount> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyBankAccountVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyBankAccount> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyBankAccount::toVo);
}
@Override
@@ -149,14 +156,14 @@ public class CompanyBankAccountService implements IEntityService<CompanyBankAcco
// 处理关联关系 - 公司
if (vo.getCompanyId() != null) {
model.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
model.setCompany(SpringApp.getBean(CompanyService.class).getById(vo.getCompanyId()));
} else {
model.setCompany(null);
}
// 处理关联关系 - 银行
if (vo.getBankId() != null) {
model.setBank(SpringApp.getBean(BankService.class).findById(vo.getBankId()));
model.setBank(SpringApp.getBean(BankService.class).getById(vo.getBankId()));
} else {
model.setBank(null);
}

View File

@@ -174,7 +174,7 @@ public abstract class CompanyBasicService {
return;
}
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
company = companyService.getById(company.getId());
}
String companyPath = company.getPath();
if (!StringUtils.hasText(companyPath)) {

View File

@@ -11,6 +11,8 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.util.StringUtils;
import com.ecep.contract.IEntityService;
@@ -26,15 +28,18 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
public class CompanyBlackReasonService implements IEntityService<CompanyBlackReason>, QueryService<CompanyBlackReason>,
@CacheConfig(cacheNames = "company-black-reason")
public class CompanyBlackReasonService
implements IEntityService<CompanyBlackReason>, QueryService<CompanyBlackReasonVo>,
VoableService<CompanyBlackReason, CompanyBlackReasonVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyContactService.class);
@Autowired
private CompanyBlackReasonRepository repository;
public CompanyBlackReason findById(Integer id) {
return repository.findById(id).orElse(null);
@Cacheable(key = "#p0")
public CompanyBlackReasonVo findById(Integer id) {
return repository.findById(id).map(CompanyBlackReason::toVo).orElse(null);
}
@Override
@@ -55,6 +60,11 @@ public class CompanyBlackReasonService implements IEntityService<CompanyBlackRea
return repository.findAll(spec, pageable);
}
@Override
public CompanyBlackReason getById(Integer id) {
return repository.findById(id).orElse(null);
}
public void delete(CompanyBlackReason entity) {
repository.delete(entity);
}
@@ -72,20 +82,27 @@ public class CompanyBlackReasonService implements IEntityService<CompanyBlackRea
}
@Override
public Page<CompanyBlackReason> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyBlackReasonVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyBlackReason> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyBlackReason::toVo);
}
@Override
public void updateByVo(CompanyBlackReason entity, CompanyBlackReasonVo vo) {
entity.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
if (vo.getCompanyId() == null) {
entity.setCompany(null);
} else {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
if (entity.getCompany() == null || !entity.getCompany().getId().equals(vo.getCompanyId())) {
entity.setCompany(companyService.getById(vo.getCompanyId()));
}
}
// 更新基础字段
entity.setType(vo.getType());
entity.setApplyName(vo.getApplyName());

View File

@@ -18,7 +18,6 @@ import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.company.repository.CompanyContactRepository;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyContact;
import com.ecep.contract.service.ServiceException;
@@ -34,20 +33,26 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-contact")
public class CompanyContactService implements IEntityService<CompanyContact>, QueryService<CompanyContact>,
public class CompanyContactService implements IEntityService<CompanyContact>, QueryService<CompanyContactVo>,
VoableService<CompanyContact, CompanyContactVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyContactService.class);
@Autowired
private CompanyContactRepository companyContactRepository;
private CompanyContactRepository repository;
@Override
public CompanyContact getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
public CompanyContact save(CompanyContact contact) {
return companyContactRepository.save(contact);
return repository.save(contact);
}
public void resetTo(Company from, Company to) {
// 曾用名 关联到 updater
List<CompanyContact> list = companyContactRepository.findAllByCompany(from);
List<CompanyContact> list = repository.findAllByCompany(from);
if (list.isEmpty()) {
return;
}
@@ -55,11 +60,11 @@ public class CompanyContactService implements IEntityService<CompanyContact>, Qu
oldName.setMemo(MyStringUtils.appendIfAbsent(oldName.getMemo(), "转自 " + from.getId()));
oldName.setCompany(to);
}
companyContactRepository.saveAll(list);
repository.saveAll(list);
}
public void deleteByCompany(Company company) {
int deleted = companyContactRepository.deleteAllByCompany(company);
int deleted = repository.deleteAllByCompany(company);
if (deleted > 0) {
if (logger.isInfoEnabled()) {
logger.info("Delete {} records by company:#{}", deleted, company.getId());
@@ -67,16 +72,18 @@ public class CompanyContactService implements IEntityService<CompanyContact>, Qu
}
}
@Override
public void delete(CompanyContact entity) {
companyContactRepository.delete(entity);
repository.delete(entity);
}
public CompanyContact findFirstByCompany(Company company) {
return companyContactRepository.findFirstByCompany(company).orElse(null);
return repository.findFirstByCompany(company).orElse(null);
}
public CompanyContact findById(Integer id) {
return companyContactRepository.findById(id).orElse(null);
@Override
public CompanyContactVo findById(Integer id) {
return repository.findById(id).map(CompanyContact::toVo).orElse(null);
}
@Override
@@ -96,33 +103,33 @@ public class CompanyContactService implements IEntityService<CompanyContact>, Qu
@Override
public Page<CompanyContact> findAll(Specification<CompanyContact> spec, Pageable pageable) {
return companyContactRepository.findAll(spec, pageable);
return repository.findAll(spec, pageable);
}
public List<CompanyContact> searchByCompany(Company company, String userText) {
Specification<CompanyContact> spec = SpecificationUtils.and((root, query, builder) -> {
return builder.equal(root.get("company"), company);
}, getSpecification(userText));
return companyContactRepository.findAll(spec);
return repository.findAll(spec);
}
public List<CompanyContact> findAll(Specification<CompanyContact> spec, Sort sort) {
return companyContactRepository.findAll(spec, sort);
return repository.findAll(spec, sort);
}
public List<CompanyContact> findAllByCompanyAndName(Company company, String contactName) {
return companyContactRepository.findAllByCompanyAndName(company, contactName);
return repository.findAllByCompanyAndName(company, contactName);
}
@Override
public Page<CompanyContact> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyContactVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyContact> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyContact::toVo);
}
@Override
@@ -133,7 +140,7 @@ public class CompanyContactService implements IEntityService<CompanyContact>, Qu
if (vo == null) {
throw new ServiceException("CompanyContactVo cannot be null");
}
// 基本属性映射
model.setName(vo.getName());
model.setPhone(vo.getPhone());
@@ -143,14 +150,14 @@ public class CompanyContactService implements IEntityService<CompanyContact>, Qu
model.setU8Code(vo.getU8Code());
model.setMemo(vo.getMemo());
model.setCreated(vo.getCreated());
// 处理关联关系 - 公司
if (vo.getCompanyId() != null) {
model.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
model.setCompany(SpringApp.getBean(CompanyService.class).getById(vo.getCompanyId()));
} else {
model.setCompany(null);
}
// 注意:
// 1. CompanyContact实体类中没有primary字段所以不需要设置
// 2. CompanyContact实体类中没有active字段所以不需要设置

View File

@@ -17,11 +17,16 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.company.repository.CompanyExtendInfoRepository;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyExtendInfo;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.CompanyExtendInfoVo;
import com.fasterxml.jackson.databind.JsonNode;
/**
@@ -30,14 +35,15 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-extend-info")
public class CompanyExtendInfoService implements QueryService<CompanyExtendInfo> {
public class CompanyExtendInfoService implements IEntityService<CompanyExtendInfo>, QueryService<CompanyExtendInfoVo>, VoableService<CompanyExtendInfo, CompanyExtendInfoVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyExtendInfoService.class);
@Autowired
private CompanyExtendInfoRepository repository;
@Cacheable(key = "#p0")
public CompanyExtendInfo findById(int id) {
return repository.findById(id).orElse(null);
@Override
public CompanyExtendInfoVo findById(Integer id) {
return repository.findById(id).map(CompanyExtendInfo::toVo).orElse(null);
}
public List<CompanyExtendInfo> findAll(Specification<CompanyExtendInfo> spec, Sort sort) {
@@ -61,14 +67,14 @@ public class CompanyExtendInfoService implements QueryService<CompanyExtendInfo>
}
@Override
public Page<CompanyExtendInfo> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyExtendInfoVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyExtendInfo> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyExtendInfo::toVo);
}
@Caching(evict = {
@@ -83,6 +89,7 @@ public class CompanyExtendInfoService implements QueryService<CompanyExtendInfo>
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'byCompany-'+#p0.company.id")
})
@Override
public CompanyExtendInfo save(CompanyExtendInfo extendInfo) {
return repository.save(extendInfo);
}
@@ -99,4 +106,18 @@ public class CompanyExtendInfoService implements QueryService<CompanyExtendInfo>
return list.getFirst();
}
@Override
public void updateByVo(CompanyExtendInfo model, CompanyExtendInfoVo vo) throws ServiceException {
if (model == null || vo == null) {
throw new ServiceException("Model or VO cannot be null");
}
// 只更新VO中已有的字段
model.setDisableVerify(vo.isDisableVerify());
}
@Override
public CompanyExtendInfo getById(Integer id) {
return repository.findById(id).orElse(null);
}
}

View File

@@ -54,25 +54,32 @@ import com.fasterxml.jackson.databind.JsonNode;
@Service
@CacheConfig(cacheNames = "company-file")
public class CompanyFileService
implements IEntityService<CompanyFile>, QueryService<CompanyFile>, VoableService<CompanyFile, CompanyFileVo> {
implements IEntityService<CompanyFile>, QueryService<CompanyFileVo>, VoableService<CompanyFile, CompanyFileVo> {
private final VendorGroupRepository vendorGroupRepository;
private static final Logger logger = LoggerFactory.getLogger(CompanyFileService.class);
@Lazy
@Autowired
private CompanyFileRepository companyFileRepository;
private CompanyFileRepository repository;
CompanyFileService(VendorGroupRepository vendorGroupRepository) {
this.vendorGroupRepository = vendorGroupRepository;
public CompanyFileService() {
}
public CompanyFile getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
public CompanyFile findById(Integer id) {
return companyFileRepository.findById(id).orElse(null);
@Override
public CompanyFileVo findById(Integer id) {
CompanyFile companyFile = getById(id);
if (companyFile != null) {
return companyFile.toVo();
}
return null;
}
public List<CompanyFile> findFileByCompanyAndType(Company company, CompanyFileType type) {
return companyFileRepository.findByCompanyAndType(company, type);
return repository.findByCompanyAndType(company, type);
}
protected Specification<CompanyFile> buildParameterSpecification(JsonNode paramsNode) {
@@ -201,25 +208,25 @@ public class CompanyFileService
@CacheEvict(key = "#p0.id")
})
public CompanyFile save(CompanyFile companyFile) {
return companyFileRepository.save(companyFile);
return repository.save(companyFile);
}
@Caching(evict = {
@CacheEvict(key = "#p0")
})
public void deleteById(int id) {
companyFileRepository.deleteById(id);
repository.deleteById(id);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id")
})
public void delete(CompanyFile file) {
companyFileRepository.delete(file);
repository.delete(file);
}
public List<CompanyFile> findByCompany(Company company) {
return companyFileRepository.findByCompany(company);
return repository.findByCompany(company);
}
/**
@@ -229,7 +236,7 @@ public class CompanyFileService
* @param status 输出
*/
public boolean reBuildingFiles(Company company, Consumer<String> status) {
List<CompanyFile> dbFiles = companyFileRepository.findByCompany(company);
List<CompanyFile> dbFiles = repository.findByCompany(company);
List<CompanyFile> retrieveFiles = new ArrayList<>();
boolean modfied = false;
@@ -239,7 +246,7 @@ public class CompanyFileService
String filePath = dbFile.getFilePath();
// 没有文件信息,无效记录,删除
if (!StringUtils.hasText(filePath)) {
companyFileRepository.delete(dbFile);
repository.delete(dbFile);
modfied = true;
continue;
}
@@ -247,7 +254,7 @@ public class CompanyFileService
// 目录不存在,删除
File dir = new File(filePath);
if (!dir.exists()) {
companyFileRepository.delete(dbFile);
repository.delete(dbFile);
modfied = true;
continue;
}
@@ -255,7 +262,7 @@ public class CompanyFileService
CompanyFile old = map.put(filePath, dbFile);
// 目录有重复删除
if (old != null) {
companyFileRepository.delete(old);
repository.delete(old);
modfied = true;
}
}
@@ -313,7 +320,7 @@ public class CompanyFileService
// update db
retrieveFiles.forEach(v -> v.setCompany(company));
companyFileRepository.saveAll(retrieveFiles);
repository.saveAll(retrieveFiles);
return true;
}
@@ -429,18 +436,17 @@ public class CompanyFileService
}
@Override
public Page<CompanyFile> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyFileVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyFile> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
spec = SpecificationUtils.and(spec, buildParameterSpecification(paramsNode));
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyFile::toVo);
}
@Override
public Page<CompanyFile> findAll(Specification<CompanyFile> spec, Pageable pageable) {
return companyFileRepository.findAll(spec, pageable);
return repository.findAll(spec, pageable);
}
@Override
@@ -479,7 +485,7 @@ public class CompanyFileService
model.setCompany(null);
} else {
CompanyService companyService = SpringApp.getBean(CompanyService.class);
Company company = companyService.findById(vo.getCompanyId());
Company company = companyService.getById(vo.getCompanyId());
model.setCompany(company);
}

View File

@@ -26,42 +26,53 @@ import org.springframework.util.StringUtils;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@Lazy
@Service
@CacheConfig(cacheNames = "company-file-type")
public class CompanyFileTypeService
implements IEntityService<CompanyFileTypeLocal>, QueryService<CompanyFileTypeLocal>, VoableService<CompanyFileTypeLocal, CompanyFileTypeLocalVo> {
implements IEntityService<CompanyFileTypeLocal>, QueryService<CompanyFileTypeLocalVo>,
VoableService<CompanyFileTypeLocal, CompanyFileTypeLocalVo> {
@Resource
private CompanyFileTypeLocalRepository repository;
@Cacheable(key = "#p0")
@Override
public CompanyFileTypeLocal findById(Integer id) {
public CompanyFileTypeLocal getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#id")
@Override
public Page<CompanyFileTypeLocal> findAll(JsonNode paramsNode, Pageable pageable) {
public CompanyFileTypeLocalVo findById(Integer id) {
Optional<CompanyFileTypeLocal> optional = repository.findById(id);
return optional.map(CompanyFileTypeLocal::toVo).orElse(null);
}
@Override
public Page<CompanyFileTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyFileTypeLocal> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
if (paramsNode.has("type")) {
spec = SpecificationUtils.and(spec, (root, query, builder) -> builder.equal(root.get("type"), CompanyFileType.valueOf(paramsNode.get("type").asText())));
spec = SpecificationUtils.and(spec, (root, query, builder) -> builder.equal(root.get("type"),
CompanyFileType.valueOf(paramsNode.get("type").asText())));
}
// field
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyFileTypeLocal::toVo);
}
@Cacheable(key = "'all-'+#p0.toLanguageTag()")
public Map<CompanyFileType, CompanyFileTypeLocal> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag());
@Cacheable(key = "'all-'+#locale.toLanguageTag()")
public Map<CompanyFileType, CompanyFileTypeLocalVo> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag()).entrySet().stream()
.collect(java.util.stream.Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().toVo()));
}
@Override
public Page<CompanyFileTypeLocal> findAll(Specification<CompanyFileTypeLocal> spec, Pageable pageable) {
return repository.findAll(spec, pageable);
@@ -74,16 +85,16 @@ public class CompanyFileTypeService
}
return (root, query, builder) -> {
return
// builder.or(
builder.like(root.get("type"), "%" + searchText + "%")
// )
;
// builder.or(
builder.like(root.get("type"), "%" + searchText + "%")
// )
;
};
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'all-'+#p0.getLang()")
@CacheEvict(key = "#entity.id"),
@CacheEvict(key = "'all-'+#entity.getLang()")
})
@Override
public void delete(CompanyFileTypeLocal entity) {
@@ -91,8 +102,8 @@ public class CompanyFileTypeService
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'all-'+#p0.getLang()")
@CacheEvict(key = "#entity.id"),
@CacheEvict(key = "'all-'+#entity.getLang()")
})
@Override
public CompanyFileTypeLocal save(CompanyFileTypeLocal entity) {

View File

@@ -6,7 +6,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -14,11 +16,15 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.ds.company.repository.CompanyInvoiceInfoRepository;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyInvoiceInfo;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.CompanyInvoiceInfoVo;
import com.fasterxml.jackson.databind.JsonNode;
/**
@@ -27,15 +33,16 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-invoice-info")
public class CompanyInvoiceInfoService implements QueryService<CompanyInvoiceInfo> {
public class CompanyInvoiceInfoService implements IEntityService<CompanyInvoiceInfo>, QueryService<CompanyInvoiceInfoVo>, VoableService<CompanyInvoiceInfo, CompanyInvoiceInfoVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyInvoiceInfoService.class);
@Lazy
@Autowired
private CompanyInvoiceInfoRepository repository;
@Cacheable(key = "#p0")
public CompanyInvoiceInfo findById(int id) {
return repository.findById(id).orElse(null);
@Override
public CompanyInvoiceInfoVo findById(Integer id) {
return repository.findById(id).map(CompanyInvoiceInfo::toVo).orElse(null);
}
public List<CompanyInvoiceInfo> searchByCompany(Company company, String searchText) {
@@ -77,13 +84,50 @@ public class CompanyInvoiceInfoService implements QueryService<CompanyInvoiceInf
}
@Override
public Page<CompanyInvoiceInfo> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyInvoiceInfoVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyInvoiceInfo> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyInvoiceInfo::toVo);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'byCompany-'+#p0.company.id")
})
@Override
public CompanyInvoiceInfo save(CompanyInvoiceInfo model) {
return repository.save(model);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'byCompany-'+#p0.company.id")
})
public void delete(CompanyInvoiceInfo model) {
repository.delete(model);
}
@Override
public void updateByVo(CompanyInvoiceInfo model, CompanyInvoiceInfoVo vo) throws ServiceException {
if (model == null || vo == null) {
throw new ServiceException("Model or VO cannot be null");
}
// 更新VO中包含的字段
model.setName(vo.getName());
model.setTaxId(vo.getTaxId());
model.setAddress(vo.getAddress());
model.setPhone(vo.getPhone());
model.setBankName(vo.getBankName());
model.setBankAccount(vo.getBankAccount());
}
@Override
public CompanyInvoiceInfo getById(Integer id) {
return repository.findById(id).orElse(null);
}
}

View File

@@ -7,6 +7,8 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -25,11 +27,13 @@ import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.MyStringUtils;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.CompanyOldNameVo;
import com.ecep.contract.vo.CompanyVo;
import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
public class CompanyOldNameService implements IEntityService<CompanyOldName>, QueryService<CompanyOldName>, VoableService<CompanyOldName, CompanyOldNameVo> {
@CacheConfig(cacheNames = "company-old-name")
public class CompanyOldNameService implements IEntityService<CompanyOldName>, QueryService<CompanyOldNameVo>, VoableService<CompanyOldName, CompanyOldNameVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyOldNameService.class);
@Lazy
@Autowired
@@ -38,8 +42,9 @@ public class CompanyOldNameService implements IEntityService<CompanyOldName>, Qu
@Autowired
private CompanyService companyService;
public CompanyOldName findById(Integer id) {
return companyOldNameRepository.findById(id).orElse(null);
@Cacheable(key = "#p0")
public CompanyOldNameVo findById(Integer id) {
return companyOldNameRepository.findById(id).map(CompanyOldName::toVo).orElse(null);
}
@Override
@@ -85,7 +90,7 @@ public class CompanyOldNameService implements IEntityService<CompanyOldName>, Qu
public File makePath(CompanyOldName companyOldName) {
String oldName = companyOldName.getName();
File basePath = companyService.getBasePath();
Company company = companyService.findById(companyOldName.getCompanyId());
CompanyVo company = companyService.findById(companyOldName.getCompanyId());
String district = company.getDistrict();
if (StringUtils.hasText(district)) {
String parentPrefix = FileUtils.getParentPrefixByDistrict(district);
@@ -199,7 +204,12 @@ public class CompanyOldNameService implements IEntityService<CompanyOldName>, Qu
}
@Override
public Page<CompanyOldName> findAll(JsonNode paramsNode, Pageable pageable) {
public CompanyOldName getById(Integer id) {
return companyOldNameRepository.findById(id).orElse(null);
}
@Override
public Page<CompanyOldNameVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyOldName> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
@@ -219,7 +229,7 @@ public class CompanyOldNameService implements IEntityService<CompanyOldName>, Qu
});
}
}
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyOldName::toVo);
}
public CompanyOldName createNew(Company company, String name, boolean ambiguity) {

View File

@@ -1,6 +1,34 @@
package com.ecep.contract.ds.company.service;
import com.ecep.contract.*;
import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.ecep.contract.EntityService;
import com.ecep.contract.IEntityService;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.QueryService;
import com.ecep.contract.cloud.rk.CloudRkService;
import com.ecep.contract.cloud.tyc.CloudTycService;
import com.ecep.contract.cloud.u8.YongYouU8Service;
@@ -14,33 +42,17 @@ import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyOldName;
import com.ecep.contract.model.Vendor;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.FileUtils;
import com.ecep.contract.util.MyStringUtils;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.service.VoableService;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.File;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* 公司服务
@@ -48,13 +60,13 @@ import java.util.stream.Collectors;
@Lazy
@Service
@CacheConfig(cacheNames = "company")
public class CompanyService extends EntityService<Company, Integer>
implements IEntityService<Company>, QueryService<Company>, VoableService<Company, CompanyVo> {
public class CompanyService extends EntityService<Company, CompanyVo, Integer>
implements IEntityService<Company>, QueryService<CompanyVo>, VoableService<Company, CompanyVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyService.class);
@Lazy
@Autowired
private CompanyRepository companyRepository;
private CompanyRepository repository;
@Lazy
@Autowired
private SysConfService confService;
@@ -85,7 +97,7 @@ public class CompanyService extends EntityService<Company, Integer>
@Override
protected CompanyRepository getRepository() {
return companyRepository;
return repository;
}
@Override
@@ -97,13 +109,13 @@ public class CompanyService extends EntityService<Company, Integer>
}
@Cacheable(key = "#p0")
public Company findById(Integer id) {
return companyRepository.findById(id).orElse(null);
public CompanyVo findById(Integer id) {
return repository.findById(id).map(Company::toVo).orElse(null);
}
@Cacheable(key = "'name-'+#p0")
public Company findByName(String name) {
return companyRepository.findFirstByName(name).orElse(null);
public CompanyVo findByName(String name) {
return repository.findFirstByName(name).map(Company::toVo).orElse(null);
}
/**
@@ -113,12 +125,15 @@ public class CompanyService extends EntityService<Company, Integer>
* @return 记录列表
*/
public List<Company> findAllByName(String name) {
return companyRepository.findAllByName(name);
return repository.findAllByName(name);
}
@Override
protected Specification<Company> buildParameterSpecification(JsonNode paramsNode) {
return null;
Specification<Company> spec = null;
// field
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name","uniscid", "abbName");
return spec;
}
/**
@@ -130,7 +145,7 @@ public class CompanyService extends EntityService<Company, Integer>
*/
public Company findAndRemoveDuplicateCompanyByUniscid(String uniscid) {
// 根据统一社会信用代码去查询
List<Company> companies = companyRepository.findAllByUniscid(uniscid);
List<Company> companies = repository.findAllByUniscid(uniscid);
if (companies.isEmpty()) {
return null;
}
@@ -163,7 +178,7 @@ public class CompanyService extends EntityService<Company, Integer>
Company updater = null;
{
// 根据公司全名去查询
List<Company> companies = companyRepository.findAllByName(name);
List<Company> companies = repository.findAllByName(name);
if (companies.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("No record match by {}", name);
@@ -183,7 +198,7 @@ public class CompanyService extends EntityService<Company, Integer>
List<CompanyOldName> oldNames = companyOldNameService.findAllByName(name);
if (!oldNames.isEmpty()) {
CompanyOldName oldName = oldNames.getFirst();
Optional<Company> optional = companyRepository.findById(oldName.getCompanyId());
Optional<Company> optional = repository.findById(oldName.getCompanyId());
if (optional.isPresent()) {
updater = optional.get();
}
@@ -192,7 +207,7 @@ public class CompanyService extends EntityService<Company, Integer>
if (updater == null && StringUtils.hasText(abbName) && !Objects.equals(abbName, name)) {
// 根据公司全面去查询
List<Company> companies = companyRepository.findAllByShortName(abbName);
List<Company> companies = repository.findAllByShortName(abbName);
if (!companies.isEmpty()) {
updater = companies.removeFirst();
}
@@ -205,7 +220,7 @@ public class CompanyService extends EntityService<Company, Integer>
Optional<CompanyOldName> optional1 = oldNames.stream().filter(CompanyOldName::getAmbiguity)
.findFirst();
oldName = optional1.orElseGet(oldNames::getFirst);
Optional<Company> optional = companyRepository.findById(oldName.getCompanyId());
Optional<Company> optional = repository.findById(oldName.getCompanyId());
if (optional.isPresent()) {
updater = optional.get();
}
@@ -291,7 +306,7 @@ public class CompanyService extends EntityService<Company, Integer>
contractService.deleteByCompany(company);
companyContactService.deleteByCompany(company);
companyRepository.delete(company);
repository.delete(company);
if (logger.isInfoEnabled()) {
logger.info("Delete Company {}", company);
}
@@ -324,7 +339,7 @@ public class CompanyService extends EntityService<Company, Integer>
contractService.resetTo(from, to);
companyContactService.resetTo(from, to);
companyRepository.delete(from);
repository.delete(from);
if (logger.isInfoEnabled()) {
logger.info("Merge {} to {}", from, to);
}
@@ -341,7 +356,7 @@ public class CompanyService extends EntityService<Company, Integer>
@CacheEvict(key = "'name-'+#p0.name")
})
public Company save(Company company) {
return companyRepository.save(company);
return repository.save(company);
}
public File getBasePath() {
@@ -493,7 +508,7 @@ public class CompanyService extends EntityService<Company, Integer>
}
public Predicate buildSearchPredicate(String searchText, Path<Company> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
CriteriaBuilder builder) {
return builder.or(
builder.like(root.get("name"), "%" + searchText + "%"),
builder.like(root.get("shortName"), "%" + searchText + "%"),
@@ -545,4 +560,5 @@ public class CompanyService extends EntityService<Company, Integer>
company.getVersion(), vo.getVersion());
}
}
}

View File

@@ -20,9 +20,8 @@ import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.company.repository.InvoiceRepository;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Invoice;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.InvoiceVo;
@@ -31,18 +30,24 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "invoice")
public class InvoiceService implements IEntityService<Invoice>, QueryService<Invoice>,
public class InvoiceService implements IEntityService<Invoice>, QueryService<InvoiceVo>,
VoableService<Invoice, InvoiceVo> {
private static final Logger logger = LoggerFactory.getLogger(InvoiceService.class);
@Autowired
private InvoiceRepository repository;
@Cacheable(key = "#p0")
public Invoice findById(Integer id) {
@Override
public Invoice getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public InvoiceVo findById(Integer id) {
return repository.findById(id).map(Invoice::toVo).orElse(null);
}
@Override
public Specification<Invoice> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {
@@ -63,6 +68,7 @@ public class InvoiceService implements IEntityService<Invoice>, QueryService<Inv
}
@CacheEvict(key = "#p0.id")
@Override
public void delete(Invoice entity) {
repository.delete(entity);
}
@@ -76,12 +82,13 @@ public class InvoiceService implements IEntityService<Invoice>, QueryService<Inv
}
@CacheEvict(key = "#p0.id")
@Override
public Invoice save(Invoice invoice) {
return repository.save(invoice);
}
@Override
public Page<Invoice> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<InvoiceVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<Invoice> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
@@ -89,12 +96,23 @@ public class InvoiceService implements IEntityService<Invoice>, QueryService<Inv
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(Invoice::toVo);
}
@Override
public void updateByVo(Invoice model, InvoiceVo vo) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'updateByVo'");
if (model == null || vo == null) {
throw new IllegalArgumentException("Model or VO cannot be null");
}
model.setCode(vo.getCode());
if (vo.getCompanyId() == null) {
model.setCompany(null);
} else {
Company company = SpringApp.getBean(CompanyService.class).getById(vo.getCompanyId());
model.setCompany(company);
}
model.setInvoiceDate(vo.getInvoiceDate());
model.setDescription(vo.getDescription());
}
}

View File

@@ -22,7 +22,7 @@ public class ContractStringConverter extends EntityStringConverter<Contract> {
@PostConstruct
private void init() {
setInitialized(project -> service.findById(project.getId()));
setInitialized(project -> service.getById(project.getId()));
setSuggestion(service::search);
// TODO 按名称找出,容易出问题
setFromString(service::findByName);

View File

@@ -32,17 +32,26 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-ven-bid")
public class ContractBidVendorService implements IEntityService<ContractBidVendor>, QueryService<ContractBidVendor>, VoableService<ContractBidVendor, ContractBidVendorVo> {
public class ContractBidVendorService implements IEntityService<ContractBidVendor>, QueryService<ContractBidVendorVo>, VoableService<ContractBidVendor, ContractBidVendorVo> {
@Lazy
@Autowired
private ContractBidVendorRepository repository;
@Override
@Cacheable(key = "#p0")
public ContractBidVendor findById(Integer id) {
public ContractBidVendor getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public ContractBidVendorVo findById(Integer id) {
ContractBidVendor entity = getById(id);
if (entity != null) {
return entity.toVo();
}
return null;
}
@Override
public Specification<ContractBidVendor> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {
@@ -99,14 +108,14 @@ public class ContractBidVendorService implements IEntityService<ContractBidVendo
}
@Override
public Page<ContractBidVendor> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractBidVendorVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractBidVendor> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "contract", "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractBidVendor::toVo);
}
@Override
@@ -115,19 +124,27 @@ public class ContractBidVendorService implements IEntityService<ContractBidVendo
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
ContractService contractService = SpringApp.getBean(ContractService.class);
if (model.getContract() == null || !model.getContract().getId().equals(vo.getContractId())) {
model.setContract(contractService.getById(vo.getContractId()));
}
}
if (vo.getCompanyId() == null) {
model.setCompany(null);
} else {
model.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
CompanyService companyService = SpringApp.getBean(CompanyService.class);
if (model.getCompany() == null || !model.getCompany().getId().equals(vo.getCompanyId())) {
model.setCompany(companyService.getById(vo.getCompanyId()));
}
}
if (vo.getQuotationSheetFileId() == null) {
model.setQuotationSheet(null);
} else {
model.setQuotationSheet(SpringApp.getBean(ContractFileRepository.class).findById(vo.getQuotationSheetFileId()).orElse(null));
if (model.getQuotationSheet() == null || !model.getQuotationSheet().getId().equals(vo.getQuotationSheetFileId())) {
model.setQuotationSheet(SpringApp.getBean(ContractFileRepository.class).findById(vo.getQuotationSheetFileId()).orElse(null));
}
}
}
}

View File

@@ -28,7 +28,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-catalog")
public class ContractCatalogService implements IEntityService<ContractCatalog>, QueryService<ContractCatalog>,
public class ContractCatalogService implements IEntityService<ContractCatalog>, QueryService<ContractCatalogVo>,
VoableService<ContractCatalog, ContractCatalogVo> {
@Lazy
@Autowired
@@ -39,7 +39,11 @@ public class ContractCatalogService implements IEntityService<ContractCatalog>,
*/
@Cacheable(key = "#p0")
@Override
public ContractCatalog findById(Integer id) {
public ContractCatalogVo findById(Integer id) {
return repository.findById(id).map(ContractCatalog::toVo).orElse(null);
}
public ContractCatalog getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -95,12 +99,12 @@ public class ContractCatalogService implements IEntityService<ContractCatalog>,
}
@Override
public Page<ContractCatalog> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractCatalogVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractCatalog> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractCatalog::toVo);
}
@Override

View File

@@ -33,7 +33,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-file")
public class ContractFileService implements IEntityService<ContractFile>, QueryService<ContractFile>,
public class ContractFileService implements IEntityService<ContractFile>, QueryService<ContractFileVo>,
VoableService<ContractFile, ContractFileVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractFileService.class);
@Lazy
@@ -41,11 +41,16 @@ public class ContractFileService implements IEntityService<ContractFile>, QueryS
private ContractFileRepository contractFileRepository;
@Override
@Cacheable(key = "#p0")
public ContractFile findById(Integer id) {
public ContractFile getById(Integer id) {
return contractFileRepository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public ContractFileVo findById(Integer id) {
return contractFileRepository.findById(id).map(ContractFile::toVo).orElse(null);
}
@Override
public Specification<ContractFile> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {
@@ -64,7 +69,7 @@ public class ContractFileService implements IEntityService<ContractFile>, QueryS
}
@Override
public Page<ContractFile> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractFileVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractFile> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
@@ -73,7 +78,7 @@ public class ContractFileService implements IEntityService<ContractFile>, QueryS
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "contract", "type");
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractFile::toVo);
}
public List<ContractFile> findAll(Specification<ContractFile> spec, Sort by) {
@@ -147,6 +152,7 @@ public class ContractFileService implements IEntityService<ContractFile>, QueryS
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'allbycontract-'+#p0.contract.id")
})
@Override
public ContractFile save(ContractFile contractFile) {
return contractFileRepository.save(contractFile);
}
@@ -177,7 +183,10 @@ public class ContractFileService implements IEntityService<ContractFile>, QueryS
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
ContractService contractService = SpringApp.getBean(ContractService.class);
if (model.getContract() == null || !model.getContract().getId().equals(vo.getContractId())) {
model.setContract(contractService.getById(vo.getContractId()));
}
}
model.setType(vo.getType());

View File

@@ -2,6 +2,7 @@ package com.ecep.contract.ds.contract.service;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
@@ -31,14 +32,14 @@ import com.fasterxml.jackson.databind.JsonNode;
@Service
@CacheConfig(cacheNames = "contract-file-type")
public class ContractFileTypeService
implements IEntityService<ContractFileTypeLocal>, QueryService<ContractFileTypeLocal>,
implements IEntityService<ContractFileTypeLocal>, QueryService<ContractFileTypeLocalVo>,
VoableService<ContractFileTypeLocal, ContractFileTypeLocalVo> {
@Lazy
@Autowired
private ContractFileTypeLocalRepository repository;
@Override
public Page<ContractFileTypeLocal> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractFileTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractFileTypeLocal> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
@@ -51,17 +52,24 @@ public class ContractFileTypeService
// field
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "lang", "value", "suggestFileName");
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractFileTypeLocal::toVo);
}
@Cacheable(key = "'all-'+#p0.toLanguageTag()")
public Map<ContractFileType, ContractFileTypeLocal> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag());
public Map<ContractFileType, ContractFileTypeLocalVo> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag()).entrySet().stream()
.collect(java.util.stream.Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().toVo()));
}
@Cacheable(key = "#p0")
@Override
public ContractFileTypeLocal findById(Integer id) {
public ContractFileTypeLocalVo findById(Integer id) {
return repository.findById(id).map(ContractFileTypeLocal::toVo).orElse(null);
}
public ContractFileTypeLocal getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -119,7 +127,8 @@ public class ContractFileTypeService
}
@Cacheable(key = "'by-type-'+#p0.name()+'-'+#p1.toLanguageTag()")
public ContractFileTypeLocal findByTypeAndLang(ContractFileType type, Locale locale) {
return repository.findByTypeAndLang(type, locale.toLanguageTag());
public ContractFileTypeLocalVo findByTypeAndLang(ContractFileType type, Locale locale) {
return Optional.ofNullable(repository.findByTypeAndLang(type, locale.toLanguageTag()))
.map(ContractFileTypeLocal::toVo).orElse(null);
}
}

View File

@@ -17,6 +17,7 @@ import org.springframework.stereotype.Service;
import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.contract.repository.ContractGroupRepository;
import com.ecep.contract.model.ContractGroup;
import com.ecep.contract.service.VoableService;
@@ -29,7 +30,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-group")
public class ContractGroupService implements IEntityService<ContractGroup>, QueryService<ContractGroup>,
public class ContractGroupService implements IEntityService<ContractGroup>, QueryService<ContractGroupVo>,
VoableService<ContractGroup, ContractGroupVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractGroupService.class);
@@ -38,23 +39,28 @@ public class ContractGroupService implements IEntityService<ContractGroup>, Quer
private ContractGroupRepository repository;
@Override
@Cacheable(key = "#p0")
public ContractGroup findById(Integer id) {
public ContractGroup getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public ContractGroupVo findById(Integer id) {
return repository.findById(id).map(ContractGroup::toVo).orElse(null);
}
@Override
public Page<ContractGroup> findAll(Specification<ContractGroup> spec, Pageable pageable) {
return repository.findAll(spec, pageable);
}
@Override
public Page<ContractGroup> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractGroupVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractGroup> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractGroup::toVo);
}
@Override
@@ -86,6 +92,7 @@ public class ContractGroupService implements IEntityService<ContractGroup>, Quer
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'code-'+#p0.code"),
})
@Override
public ContractGroup save(ContractGroup group) {
return repository.save(group);
}

View File

@@ -20,13 +20,13 @@ import org.springframework.util.StringUtils;
import com.ecep.contract.IEntityService;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.contract.repository.ContractItemRepository;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.ds.other.service.InventoryService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.ContractItemVo;
@@ -37,7 +37,8 @@ import jakarta.persistence.criteria.Path;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-item")
public class ContractItemService implements IEntityService<ContractItem>, QueryService<ContractItem>, VoableService<ContractItem, ContractItemVo> {
public class ContractItemService implements IEntityService<ContractItem>, QueryService<ContractItemVo>,
VoableService<ContractItem, ContractItemVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractItemService.class);
@Lazy
@@ -45,11 +46,16 @@ public class ContractItemService implements IEntityService<ContractItem>, QueryS
private ContractItemRepository itemRepository;
@Override
@Cacheable(key = "#p0")
public ContractItem findById(Integer id) {
public ContractItem getById(Integer id) {
return itemRepository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public ContractItemVo findById(Integer id) {
return itemRepository.findById(id).map(ContractItem::toVo).orElse(null);
}
@Override
public Specification<ContractItem> getSpecification(String searchText) {
if (!StringUtils.hasText(searchText)) {
@@ -77,16 +83,15 @@ public class ContractItemService implements IEntityService<ContractItem>, QueryS
}
@Override
public Page<ContractItem> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractItemVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractItem> spec = null;
// TODO 完成参数处理
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "contract");
logger.warn("Unsupported paramsNode: {}", paramsNode);
return findAll(spec, pageable);
spec = SpecificationUtils.andParam(spec, paramsNode, "contract", "inventory", "creator", "updater");
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "itemCode", "refId", "title", "specification");
return findAll(spec, pageable).map(ContractItem::toVo);
}
public List<ContractItem> findAllByContract(Contract contract) {
@@ -120,35 +125,48 @@ public class ContractItemService implements IEntityService<ContractItem>, QueryS
model.setStartDate(vo.getStartDate());
model.setEndDate(vo.getEndDate());
model.setRemark(vo.getRemark());
// 处理关联对象
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
ContractService contractService = SpringApp.getBean(ContractService.class);
if (model.getContract() == null || !model.getContract().getId().equals(vo.getContractId())) {
model.setContract(contractService.getById(vo.getContractId()));
}
}
if (vo.getInventoryId() == null) {
model.setInventory(null);
} else {
model.setInventory(SpringApp.getBean(InventoryService.class).findById(vo.getInventoryId()));
InventoryService inventoryService = SpringApp.getBean(InventoryService.class);
if (model.getInventory() == null || !model.getInventory().getId().equals(vo.getInventoryId())) {
model.setInventory(inventoryService.getById(vo.getInventoryId()));
}
}
if (vo.getCreatorId() == null) {
model.setCreator(null);
} else {
model.setCreator(SpringApp.getBean(EmployeeService.class).findById(vo.getCreatorId()));
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
if (model.getCreator() == null || !model.getCreator().getId().equals(vo.getCreatorId())) {
model.setCreator(employeeService.getById(vo.getCreatorId()));
}
}
if (vo.getUpdaterId() == null) {
model.setUpdater(null);
} else {
model.setUpdater(SpringApp.getBean(EmployeeService.class).findById(vo.getUpdaterId()));
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
if (model.getUpdater() == null || !model.getUpdater().getId().equals(vo.getUpdaterId())) {
model.setUpdater(employeeService.getById(vo.getUpdaterId()));
}
}
// 创建日期和更新日期通常由系统自动维护,这里不设置
}
@Override
public ContractItem save(ContractItem item) {
return itemRepository.save(item);
}

View File

@@ -29,7 +29,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-kind")
public class ContractKindService implements IEntityService<ContractKind>, QueryService<ContractKind>,
public class ContractKindService implements IEntityService<ContractKind>, QueryService<ContractKindVo>,
VoableService<ContractKind, ContractKindVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractKindService.class);
@@ -39,7 +39,11 @@ public class ContractKindService implements IEntityService<ContractKind>, QueryS
@Override
@Cacheable(key = "'kind-'+#p0")
public ContractKind findById(Integer id) {
public ContractKindVo findById(Integer id) {
return repository.findById(id).map(ContractKind::toVo).orElse(null);
}
public ContractKind getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -49,12 +53,12 @@ public class ContractKindService implements IEntityService<ContractKind>, QueryS
}
@Override
public Page<ContractKind> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractKindVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractKind> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractKind::toVo);
}
@Override

View File

@@ -31,16 +31,21 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-pay-plan")
public class ContractPayPlanService implements IEntityService<ContractPayPlan>, QueryService<ContractPayPlan>,
public class ContractPayPlanService implements IEntityService<ContractPayPlan>, QueryService<ContractPayPlanVo>,
VoableService<ContractPayPlan, ContractPayPlanVo> {
@Lazy
@Autowired
private ContractPayPlanRepository repository;
@Override
public ContractPayPlan getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public ContractPayPlan findById(Integer id) {
return repository.findById(id).orElse(null);
public ContractPayPlanVo findById(Integer id) {
return repository.findById(id).map(ContractPayPlan::toVo).orElse(null);
}
@Override
@@ -59,14 +64,14 @@ public class ContractPayPlanService implements IEntityService<ContractPayPlan>,
}
@Override
public Page<ContractPayPlan> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractPayPlanVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractPayPlan> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "contract");
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractPayPlan::toVo);
}
public List<ContractPayPlan> findAllByContract(Contract contract) {
@@ -104,7 +109,7 @@ public class ContractPayPlanService implements IEntityService<ContractPayPlan>,
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
model.setContract(SpringApp.getBean(ContractService.class).getById(vo.getContractId()));
}
if (vo.getUpdateDate() != null) {

View File

@@ -54,8 +54,8 @@ import jakarta.persistence.criteria.Predicate;
@Lazy
@Service
@CacheConfig(cacheNames = "contract")
public class ContractService extends EntityService<Contract, Integer>
implements IEntityService<Contract>, QueryService<Contract>, VoableService<Contract, ContractVo> {
public class ContractService extends EntityService<Contract, ContractVo, Integer>
implements IEntityService<Contract>, QueryService<ContractVo>, VoableService<Contract, ContractVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractService.class);
@Lazy
@Autowired
@@ -73,8 +73,8 @@ public class ContractService extends EntityService<Contract, Integer>
}
@Cacheable(key = "#p0")
public Contract findById(Integer id) {
return getRepository().findById(id).orElse(null);
public ContractVo findById(Integer id) {
return getRepository().findById(id).map(Contract::toVo).orElse(null);
}
public Contract findByName(String name) {
@@ -395,30 +395,50 @@ public class ContractService extends EntityService<Contract, Integer>
contract.setExecUnTaxAmount(vo.getExecUnTaxAmount());
contract.setPayWay(vo.getPayWay());
// 处理关联实体 - 公司
if (vo.getCompanyId() == null) {
contract.setCompany(null);
} else {
contract.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
CompanyService companyService = SpringApp.getBean(CompanyService.class);
if (contract.getCompany() == null || !contract.getCompany().getId().equals(vo.getCompanyId())) {
contract.setCompany(companyService.getById(vo.getCompanyId()));
}
}
// 处理关联实体 - 合同组
if (vo.getGroupId() == null) {
contract.setGroup(null);
} else {
contract.setGroup(SpringApp.getBean(ContractGroupService.class).findById(vo.getGroupId()));
ContractGroupService contractGroupService = SpringApp.getBean(ContractGroupService.class);
if (contract.getGroup() == null || !contract.getGroup().getId().equals(vo.getGroupId())) {
contract.setGroup(contractGroupService.getById(vo.getGroupId()));
}
}
// 处理关联实体 - 合同类型
if (vo.getTypeId() == null) {
contract.setType(null);
} else {
contract.setType(SpringApp.getBean(ContractTypeService.class).findById(vo.getTypeId()));
ContractTypeService contractTypeService = SpringApp.getBean(ContractTypeService.class);
if (contract.getType() == null || !contract.getType().getId().equals(vo.getTypeId())) {
contract.setType(contractTypeService.getById(vo.getTypeId()));
}
}
// 处理关联实体 - 合同种类
if (vo.getKindId() == null) {
contract.setKind(null);
} else {
contract.setKind(SpringApp.getBean(ContractKindService.class).findById(vo.getKindId()));
ContractKindService contractKindService = SpringApp.getBean(ContractKindService.class);
if (contract.getKind() == null || !contract.getKind().getId().equals(vo.getKindId())) {
contract.setKind(contractKindService.getById(vo.getKindId()));
}
}
// 处理关联实体 - 项目
if (vo.getProject() == null) {
contract.setProject(null);
} else {
contract.setProject(SpringApp.getBean(ProjectService.class).findById(vo.getProject()));
ProjectService projectService = SpringApp.getBean(ProjectService.class);
if (contract.getProject() == null || !contract.getProject().getId().equals(vo.getProject())) {
contract.setProject(projectService.getById(vo.getProject()));
}
}
contract.setParentCode(vo.getParentCode());
contract.setOrderDate(vo.getOrderDate());
@@ -426,32 +446,47 @@ public class ContractService extends EntityService<Contract, Integer>
contract.setEndDate(vo.getEndDate());
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
// 处理关联实体 - 员工
if (vo.getEmployeeId() == null) {
contract.setEmployee(null);
} else {
contract.setEmployee(employeeService.findById(vo.getEmployeeId()));
if (contract.getEmployee() == null || !contract.getEmployee().getId().equals(vo.getEmployeeId())) {
contract.setEmployee(employeeService.getById(vo.getEmployeeId()));
}
}
// 处理关联实体 - 处理人
if (vo.getHandlerId() == null) {
contract.setHandler(null);
} else {
contract.setHandler(employeeService.findById(vo.getHandlerId()));
if (contract.getHandler() == null || !contract.getHandler().getId().equals(vo.getHandlerId())) {
contract.setHandler(employeeService.getById(vo.getHandlerId()));
}
}
// 处理关联实体 - 订立人
if (vo.getSetupPersonId() == null) {
contract.setSetupPerson(null);
} else {
contract.setSetupPerson(employeeService.findById(vo.getSetupPersonId()));
if (contract.getSetupPerson() == null || !contract.getSetupPerson().getId().equals(vo.getSetupPersonId())) {
contract.setSetupPerson(employeeService.getById(vo.getSetupPersonId()));
}
}
contract.setSetupDate(vo.getSetupDate());
// 处理关联实体 - 生效人
if (vo.getInurePersonId() == null) {
contract.setInurePerson(null);
} else {
contract.setInurePerson(employeeService.findById(vo.getInurePersonId()));
if (contract.getInurePerson() == null || !contract.getInurePerson().getId().equals(vo.getInurePersonId())) {
contract.setInurePerson(employeeService.getById(vo.getInurePersonId()));
}
}
contract.setInureDate(vo.getInureDate());
// 处理关联实体 - 变更人
if (vo.getVaryPersonId() == null) {
contract.setVaryPerson(null);
} else {
contract.setVaryPerson(employeeService.findById(vo.getVaryPersonId()));
if (contract.getVaryPerson() == null || !contract.getVaryPerson().getId().equals(vo.getVaryPersonId())) {
contract.setVaryPerson(employeeService.getById(vo.getVaryPersonId()));
}
}
contract.setVaryDate(vo.getVaryDate());
}

View File

@@ -29,7 +29,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-type")
public class ContractTypeService implements IEntityService<ContractType>, QueryService<ContractType>,
public class ContractTypeService implements IEntityService<ContractType>, QueryService<ContractTypeVo>,
VoableService<ContractType, ContractTypeVo> {
private static final Logger logger = LoggerFactory.getLogger(ContractTypeService.class);
@@ -38,7 +38,12 @@ public class ContractTypeService implements IEntityService<ContractType>, QueryS
private ContractTypeRepository repository;
@Cacheable(key = "#p0")
public ContractType findById(Integer id) {
@Override
public ContractTypeVo findById(Integer id) {
return repository.findById(id).map(ContractType::toVo).orElse(null);
}
public ContractType getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -48,12 +53,12 @@ public class ContractTypeService implements IEntityService<ContractType>, QueryS
}
@Override
public Page<ContractType> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ContractTypeVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ContractType> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
return findAll(spec, pageable);
return findAll(spec, pageable).map(ContractType::toVo);
}
public ContractType findByName(String name) {
@@ -70,6 +75,16 @@ public class ContractTypeService implements IEntityService<ContractType>, QueryS
return repository.findAll();
}
@Override
public Specification<ContractType> getSpecification(String searchText) {
return (root, query, builder) -> {
return builder.or(
builder.like(root.get("code"), "%" + searchText + "%"),
builder.like(root.get("name"), "%" + searchText + "%"),
builder.like(root.get("title"), "%" + searchText + "%"));
};
}
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'code-'+#p0.code"),
@@ -96,16 +111,6 @@ public class ContractTypeService implements IEntityService<ContractType>, QueryS
repository.delete(type);
}
@Override
public Specification<ContractType> getSpecification(String searchText) {
return (root, query, builder) -> {
return builder.or(
builder.like(root.get("code"), "%" + searchText + "%"),
builder.like(root.get("name"), "%" + searchText + "%"),
builder.like(root.get("title"), "%" + searchText + "%"));
};
}
@Override
public void updateByVo(ContractType model, ContractTypeVo vo) {
// 更新基本属性

View File

@@ -31,7 +31,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-ext-ven-info")
public class ExtendVendorInfoService implements IEntityService<ExtendVendorInfo>, QueryService<ExtendVendorInfo>,
public class ExtendVendorInfoService implements IEntityService<ExtendVendorInfo>, QueryService<ExtendVendorInfoVo>,
VoableService<ExtendVendorInfo, ExtendVendorInfoVo> {
@Lazy
@Autowired
@@ -44,11 +44,20 @@ public class ExtendVendorInfoService implements IEntityService<ExtendVendorInfo>
private ContractService contractService;
@Override
@Cacheable(key = "#p0")
public ExtendVendorInfo findById(Integer id) {
public ExtendVendorInfo getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public ExtendVendorInfoVo findById(Integer id) {
ExtendVendorInfo entity = getById(id);
if (entity != null) {
return entity.toVo();
}
return null;
}
@Cacheable(key = "'bycontract-'+#p0.id")
public ExtendVendorInfo findByContract(Contract contract) {
return repository.findByContractId(contract.getId()).orElse(null);
@@ -90,14 +99,14 @@ public class ExtendVendorInfoService implements IEntityService<ExtendVendorInfo>
}
@Override
public Page<ExtendVendorInfo> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<ExtendVendorInfoVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<ExtendVendorInfo> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "contract");
return findAll(spec, pageable);
return findAll(spec, pageable).map(ExtendVendorInfo::toVo);
}
public List<ExtendVendorInfo> findAll(Specification<ExtendVendorInfo> spec, Sort sort) {
@@ -138,16 +147,20 @@ public class ExtendVendorInfoService implements IEntityService<ExtendVendorInfo>
entity.setPrePurchase(vo.isPrePurchase());
// 处理关联对象
if (vo.getContractId() != null) {
entity.setContract(contractService.findById(vo.getContractId()));
} else {
if (vo.getContractId() == null) {
entity.setContract(null);
} else {
if (entity.getContract() == null || !entity.getContract().getId().equals(vo.getContractId())) {
entity.setContract(contractService.getById(vo.getContractId()));
}
}
if (vo.getGroupId() != null) {
entity.setGroup(vendorGroupService.findById(vo.getGroupId()));
} else {
if (vo.getGroupId() == null) {
entity.setGroup(null);
} else {
if (entity.getGroup() == null || !entity.getGroup().getId().equals(vo.getGroupId())) {
entity.setGroup(vendorGroupService.getById(vo.getGroupId()));
}
}
}
}

View File

@@ -26,12 +26,17 @@ import com.fasterxml.jackson.databind.JsonNode;
@Service
@CacheConfig(cacheNames = "purchase-bill-voucher-item")
public class PurchaseBillVoucherItemService
implements IEntityService<PurchaseBillVoucherItem>, QueryService<PurchaseBillVoucherItem>,
implements IEntityService<PurchaseBillVoucherItem>, QueryService<PurchaseBillVoucherItem>,
VoableService<PurchaseBillVoucherItem, PurchaseBillVoucherItemVo> {
@Lazy
@Autowired
private PurchaseBillVoucherItemRepository repository;
@Override
public PurchaseBillVoucherItem getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public PurchaseBillVoucherItem findById(Integer id) {
@@ -86,11 +91,11 @@ public class PurchaseBillVoucherItemService
public void updateByVo(PurchaseBillVoucherItem model, PurchaseBillVoucherItemVo vo) {
model.setId(vo.getId());
model.setRefId(vo.getRefId());
// 关联实体ID设置
// voucher, orderItem, invoice, inventory, contract等实体需要在service层进行设置
// 这里只设置基本属性,关联实体的设置需要在调用此方法的地方处理
model.setQuantity(vo.getQuantity());
model.setPrice(vo.getPrice());
model.setDescription(vo.getDescription());

View File

@@ -45,6 +45,11 @@ public class PurchaseBillVoucherService
@Autowired
private PurchaseBillVoucherRepository repository;
@Override
public PurchaseBillVoucher getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public PurchaseBillVoucher findById(Integer id) {
@@ -149,32 +154,32 @@ public class PurchaseBillVoucherService
if (vo.getInvoiceId() == null) {
model.setInvoice(null);
} else {
model.setInvoice(SpringApp.getBean(InvoiceService.class).findById(vo.getInvoiceId()));
model.setInvoice(SpringApp.getBean(InvoiceService.class).getById(vo.getInvoiceId()));
}
if (vo.getCompanyId() == null) {
model.setCompany(null);
} else {
model.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
model.setCompany(SpringApp.getBean(CompanyService.class).getById(vo.getCompanyId()));
}
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
if (vo.getEmployeeId() == null) {
model.setEmployee(null);
} else {
model.setEmployee(employeeService.findById(vo.getEmployeeId()));
model.setEmployee(employeeService.getById(vo.getEmployeeId()));
}
if (vo.getMakerId() == null) {
model.setMaker(null);
} else {
model.setMaker(employeeService.findById(vo.getMakerId()));
model.setMaker(employeeService.getById(vo.getMakerId()));
}
if (vo.getVerifierId() == null) {
model.setVerifier(null);
} else {
model.setVerifier(employeeService.findById(vo.getVerifierId()));
model.setVerifier(employeeService.getById(vo.getVerifierId()));
}
}
}

View File

@@ -32,17 +32,22 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-purchase-order-item")
public class PurchaseOrderItemService implements IEntityService<PurchaseOrderItem>, QueryService<PurchaseOrderItem>,
public class PurchaseOrderItemService implements IEntityService<PurchaseOrderItem>, QueryService<PurchaseOrderItemVo>,
VoableService<PurchaseOrderItem, PurchaseOrderItemVo> {
private static final Logger logger = LoggerFactory.getLogger(PurchaseOrderItemService.class);
@Lazy
@Autowired
private PurchaseOrderItemRepository repository;
@Override
public PurchaseOrderItem getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public PurchaseOrderItem findById(Integer id) {
return repository.findById(id).orElse(null);
public PurchaseOrderItemVo findById(Integer id) {
return repository.findById(id).map(PurchaseOrderItem::toVo).orElse(null);
}
@Cacheable(key = "'refId-'+#p0")
@@ -77,14 +82,14 @@ public class PurchaseOrderItemService implements IEntityService<PurchaseOrderIte
}
@Override
public Page<PurchaseOrderItem> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<PurchaseOrderItemVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<PurchaseOrderItem> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "order", "inventory");
return findAll(spec, pageable);
return findAll(spec, pageable).map(PurchaseOrderItem::toVo);
}
@Caching(evict = {
@@ -128,13 +133,13 @@ public class PurchaseOrderItemService implements IEntityService<PurchaseOrderIte
if (vo.getOrder() == null) {
model.setOrder(null);
} else {
model.setOrder(SpringApp.getBean(PurchaseOrdersService.class).findById(vo.getOrder()));
model.setOrder(SpringApp.getBean(PurchaseOrdersService.class).getById(vo.getOrder()));
}
if(vo.getInventoryId() == null) {
if (vo.getInventoryId() == null) {
model.setInventory(null);
} else {
model.setInventory(SpringApp.getBean(InventoryService.class).findById(vo.getInventoryId()));
model.setInventory(SpringApp.getBean(InventoryService.class).getById(vo.getInventoryId()));
}
}
}

View File

@@ -22,7 +22,6 @@ import com.ecep.contract.SpringApp;
import com.ecep.contract.ds.contract.repository.PurchaseOrderRepository;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.PurchaseOrder;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
@@ -36,22 +35,27 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-purchase-order")
public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, QueryService<PurchaseOrder>,
public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, QueryService<PurchaseOrderVo>,
VoableService<PurchaseOrder, PurchaseOrderVo> {
private static final Logger logger = LoggerFactory.getLogger(PurchaseOrdersService.class);
@Lazy
@Autowired
private PurchaseOrderRepository purchaseOrderRepository;
private PurchaseOrderRepository repository;
@Lazy
@Autowired
private EmployeeService employeeService;
@Override
public PurchaseOrder getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
@Cacheable(key = "#p0")
public PurchaseOrder findById(Integer id) {
return purchaseOrderRepository.findById(id).orElse(null);
public PurchaseOrderVo findById(Integer id) {
return repository.findById(id).map(PurchaseOrder::toVo).orElse(null);
}
@Override
@@ -65,12 +69,12 @@ public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, Que
@Cacheable(key = "'code-'+#p0")
public PurchaseOrder findByCode(String code) {
return purchaseOrderRepository.findByCode(code).orElse(null);
return repository.findByCode(code).orElse(null);
}
@Cacheable(key = "'refId-'+#p0")
public PurchaseOrder findByRefId(Integer refId) {
return purchaseOrderRepository.findByRefId(refId).orElse(null);
return repository.findByRefId(refId).orElse(null);
}
/**
@@ -84,8 +88,9 @@ public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, Que
@CacheEvict(key = "'code-'+#p0.code"),
@CacheEvict(key = "'refId-'+#p0.refId")
})
@Override
public PurchaseOrder save(PurchaseOrder order) {
return purchaseOrderRepository.save(order);
return repository.save(order);
}
@Caching(evict = {
@@ -95,38 +100,38 @@ public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, Que
})
@Override
public void delete(PurchaseOrder order) {
purchaseOrderRepository.delete(order);
repository.delete(order);
}
public long count() {
return purchaseOrderRepository.count();
return repository.count();
}
public long count(Specification<PurchaseOrder> spec) {
return purchaseOrderRepository.count(spec);
return repository.count(spec);
}
@Override
public Page<PurchaseOrder> findAll(Specification<PurchaseOrder> spec, Pageable pageable) {
return purchaseOrderRepository.findAll(spec, pageable);
return repository.findAll(spec, pageable);
}
@Override
public Page<PurchaseOrder> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<PurchaseOrderVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<PurchaseOrder> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
spec = SpecificationUtils.andParam(spec, paramsNode, "contract");
return findAll(spec, pageable);
return findAll(spec, pageable).map(PurchaseOrder::toVo);
}
public List<PurchaseOrder> findAll(Specification<PurchaseOrder> spec, Sort sort) {
return purchaseOrderRepository.findAll(spec, sort);
return repository.findAll(spec, sort);
}
public List<PurchaseOrder> findAllByContract(Contract contract) {
return purchaseOrderRepository.findAllByContract(contract);
return repository.findAllByContract(contract);
}
public List<PurchaseOrder> search(String searchText) {
@@ -135,7 +140,7 @@ public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, Que
builder.like(root.get("name"), "%" + searchText + "%"),
builder.like(root.get("code"), "%" + searchText + "%"));
};
return purchaseOrderRepository.findAll(spec, Pageable.ofSize(10)).getContent();
return repository.findAll(spec, Pageable.ofSize(10)).getContent();
}
@Override
@@ -150,41 +155,52 @@ public class PurchaseOrdersService implements IEntityService<PurchaseOrder>, Que
model.setVendorCode(vo.getVendorCode());
model.setDescription(vo.getDescription());
// 更新负责人信息
// 处理关联实体 - 合同
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
ContractService contractService = SpringApp.getBean(ContractService.class);
if (model.getContract() == null || !model.getContract().getId().equals(vo.getContractId())) {
model.setContract(contractService.getById(vo.getContractId()));
}
}
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
// 更新采购人信息
// 处理关联实体 - 采购人
if (vo.getEmployeeId() == null) {
model.setEmployee(null);
} else {
model.setEmployee(employeeService.findById(vo.getEmployeeId()));
if (model.getEmployee() == null || !model.getEmployee().getId().equals(vo.getEmployeeId())) {
model.setEmployee(employeeService.getById(vo.getEmployeeId()));
}
}
// 更新制单人信息
// 处理关联实体 - 制单人
if (vo.getMakerId() == null) {
model.setMaker(null);
} else {
model.setMaker(employeeService.findById(vo.getMakerId()));
if (model.getMaker() == null || !model.getMaker().getId().equals(vo.getMakerId())) {
model.setMaker(employeeService.getById(vo.getMakerId()));
}
}
model.setMakerDate(vo.getMakerDate());
model.setModifyDate(vo.getModifyDate());
// 更新审核人信息
// 处理关联实体 - 审核人
if (vo.getVerifierId() == null) {
model.setVerifier(null);
} else {
model.setVerifier(employeeService.findById(vo.getVerifierId()));
if (model.getVerifier() == null || !model.getVerifier().getId().equals(vo.getVerifierId())) {
model.setVerifier(employeeService.getById(vo.getVerifierId()));
}
}
model.setVerifierDate(vo.getVerifierDate());
// 更新关闭人信息
// 处理关联实体 - 关闭人
if (vo.getCloserId() == null) {
model.setCloser(null);
} else {
model.setCloser(employeeService.findById(vo.getCloserId()));
if (model.getCloser() == null || !model.getCloser().getId().equals(vo.getCloserId())) {
model.setCloser(employeeService.getById(vo.getCloserId()));
}
}
model.setCloserDate(vo.getCloserDate());
}

View File

@@ -32,17 +32,17 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-sale-order")
public class SaleOrdersService extends EntityService<SalesOrder, Integer>
implements IEntityService<SalesOrder>, QueryService<SalesOrder>, VoableService<SalesOrder, SalesOrderVo> {
public class SaleOrdersService extends EntityService<SalesOrder, SalesOrderVo, Integer>
implements IEntityService<SalesOrder>, QueryService<SalesOrderVo>, VoableService<SalesOrder, SalesOrderVo> {
private static final Logger logger = LoggerFactory.getLogger(SaleOrdersService.class);
@Lazy
@Autowired
private SalesOrderRepository salesOrderRepository;
private SalesOrderRepository repository;
@Override
protected SalesOrderRepository getRepository() {
return salesOrderRepository;
return repository;
}
@Override
@@ -50,9 +50,14 @@ public class SaleOrdersService extends EntityService<SalesOrder, Integer>
return new SalesOrder();
}
@Override
public SalesOrder getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
public SalesOrder findById(Integer id) {
return salesOrderRepository.findById(id).orElse(null);
public SalesOrderVo findById(Integer id) {
return repository.findById(id).map(SalesOrder::toVo).orElse(null);
}
@Override
@@ -74,7 +79,7 @@ public class SaleOrdersService extends EntityService<SalesOrder, Integer>
@Cacheable(key = "'code-'+#p0")
public SalesOrder findByCode(String code) {
return salesOrderRepository.findByCode(code).orElse(null);
return repository.findByCode(code).orElse(null);
}
/**
@@ -88,7 +93,7 @@ public class SaleOrdersService extends EntityService<SalesOrder, Integer>
@CacheEvict(key = "'code-'+#p0.code")
})
public SalesOrder save(SalesOrder order) {
return salesOrderRepository.save(order);
return repository.save(order);
}
@Caching(evict = {
@@ -96,7 +101,7 @@ public class SaleOrdersService extends EntityService<SalesOrder, Integer>
@CacheEvict(key = "'code-'+#p0.code")
})
public void delete(SalesOrder order) {
salesOrderRepository.delete(order);
repository.delete(order);
}
@Override
@@ -111,31 +116,31 @@ public class SaleOrdersService extends EntityService<SalesOrder, Integer>
if (vo.getContractId() == null) {
model.setContract(null);
} else {
model.setContract(SpringApp.getBean(ContractService.class).findById(vo.getContractId()));
model.setContract(SpringApp.getBean(ContractService.class).getById(vo.getContractId()));
}
if (vo.getEmployeeId() == null) {
model.setEmployee(null);
} else {
model.setEmployee(SpringApp.getBean(EmployeeService.class).findById(vo.getEmployeeId()));
model.setEmployee(SpringApp.getBean(EmployeeService.class).getById(vo.getEmployeeId()));
}
if (vo.getMakerId() == null) {
model.setMaker(null);
} else {
model.setMaker(SpringApp.getBean(EmployeeService.class).findById(vo.getMakerId()));
model.setMaker(SpringApp.getBean(EmployeeService.class).getById(vo.getMakerId()));
}
if (vo.getVerifierId() == null) {
model.setVerifier(null);
} else {
model.setVerifier(SpringApp.getBean(EmployeeService.class).findById(vo.getVerifierId()));
model.setVerifier(SpringApp.getBean(EmployeeService.class).getById(vo.getVerifierId()));
}
// active字段在SalesOrder实体类中不存在不需要设置
}
public List<SalesOrder> findAllByContract(Contract contract) {
return salesOrderRepository.findAllByContract(contract);
return repository.findAllByContract(contract);
}
}

View File

@@ -37,8 +37,8 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "sales-bill-voucher")
public class SalesBillVoucherService extends EntityService<SalesBillVoucher, Integer>
implements IEntityService<SalesBillVoucher>, QueryService<SalesBillVoucher>,
public class SalesBillVoucherService extends EntityService<SalesBillVoucher, SalesBillVoucherVo, Integer>
implements IEntityService<SalesBillVoucher>, QueryService<SalesBillVoucherVo>,
VoableService<SalesBillVoucher, SalesBillVoucherVo> {
private static final Logger logger = LoggerFactory.getLogger(SalesBillVoucherService.class);
@@ -60,8 +60,9 @@ public class SalesBillVoucherService extends EntityService<SalesBillVoucher, Int
}
@Cacheable(key = "#p0")
public SalesBillVoucher findById(Integer id) {
return salesBillVoucherRepository.findById(id).orElse(null);
@Override
public SalesBillVoucherVo findById(Integer id) {
return salesBillVoucherRepository.findById(id).map(SalesBillVoucher::toVo).orElse(null);
}
@Override
@@ -163,32 +164,32 @@ public class SalesBillVoucherService extends EntityService<SalesBillVoucher, Int
if (vo.getCompanyId() == null) {
model.setCompany(null);
} else {
model.setCompany(SpringApp.getBean(CompanyService.class).findById(vo.getCompanyId()));
model.setCompany(SpringApp.getBean(CompanyService.class).getById(vo.getCompanyId()));
}
if (vo.getOrderId() == null) {
model.setOrder(null);
} else {
model.setOrder(SpringApp.getBean(SaleOrdersService.class).findById(vo.getOrderId()));
model.setOrder(SpringApp.getBean(SaleOrdersService.class).getById(vo.getOrderId()));
}
EmployeeService employeeService = SpringApp.getBean(EmployeeService.class);
if (vo.getEmployeeId() == null) {
model.setEmployee(null);
} else {
model.setEmployee(employeeService.findById(vo.getEmployeeId()));
model.setEmployee(employeeService.getById(vo.getEmployeeId()));
}
if (vo.getMakerId() == null) {
model.setMaker(null);
} else {
model.setMaker(employeeService.findById(vo.getMakerId()));
model.setMaker(employeeService.getById(vo.getMakerId()));
}
if (vo.getVerifierId() == null) {
model.setVerifier(null);
} else {
model.setVerifier(employeeService.findById(vo.getVerifierId()));
model.setVerifier(employeeService.getById(vo.getVerifierId()));
}
}
}

View File

@@ -24,8 +24,8 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "contract-sale-order-item")
public class SalesOrderItemService extends EntityService<SalesOrderItem, Integer>
implements IEntityService<SalesOrderItem>, QueryService<SalesOrderItem>,
public class SalesOrderItemService extends EntityService<SalesOrderItem, SalesOrderItemVo, Integer>
implements IEntityService<SalesOrderItem>, QueryService<SalesOrderItemVo>,
VoableService<SalesOrderItem, SalesOrderItemVo> {
@Lazy
@Autowired
@@ -43,8 +43,8 @@ public class SalesOrderItemService extends EntityService<SalesOrderItem, Integer
@Cacheable(key = "#p0")
@Override
public SalesOrderItem findById(Integer id) {
return repository.findById(id).orElse(null);
public SalesOrderItemVo findById(Integer id) {
return repository.findById(id).map(SalesOrderItem::toVo).orElse(null);
}
@Override
@@ -105,7 +105,7 @@ public class SalesOrderItemService extends EntityService<SalesOrderItem, Integer
if (vo.getOrderId() == null) {
model.setOrder(null);
} else {
model.setOrder(SpringApp.getBean(SaleOrdersService.class).findById(vo.getOrderId()));
model.setOrder(SpringApp.getBean(SaleOrdersService.class).getById(vo.getOrderId()));
}
}
}

View File

@@ -259,7 +259,7 @@ public abstract class AbstContractRepairTasker extends Tasker<Object> {
}
} catch (Exception e) {
if (!Hibernate.isInitialized(v)) {
v = contractService.findById(v.getId());
v = contractService.getById(v.getId());
}
throw new RuntimeException("同步子合同失败, contract=" + v.toPrettyString(), e);
}

View File

@@ -133,11 +133,11 @@ public class ContractRepairComm {
}
public Project findProjectById(Integer projectId) {
return getProjectService().findById(projectId);
return getProjectService().getById(projectId);
}
public Company findCompanyById(Integer companyId) {
return getCompanyService().findById(companyId);
return getCompanyService().getById(companyId);
}
public Contract findContractByGuid(String guid) {

View File

@@ -14,7 +14,6 @@ import java.util.Objects;
import java.util.stream.Collectors;
import org.hibernate.Hibernate;
import org.springframework.security.core.userdetails.User;
import org.springframework.util.StringUtils;
import com.ecep.contract.ContractFileType;
@@ -28,7 +27,6 @@ import com.ecep.contract.ds.company.service.CompanyExtendInfoService;
import com.ecep.contract.ds.contract.service.ExtendVendorInfoService;
import com.ecep.contract.ds.converter.NumberStringConverter;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.service.tasker.ProjectCostImportItemsFromContractsTasker;
import com.ecep.contract.ds.project.service.ProjectBidService;
import com.ecep.contract.ds.project.service.ProjectCostService;
import com.ecep.contract.ds.project.service.ProjectQuotationService;
@@ -53,8 +51,10 @@ import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.model.ProjectSaleTypeRequireFileType;
import com.ecep.contract.model.VendorGroup;
import com.ecep.contract.model.VendorGroupRequireFileType;
import com.ecep.contract.util.SecurityUtils;
import com.ecep.contract.service.tasker.ProjectCostImportItemsFromContractsTasker;
import com.ecep.contract.util.VerifyContext;
import com.ecep.contract.vo.ContractFileTypeLocalVo;
import com.ecep.contract.vo.ProjectSaleTypeVo;
import lombok.Data;
@@ -95,7 +95,7 @@ public class ContractVerifyComm extends VerifyContext {
/**
*
*/
private Map<ContractFileType, ContractFileTypeLocal> fileTypeLocalMap = null;
private Map<ContractFileType, ContractFileTypeLocalVo> fileTypeLocalMap = null;
private Contract contract;
/**
@@ -151,7 +151,7 @@ public class ContractVerifyComm extends VerifyContext {
return false;
}
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = getCompanyService().getById(company.getId());
}
if (!verify(company, contract, holder)) {
passed = false;
@@ -297,7 +297,7 @@ public class ContractVerifyComm extends VerifyContext {
if (group != null) {
if (!Hibernate.isInitialized(group)) {
group = getVendorGroupService().findById(group.getId());
group = getVendorGroupService().getById(group.getId());
vendorInfo.setGroup(group);
}
}
@@ -372,7 +372,7 @@ public class ContractVerifyComm extends VerifyContext {
}
} else {
if (!Hibernate.isInitialized(contractFile)) {
contractFile = getContractFileService().findById(contractFile.getId());
contractFile = getContractFileService().getById(contractFile.getId());
}
ContractFileType type = contractFile.getType();
if (type != ContractFileType.QuotationSheet) {
@@ -393,7 +393,7 @@ public class ContractVerifyComm extends VerifyContext {
}
}
ContractFileTypeLocal getFileTypeLocal(ContractFileType type) {
ContractFileTypeLocalVo getFileTypeLocal(ContractFileType type) {
if (fileTypeLocalMap == null) {
fileTypeLocalMap = getContractFileTypeService().findAll(getLocale());
}
@@ -401,7 +401,7 @@ public class ContractVerifyComm extends VerifyContext {
}
String getFileTypeLocalValue(ContractFileType type) {
ContractFileTypeLocal fileTypeLocal = getContractFileTypeService().findByTypeAndLang(type, getLocale());
ContractFileTypeLocalVo fileTypeLocal = getContractFileTypeService().findByTypeAndLang(type, getLocale());
if (fileTypeLocal == null) {
return type.name();
}
@@ -451,7 +451,7 @@ public class ContractVerifyComm extends VerifyContext {
// fixed no hibernate session
if (project != null) {
if (!Hibernate.isInitialized(project)) {
project = getProjectService().findById(project.getId());
project = getProjectService().getById(project.getId());
// fixed
contract.setProject(project);
}
@@ -467,7 +467,7 @@ public class ContractVerifyComm extends VerifyContext {
if (saleType != null) {
if (CompanyFileUtils.exists(contract.getPath())) {
if (!Hibernate.isInitialized(saleType)) {
saleType = getProjectSaleTypeService().findById(saleType.getId());
saleType = getProjectSaleTypeService().getById(saleType.getId());
project.setSaleType(saleType);
}
if (!contract.getPath().startsWith(saleType.getPath())) {
@@ -624,7 +624,10 @@ public class ContractVerifyComm extends VerifyContext {
if (saleType == null) {
String code = contract.getCode();
if (code != null && code.length() > 5) {
saleType = getProjectSaleTypeService().findByCode(code.substring(0, 1));
ProjectSaleTypeVo saleTypeVo = getProjectSaleTypeService().findByCode(code.substring(0, 1));
if (saleTypeVo != null) {
saleType = getProjectSaleTypeService().getById(saleTypeVo.getId());
}
}
}
@@ -639,12 +642,23 @@ public class ContractVerifyComm extends VerifyContext {
//
boolean needImport = false;
ProjectCost autoCost = getProjectCostService().findAutoCostByProject(project);
List<ProjectCost> projectCosts = getProjectCostService().findByProject(project);
ProjectCost autoCost = null;
for (ProjectCost cost : projectCosts) {
if (cost.getVersion() == 0) {
autoCost = cost;
break;
}
}
if (autoCost == null) {
// 创建 V0 项目成本
ProjectCost cost = getProjectCostService().newInstanceByProject(project);
ProjectCost cost = new ProjectCost();
cost.setProject(project);
cost.setVersion(0);
cost.setApplicant(getEmployeeService().findById(EmployeeService.DEFAULT_SYSTEM_EMPLOYEE_ID));
cost.setStampTax(0.03f);
cost.setTaxAndSurcharges(11f);
cost.setApplicant(getEmployeeService().getById(EmployeeService.DEFAULT_SYSTEM_EMPLOYEE_ID));
cost.setApplyTime(LocalDateTime.now());
cost.setDescription("自动导入");
autoCost = getProjectCostService().save(cost);
@@ -667,15 +681,9 @@ public class ContractVerifyComm extends VerifyContext {
// 在类中添加以下依赖,假设使用 Spring Security 来获取当前登录用户
// 以下代码替换原有的获取当前用户逻辑,假设用户名即为员工 ID
// 需根据实际业务调整 UserDetails 中的信息获取方式
// 使用系统员工作为当前用户,实际项目中可能需要修改这里的逻辑
tasker.setCurrentUser(() -> {
User currentUser = SecurityUtils.getCurrentUser();
String username = currentUser.getUsername();
try {
return getEmployeeService().findByName(username);
} catch (NumberFormatException e) {
// 处理转换失败的情况,可根据实际需求调整
return null;
}
return getEmployeeService().getById(EmployeeService.DEFAULT_SYSTEM_EMPLOYEE_ID);
});
autoCost.setApplyTime(LocalDateTime.now());
@@ -684,10 +692,23 @@ public class ContractVerifyComm extends VerifyContext {
}
// 检查最新的项目成本,默认 V1
ProjectCost latestCost = getProjectCostService().findLatestByProject(project);
ProjectCost latestCost = null;
if (!projectCosts.isEmpty()) {
int maxVersion = -1;
for (ProjectCost cost : projectCosts) {
if (cost.getVersion() > maxVersion) {
maxVersion = cost.getVersion();
latestCost = cost;
}
}
}
if (latestCost == null) {
ProjectCost cost = getProjectCostService().newInstanceByProject(project);
ProjectCost cost = new ProjectCost();
cost.setProject(project);
cost.setVersion(1);
cost.setStampTax(0.03f);
cost.setTaxAndSurcharges(11f);
Employee applicant = project.getApplicant();
if (applicant == null) {
applicant = contract.getEmployee();
@@ -766,7 +787,7 @@ public class ContractVerifyComm extends VerifyContext {
}
}
if (!Hibernate.isInitialized(saleType)) {
saleType = getProjectSaleTypeService().findById(saleType.getId());
saleType = getProjectSaleTypeService().getById(saleType.getId());
}
if (saleType.isCriticalProjectDecision()) {
if (contract.getAmount() != null && contract.getAmount() >= saleType.getCriticalProjectLimit()) {

View File

@@ -30,6 +30,7 @@ import com.ecep.contract.ds.project.service.ProjectIndustryService;
import com.ecep.contract.ds.project.service.ProjectService;
import com.ecep.contract.ds.project.service.ProjectTypeService;
import com.ecep.contract.ds.project.service.ProjectSaleTypeService;
import com.ecep.contract.ds.project.repository.ProjectCostRepository;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.Employee;
@@ -254,7 +255,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
String address = null;
if (project != null) {
if (!Hibernate.isInitialized(project)) {
project = getProjectService().findById(project.getId());
project = getProjectService().getById(project.getId());
contract.setProject(project);
}
projectName = project.getName();
@@ -262,7 +263,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
saleType = project.getSaleType();
if (saleType != null) {
if (!Hibernate.isInitialized(saleType)) {
saleType = getProjectSaleTypeService().findById(saleType.getId());
saleType = getProjectSaleTypeService().getById(saleType.getId());
project.setSaleType(saleType);
}
}
@@ -270,7 +271,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
projectType = project.getProjectType();
if (projectType != null) {
if (!Hibernate.isInitialized(projectType)) {
projectType = getProjectTypeService().findById(projectType.getId());
projectType = getProjectTypeService().getById(projectType.getId());
project.setProjectType(projectType);
}
}
@@ -278,7 +279,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
industry = project.getIndustry();
if (industry != null) {
if (!Hibernate.isInitialized(industry)) {
industry = getProjectIndustryService().findById(industry.getId());
industry = getProjectIndustryService().getById(industry.getId());
project.setIndustry(industry);
}
}
@@ -286,7 +287,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
productType = project.getProductType();
if (productType != null) {
if (!Hibernate.isInitialized(productType)) {
productType = getProductTypeService().findById(productType.getId());
productType = getProductTypeService().getById(productType.getId());
project.setProductType(productType);
}
@@ -310,7 +311,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
Company company = contract.getCompany();
if (company != null) {
if (!Hibernate.isInitialized(company)) {
company = getCompanyService().findById(company.getId());
company = getCompanyService().getById(company.getId());
contract.setCompany(company);
}
setCellValue(sheet, "C6", company.getName());
@@ -320,7 +321,7 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
Employee employee = contract.getEmployee();
if (employee != null) {
if (!Hibernate.isInitialized(employee)) {
employee = getEmployeeService().findById(employee.getId());
employee = getEmployeeService().getById(employee.getId());
contract.setEmployee(employee);
}
setCellValue(sheet, "M7", employee.getName());
@@ -328,7 +329,8 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
float taxAndSurcharges = 11;
ProjectCost cost = getCostService().findByContract(contract);
ProjectCostRepository costRepository = SpringApp.getBean(ProjectCostRepository.class);
ProjectCost cost = costRepository.findByContract(contract).orElse(null);
if (cost != null) {
taxAndSurcharges = cost.getTaxAndSurcharges();
if (cost.getApplyTime() != null) {
@@ -340,7 +342,10 @@ public class CustomerContractCostFormUpdateTask extends Tasker<Object> {
float stampTax = 0.03f;
setCellValue(sheet, "D39", stampTax + "%");
List<ProjectCostItem> items = getCostItemService().findByCost(cost);
List<ProjectCostItem> items = List.of();
if (cost != null) {
items = getCostItemService().findByCostId(cost.getId());
}
for (int i = 0; i < items.size(); i++) {
ProjectCostItem item = items.get(i);
int row = 10 + i;

View File

@@ -28,7 +28,7 @@ import com.fasterxml.jackson.databind.JsonNode;
@Lazy
@Service
@CacheConfig(cacheNames = "company-customer-entity")
public class CompanyCustomerEntityService implements IEntityService<CompanyCustomerEntity>, QueryService<CompanyCustomerEntity>, VoableService<CompanyCustomerEntity, CompanyCustomerEntityVo> {
public class CompanyCustomerEntityService implements IEntityService<CompanyCustomerEntity>, QueryService<CompanyCustomerEntityVo>, VoableService<CompanyCustomerEntity, CompanyCustomerEntityVo> {
@Lazy
@Autowired
private CompanyCustomerEntityRepository repository;
@@ -42,10 +42,15 @@ public class CompanyCustomerEntityService implements IEntityService<CompanyCusto
@Autowired
private EmployeeService employeeService;
@Override
public CompanyCustomerEntity getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public CompanyCustomerEntity findById(Integer id) {
return repository.findById(id).orElse(null);
public CompanyCustomerEntityVo findById(Integer id) {
return repository.findById(id).map(CompanyCustomerEntity::toVo).orElse(null);
}
@Override
@@ -76,14 +81,14 @@ public class CompanyCustomerEntityService implements IEntityService<CompanyCusto
}
@Override
public Page<CompanyCustomerEntity> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyCustomerEntityVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyCustomerEntity> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "customer");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyCustomerEntity::toVo);
}
@Caching(evict = {
@@ -123,26 +128,56 @@ public class CompanyCustomerEntityService implements IEntityService<CompanyCusto
@Override
public void updateByVo(CompanyCustomerEntity entity, CompanyCustomerEntityVo vo) {
if (vo.getCustomerId() != null) {
entity.setCustomer(companyCustomerService.findById(vo.getCustomerId()));
// 优化关联实体处理逻辑 - 客户关联
if (vo.getCustomerId() == null) {
entity.setCustomer(null);
} else {
// 添加实体匹配检查
if (entity.getCustomer() == null || !entity.getCustomer().getId().equals(vo.getCustomerId())) {
// 使用getById方法替代findById
entity.setCustomer(companyCustomerService.getById(vo.getCustomerId()));
}
}
// 映射基本属性
entity.setName(vo.getName());
entity.setAbbName(vo.getAbbName());
entity.setCode(vo.getCode());
if (vo.getCustomerCatalogId() != null) {
entity.setCatalog(customerCatalogService.findById(vo.getCustomerCatalogId()));
// 优化关联实体处理逻辑 - 客户分类关联
if (vo.getCustomerCatalogId() == null) {
entity.setCatalog(null);
} else {
// 添加实体匹配检查
if (entity.getCatalog() == null || !entity.getCatalog().getId().equals(vo.getCustomerCatalogId())) {
// 使用getById方法替代findById
entity.setCatalog(customerCatalogService.getById(vo.getCustomerCatalogId()));
}
}
if (vo.getCreatorId() != null) {
entity.setCreator(employeeService.findById(vo.getCreatorId()));
// 优化关联实体处理逻辑 - 创建者关联
if (vo.getCreatorId() == null) {
entity.setCreator(null);
} else {
// 添加实体匹配检查
if (entity.getCreator() == null || !entity.getCreator().getId().equals(vo.getCreatorId())) {
// 使用getById方法替代findById
entity.setCreator(employeeService.getById(vo.getCreatorId()));
}
}
if (vo.getModifierId() != null) {
entity.setModifier(employeeService.findById(vo.getModifierId()));
// 优化关联实体处理逻辑 - 修改者关联
if (vo.getModifierId() == null) {
entity.setModifier(null);
} else {
// 添加实体匹配检查
if (entity.getModifier() == null || !entity.getModifier().getId().equals(vo.getModifierId())) {
// 使用getById方法替代findById
entity.setModifier(employeeService.getById(vo.getModifierId()));
}
}
// 映射其他属性
entity.setModifyDate(vo.getModifyDate());
entity.setDevelopDate(vo.getDevelopDate());
entity.setUpdatedDate(vo.getUpdatedDate());

View File

@@ -41,10 +41,15 @@ public class CompanyCustomerEvaluationFormFileService
@Autowired
private CompanyCustomerEvaluationFormFileRepository repository;
@Override
public CompanyCustomerEvaluationFormFile getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public CompanyCustomerEvaluationFormFile findById(Integer id) {
return repository.findById(id).orElse(null);
return getById(id);
}
@Override
@@ -154,11 +159,16 @@ public class CompanyCustomerEvaluationFormFileService
model.setScoreTemplateVersion(vo.getScoreTemplateVersion());
if (vo.getCustomerFile() != null) {
CompanyCustomerFileService service = SpringApp.getBean(CompanyCustomerFileService.class);
model.setCustomerFile(service.findById(vo.getCustomerFile()));
} else {
// 优化关联实体处理逻辑
if (vo.getCustomerFile() == null) {
model.setCustomerFile(null);
} else {
CompanyCustomerFileService service = SpringApp.getBean(CompanyCustomerFileService.class);
// 添加实体匹配检查
if (model.getCustomerFile() == null || !model.getCustomerFile().getId().equals(vo.getCustomerFile())) {
// 使用getById方法替代findById
model.setCustomerFile(service.getById(vo.getCustomerFile()));
}
}
}
}

View File

@@ -63,6 +63,11 @@ public class CompanyCustomerFileService
@Cacheable(key = "#p0")
public CompanyCustomerFile findById(Integer id) {
return getById(id);
}
@Override
public CompanyCustomerFile getById(Integer id) {
return companyCustomerFileRepository.findById(id).orElse(null);
}
@@ -276,19 +281,24 @@ public class CompanyCustomerFileService
model.setFilePath(vo.getFilePath());
model.setType(vo.getType());
model.setFilePath(vo.getFilePath());
model.setEditFilePath(vo.getEditFilePath());
model.setSignDate(vo.getSignDate());
model.setValid(vo.isValid());
if (vo.getCustomer() != null) {
CompanyCustomerService service = SpringApp.getBean(CompanyCustomerService.class);
model.setCustomer(service.findById(vo.getCustomer()));
} else {
// 优化关联实体处理逻辑
if (vo.getCustomer() == null) {
model.setCustomer(null);
} else {
CompanyCustomerService service = SpringApp.getBean(CompanyCustomerService.class);
// 添加实体匹配检查
if (model.getCustomer() == null || !model.getCustomer().getId().equals(vo.getCustomer())) {
// 使用getById方法替代findById
model.setCustomer(service.getById(vo.getCustomer()));
}
}
}
}

View File

@@ -2,6 +2,7 @@ package com.ecep.contract.ds.customer.service;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
@@ -31,19 +32,23 @@ import jakarta.annotation.Resource;
@Lazy
@Service
@CacheConfig(cacheNames = "customer-file-type")
public class CompanyCustomerFileTypeService implements IEntityService<CompanyCustomerFileTypeLocal>, QueryService<CompanyCustomerFileTypeLocal>,
public class CompanyCustomerFileTypeService implements IEntityService<CompanyCustomerFileTypeLocal>, QueryService<CompanyCustomerFileTypeLocalVo>,
VoableService<CompanyCustomerFileTypeLocal, CompanyCustomerFileTypeLocalVo> {
@Resource
private CompanyCustomerFileTypeLocalRepository repository;
@Cacheable(key = "#p0")
@Override
public CompanyCustomerFileTypeLocal findById(Integer id) {
public CompanyCustomerFileTypeLocalVo findById(Integer id) {
return repository.findById(id).map(CompanyCustomerFileTypeLocal::toVo).orElse(null);
}
public CompanyCustomerFileTypeLocal getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Override
public Page<CompanyCustomerFileTypeLocal> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyCustomerFileTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyCustomerFileTypeLocal> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
@@ -53,12 +58,16 @@ public class CompanyCustomerFileTypeService implements IEntityService<CompanyCus
spec = SpecificationUtils.and(spec, (root, query, builder) -> builder.equal(root.get("type"), CustomerFileType.valueOf(paramsNode.get("type").asText())));
}
// field
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyCustomerFileTypeLocal::toVo);
}
@Cacheable(key = "'all-'+#p0.toLanguageTag()")
public Map<CustomerFileType, CompanyCustomerFileTypeLocal> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag());
public Map<CustomerFileType, CompanyCustomerFileTypeLocalVo> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag()).entrySet().stream()
.collect(java.util.stream.Collectors.toMap(
java.util.Map.Entry::getKey,
entry -> entry.getValue().toVo()
));
}
@Override

View File

@@ -59,13 +59,13 @@ import jakarta.persistence.criteria.Path;
@Service
@CacheConfig(cacheNames = "company-customer")
public class CompanyCustomerService extends CompanyBasicService
implements IEntityService<CompanyCustomer>, QueryService<CompanyCustomer>,
implements IEntityService<CompanyCustomer>, QueryService<CompanyCustomerVo>,
VoableService<CompanyCustomer, CompanyCustomerVo> {
private static final Logger logger = LoggerFactory.getLogger(CompanyCustomerService.class);
@Lazy
@Autowired
private CompanyCustomerRepository companyCustomerRepository;
private CompanyCustomerRepository repository;
@Lazy
@Autowired
private CompanyCustomerFileService companyCustomerFileService;
@@ -77,19 +77,24 @@ public class CompanyCustomerService extends CompanyBasicService
private CompanyCustomerEntityService companyCustomerEntityService;
public CompanyCustomer findByCompany(Company company) {
return companyCustomerRepository.findByCompany(company).orElse(null);
return repository.findByCompany(company).orElse(null);
}
@Override
public CompanyCustomer getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
public CompanyCustomer findById(Integer id) {
return companyCustomerRepository.findById(id).orElse(null);
public CompanyCustomerVo findById(Integer id) {
return repository.findById(id).map(CompanyCustomer::toVo).orElse(null);
}
@Caching(evict = {
@CacheEvict(key = "#p0.id")
})
public CompanyCustomer save(CompanyCustomer companyCustomer) {
return companyCustomerRepository.save(companyCustomer);
return repository.save(companyCustomer);
}
@Caching(evict = {
@@ -97,7 +102,7 @@ public class CompanyCustomerService extends CompanyBasicService
})
@Override
public void delete(CompanyCustomer entity) {
companyCustomerRepository.delete(entity);
repository.delete(entity);
}
@Override
@@ -138,7 +143,7 @@ public class CompanyCustomerService extends CompanyBasicService
@Override
public List<CompanyCustomer> search(String searchText) {
Specification<CompanyCustomer> spec = getSpecification(searchText);
return companyCustomerRepository.findAll(spec, Pageable.ofSize(10)).getContent();
return repository.findAll(spec, Pageable.ofSize(10)).getContent();
}
@Override
@@ -188,7 +193,7 @@ public class CompanyCustomerService extends CompanyBasicService
@SuppressWarnings("unchecked")
@Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsDefaultType(F dbFile, File file,
Consumer<String> status) {
Consumer<String> status) {
dbFile.setType((T) CustomerFileType.General);
fillFile(dbFile, file, null, status);
companyCustomerFileService.save((CompanyCustomerFile) dbFile);
@@ -197,7 +202,7 @@ public class CompanyCustomerService extends CompanyBasicService
@Override
protected <T, F extends CompanyBasicFile<T>> boolean fillFileAsEvaluationFile(F customerFile, File file,
List<File> fileList, Consumer<String> status) {
List<File> fileList, Consumer<String> status) {
boolean modified = super.fillFileAsEvaluationFile(customerFile, file, fileList, status);
if (fileList != null) {
@@ -231,7 +236,7 @@ public class CompanyCustomerService extends CompanyBasicService
}
private <T, F extends CompanyBasicFile<T>> void updateEvaluationFileByJsonFile(F customerFile, File jsonFile,
Consumer<String> status) throws IOException {
Consumer<String> status) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(jsonFile);
if (!root.isObject()) {
@@ -257,7 +262,7 @@ public class CompanyCustomerService extends CompanyBasicService
@SuppressWarnings("unchecked")
@Override
protected <T, F extends CompanyBasicFile<T>> F fillFileType(File file, List<File> fileList,
Consumer<String> status) {
Consumer<String> status) {
CompanyCustomerFile customerFile = new CompanyCustomerFile();
customerFile.setType(CustomerFileType.General);
if (fillFile(customerFile, file, fileList, status)) {
@@ -281,7 +286,7 @@ public class CompanyCustomerService extends CompanyBasicService
return (fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME1)
|| fileName.contains(CompanyCustomerConstant.EVALUATION_FORM_NAME2))
&& (FileUtils.withExtensions(fileName, FileUtils.JPG, FileUtils.JPEG,
FileUtils.PDF));
FileUtils.PDF));
}
public boolean makePathAbsent(CompanyCustomer companyCustomer) {
@@ -308,7 +313,7 @@ public class CompanyCustomerService extends CompanyBasicService
File basePath = getBasePath();
Company company = companyCustomer.getCompany();
if (!Hibernate.isInitialized(company)) {
company = companyService.findById(company.getId());
company = companyService.getById(company.getId());
}
String companyName = company.getName();
@@ -325,18 +330,18 @@ public class CompanyCustomerService extends CompanyBasicService
}
public Page<CompanyCustomer> findAll(Specification<CompanyCustomer> spec, Pageable pageable) {
return companyCustomerRepository.findAll(spec, pageable);
return repository.findAll(spec, pageable);
}
@Override
public Page<CompanyCustomer> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CompanyCustomerVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CompanyCustomer> spec = null;
if (paramsNode.has("searchText")) {
spec = getSpecification(paramsNode.get("searchText").asText());
}
// field
spec = SpecificationUtils.andParam(spec, paramsNode, "company");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CompanyCustomer::toVo);
}
/**
@@ -347,17 +352,17 @@ public class CompanyCustomerService extends CompanyBasicService
*/
public void resetTo(Company from, Company to) {
// 这里使用Optional对象来处理可能为空的情况
Optional<CompanyCustomer> fromCustomer = companyCustomerRepository.findByCompany(from);
Optional<CompanyCustomer> fromCustomer = repository.findByCompany(from);
if (fromCustomer.isEmpty()) {
// 无效数据
return;
}
Optional<CompanyCustomer> toCustomer = companyCustomerRepository.findByCompany(to);
Optional<CompanyCustomer> toCustomer = repository.findByCompany(to);
if (toCustomer.isEmpty()) {
CompanyCustomer customer = fromCustomer.get();
customer.setCompany(to);
// 保存更新后的CompanyCustomer对象到数据库
companyCustomerRepository.save(customer);
repository.save(customer);
return;
}
@@ -378,14 +383,14 @@ public class CompanyCustomerService extends CompanyBasicService
// entity
companyCustomerEntityService.resetTo(from, to);
// 删除源客户对象
companyCustomerRepository.delete(from);
repository.delete(from);
}
/**
* 删除 company 的 客户
*/
public void deleteByCompany(Company company) {
int deleted = companyCustomerRepository.deleteAllByCompany(company);
int deleted = repository.deleteAllByCompany(company);
if (deleted > 0) {
if (logger.isInfoEnabled()) {
logger.info("Delete {} records by company:#{}", deleted, company.getId());
@@ -395,29 +400,47 @@ public class CompanyCustomerService extends CompanyBasicService
@Override
public void updateByVo(CompanyCustomer customer, CompanyCustomerVo vo) {
// 优化关联实体处理逻辑 - 公司关联
if (vo.getCompanyId() == null) {
customer.setCompany(null);
} else {
CompanyService service = SpringApp.getBean(CompanyService.class);
customer.setCompany(service.findById(vo.getCompanyId()));
// 添加实体匹配检查
if (customer.getCompany() == null || !customer.getCompany().getId().equals(vo.getCompanyId())) {
// 使用getById方法替代findById
customer.setCompany(service.getById(vo.getCompanyId()));
}
}
// 优化关联实体处理逻辑 - 客户分类关联
if (vo.getCatalogId() == null) {
customer.setCatalog(null);
} else {
CustomerCatalogService service = SpringApp.getBean(CustomerCatalogService.class);
customer.setCatalog(service.findById(vo.getCatalogId()));
// 添加实体匹配检查
if (customer.getCatalog() == null || !customer.getCatalog().getId().equals(vo.getCatalogId())) {
// 使用getById方法替代findById
customer.setCatalog(service.getById(vo.getCatalogId()));
}
}
// 映射基本属性
customer.setDevelopDate(vo.getDevelopDate());
customer.setPath(vo.getPath());
// 优化关联实体处理逻辑 - 联系人关联
if (vo.getContactId() == null) {
customer.setContact(null);
} else {
CompanyContactService service = SpringApp.getBean(CompanyContactService.class);
customer.setContact(service.findById(vo.getContactId()));
// 添加实体匹配检查
if (customer.getContact() == null || !customer.getContact().getId().equals(vo.getContactId())) {
// 使用getById方法替代findById
customer.setContact(service.getById(vo.getContactId()));
}
}
// 映射其他属性
customer.setDescription(vo.getDescription());
customer.setCreated(vo.getCreated());

View File

@@ -5,11 +5,11 @@ import com.ecep.contract.QueryService;
import com.ecep.contract.constant.ServiceConstant;
import com.ecep.contract.ds.customer.repository.CustomerCatalogRepository;
import com.ecep.contract.model.CustomerCatalog;
import com.ecep.contract.util.SpecificationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.ecep.contract.service.ServiceException;
import com.ecep.contract.service.VoableService;
import com.ecep.contract.util.SpecificationUtils;
import com.ecep.contract.vo.CustomerCatalogVo;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
@@ -30,7 +30,7 @@ import java.util.List;
@Lazy
@Service
@CacheConfig(cacheNames = "customer-catalog")
public class CustomerCatalogService implements IEntityService<CustomerCatalog>, QueryService<CustomerCatalog>,
public class CustomerCatalogService implements IEntityService<CustomerCatalog>, QueryService<CustomerCatalogVo>,
VoableService<CustomerCatalog, CustomerCatalogVo> {
@Lazy
@Autowired
@@ -39,10 +39,15 @@ public class CustomerCatalogService implements IEntityService<CustomerCatalog>,
/**
* 根据 id 查找 CustomerCatalog
*/
@Override
public CustomerCatalog getById(Integer id) {
return repository.findById(id).orElse(null);
}
@Cacheable(key = "#p0")
@Override
public CustomerCatalog findById(Integer id) {
return repository.findById(id).orElse(null);
public CustomerCatalogVo findById(Integer id) {
return repository.findById(id).map(CustomerCatalog::toVo).orElse(null);
}
@@ -112,7 +117,7 @@ public class CustomerCatalogService implements IEntityService<CustomerCatalog>,
* 分页查询 CustomerCatalog
*/
@Override
public Page<CustomerCatalog> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CustomerCatalogVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CustomerCatalog> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
@@ -120,7 +125,7 @@ public class CustomerCatalogService implements IEntityService<CustomerCatalog>,
// 字段等值查询
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "code", "name", "description");
return repository.findAll(spec, pageable);
return repository.findAll(spec, pageable).map(CustomerCatalog::toVo);
}
/**
@@ -154,6 +159,7 @@ public class CustomerCatalogService implements IEntityService<CustomerCatalog>,
@Override
public void updateByVo(CustomerCatalog model, CustomerCatalogVo vo) {
// 参数校验
if (model == null) {
throw new ServiceException("实体对象不能为空");
}

View File

@@ -2,6 +2,7 @@ package com.ecep.contract.ds.customer.service;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
@@ -31,14 +32,14 @@ import com.ecep.contract.vo.CustomerFileTypeLocalVo;
@Lazy
@Service
@CacheConfig(cacheNames = "customer-file-type")
public class CustomerFileTypeService implements IEntityService<CustomerFileTypeLocal>, QueryService<CustomerFileTypeLocal>,
public class CustomerFileTypeService implements IEntityService<CustomerFileTypeLocal>, QueryService<CustomerFileTypeLocalVo>,
VoableService<CustomerFileTypeLocal, CustomerFileTypeLocalVo> {
@Lazy
@Autowired
private CustomerFileTypeLocalRepository repository;
@Override
public Page<CustomerFileTypeLocal> findAll(JsonNode paramsNode, Pageable pageable) {
public Page<CustomerFileTypeLocalVo> findAll(JsonNode paramsNode, Pageable pageable) {
Specification<CustomerFileTypeLocal> spec = null;
if (paramsNode.has(ServiceConstant.KEY_SEARCH_TEXT)) {
spec = getSpecification(paramsNode.get(ServiceConstant.KEY_SEARCH_TEXT).asText());
@@ -50,12 +51,16 @@ public class CustomerFileTypeService implements IEntityService<CustomerFileTypeL
// field
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "lang", "value");
return findAll(spec, pageable);
return findAll(spec, pageable).map(CustomerFileTypeLocal::toVo);
}
@Cacheable(key = "#p0")
@Override
public CustomerFileTypeLocal findById(Integer id) {
public CustomerFileTypeLocalVo findById(Integer id) {
return repository.findById(id).map(CustomerFileTypeLocal::toVo).orElse(null);
}
public CustomerFileTypeLocal getById(Integer id) {
return repository.findById(id).orElse(null);
}
@@ -65,8 +70,12 @@ public class CustomerFileTypeService implements IEntityService<CustomerFileTypeL
}
@Cacheable(key = "'all-'+#p0.toLanguageTag()")
public Map<CustomerFileType, CustomerFileTypeLocal> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag());
public Map<CustomerFileType, CustomerFileTypeLocalVo> findAll(Locale locale) {
return repository.getCompleteMapByLocal(locale.toLanguageTag()).entrySet().stream()
.collect(java.util.stream.Collectors.toMap(
java.util.Map.Entry::getKey,
entry -> entry.getValue().toVo()
));
}
@Override

Some files were not shown because too many files have changed in this diff Show More