feat(contract): 新增合同余额功能及重构文件管理

重构合同文件管理逻辑,增加错误处理和日志记录
新增ContractBalance实体、Repository和VO类
完善Voable接口文档和实现规范
更新项目架构文档和数据库设计
修复SmbFileService的连接问题
移动合同相关TabSkin类到contract包
添加合同文件重建任务的WebSocket支持
This commit is contained in:
2025-11-19 00:50:16 +08:00
parent 87290f15b0
commit 02afa189f8
49 changed files with 7577 additions and 441 deletions

View File

@@ -1,9 +1,10 @@
package com.ecep.contract.controller.tab;
package com.ecep.contract.controller.contract;
import com.ecep.contract.*;
import com.ecep.contract.constant.ContractConstant;
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.tab.ContractFilesRebuildTasker;
import com.ecep.contract.controller.tab.CustomerContractCostFormUpdateTask;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.controller.table.cell.ContractFileTypeTableCell;
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;

View File

@@ -1,7 +1,5 @@
package com.ecep.contract.controller.tab;
package com.ecep.contract.controller.contract;
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.service.ContractPayPlanService;
import com.ecep.contract.util.FxmlPath;

View File

@@ -1,10 +1,9 @@
package com.ecep.contract.controller.tab;
package com.ecep.contract.controller.contract;
import java.time.LocalDate;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.ContractService;

View File

@@ -1,4 +1,4 @@
package com.ecep.contract.controller.tab;
package com.ecep.contract.controller.contract;
import java.util.List;
@@ -7,8 +7,7 @@ import org.controlsfx.control.textfield.TextFields;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.SpringApp;
import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
import com.ecep.contract.controller.contract.ContractWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.ContractFileTableCell;

View File

@@ -12,13 +12,8 @@ import com.ecep.contract.DesktopUtils;
import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.ContractTabSkinBase;
import com.ecep.contract.controller.tab.ContractTabSkinFiles;
import com.ecep.contract.controller.tab.ContractTabSkinInvoices;
import com.ecep.contract.controller.tab.ContractTabSkinItemsV2;
import com.ecep.contract.controller.tab.ContractTabSkinPayPlan;
import com.ecep.contract.controller.tab.ContractTabSkinSubContract;
import com.ecep.contract.controller.tab.ContractTabSkinVendorBid;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.task.ContractRepairTask;

View File

@@ -1,23 +1,65 @@
package com.ecep.contract.controller.tab;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.SpringApp;
import com.ecep.contract.task.Tasker;
import com.ecep.contract.WebSocketClientService;
import com.ecep.contract.WebSocketClientTasker;
import com.ecep.contract.vo.ContractVo;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import lombok.Getter;
import lombok.Setter;
public class ContractFilesRebuildTasker extends Tasker<Object> {
import java.util.Locale;
@Slf4j
public class ContractFilesRebuildTasker extends Tasker<Object> implements WebSocketClientTasker {
@Setter
private ContractVo contract;
@Getter
@Setter
private boolean repaired = false;
@Override
public Object execute(MessageHolder holder) {
return null;
public String getTaskName() {
return "ContractFilesRebuildTasker";
}
public boolean isRepaired() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isRepaired'");
@Override
public void updateProgress(long workDone, long max) {
super.updateProgress(workDone, max);
}
@Override
public Object execute(MessageHolder holder) {
log.info("开始执行合同文件重建任务合同ID: {}", contract != null ? contract.getId() : "unknown");
if (contract == null) {
String errorMsg = "合同信息为空,无法执行文件重建任务";
holder.error(errorMsg);
throw new RuntimeException(errorMsg);
}
try {
holder.info("开始重建合同文件,合同编号: " + contract.getCode());
updateProgress(0, 100);
// 使用WebSocket调用远程任务
Object result = callRemoteTask(holder, Locale.getDefault(), contract.getId());
updateProgress(100, 100);
holder.info("合同文件重建任务已提交到服务器");
return result;
} catch (Exception e) {
log.error("合同文件重建任务执行失败", e);
holder.error("任务执行失败: " + e.getMessage());
throw new RuntimeException("合同文件重建任务执行失败", e);
}
}
}

View File

@@ -0,0 +1,217 @@
package com.ecep.contract.service;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
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.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.ecep.contract.util.ParamUtils;
import com.ecep.contract.vm.ContractBalanceViewModel;
import com.ecep.contract.vo.ContractBalanceVo;
/**
* 合同余额服务客户端实现
* 继承QueryService提供基于WebSocket的异步通信和缓存机制
*/
@Service
@CacheConfig(cacheNames = "contract-balance")
public class ContractBalanceService extends QueryService<ContractBalanceVo, ContractBalanceViewModel> {
@Autowired
private ContractService contractService;
/**
* 根据ID查询合同余额信息
* 使用缓存机制提高查询性能
*
* @param id 余额记录ID
* @return 合同余额视图对象如果不存在则返回null
*/
@Cacheable(key = "#p0")
public ContractBalanceVo findById(Integer id) {
return super.findById(id);
}
/**
* 保存合同余额信息
* 支持新建和更新操作,包含缓存失效机制
*
* @param contractBalance 合同余额视图对象
* @return 保存后的合同余额视图对象
*/
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'contract-'+#p0.contractId")
})
public ContractBalanceVo save(ContractBalanceVo contractBalance) {
return super.save(contractBalance);
}
/**
* 删除合同余额信息
* 包含相关缓存的失效处理
*
* @param contractBalance 合同余额视图对象
*/
@Caching(evict = {
@CacheEvict(key = "#p0.id"),
@CacheEvict(key = "'contract-'+#p0.contractId")
})
public void delete(ContractBalanceVo contractBalance) {
super.delete(contractBalance);
}
/**
* 根据合同ID查询所有余额记录
*
* @param contractId 合同ID
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAllByContractId(Integer contractId) {
return findAll(ParamUtils.builder()
.equals("contractId", contractId)
.build(), Pageable.unpaged()).getContent();
}
/**
* 根据业务员ID查询余额记录
*
* @param employeeId 业务员ID
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAllByEmployeeId(Integer employeeId) {
return findAll(ParamUtils.builder()
.equals("employeeId", employeeId)
.build(), Pageable.unpaged()).getContent();
}
/**
* 根据引用ID查询余额记录
*
* @param refId 引用ID
* @return 合同余额视图对象如果不存在则返回null
*/
public ContractBalanceVo findByRefId(String refId) {
return findAll(ParamUtils.builder()
.equals("refId", refId)
.build(), Pageable.ofSize(1)).stream()
.findFirst()
.orElse(null);
}
/**
* 根据GUID查询余额记录
*
* @param guid GUID
* @return 合同余额视图对象如果不存在则返回null
*/
public ContractBalanceVo findByGuid(String guid) {
return findAll(ParamUtils.builder()
.equals("guid", guid)
.build(), Pageable.ofSize(1)).stream()
.findFirst()
.orElse(null);
}
/**
* 根据合同ID分页查询余额记录
*
* @param contractId 合同ID
* @param pageable 分页参数
* @return 分页结果
*/
public Page<ContractBalanceVo> findByContractId(Integer contractId, Pageable pageable) {
return findAll(ParamUtils.builder()
.equals("contractId", contractId)
.build(), pageable);
}
/**
* 根据业务员ID和日期范围查询余额记录
*
* @param employeeId 业务员ID
* @param beginDate 开始日期
* @param endDate 结束日期
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAllByEmployeeAndDateRange(Integer employeeId, LocalDate beginDate,
LocalDate endDate) {
return findAll(ParamUtils.builder()
.equals("employeeId", employeeId)
.between("setupDate", beginDate, endDate)
.build(), Pageable.unpaged()).getContent();
}
/**
* 根据凭证ID查询余额记录
*
* @param pzId 凭证ID
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAllByPzId(String pzId) {
return findAll(ParamUtils.builder()
.equals("pzId", pzId)
.build(), Pageable.unpaged()).getContent();
}
/**
* 根据JSD类型查询余额记录
*
* @param jsdType JSD类型
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAllByJsdType(String jsdType) {
return findAll(ParamUtils.builder()
.equals("jsdType", jsdType)
.build(), Pageable.unpaged()).getContent();
}
/**
* 检查指定的合同是否存在余额记录
*
* @param contractId 合同ID
* @return 如果存在余额记录返回true否则返回false
*/
public boolean hasBalanceByContractId(Integer contractId) {
Page<ContractBalanceVo> page = findByContractId(contractId, Pageable.ofSize(1));
return !page.isEmpty();
}
/**
* 统计指定合同的总余额数量
*
* @param contractId 合同ID
* @return 余额记录总数
*/
public long countByContractId(Integer contractId) {
return findByContractId(contractId, Pageable.unpaged()).getTotalElements();
}
/**
* 根据查询参数进行复杂查询
*
* @param params 查询参数
* @param pageable 分页参数
* @return 分页结果
*/
public Page<ContractBalanceVo> findAll(Map<String, Object> params, Pageable pageable) {
return super.findAll(params, pageable);
}
/**
* 获取所有余额记录
*
* @return 合同余额列表
*/
public List<ContractBalanceVo> findAll() {
return super.findAll();
}
}

View File

@@ -0,0 +1,180 @@
package com.ecep.contract.vm;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import com.ecep.contract.vo.ContractBalanceVo;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class ContractBalanceViewModel extends IdentityViewModel<ContractBalanceVo> {
/**
* 余额ID (对应cBalanceID)
*/
private SimpleStringProperty refId = new SimpleStringProperty();
/**
* GUID余额在系统中的唯一标识 (对应GUID字段)
*/
private SimpleObjectProperty<UUID> guid = new SimpleObjectProperty<>();
/**
* 关联合同ID (对应cContractID)
*/
private SimpleObjectProperty<Integer> contractId = new SimpleObjectProperty<>();
/**
* 业务员ID (对应cFunctionaryID)
*/
private SimpleObjectProperty<Integer> employeeId = new SimpleObjectProperty<>();
/**
* 余额类型ID (对应cBalancelTypeID)
*/
private SimpleStringProperty balanceTypeId = new SimpleStringProperty();
/**
* 汇率 (对应decExchangeRate)
*/
private SimpleObjectProperty<Double> exchangeRate = new SimpleObjectProperty<>();
/**
* 发票号码 (对应cBalanceDetails)
*/
private SimpleStringProperty invoiceNumber = new SimpleStringProperty();
/**
* 创建人 (对应cProducer)
*/
private SimpleObjectProperty<Integer> setupPersonId = new SimpleObjectProperty<>();
/**
* 创建日期 (对应dtProduceDate)
*/
private SimpleObjectProperty<LocalDate> setupDate = new SimpleObjectProperty<>();
/**
* 审核人 (对应cAuditer)
*/
private SimpleObjectProperty<Integer> auditerId = new SimpleObjectProperty<>();
/**
* 审核日期 (对应dtAuditeDate)
*/
private SimpleObjectProperty<LocalDate> auditeDate = new SimpleObjectProperty<>();
/**
* 管理员 (对应cAdmin)
*/
private SimpleObjectProperty<Integer> adminId = new SimpleObjectProperty<>();
/**
* 管理员日期 (对应dtAdminDate)
*/
private SimpleObjectProperty<LocalDate> adminDate = new SimpleObjectProperty<>();
/**
* 凭证ID (对应cPZID)
*/
private SimpleStringProperty pzId = new SimpleStringProperty();
/**
* 凭证编号 (对应cPZNum)
*/
private SimpleStringProperty pzNum = new SimpleStringProperty();
/**
* JSD类型 (对应cJsdType)
*/
private SimpleStringProperty jsdType = new SimpleStringProperty();
/**
* 源余额ID (对应cSrcBalanceID)
*/
private SimpleStringProperty srcBalanceId = new SimpleStringProperty();
/**
* 创建时间 (对应dtCreateTime)
*/
private SimpleObjectProperty<LocalDateTime> createTime = new SimpleObjectProperty<>();
/**
* 修改时间 (对应dtModifyTime)
*/
private SimpleObjectProperty<LocalDateTime> modifyTime = new SimpleObjectProperty<>();
/**
* 修改人 (对应cModifer)
*/
private SimpleObjectProperty<Integer> modiferId = new SimpleObjectProperty<>();
/**
* 生效时间 (对应dtEffectTime)
*/
private SimpleObjectProperty<LocalDateTime> effectTime = new SimpleObjectProperty<>();
@Override
protected void updateFrom(ContractBalanceVo v) {
super.updateFrom(v);
// 设置各个属性值
refId.set(v.getRefId());
guid.set(v.getGuid());
contractId.set(v.getContractId());
employeeId.set(v.getEmployeeId());
balanceTypeId.set(v.getBalanceTypeId());
exchangeRate.set(v.getExchangeRate());
invoiceNumber.set(v.getInvoiceNumber());
setupPersonId.set(v.getSetupPersonId());
setupDate.set(v.getSetupDate());
auditerId.set(v.getAuditerId());
auditeDate.set(v.getAuditeDate());
adminId.set(v.getAdminId());
adminDate.set(v.getAdminDate());
pzId.set(v.getPzId());
pzNum.set(v.getPzNum());
jsdType.set(v.getJsdType());
srcBalanceId.set(v.getSrcBalanceId());
createTime.set(v.getCreateTime());
modifyTime.set(v.getModifyTime());
modiferId.set(v.getModiferId());
effectTime.set(v.getEffectTime());
}
@Override
public boolean copyTo(ContractBalanceVo v) {
boolean result = super.copyTo(v);
// 从ViewModel复制属性到VO
v.setRefId(refId.get());
v.setGuid(guid.get());
v.setContractId(contractId.get());
v.setEmployeeId(employeeId.get());
v.setBalanceTypeId(balanceTypeId.get());
v.setExchangeRate(exchangeRate.get());
v.setInvoiceNumber(invoiceNumber.get());
v.setSetupPersonId(setupPersonId.get());
v.setSetupDate(setupDate.get());
v.setAuditerId(auditerId.get());
v.setAuditeDate(auditeDate.get());
v.setAdminId(adminId.get());
v.setAdminDate(adminDate.get());
v.setPzId(pzId.get());
v.setPzNum(pzNum.get());
v.setJsdType(jsdType.get());
v.setSrcBalanceId(srcBalanceId.get());
v.setCreateTime(createTime.get());
v.setModifyTime(modifyTime.get());
v.setModiferId(modiferId.get());
v.setEffectTime(effectTime.get());
return result;
}
}