| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.ReviewStatusEnum; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderPickDto; |
| | |
| | | import com.ruoyi.production.service.ProductionOrderPickService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.stock.pojo.StockOutRecord; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import com.ruoyi.stock.service.StockOutRecordService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | |
| | | private static final byte PICK_TYPE_NORMAL = 1; |
| | | private static final byte PICK_TYPE_FEEDING = 2; |
| | | private static final String PICK_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.PICK_STOCK_OUT.getCode(); |
| | | private static final String FEED_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.FEED_STOCK_OUT.getCode(); |
| | | private static final String PICK_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode(); |
| | | private static final String FEED_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode(); |
| | | |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StockInventoryService stockInventoryService; |
| | | private final StockInRecordService stockInRecordService; |
| | | private final StockOutRecordService stockOutRecordService; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | // 领料新增总流程: |
| | | // 1) 解析前端行数据并逐行合并参数; |
| | | // 2) 校验参数与批次; |
| | | // 3) 先扣减库存,再落库领料主记录; |
| | | // 4) 写入领料流水,记录数量变化轨迹。 |
| | | // 3) 先保存领料主记录; |
| | | // 4) 再走“出库申请 + 审批通过”完成库存扣减; |
| | | // 5) 写入领料流水,记录数量变化轨迹。 |
| | | List<ProductionOrderPickDto> pickItems = resolvePickItems(dto); |
| | | // 逐行处理领料数据,行号用于拼装精确的报错信息。 |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | |
| | | List<String> batchNoList = resolveBatchNoList(resolvedDto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo); |
| | | |
| | | // 保存领料主记录快照。 |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | |
| | | orderPick.setReturned(false); |
| | | // 新增主记录。 |
| | | baseMapper.insert(orderPick); |
| | | |
| | | // 先新增出库申请,再审批通过,完成库存扣减。 |
| | | subtractInventory(orderPick.getId(), resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | // 记录本次领料流水(before=0,after=本次领料量)。 |
| | | insertPickRecord(orderPick.getId(), |
| | |
| | | } |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity()); |
| | | addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | addInventory(existingPick.getId(), existingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | // 删除关联领料流水,避免遗留无主记录。 |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, existingPick.getId()) |
| | | ); |
| | | int affected = baseMapper.deleteById(deleteId); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("删除领料记录失败,ID=" + deleteId); |
| | |
| | | for (ProductionOrderPick missingPick : missingPickList) { |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity()); |
| | | addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | addInventory(missingPick.getId(), missingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | // 删除关联领料流水,避免遗留无主记录。 |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, missingPick.getId()) |
| | | ); |
| | | int affected = baseMapper.deleteById(missingPick.getId()); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("删除未回传领料记录失败,ID=" + missingPick.getId()); |
| | |
| | | } |
| | | |
| | | private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) { |
| | | // 更新场景下新增一条领料:扣库存 -> 新增主记录 -> 写流水。 |
| | | // 更新场景下新增一条领料: |
| | | // 新增主记录 -> 出库申请并审批 -> 写流水。 |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(dto.getProductionOrderId()); |
| | |
| | | orderPick.setBom(dto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | // 先新增出库申请,再审批通过,完成库存扣减。 |
| | | subtractInventory(orderPick.getId(), dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | | dto.getProductionOrderId(), |
| | |
| | | : formatBatchNoStorage(batchNoList); |
| | | BigDecimal feedingQuantity = dto.getFeedingQuantity(); |
| | | |
| | | subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo); |
| | | subtractInventory(oldPick.getId(), productModelId, inventoryBatchNo, feedingQuantity, rowNo, FEED_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | // 计算补料前后数量并写补料流水。 |
| | | BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId()); |
| | |
| | | } |
| | | |
| | | private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) { |
| | | // 退料更新只改主领料记录中的退料字段与实际量。 |
| | | // 退料更新: |
| | | // 1) returnQty 按“本次退料量”处理; |
| | | // 2) 本次退料量回补到“生产退料入库”; |
| | | // 3) 累加主记录退料总量并重算实际量。 |
| | | BigDecimal oldReturnQty = defaultDecimal(oldPick.getReturnQty()); |
| | | BigDecimal currentReturnQty = defaultDecimal(dto.getReturnQty()); |
| | | BigDecimal totalReturnQty = oldReturnQty.add(currentReturnQty); |
| | | if (currentReturnQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | String returnBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo()); |
| | | addInventory(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE); |
| | | } |
| | | |
| | | BigDecimal actualQty = defaultDecimal(oldPick.getQuantity()) |
| | | .add(defaultDecimal(oldPick.getFeedingQty())) |
| | | .subtract(totalReturnQty); |
| | | if (actualQty.compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "行退料失败:累计退料数量不能大于可用数量"); |
| | | } |
| | | ProductionOrderPick updatePick = new ProductionOrderPick(); |
| | | updatePick.setId(oldPick.getId()); |
| | | updatePick.setReturnQty(dto.getReturnQty()); |
| | | updatePick.setActualQty(dto.getActualQty()); |
| | | updatePick.setReturned(true); |
| | | updatePick.setReturnQty(totalReturnQty); |
| | | updatePick.setActualQty(actualQty); |
| | | updatePick.setReturned(totalReturnQty.compareTo(BigDecimal.ZERO) > 0); |
| | | int affected = baseMapper.updateById(updatePick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "行退料失败:更新领料主记录失败"); |
| | | } |
| | | oldPick.setReturnQty(updatePick.getReturnQty()); |
| | | oldPick.setActualQty(updatePick.getActualQty()); |
| | | oldPick.setReturned(true); |
| | | oldPick.setReturned(updatePick.getReturned()); |
| | | } |
| | | |
| | | private void updateExistingPick(ProductionOrderPickDto dto, |
| | |
| | | String newStoredBatchNo = formatBatchNoStorage(newBatchNoList); |
| | | BigDecimal newQuantity = dto.getPickQuantity(); |
| | | |
| | | // 判断规格+批次是否变化,决定库存处理策略。 |
| | | // 判断规格+批次或数量是否变化,并按场景处理库存: |
| | | // 1) 同规格同批次:按差值处理(增量扣减 / 减量回退); |
| | | // 2) 规格或批次变化:回退旧领料后再重提新领料。 |
| | | boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId) |
| | | && Objects.equals(oldBatchNo, newBatchNo); |
| | | boolean quantityChanged = oldQuantity.compareTo(newQuantity) != 0; |
| | | boolean needReissuePickRecord = !sameStockKey || quantityChanged; |
| | | if (sameStockKey) { |
| | | // 规格与批次不变:只按差值增减库存。 |
| | | BigDecimal delta = newQuantity.subtract(oldQuantity); |
| | | if (delta.compareTo(BigDecimal.ZERO) > 0) { |
| | | subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo); |
| | | } else if (delta.compareTo(BigDecimal.ZERO) < 0) { |
| | | addInventory(oldProductModelId, oldBatchNo, delta.abs()); |
| | | BigDecimal deltaQuantity = newQuantity.subtract(oldQuantity); |
| | | if (deltaQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | // 数量增加,只扣减新增部分。 |
| | | subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, deltaQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | } else if (deltaQuantity.compareTo(BigDecimal.ZERO) < 0) { |
| | | // 数量减少,只回退差值部分。 |
| | | addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, deltaQuantity.abs(), PICK_RETURN_IN_RECORD_TYPE); |
| | | } |
| | | } else { |
| | | // 规格或批次变化:先回补旧库存,再扣减新库存。 |
| | | addInventory(oldProductModelId, oldBatchNo, oldQuantity); |
| | | subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo); |
| | | // 规格或批次变化:先全量回退旧领料,再全量扣减新领料。 |
| | | addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, newQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | } |
| | | if (needReissuePickRecord) { |
| | | // 正常领料流水按“最新领料量”重建,避免保留历史旧值。 |
| | | deleteNormalPickRecord(oldPick.getId()); |
| | | } |
| | | |
| | | oldPick.setProductModelId(newProductModelId); |
| | |
| | | oldPick.setRemark(dto.getRemark()); |
| | | oldPick.setOperationName(dto.getOperationName()); |
| | | oldPick.setTechnologyOperationId(dto.getTechnologyOperationId()); |
| | | // 普通更新也要同步重算实际用量,避免沿用旧值。 |
| | | // 规则:实际用量 = 领料数量 + 补料数量 - 退料数量。 |
| | | oldPick.setActualQty(calculateActualQty(oldPick, oldPick.getFeedingQty())); |
| | | if (dto.getDemandedQuantity() != null) { |
| | | oldPick.setDemandedQuantity(dto.getDemandedQuantity()); |
| | | } |
| | |
| | | throw new ServiceException("第" + rowNo + "行更新失败:更新领料记录失败"); |
| | | } |
| | | |
| | | // 写入更新流水,保留本次数量变化轨迹。 |
| | | BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity; |
| | | if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) { |
| | | // 如果发生领料重提,补写一条新的正常领料流水。 |
| | | if (needReissuePickRecord) { |
| | | insertPickRecord(oldPick.getId(), |
| | | dto.getProductionOrderId(), |
| | | dto.getProductionOperationTaskId(), |
| | | newProductModelId, |
| | | newBatchNo, |
| | | recordQuantity, |
| | | oldQuantity, |
| | | newQuantity, |
| | | BigDecimal.ZERO, |
| | | newQuantity, |
| | | dto.getPickType(), |
| | | dto.getRemark(), |
| | |
| | | productionOrderPickRecordMapper.insert(pickRecord); |
| | | } |
| | | |
| | | private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) { |
| | | private void deleteNormalPickRecord(Long pickId) { |
| | | // 删除该领料单历史上的“正常领料”流水,保留补料/退料流水。 |
| | | if (pickId == null) { |
| | | return; |
| | | } |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, pickId) |
| | | .eq(ProductionOrderPickRecord::getPickType, PICK_TYPE_NORMAL) |
| | | ); |
| | | } |
| | | |
| | | private void subtractInventory(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | int rowNo, |
| | | String stockOutRecordType) { |
| | | // 扣减库存总流程: |
| | | // 1) 解析批次列表; |
| | | // 2) 计算每个批次可用量与总可用量; |
| | | // 3) 按批次顺序逐笔扣减,直到扣完目标数量; |
| | | // 3) 按批次顺序逐笔“新增出库记录并审批通过”,直到扣完目标数量; |
| | | // 4) 任一步失败即抛错并回滚事务。 |
| | | BigDecimal deductQuantity = defaultDecimal(quantity); |
| | | // 领料数量小于等于0时,不需要执行库存扣减。 |
| | |
| | | continue; |
| | | } |
| | | BigDecimal currentDeductQuantity = remainingQuantity.min(availableQuantity); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(entry.getKey()); |
| | | stockInventoryDto.setQualitity(currentDeductQuantity); |
| | | int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "行扣减库存失败:库存更新失败"); |
| | | } |
| | | createAndApproveStockOutRecord(recordId, productModelId, entry.getKey(), currentDeductQuantity, rowNo, stockOutRecordType); |
| | | remainingQuantity = remainingQuantity.subtract(currentDeductQuantity); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) { |
| | | // 回补库存(用于删除领料、改小领料、切换批次等场景)。 |
| | | private void createAndApproveStockOutRecord(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | int rowNo, |
| | | String stockOutRecordType) { |
| | | // 库存扣减改为两步: |
| | | // 1) 先调用 addStockOutRecordOnly 新增待审批出库记录; |
| | | // 2) 再调用出库审批,审批状态固定传 1(通过)。 |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryDto.setRecordType(stockOutRecordType); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | stockInventoryService.addStockOutRecordOnly(stockInventoryDto); |
| | | |
| | | LambdaQueryWrapper<StockOutRecord> recordWrapper = Wrappers.<StockOutRecord>lambdaQuery() |
| | | .eq(StockOutRecord::getRecordId, stockInventoryDto.getRecordId()) |
| | | .eq(StockOutRecord::getRecordType, stockOutRecordType) |
| | | .eq(StockOutRecord::getProductModelId, productModelId) |
| | | .eq(StockOutRecord::getType, "0") |
| | | .orderByDesc(StockOutRecord::getId) |
| | | .last("limit 1"); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | recordWrapper.isNull(StockOutRecord::getBatchNo); |
| | | } else { |
| | | recordWrapper.eq(StockOutRecord::getBatchNo, batchNo); |
| | | } |
| | | StockOutRecord stockOutRecord = stockOutRecordService.getOne(recordWrapper, false); |
| | | if (stockOutRecord == null || stockOutRecord.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "行扣减库存失败:未找到对应出库申请记录"); |
| | | } |
| | | stockOutRecordService.batchApprove( |
| | | Collections.singletonList(stockOutRecord.getId()), |
| | | ReviewStatusEnum.APPROVED.getCode() |
| | | ); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("第" + rowNo + "行扣减库存失败:" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void addInventory(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | String stockInRecordType) { |
| | | // 回补库存改为两步: |
| | | // 1) 先新增入库申请; |
| | | // 2) 再审批通过,确保库存立刻回补生效。 |
| | | BigDecimal addQuantity = defaultDecimal(quantity); |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode())); |
| | | stockInventoryDto.setRecordId(0L); |
| | | stockInventoryDto.setRecordType(stockInRecordType); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | |
| | | LambdaQueryWrapper<StockInRecord> recordWrapper = Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordId, stockInventoryDto.getRecordId()) |
| | | .eq(StockInRecord::getRecordType, stockInventoryDto.getRecordType()) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .eq(StockInRecord::getType, "0") |
| | | .orderByDesc(StockInRecord::getId) |
| | | .last("limit 1"); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | recordWrapper.isNull(StockInRecord::getBatchNo); |
| | | } else { |
| | | recordWrapper.eq(StockInRecord::getBatchNo, batchNo); |
| | | } |
| | | StockInRecord stockInRecord = stockInRecordService.getOne(recordWrapper, false); |
| | | if (stockInRecord == null || stockInRecord.getId() == null) { |
| | | throw new ServiceException("回补库存失败:未找到对应入库申请记录"); |
| | | } |
| | | stockInRecordService.batchApprove( |
| | | Collections.singletonList(stockInRecord.getId()), |
| | | ReviewStatusEnum.APPROVED.getCode() |
| | | ); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("回补库存失败:" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) { |
| | |
| | | } |
| | | |
| | | private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) { |
| | | // 校验退料参数(订单、领料ID、退料量、实际量)。 |
| | | // 校验退料参数(订单、领料ID、退料量)。 |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "行参数错误:生产订单ID不能为空"); |
| | | } |
| | |
| | | } |
| | | if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "行参数错误:退料数量不能小于0"); |
| | | } |
| | | if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "行参数错误:实际数量不能小于0"); |
| | | } |
| | | } |
| | | |