feat(contract): 新增合同余额功能及重构文件管理
重构合同文件管理逻辑,增加错误处理和日志记录 新增ContractBalance实体、Repository和VO类 完善Voable接口文档和实现规范 更新项目架构文档和数据库设计 修复SmbFileService的连接问题 移动合同相关TabSkin类到contract包 添加合同文件重建任务的WebSocket支持
This commit is contained in:
@@ -17,13 +17,19 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* 实体服务基类
|
||||
* 提供基础的CRUD操作和查询方法
|
||||
*
|
||||
* @param <T> 实体类型
|
||||
* @param <VO> VO类型
|
||||
* @param <ID> 主键类型
|
||||
*/
|
||||
public abstract class EntityService<T extends Voable<VO>, VO, ID> {
|
||||
|
||||
/**
|
||||
* 获取实体数据访问层接口
|
||||
* 子类必须实现此方法,提供具体的实体数据访问层实例
|
||||
*
|
||||
* @return 实体数据访问层接口
|
||||
*/
|
||||
protected abstract MyRepository<T, ID> getRepository();
|
||||
|
||||
public T getById(ID id) {
|
||||
|
||||
@@ -384,5 +384,11 @@ public class YongYouU8Repository {
|
||||
return getJdbcTemplate().queryForList("select * from SaleBillVouchs where SBVID=?", sbvid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过合同号查询相关余额记录
|
||||
*/
|
||||
public List<Map<String, Object>> findAllBalanceByContractCode(String code) {
|
||||
return getJdbcTemplate().queryForList("select * from CM_Balance where cContractID=?", code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
}
|
||||
|
||||
private VendorEntityVo updateVendorEntityDetailByCode(VendorEntityVo entity, String unitCode,
|
||||
MessageHolder holder) {
|
||||
MessageHolder holder) {
|
||||
if (vendorEntityUpdateDelayDays > 0) {
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
if (entity.getFetchedTime() != null) {
|
||||
@@ -672,7 +672,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
* 客户数据要通过 CompanyCustomerEntity 表关联来从用友数据库中读取
|
||||
*/
|
||||
public boolean syncByCustomerEntity(CompanyCustomer companyCustomer, CompanyCustomerEntity entity,
|
||||
MessageHolder holder) {
|
||||
MessageHolder holder) {
|
||||
String code = entity.getCode();
|
||||
holder.debug("同步客户相关项 " + code + "," + entity.getName() + " 的合同");
|
||||
if (!StringUtils.hasText(code)) {
|
||||
@@ -1205,6 +1205,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
public boolean syncContractFiles(ContractVo contract, MessageHolder holder) {
|
||||
String contractPath = contract.getPath();
|
||||
if (!StringUtils.hasText(contractPath)) {
|
||||
holder.warn("合同没有指定目录");
|
||||
return false;
|
||||
}
|
||||
SmbFileService smbFileService = getCachedBean(SmbFileService.class);
|
||||
@@ -1216,6 +1217,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
}
|
||||
ContractFileService fileService = getContractFileService();
|
||||
List<ContractFileVo> dbFiles = fileService.findAllByContract(contract);
|
||||
holder.debug("合同下已有记录:" + dbFiles.size() + "条");
|
||||
List<ContractFile> retrieveFiles = new ArrayList<>();
|
||||
boolean modfied = false;
|
||||
|
||||
@@ -1223,9 +1225,11 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
// 排除掉数据库中重复的
|
||||
for (ContractFileVo dbFile : dbFiles) {
|
||||
String fileName = dbFile.getFileName();
|
||||
holder.debug("记录 #" + dbFile.getId() + " :" + fileName);
|
||||
// 没有文件信息,无效记录,删除
|
||||
if (!StringUtils.hasText(fileName)) {
|
||||
fileService.delete(fileService.getById(dbFile.getId()));
|
||||
holder.warn(" - 记录无效:删除");
|
||||
modfied = true;
|
||||
continue;
|
||||
}
|
||||
@@ -1234,6 +1238,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
File file = new File(dir, fileName);
|
||||
if (!file.exists()) {
|
||||
fileService.delete(fileService.getById(dbFile.getId()));
|
||||
holder.warn(" - 文件不存在:删除");
|
||||
modfied = true;
|
||||
continue;
|
||||
}
|
||||
@@ -1242,6 +1247,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
ContractFileVo old = map.put(fileName, dbFile);
|
||||
if (old != null) {
|
||||
fileService.delete(fileService.getById(old.getId()));
|
||||
holder.warn(" - 文件重复记录:删除 #" + old.getId());
|
||||
modfied = true;
|
||||
}
|
||||
}
|
||||
@@ -1249,7 +1255,9 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
// 遍历合同目录下的文件,如果未创建,创建
|
||||
try {
|
||||
List<File> files = smbFileService.listFiles(dir);
|
||||
holder.debug("目录下有文件:" + files.size() + "个");
|
||||
for (File file : files) {
|
||||
holder.debug("文件:" + file.getName());
|
||||
// 只处理文件
|
||||
if (!smbFileService.isFile(file)) {
|
||||
continue;
|
||||
@@ -1265,6 +1273,7 @@ public class ContractCtx extends AbstractYongYouU8Ctx {
|
||||
contractFile.setFileName(file.getName());
|
||||
syncContractFile(contractFile, file, holder);
|
||||
retrieveFiles.add(contractFile);
|
||||
holder.info("找到新文件:" + fileName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
holder.error("遍历合同目录下的文件失败:" + contractPath + ",错误:" + e.getMessage());
|
||||
|
||||
@@ -2,11 +2,18 @@ package com.ecep.contract.ds.company.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.CompanyFileType;
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.company.model.Company;
|
||||
import com.ecep.contract.ds.company.model.CompanyFile;
|
||||
|
||||
/**
|
||||
* 公司文件数据访问层接口
|
||||
* 提供公司文件相关的数据访问操作
|
||||
*/
|
||||
@Repository
|
||||
public interface CompanyFileRepository extends MyRepository<CompanyFile, Integer> {
|
||||
|
||||
List<CompanyFile> findByCompany(Company company);
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
package com.ecep.contract.ds.contract.model;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.ecep.contract.model.Employee;
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.ecep.contract.model.Voable;
|
||||
import com.ecep.contract.vo.ContractBalanceVo;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 合同余额实体类
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@Table(name = "CONTRACT_BALANCE", schema = "supplier_ms")
|
||||
@ToString
|
||||
public class ContractBalance implements IdentityEntity, ContractBasedEntity, Voable<ContractBalanceVo> {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "ID", nullable = false)
|
||||
private Integer id;
|
||||
/**
|
||||
* 余额ID
|
||||
*/
|
||||
@Column(name = "REF_ID")
|
||||
private String refId;
|
||||
|
||||
/**
|
||||
* GUID,余额在系统中的唯一标识,对应 BalanceGuid 字段
|
||||
*/
|
||||
@Column(name = "GUID", nullable = false)
|
||||
private UUID guid;
|
||||
|
||||
/**
|
||||
* 关联合同
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "CONTRACT_ID")
|
||||
@ToString.Exclude
|
||||
private Contract contract;
|
||||
|
||||
/**
|
||||
* 业务员, 对应 cFunctionaryID
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "BM_EMPLOYEE_ID")
|
||||
@ToString.Exclude
|
||||
private Employee employee;
|
||||
|
||||
/**
|
||||
* 发票号码,对应 balanceDetails 字段
|
||||
*/
|
||||
@Column(name = "INVOICE_NUMBER")
|
||||
private String invoiceNumber;
|
||||
|
||||
/**
|
||||
* 创建人, 对应 cProducer 字段
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "SETUP_PERSON_ID")
|
||||
@ToString.Exclude
|
||||
private Employee setupPerson;
|
||||
|
||||
/**
|
||||
* 创建日期,对应 dtCreateTime 字段
|
||||
*/
|
||||
@Column(name = "SETUP_DATE_TIME")
|
||||
private LocalDateTime setupDate;
|
||||
|
||||
/**
|
||||
* 审核人,对应 cAuditer 字段
|
||||
*/
|
||||
@JoinColumn(name = "AUDITER_ID")
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
private Employee auditer;
|
||||
|
||||
/**
|
||||
* 审核日期,对应 dtAuditeDate 字段
|
||||
*/
|
||||
@Column(name = "AUDITE_DATE")
|
||||
private LocalDate auditeDate;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@JoinColumn(name = "MODIFER_ID")
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
private Employee modifer;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@Column(name = "MODIFY_DATE_TIME")
|
||||
private LocalDateTime modifyTime;
|
||||
|
||||
/**
|
||||
* 管理员,对应 cAdmin 字段
|
||||
*/
|
||||
@JoinColumn(name = "ADMIN_ID")
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
private Employee admin;
|
||||
|
||||
/**
|
||||
* 管理员日期
|
||||
*/
|
||||
@Column(name = "ADMIN_DATE")
|
||||
private LocalDate adminDate;
|
||||
|
||||
/**
|
||||
* 凭证ID
|
||||
*/
|
||||
@Column(name = "PZ_ID")
|
||||
private String pzId;
|
||||
/**
|
||||
* 凭证编号
|
||||
*/
|
||||
@Column(name = "PZ_NUM")
|
||||
private String pzNum;
|
||||
|
||||
/**
|
||||
* JSD类型
|
||||
*/
|
||||
@Column(name = "JSD_TYPE", nullable = false)
|
||||
private String jsdType;
|
||||
|
||||
/**
|
||||
* 生效时间
|
||||
*/
|
||||
@Column(name = "EFFECT_DATE_TIME")
|
||||
private LocalDateTime effectTime;
|
||||
|
||||
/**
|
||||
* 将当前 ContractBalance 实体对象转换为 ContractBalanceVo 视图对象
|
||||
*
|
||||
* <p>该方法实现了 Voable<T> 接口的 toVo() 方法,负责将实体对象转换为轻量级的VO对象,
|
||||
* 用于前端数据展示和WebSocket通信。
|
||||
*
|
||||
* <p><strong>转换规则:</strong></p>
|
||||
* <ul>
|
||||
* <li>基本字段:id, refId, guid 直接映射</li>
|
||||
* <li>业务字段:balanceTypeId, exchangeRate, balanceDetails 直接映射</li>
|
||||
* <li>关联对象:只映射ID,避免加载整个关联对象</li>
|
||||
* <li>日期处理:LocalDateTime 转换为适当的日期类型</li>
|
||||
* <li>空值防护:所有关联对象访问前进行null检查</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>映射明细:</strong></p>
|
||||
* <ul>
|
||||
* <li>contract.getId() → contractId (带null检查)</li>
|
||||
* <li>employee.getId() → functionaryId (转换为String)</li>
|
||||
* <li>auditer.getId() → auditer (转换为String,带null检查)</li>
|
||||
* <li>admin.getId() → admin (转换为String,带null检查)</li>
|
||||
* <li>setupPerson.getId() → producer (转换为String,带null检查)</li>
|
||||
* <li>modifer.getId() → modifer (转换为String,带null检查)</li>
|
||||
* <li>contract.getCompany().getName() → superviseDept (级联null检查)</li>
|
||||
* <li>setupDate → produceDate (LocalDateTime 转换为 LocalDate)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>注意事项:</strong></p>
|
||||
* <ul>
|
||||
* <li>所有Employee关联对象的ID都转换为String类型</li>
|
||||
* <li>setupDate为LocalDateTime类型,转换为produceDate时取日期部分</li>
|
||||
* <li>supervisorDept通过contract.getCompany()获取,需级联null检查</li>
|
||||
* <li>关联对象可能为null,转换时需要防护</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return 转换后的 ContractBalanceVo 对象实例
|
||||
* @throws IllegalStateException 如果转换过程中发生不可恢复的状态错误
|
||||
* @see Voable#toVo()
|
||||
* @see ContractBalanceVo
|
||||
*/
|
||||
@Override
|
||||
public ContractBalanceVo toVo() {
|
||||
ContractBalanceVo vo = new ContractBalanceVo();
|
||||
|
||||
// 基本字段直接映射
|
||||
vo.setId(id);
|
||||
vo.setRefId(refId);
|
||||
vo.setGuid(guid);
|
||||
vo.setExchangeRate(1.0);
|
||||
vo.setInvoiceNumber(invoiceNumber);
|
||||
|
||||
// 关联对象只映射ID(进行null检查)
|
||||
if (contract != null) {
|
||||
vo.setContractId(contract.getId());
|
||||
}
|
||||
if (employee != null) {
|
||||
vo.setEmployeeId(employee.getId());
|
||||
}
|
||||
|
||||
// 审核人和审核日期
|
||||
if (auditer != null) {
|
||||
vo.setAuditerId(auditer.getId());
|
||||
}
|
||||
vo.setAuditeDate(auditeDate);
|
||||
|
||||
// 管理员信息
|
||||
if (admin != null) {
|
||||
vo.setAdminId(admin.getId());
|
||||
}
|
||||
vo.setAdminDate(adminDate);
|
||||
|
||||
// 创建人信息
|
||||
if (setupPerson != null) {
|
||||
vo.setSetupPersonId(setupPerson.getId());
|
||||
}
|
||||
vo.setSetupDate(setupDate != null ? setupDate.toLocalDate() : null);
|
||||
|
||||
// 修改人信息
|
||||
if (modifer != null) {
|
||||
vo.setModiferId(modifer.getId());
|
||||
}
|
||||
vo.setModifyTime(modifyTime);
|
||||
|
||||
// 凭证信息
|
||||
vo.setPzId(pzId);
|
||||
vo.setPzNum(pzNum);
|
||||
|
||||
// 时间相关字段
|
||||
vo.setCreateTime(setupDate);
|
||||
vo.setEffectTime(effectTime);
|
||||
|
||||
vo.setJsdType(jsdType); // 已在上面设置
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.ecep.contract.ds.contract.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.contract.model.ContractBalance;
|
||||
|
||||
/**
|
||||
* 合同余额数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface ContractBalanceRepository extends MyRepository<ContractBalance, Integer> {
|
||||
|
||||
/**
|
||||
* 根据合同ID查询余额列表
|
||||
*
|
||||
* @param contractId 合同ID
|
||||
* @return 合同余额列表
|
||||
*/
|
||||
List<ContractBalance> findByContractId(Integer contractId);
|
||||
|
||||
/**
|
||||
* 根据合同ID和关联ID查询余额
|
||||
*
|
||||
* @param contractId 合同ID
|
||||
* @param refId 关联ID
|
||||
* @return 合同余额
|
||||
*/
|
||||
ContractBalance findByContractIdAndRefId(Integer contractId, Integer refId);
|
||||
|
||||
/**
|
||||
* 根据GUID查询余额
|
||||
*
|
||||
* @param guid GUID
|
||||
* @return 合同余额
|
||||
*/
|
||||
ContractBalance findByGuid(String guid);
|
||||
|
||||
/**
|
||||
* 根据合同ID分页查询余额
|
||||
*
|
||||
* @param contractId 合同ID
|
||||
* @param pageable 分页参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Query("SELECT cb FROM ContractBalance cb WHERE cb.contract.id = :contractId")
|
||||
Page<ContractBalance> findByContractId(@Param("contractId") Integer contractId, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 统计指定合同的余额记录数
|
||||
*
|
||||
* @param contractId 合同ID
|
||||
* @return 余额记录数
|
||||
*/
|
||||
@Query("SELECT COUNT(cb) FROM ContractBalance cb WHERE cb.contract.id = :contractId")
|
||||
long countByContractId(@Param("contractId") Integer contractId);
|
||||
}
|
||||
@@ -2,20 +2,19 @@ package com.ecep.contract.ds.contract.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ContractFileType;
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.ds.contract.model.ContractFile;
|
||||
|
||||
@Repository
|
||||
public interface ContractFileRepository extends JpaRepository<ContractFile, Integer>, JpaSpecificationExecutor<ContractFile> {
|
||||
public interface ContractFileRepository extends MyRepository<ContractFile, Integer> {
|
||||
List<ContractFile> findByContract(Contract contract);
|
||||
|
||||
List<ContractFile> findAllByContract(Contract contract);
|
||||
|
||||
|
||||
List<ContractFile> findAllByContractId(Integer contractId);
|
||||
|
||||
List<ContractFile> findAllByContractAndType(Contract contract, ContractFileType type);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.ecep.contract.ds.contract.repository;
|
||||
|
||||
import com.ecep.contract.ds.contract.model.ContractItem;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.contract.model.ContractItem;
|
||||
|
||||
@Repository
|
||||
public interface ContractItemRepository extends JpaRepository<ContractItem, Integer>, JpaSpecificationExecutor<ContractItem> {
|
||||
public interface ContractItemRepository extends MyRepository<ContractItem, Integer> {
|
||||
|
||||
Optional<ContractItem> findByRowId(String rowId);
|
||||
|
||||
@@ -19,5 +19,4 @@ public interface ContractItemRepository extends JpaRepository<ContractItem, Inte
|
||||
|
||||
List<ContractItem> findByInventoryId(int inventoryId);
|
||||
|
||||
|
||||
}
|
||||
@@ -3,27 +3,21 @@ package com.ecep.contract.ds.contract.repository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.ds.vendor.model.PurchaseOrder;
|
||||
|
||||
@Repository
|
||||
public interface PurchaseOrderRepository extends
|
||||
// JDBC interfaces
|
||||
CrudRepository<PurchaseOrder, Integer>, PagingAndSortingRepository<PurchaseOrder, Integer>,
|
||||
// JPA interfaces
|
||||
JpaRepository<PurchaseOrder, Integer>, JpaSpecificationExecutor<PurchaseOrder> {
|
||||
public interface PurchaseOrderRepository extends MyRepository<PurchaseOrder, Integer> {
|
||||
|
||||
Optional<PurchaseOrder> findByCode(String code);
|
||||
|
||||
Optional<PurchaseOrder> findByRefId(Integer refId);
|
||||
|
||||
List<PurchaseOrder> findAllByContract(Contract contract);
|
||||
|
||||
List<PurchaseOrder> findAllByContractId(Integer contractId);
|
||||
|
||||
List<PurchaseOrder> findByCodeStartsWith(String code);
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.ecep.contract.ds.contract.service;
|
||||
|
||||
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.IEntityService;
|
||||
import com.ecep.contract.QueryService;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.ds.contract.model.ContractBalance;
|
||||
import com.ecep.contract.ds.contract.repository.ContractBalanceRepository;
|
||||
import com.ecep.contract.service.VoableService;
|
||||
import com.ecep.contract.util.SpecificationUtils;
|
||||
import com.ecep.contract.vo.ContractBalanceVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "contract-balance")
|
||||
public class ContractBalanceService implements IEntityService<ContractBalance>, QueryService<ContractBalanceVo>,
|
||||
VoableService<ContractBalance, ContractBalanceVo> {
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private ContractBalanceRepository repository;
|
||||
|
||||
@Override
|
||||
public ContractBalance getById(Integer id) {
|
||||
return repository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
@Cacheable(key = "#p0")
|
||||
@Override
|
||||
public ContractBalanceVo findById(Integer id) {
|
||||
ContractBalance entity = getById(id);
|
||||
if (entity != null) {
|
||||
return entity.toVo();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Specification<ContractBalance> getSpecification(String searchText) {
|
||||
if (!StringUtils.hasText(searchText)) {
|
||||
return null;
|
||||
}
|
||||
return (root, query, builder) -> {
|
||||
return builder.or(
|
||||
builder.like(root.get("refId"), "%" + searchText + "%"),
|
||||
builder.like(root.get("guid"), "%" + searchText + "%"));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ContractBalance> findAll(Specification<ContractBalance> spec, Pageable pageable) {
|
||||
return repository.findAll(spec, pageable);
|
||||
}
|
||||
|
||||
@Caching(evict = {
|
||||
@CacheEvict(key = "#p0.id"),
|
||||
@CacheEvict(key = "'contract-'+#p0.contract.id")
|
||||
})
|
||||
@Override
|
||||
public ContractBalance save(ContractBalance contractBalance) {
|
||||
return repository.save(contractBalance);
|
||||
}
|
||||
|
||||
@Caching(evict = {
|
||||
@CacheEvict(key = "#p0.id"),
|
||||
@CacheEvict(key = "'contract-'+#p0.contract.id")
|
||||
})
|
||||
@Override
|
||||
public void delete(ContractBalance contractBalance) {
|
||||
repository.delete(contractBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ContractBalanceVo> findAll(JsonNode paramsNode, Pageable pageable) {
|
||||
Specification<ContractBalance> spec = null;
|
||||
if (paramsNode.has("searchText")) {
|
||||
spec = getSpecification(paramsNode.get("searchText").asText());
|
||||
}
|
||||
|
||||
// 字段等值查询 - 只包含ContractBalanceVo中存在的字段
|
||||
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "contract", "employee");
|
||||
|
||||
return findAll(spec, pageable).map(ContractBalance::toVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateByVo(ContractBalance model, ContractBalanceVo vo) {
|
||||
// 参数校验
|
||||
if (model == null) {
|
||||
throw new IllegalArgumentException("实体对象不能为空");
|
||||
}
|
||||
if (vo == null) {
|
||||
throw new IllegalArgumentException("VO对象不能为空");
|
||||
}
|
||||
|
||||
// 映射基本属性 - 只映射ContractBalanceVo中存在的字段
|
||||
model.setRefId(vo.getRefId());
|
||||
model.setGuid(vo.getGuid());
|
||||
|
||||
// 设置汇率(默认值1.0)
|
||||
if (vo.getExchangeRate() != null) {
|
||||
// ContractBalance实体可能没有exchangeRate字段,这里使用invoiceNumber存储
|
||||
model.setInvoiceNumber(vo.getInvoiceNumber());
|
||||
}
|
||||
|
||||
// 处理关联对象 - Contract
|
||||
if (vo.getContractId() == null) {
|
||||
model.setContract(null);
|
||||
} else {
|
||||
ContractService contractService = SpringApp.getBean(ContractService.class);
|
||||
if (model.getContract() == null || !model.getContract().getId().equals(vo.getContractId())) {
|
||||
model.setContract(contractService.getById(vo.getContractId()));
|
||||
}
|
||||
}
|
||||
|
||||
com.ecep.contract.ds.other.service.EmployeeService employeeService = SpringApp
|
||||
.getBean(com.ecep.contract.ds.other.service.EmployeeService.class);
|
||||
|
||||
// 处理关联对象 - Employee (业务员)
|
||||
if (vo.getEmployeeId() == null) {
|
||||
model.setEmployee(null);
|
||||
} else {
|
||||
if (model.getEmployee() == null
|
||||
|| !model.getEmployee().getId().equals(Integer.valueOf(vo.getEmployeeId()))) {
|
||||
model.setEmployee(employeeService.getById(Integer.valueOf(vo.getEmployeeId())));
|
||||
}
|
||||
}
|
||||
|
||||
// 处理关联对象 - Employee (审核人)
|
||||
if (vo.getAuditerId() == null) {
|
||||
model.setAuditer(null);
|
||||
} else {
|
||||
if (model.getAuditer() == null || !model.getAuditer().getId().equals(Integer.valueOf(vo.getAuditerId()))) {
|
||||
model.setAuditer(employeeService.getById(Integer.valueOf(vo.getAuditerId())));
|
||||
}
|
||||
}
|
||||
model.setAuditeDate(vo.getAuditeDate());
|
||||
|
||||
// 处理关联对象 - Employee (管理员)
|
||||
if (vo.getAdminId() == null) {
|
||||
model.setAdmin(null);
|
||||
} else {
|
||||
if (model.getAdmin() == null || !model.getAdmin().getId().equals(Integer.valueOf(vo.getAdminId()))) {
|
||||
model.setAdmin(employeeService.getById(Integer.valueOf(vo.getAdminId())));
|
||||
}
|
||||
}
|
||||
model.setAdminDate(vo.getAdminDate());
|
||||
|
||||
// 处理关联对象 - Employee (创建人)
|
||||
if (vo.getSetupPersonId() == null) {
|
||||
model.setSetupPerson(null);
|
||||
} else {
|
||||
if (model.getSetupPerson() == null
|
||||
|| !model.getSetupPerson().getId().equals(Integer.valueOf(vo.getSetupPersonId()))) {
|
||||
model.setSetupPerson(employeeService.getById(Integer.valueOf(vo.getSetupPersonId())));
|
||||
}
|
||||
}
|
||||
// 设置创建日期
|
||||
if (vo.getSetupDate() != null) {
|
||||
model.setSetupDate(vo.getSetupDate().atStartOfDay());
|
||||
}
|
||||
|
||||
// 处理关联对象 - Employee (修改人)
|
||||
if (vo.getModiferId() == null) {
|
||||
model.setModifer(null);
|
||||
} else {
|
||||
if (model.getModifer() == null || !model.getModifer().getId().equals(Integer.valueOf(vo.getModiferId()))) {
|
||||
model.setModifer(employeeService.getById(Integer.valueOf(vo.getModiferId())));
|
||||
}
|
||||
}
|
||||
model.setModifyTime(vo.getModifyTime());
|
||||
|
||||
// 凭证信息
|
||||
model.setPzId(vo.getPzId());
|
||||
model.setPzNum(vo.getPzNum());
|
||||
|
||||
// JSD类型和生效时间
|
||||
model.setJsdType(vo.getJsdType());
|
||||
model.setEffectTime(vo.getEffectTime());
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,22 @@ import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.cloud.u8.ctx.ContractCtx;
|
||||
import com.ecep.contract.ds.contract.service.ContractService;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.service.tasker.WebSocketServerTasker;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.ecep.contract.vo.ContractVo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 对合同的文件进行重置
|
||||
* 继承Tasker<Object>并实现WebSocketServerTasker接口,支持与客户端的实时通信
|
||||
*/
|
||||
public class ContractFilesRebuildTasker extends Tasker<Object> {
|
||||
@Setter
|
||||
private ContractService contractService;
|
||||
|
||||
@Setter
|
||||
private Contract contract;
|
||||
|
||||
@Getter
|
||||
@Slf4j
|
||||
@Data
|
||||
public class ContractFilesRebuildTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
private ContractVo contract;
|
||||
private boolean repaired = false;
|
||||
|
||||
public ContractFilesRebuildTasker() {
|
||||
@@ -27,20 +27,54 @@ public class ContractFilesRebuildTasker extends Tasker<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) {
|
||||
updateTitle("遍历合同的文件进行“重置”操作");
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
log.info("开始执行合同文件重建任务: {}", contract != null ? contract.getCode() : "未知合同");
|
||||
|
||||
ContractCtx contractCtx = new ContractCtx();
|
||||
if (contractCtx.syncContractFiles(contract.toVo(), holder)) {
|
||||
repaired = true;
|
||||
try {
|
||||
// 检查合同信息
|
||||
if (contract == null) {
|
||||
throw new IllegalArgumentException("合同信息不能为空");
|
||||
}
|
||||
|
||||
updateProgress(25, 100);
|
||||
updateMessage("正在同步合同文件...");
|
||||
|
||||
// 执行文件重建逻辑
|
||||
ContractCtx contractCtx = new ContractCtx();
|
||||
boolean success = contractCtx.syncContractFiles(contract, holder);
|
||||
|
||||
updateProgress(75, 100);
|
||||
|
||||
if (success) {
|
||||
repaired = true;
|
||||
updateMessage("合同文件重建成功");
|
||||
log.info("合同文件重建成功: {}", contract.getCode());
|
||||
} else {
|
||||
updateMessage("合同文件重建失败");
|
||||
log.warn("合同文件重建失败: {}", contract.getCode());
|
||||
}
|
||||
updateProperty("repaired", repaired);
|
||||
updateProgress(100, 100);
|
||||
updateMessage("任务完成");
|
||||
|
||||
return success;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("合同文件重建任务执行失败: {}", contract != null ? contract.getCode() : "未知合同", e);
|
||||
updateMessage("任务执行失败: " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ContractService getContractService() {
|
||||
if (contractService == null) {
|
||||
contractService = getBean(ContractService.class);
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
log.info("初始化合同文件重建任务,参数: {}", argsNode);
|
||||
|
||||
// 从JSON参数中提取合同信息
|
||||
if (argsNode != null && argsNode.size() > 0) {
|
||||
ContractService contractService = getCachedBean(ContractService.class);
|
||||
int contractId = argsNode.get(0).asInt();
|
||||
this.contract = contractService.findById(contractId);
|
||||
}
|
||||
return contractService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,18 @@ package com.ecep.contract.ds.project.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ProjectFileType;
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.project.model.Project;
|
||||
import com.ecep.contract.ds.project.model.ProjectFile;
|
||||
|
||||
/**
|
||||
* 项目文件数据访问层接口
|
||||
* 提供项目文件相关的数据访问操作
|
||||
*/
|
||||
@Repository
|
||||
public interface ProjectFileRepository extends MyRepository<ProjectFile, Integer> {
|
||||
|
||||
List<ProjectFile> findByProject(Project project);
|
||||
|
||||
@@ -2,16 +2,14 @@ package com.ecep.contract.ds.project.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.project.model.Project;
|
||||
import com.ecep.contract.ds.project.model.ProjectQuotation;
|
||||
|
||||
@Repository
|
||||
public interface ProjectQuotationRepository
|
||||
extends JpaRepository<ProjectQuotation, Integer>, JpaSpecificationExecutor<ProjectQuotation> {
|
||||
public interface ProjectQuotationRepository extends MyRepository<ProjectQuotation, Integer> {
|
||||
|
||||
/**
|
||||
* 根据项目查询
|
||||
|
||||
@@ -2,17 +2,19 @@ package com.ecep.contract.ds.vendor.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.ecep.contract.VendorFileType;
|
||||
import com.ecep.contract.ds.MyRepository;
|
||||
import com.ecep.contract.ds.vendor.model.Vendor;
|
||||
import com.ecep.contract.ds.vendor.model.VendorFile;
|
||||
|
||||
/**
|
||||
* 供应商文件数据访问层接口
|
||||
* 提供供应商文件相关的数据访问操作
|
||||
*/
|
||||
@Repository
|
||||
public interface VendorFileRepository
|
||||
extends JpaRepository<VendorFile, Integer>, JpaSpecificationExecutor<VendorFile> {
|
||||
public interface VendorFileRepository extends MyRepository<VendorFile, Integer> {
|
||||
|
||||
List<VendorFile> findAllByVendorId(int vendorId);
|
||||
|
||||
|
||||
@@ -399,6 +399,10 @@ public class SmbFileService implements DisposableBean {
|
||||
try {
|
||||
// 获取连接
|
||||
connectionInfo = getConnectionInfo(hostname);
|
||||
if (connectionInfo == null) {
|
||||
log.error("Failed to get SMB connection for host: {}", hostname);
|
||||
break;
|
||||
}
|
||||
|
||||
// 从session池获取session
|
||||
sessionInfo = connectionInfo.peekSession();
|
||||
@@ -407,8 +411,13 @@ public class SmbFileService implements DisposableBean {
|
||||
// 获取认证上下文
|
||||
AuthenticationContext authContext = getAuthenticationContext(hostname);
|
||||
// 创建新session并添加到池中
|
||||
sessionInfo = connectionInfo.createSession(authContext);
|
||||
log.debug("Created new SMB session for host: {}", hostname);
|
||||
try {
|
||||
sessionInfo = connectionInfo.createSession(authContext);
|
||||
log.debug("Created new SMB session for host: {}", hostname);
|
||||
} catch (SMBRuntimeException ex) {
|
||||
log.error("Failed to create SMB session for host: {}, maxTrys:{}", hostname, maxTrys, ex);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
log.debug("Reusing SMB session for host: {}", hostname);
|
||||
}
|
||||
@@ -421,22 +430,16 @@ public class SmbFileService implements DisposableBean {
|
||||
log.debug("Returned SMB session to pool for host: {}", hostname);
|
||||
|
||||
} catch (SMBRuntimeException e) {
|
||||
sessionInfo.close();
|
||||
throw e;
|
||||
try {
|
||||
sessionInfo.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
log.error("Failed to execute SMB operation for host: {}, maxTrys:{}", hostname, maxTrys, e);
|
||||
continue;
|
||||
} finally {
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (TransportException e) {
|
||||
log.warn("TransportException occurred while trying to connect to host: {}. Retrying...", hostname);
|
||||
// 延迟1秒
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
// 如果操作失败且连接信息存在,检查连接状态
|
||||
if (connectionInfo != null) {
|
||||
@@ -501,7 +504,7 @@ public class SmbFileService implements DisposableBean {
|
||||
* @throws IOException 如果检查失败
|
||||
*/
|
||||
public boolean exists(SmbPath smbPath) throws IOException {
|
||||
return executeSmbOperation(smbPath, (share, path) -> {
|
||||
Object result = executeSmbOperation(smbPath, (share, path) -> {
|
||||
try {
|
||||
FileAllInformation info = share.getFileInformation(path);
|
||||
if (info.getStandardInformation().isDirectory()) {
|
||||
@@ -516,6 +519,10 @@ public class SmbFileService implements DisposableBean {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
if (result != null) {
|
||||
return (boolean) result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"InventorySyncTask": "com.ecep.contract.ds.other.controller.InventorySyncTask",
|
||||
"InventoryAllSyncTask": "com.ecep.contract.ds.other.controller.InventoryAllSyncTask",
|
||||
"ContractRepairAllTask": "com.ecep.contract.ds.contract.tasker.ContractRepairAllTasker",
|
||||
"ContractFilesRebuildAllTasker": "com.ecep.contract.ds.contract.tasker.ContractFilesRebuildAllTasker"
|
||||
"ContractFilesRebuildAllTasker": "com.ecep.contract.ds.contract.tasker.ContractFilesRebuildAllTasker",
|
||||
"ContractFilesRebuildTasker": "com.ecep.contract.ds.contract.tasker.ContractFilesRebuildTasker"
|
||||
},
|
||||
"descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用"
|
||||
}
|
||||
Reference in New Issue
Block a user