huminmin
21 小时以前 228b4fe942f70ee4dbe28ffd451bfd854c648c85
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -22,10 +22,11 @@
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.enums.ApprovalStatusEnum;
import com.ruoyi.common.enums.ReviewStatusEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.TypeEnums;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
@@ -129,7 +130,15 @@
        if (purchaseLedger.getApprovalStatus() != null) {
            queryWrapper.eq(PurchaseLedger::getApprovalStatus, purchaseLedger.getApprovalStatus());
        }
        // 只查询入库已审批通过的采购台账
        queryWrapper.inSql(PurchaseLedger::getId,
                "SELECT DISTINCT record_id FROM stock_in_record WHERE approval_status = 1");
        return purchaseLedgerMapper.selectList(queryWrapper);
    }
    @Override
    public List<PurchaseLedger> selectReturnablePurchaseLedgerList(PurchaseLedger purchaseLedger) {
        return purchaseLedgerMapper.selectReturnablePurchaseLedgerList(purchaseLedger);
    }
    @Override
@@ -162,21 +171,13 @@
        purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId());
        purchaseLedger.setApprovalStatus(1);
        // 实际合同金额始终等于合同金额
        purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount());
        // 3. 新增或更新主表
        if (purchaseLedger.getId() == null) {
            purchaseLedgerMapper.insert(purchaseLedger);
        } else {
            // 删除采购审核,重新提交
            ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                    .eq(ApproveProcess::getApproveType, 5)
                    .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber())
                    .eq(ApproveProcess::getApproveDelete, 0)
                    .last("limit 1"));
            if (one != null) {
                approveProcessService.delByIds(Collections.singletonList(one.getId()));
            }
            purchaseLedgerMapper.updateById(purchaseLedger);
        if (purchaseLedger.getId() == null && !StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
            purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
        }
        this.saveOrUpdate(purchaseLedger);
        // 4. 处理子表数据
        List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData();
@@ -184,7 +185,17 @@
            handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType());
        }
        // 6.采购审核新增;审批管理未配置采购审批人时,审批服务会自动置为审批通过。
        addApproveByPurchase(loginUser, purchaseLedger);
        //查询当前采购合同号是否已添加审批,如果有且不是驳回状态,则不重复添加
        ApprovalInstance oldApprovalInstance = approvalInstanceService.getOne(Wrappers.<ApprovalInstance>lambdaQuery()
                .eq(ApprovalInstance::getDeleted, 0)
                .eq(ApprovalInstance::getBusinessType, TypeEnums.PURCHASE_APPROVAL.getCode())
                .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                .ne(ApprovalInstance::getStatus,"REJECTED")
                .last("limit 1")
        );
        if(Objects.isNull(oldApprovalInstance)){
            addApproveByPurchase(loginUser, purchaseLedger);
        }
        // 5. 迁移临时文件到正式目录
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS());
        return 1;
@@ -751,13 +762,30 @@
        for (Long id : ids) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
            if(purchaseLedger != null){
                ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>()
                        .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                        .eq(ApprovalInstance::getBusinessType, 5L)
                        .eq(ApprovalInstance::getDeleted, 0));
                if (CollectionUtils.isNotEmpty(approvalInstances)) {
                    approvalInstanceService.delete(
                            approvalInstances.stream()
                                    .map(ApprovalInstance::getId)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList())
                    );
                }
                List<ApproveProcess> approveProcesses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
                        .eq(ApproveProcess::getApproveType, 5)
                        .eq(ApproveProcess::getApproveDelete, 0)
                        .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber())
                        .last("limit 1"));
                if (one != null) {
                    approveProcessService.delByIds(Collections.singletonList(one.getId()));
                        .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber()));
                if (CollectionUtils.isNotEmpty(approveProcesses)) {
                    approveProcessService.delByIds(
                            approveProcesses.stream()
                                    .map(ApproveProcess::getId)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList())
                    );
                }
            }
        }
@@ -938,6 +966,7 @@
                salesLedger.setContractAmount(salesLedgerProductImportDtos.stream()
                        .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice)
                        .reduce(BigDecimal.ZERO,BigDecimal::add));
                salesLedger.setNetContractAmount(salesLedger.getContractAmount());
                // 通过销售单号绑定销售
                SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                        .eq(SalesLedger::getSalesContractNo, salesLedger.getSalesContractNo())
@@ -1023,13 +1052,14 @@
        return resultDto;
    }
    public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) throws Exception {
    public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) {
        if (loginUser == null) {
            return;
        }
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
                new LambdaQueryWrapper<ApprovalTemplate>()
                        .eq(ApprovalTemplate::getBusinessType, 5L)
                        .eq(ApprovalTemplate::getDeleted, 0)
                        .orderByDesc(ApprovalTemplate::getId)
                        .last("LIMIT 1")
        );
@@ -1072,4 +1102,82 @@
        }
        return sb.toString();
    }
    private static final String PURCHASE_LOCK_PREFIX = "purchase_contract_no:";
    private static final long PURCHASE_LOCK_WAIT_TIMEOUT = 10;
    private static final long PURCHASE_LOCK_EXPIRE_TIME = 30;
    private String generatePurchaseContractNo(Date entryDate) {
        LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now();
        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
        String lockKey = PURCHASE_LOCK_PREFIX + datePart;
        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime();
        try {
            long startWaitTime = System.currentTimeMillis();
            while (System.currentTimeMillis() - startWaitTime < PURCHASE_LOCK_WAIT_TIMEOUT * 1000) {
                Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, PURCHASE_LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
                if (Boolean.TRUE.equals(locked)) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取锁时被中断", e);
                }
            }
            if (Boolean.FALSE.equals(redisTemplate.hasKey(lockKey))) {
                throw new RuntimeException("获取采购合同编号生成锁失败:超时");
            }
            String prefix = "CG-" + datePart + "-";
            List<PurchaseLedger> existingList = purchaseLedgerMapper.selectList(
                    new LambdaQueryWrapper<PurchaseLedger>()
                            .likeRight(PurchaseLedger::getPurchaseContractNumber, prefix)
                            .select(PurchaseLedger::getPurchaseContractNumber));
            List<Integer> existingSequences = existingList.stream()
                    .map(PurchaseLedger::getPurchaseContractNumber)
                    .filter(Objects::nonNull)
                    .map(no -> {
                        int lastDash = no.lastIndexOf('-');
                        if (lastDash >= 0) {
                            try {
                                return Integer.parseInt(no.substring(lastDash + 1));
                            } catch (NumberFormatException e) {
                                return 0;
                            }
                        }
                        return 0;
                    })
                    .collect(Collectors.toList());
            int nextSequence = findFirstMissingSequence(existingSequences);
            return prefix + String.format("%03d", nextSequence);
        } finally {
            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
            redisTemplate.execute(
                    new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    lockValue
            );
        }
    }
    private int findFirstMissingSequence(List<Integer> sequences) {
        if (sequences.isEmpty()) {
            return 1;
        }
        sequences.sort(Integer::compareTo);
        int next = 1;
        for (int seq : sequences) {
            if (seq == next) {
                next++;
            } else if (seq > next) {
                break;
            }
        }
        return next;
    }
}