yuan
3 天以前 3c8fa3452ed9fb41ce9d3f68c81bcc4b38d19612
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -22,10 +22,10 @@
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.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
@@ -129,6 +129,7 @@
        if (purchaseLedger.getApprovalStatus() != null) {
            queryWrapper.eq(PurchaseLedger::getApprovalStatus, purchaseLedger.getApprovalStatus());
        }
        queryWrapper.orderByDesc(PurchaseLedger::getEntryDate);
        return purchaseLedgerMapper.selectList(queryWrapper);
    }
@@ -164,6 +165,9 @@
        purchaseLedger.setApprovalStatus(1);
        // 3. 新增或更新主表
        if (purchaseLedger.getId() == null) {
            if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
                purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
            }
            purchaseLedgerMapper.insert(purchaseLedger);
        } else {
            // 删除采购审核,重新提交
@@ -907,20 +911,37 @@
            List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
                    salesLedgerImportDtoList.stream().map(PurchaseLedgerImportDto::getRecorderName).collect(Collectors.toList())));
            for (PurchaseLedgerImportDto salesLedgerImportDto : salesLedgerImportDtoList) {
                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                PurchaseLedger existPurchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                        .eq(PurchaseLedger::getPurchaseContractNumber, salesLedgerImportDto.getPurchaseContractNumber())
                        .last("limit 1"));
                if(purchaseLedger != null){
                // 已审核通过的采购单跳过,不再重复导入
                if (existPurchaseLedger != null && ApprovalStatusEnum.APPROVED.getCode().equals(existPurchaseLedger.getApprovalStatus())) {
                    continue;
                }
                PurchaseLedger salesLedger = new PurchaseLedger();
                BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
                // 通过供应商名称查询ID
                salesLedger.setSupplierId(customers.stream()
                        .filter(customer -> customer.getSupplierName().equals(salesLedger.getSupplierName()))
                        .findFirst()
                        .map(SupplierManage::getId)
                        .orElse(null));
                // 待审核状态的采购单:删除该单下的产品详情和审批实例,重新录入详情
                if (existPurchaseLedger != null) {
                    salesLedgerProductMapper.delete(new LambdaQueryWrapper<SalesLedgerProduct>()
                            .eq(SalesLedgerProduct::getSalesLedgerId, existPurchaseLedger.getId())
                            .eq(SalesLedgerProduct::getType, 2));
                    // 清理该单下的质检记录及质检参数
                    deleteQualityInspectByPurchaseLedger(existPurchaseLedger.getId());
                    ApprovalInstance existApprovalInstance = approvalInstanceService.getOne(
                            Wrappers.<ApprovalInstance>lambdaQuery()
                                    .eq(ApprovalInstance::getBusinessId, existPurchaseLedger.getId())
                                    .eq(ApprovalInstance::getBusinessType, 5L)
                                    .eq(ApprovalInstance::getDeleted, 0)
                                    .orderByDesc(ApprovalInstance::getId)
                                    .last("limit 1"));
                    if (existApprovalInstance != null) {
                        approvalInstanceService.delete(Collections.singletonList(existApprovalInstance.getId()));
                    }
                    salesLedger.setId(existPurchaseLedger.getId());
                    salesLedger.setApprovalStatus(ApprovalStatusEnum.PENDING.getCode());
                }
                // 通过供应商名称查询ID,不存在则自动新增
                salesLedger.setSupplierId(findOrCreateSupplier(salesLedger.getSupplierName(), customers));
                Long aLong = sysUsers.stream()
                        .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getRecorderName()))
                        .findFirst()
@@ -960,7 +981,12 @@
                    String collect = ids.stream().map(Object::toString).collect(Collectors.joining(","));
                    salesLedger.setApproveUserIds(collect);
                }
                purchaseLedgerMapper.insert(salesLedger);
                if (salesLedger.getId() == null) {
                    purchaseLedgerMapper.insert(salesLedger);
                } else {
                    // 待审核状态重新录入时,更新主表
                    purchaseLedgerMapper.updateById(salesLedger);
                }
                for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) {
                    SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct();
@@ -968,22 +994,25 @@
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(2);
                    // 计算不含税总价
                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
                    list.stream()
                            .filter(map -> map.get("productName").equals(salesLedgerProduct.getProductCategory()) && map.get("model").equals(salesLedgerProduct.getSpecificationModel()))
                            .findFirst()
                            .ifPresent(map -> {
                                salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString()));
                                salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString()));
                            });
                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 6, RoundingMode.HALF_UP));
                    // 产品大类/规格型号不存在时自动创建
                    String productName = salesLedgerProduct.getProductCategory();
                    if (StringUtils.isNotBlank(productName)) {
                        // 规格型号为空时用产品大类作为规格型号
                        String model = StringUtils.isNotBlank(salesLedgerProduct.getSpecificationModel())
                                ? salesLedgerProduct.getSpecificationModel()
                                : productName;
                        salesLedgerProduct.setSpecificationModel(model);
                        Long productId = findOrCreateProduct(productName, list);
                        Long modelId = findOrCreateProductModel(productId, productName, model,
                                salesLedgerProduct.getUnit(), list);
                        salesLedgerProduct.setProductId(productId);
                        salesLedgerProduct.setProductModelId(modelId);
                    }
                    salesLedgerProduct.setRegister(loginUser.getNickName());
                    salesLedgerProduct.setRegisterDate(LocalDateTime.now());
                    salesLedgerProduct.setApproveStatus(0);
                    // 是否质检判断
                    salesLedgerProduct.setIsChecked(salesLedgerProductImportDto.getIsChecked() == 1);
                    if(salesLedgerProductImportDto.getIsChecked() == 1){
                        addQualityInspect(salesLedger, salesLedgerProduct);
                    }
                    salesLedgerProductMapper.insert(salesLedgerProduct);
                }
                // 采购审核
@@ -995,6 +1024,130 @@
            e.printStackTrace();
        }
        return AjaxResult.success("导入失败");
    }
    /**
     * 查找或新增供应商:supplier_manage 表按 supplier_name 匹配,不存在则新增,缺失信息默认写"/"
     */
    private Long findOrCreateSupplier(String supplierName, List<SupplierManage> supplierList) {
        if (StringUtils.isBlank(supplierName)) {
            return null;
        }
        // 内存列表中查找(本次导入已新增的也包含在内)
        for (SupplierManage supplier : supplierList) {
            if (supplierName.equals(supplier.getSupplierName())) {
                return supplier.getId();
            }
        }
        // 数据库查找
        SupplierManage existSupplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>()
                .eq(SupplierManage::getSupplierName, supplierName)
                .last("limit 1"));
        if (existSupplier != null) {
            supplierList.add(existSupplier);
            return existSupplier.getId();
        }
        // 新增供应商,缺失信息(税号、地址、电话等)默认写"/"
        SupplierManage newSupplier = new SupplierManage();
        newSupplier.setSupplierName(supplierName);
        newSupplier.setTaxpayerIdentificationNum("/");
        newSupplier.setCompanyAddress("/");
        newSupplier.setSupplierType("/");
        newSupplier.setCompanyPhone("/");
        newSupplier.setBankAccountName("/");
        newSupplier.setBankAccountNum("/");
        newSupplier.setContactUserName("/");
        newSupplier.setContactUserPhone("/");
        supplierManageMapper.insert(newSupplier);
        supplierList.add(newSupplier);
        return newSupplier.getId();
    }
    /**
     * 查找或新增产品大类:product 表按 product_name 匹配,不存在则新增,默认父ID为278
     */
    private Long findOrCreateProduct(String productName, List<Map<String, Object>> productAndModelList) {
        // 内存列表中查找(本次导入已新增的也包含在内)
        for (Map<String, Object> map : productAndModelList) {
            if (map.get("productName") != null && productName.equals(map.get("productName"))) {
                return Long.valueOf(map.get("id").toString());
            }
        }
        // 数据库查找(已存在但暂无规格型号的产品不在内存列表中)
        Product existProduct = productMapper.selectOne(new LambdaQueryWrapper<Product>()
                .eq(Product::getProductName, productName)
                .last("limit 1"));
        if (existProduct != null) {
            return existProduct.getId();
        }
        // 新增产品大类,默认父ID为278
        Product product = new Product();
        product.setProductName(productName);
        product.setParentId(278L);
        productMapper.insert(product);
        return product.getId();
    }
    /**
     * 查找或新增产品规格型号:product_model 按 产品ID+规格型号 匹配,不存在则新增。
     * 产品编码 = 规格型号 + "_" + 三位流水号;规格型号为空时用产品大类作为规格型号。
     */
    private Long findOrCreateProductModel(Long productId, String productName, String model, String unit,
                                          List<Map<String, Object>> productAndModelList) {
        // 内存列表中查找
        for (Map<String, Object> map : productAndModelList) {
            if (map.get("id") != null && Long.valueOf(map.get("id").toString()).equals(productId)
                    && model.equals(map.get("model"))) {
                return Long.valueOf(map.get("modelId").toString());
            }
        }
        // 数据库查找
        ProductModel existProductModel = productModelMapper.selectOne(new LambdaQueryWrapper<ProductModel>()
                .eq(ProductModel::getProductId, productId)
                .eq(ProductModel::getModel, model)
                .last("limit 1"));
        if (existProductModel != null) {
            return existProductModel.getId();
        }
        // 新增规格型号,产品编码 = 规格型号 + "_" + 三位流水号(同产品下自增)
        long count = productModelMapper.selectCount(new LambdaQueryWrapper<ProductModel>()
                .eq(ProductModel::getProductId, productId));
        ProductModel productModel = new ProductModel();
        productModel.setProductId(productId);
        productModel.setModel(model);
        productModel.setUnit(unit);
        productModel.setProductCode(model + "_" + String.format("%03d", count + 1));
        productModelMapper.insert(productModel);
        // 记录到内存列表,避免本次导入内重复创建
        Map<String, Object> newMap = new HashMap<>();
        newMap.put("id", productId);
        newMap.put("modelId", productModel.getId());
        newMap.put("productName", productName);
        newMap.put("model", model);
        productAndModelList.add(newMap);
        return productModel.getId();
    }
    /**
     * 删除采购单下的质检记录及质检参数(已提交的检验单不允许删除)
     */
    private void deleteQualityInspectByPurchaseLedger(Long purchaseLedgerId) {
        LambdaQueryWrapper<QualityInspect> inspectWrapper = new LambdaQueryWrapper<>();
        inspectWrapper.eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId);
        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(inspectWrapper);
        for (QualityInspect qualityInspect : qualityInspects) {
            if (ObjectUtils.isNotEmpty(qualityInspect.getInspectState()) && qualityInspect.getInspectState().equals(1)) {
                throw new BaseException("已提交的检验单不能删除");
            }
        }
        List<Long> inspectIds = qualityInspects.stream()
                .map(QualityInspect::getId)
                .collect(Collectors.toList());
        if (!inspectIds.isEmpty()) {
            qualityInspectParamMapper.delete(new LambdaQueryWrapper<QualityInspectParam>()
                    .in(QualityInspectParam::getInspectId, inspectIds));
        }
        qualityInspectMapper.delete(inspectWrapper);
    }
    @Override
@@ -1073,4 +1226,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;
    }
}