package com.vincent.rsf.server.manager.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.manager.controller.params.ReviseLogParams; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.enums.AsnExceStatus; import com.vincent.rsf.server.manager.enums.CommonExceStatus; import com.vincent.rsf.server.manager.enums.OrderType; import com.vincent.rsf.server.manager.enums.OrderWorkType; import com.vincent.rsf.server.manager.mapper.ReviseLogMapper; import com.vincent.rsf.server.manager.service.*; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @Service("reviseLogService") public class ReviseLogServiceImpl extends ServiceImpl implements ReviseLogService { @Autowired private LocReviseService locReviseService; @Autowired private ReviseLogService reviseLogService; @Autowired private LocItemService locItemService; @Autowired private LocService locService; @Autowired private ReviseLogItemService reviseLogItemService; @Autowired private OutStockService outStockService; @Autowired private OutStockItemService outStockItemService; /** * 库存调整单明细添加 * * @param revise * @param loginUserId * @return */ @Override @Transactional(rollbackFor = Exception.class) public List reviseLoc(ReviseLogParams revise, Long loginUserId) { LocRevise locRevise = locReviseService.getById(revise.getReviseId()); if (Objects.isNull(locRevise)) { throw new CoolException("调整单据不存在!!"); } List items = revise.getItems(); if (items.isEmpty()) { throw new CoolException("调整单明细参数为空!!"); } items.forEach(item -> { ReviseLog reviseLog = new ReviseLog(); BeanUtils.copyProperties(item, reviseLog); reviseLog.setAreaId(locRevise.getAreaId()) .setReviseId(locRevise.getId()) .setCreateBy(loginUserId) .setUpdateBy(loginUserId) .setReviseCode(locRevise.getCode()); if (!reviseLogService.save(reviseLog)) { throw new CoolException("调整单明细保存失败!!"); } List locItems = locItemService.list(new LambdaQueryWrapper() .eq(LocItem::getLocCode, item.getLocCode())); if (!locItems.isEmpty()) { locItems.forEach(ote -> { ReviseLogItem logItem = new ReviseLogItem(); BeanUtils.copyProperties(ote, logItem); logItem.setReviseLogId(reviseLog.getId()); if (!reviseLogItemService.save(logItem)) { throw new CoolException("明细保存失败!!"); } }); } Double sum = locItems.stream().mapToDouble(LocItem::getAnfme).sum(); locRevise.setAnfme(Math.round((sum + locRevise.getAnfme()) * 1000000) / 1000000.0); }); locRevise.setExceStatus(CommonExceStatus.COMMON_EXCE_STATUS_UN_EXCE.val); if (!locReviseService.updateById(locRevise)) { throw new CoolException("状态更新失败!!"); } return items; } /** * @author Ryan * @date 2025/8/18 * @description: 确认调整库存 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public R complete(Long id, Long loginUserId) { LocRevise revise = locReviseService.getById(id); if (Objects.isNull(revise)) { throw new CoolException("调整单不存在!!"); } if (!revise.getExceStatus().equals(CommonExceStatus.COMMON_EXCE_STATUS_EXCE_ING.val)) { throw new CoolException("单据状态未执行或已完成,无法执行完成操作!!"); } List logs = reviseLogService.list(new LambdaQueryWrapper().eq(ReviseLog::getReviseId, revise.getId())); if (logs.isEmpty()) { throw new CoolException("库存日志不存在!!"); } Set longs = logs.stream().map(ReviseLog::getId).collect(Collectors.toSet()); List logItems = reviseLogItemService.list(new LambdaQueryWrapper().in(ReviseLogItem::getReviseLogId, longs)); if (logItems.isEmpty()) { throw new CoolException("调整明细为空!!"); } // 按库位ID分组,如果locId为null则按locCode分组 Map> listMap = logItems.stream() .filter(item -> item.getLocCode() != null && !item.getLocCode().isEmpty()) .collect(Collectors.groupingBy(item -> { // 优先使用locId,如果为null则使用locCode作为key return item.getLocId() != null ? String.valueOf(item.getLocId()) : item.getLocCode(); })); // 优化:在循环外查询所有未完成的出库单和明细,避免重复查询 // 查询所有未完成的出库单状态:初始化、待处理、生成波次、生成工作档、作业中 List outOrders = outStockService.list(new LambdaQueryWrapper() .eq(WkOrder::getType, OrderType.ORDER_OUT.type) .in(WkOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val, AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val, AsnExceStatus.OUT_STOCK_STATUS_TASK_WAVE.val, AsnExceStatus.OUT_STOCK_STATUS_TASK_CREATE.val, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)); // 预先查询所有出库单的明细,建立索引以提高查询效率 Map> orderItemsMap = new HashMap<>(); Map orderHasItemsMap = new HashMap<>(); if (!outOrders.isEmpty()) { Set orderIds = outOrders.stream().map(WkOrder::getId).collect(Collectors.toSet()); List allOrderItems = outStockItemService.list( new LambdaQueryWrapper().in(WkOrderItem::getOrderId, orderIds)); // 按orderId分组建立索引 orderItemsMap = allOrderItems.stream() .collect(Collectors.groupingBy(WkOrderItem::getOrderId)); // 记录每个出库单是否有明细 for (Long orderId : orderIds) { orderHasItemsMap.put(orderId, orderItemsMap.containsKey(orderId) && !orderItemsMap.get(orderId).isEmpty()); } } // 使用final变量以便在lambda中使用 final Map> finalOrderItemsMap = orderItemsMap; final Map finalOrderHasItemsMap = orderHasItemsMap; // 批量收集需要创建的WkOrderItem List newOrderItems = new ArrayList<>(); listMap.keySet().forEach(key -> { List reviseItems = listMap.get(key); if (Objects.isNull(reviseItems) || reviseItems.isEmpty()) { throw new CoolException("调整明细为空!!"); } // 获取第一个明细的库位信息 ReviseLogItem firstItem = reviseItems.get(0); String locCode = firstItem.getLocCode(); Long locId = firstItem.getLocId(); // 查询库位:优先使用locId,如果为null则使用locCode Loc loc = null; if (locId != null) { loc = locService.getById(locId); } if (loc == null && locCode != null && !locCode.isEmpty()) { loc = locService.getOne(new LambdaQueryWrapper().eq(Loc::getCode, locCode).eq(Loc::getDeleted, 0)); } if (Objects.isNull(loc)) { throw new CoolException("库位不存在!库位编码:" + locCode + ", 库位ID:" + locId); } // 删除原库位的库存明细(如果存在) locItemService.remove(new LambdaQueryWrapper().eq(LocItem::getLocId, loc.getId())); Loc finalLoc = loc; reviseItems.forEach(logItem -> { LocItem locDetl = new LocItem(); BeanUtils.copyProperties(logItem, locDetl); locDetl.setLocId(finalLoc.getId()) .setType(OrderType.ORDER_REVISE.type) .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_REVISE.type)) .setLocCode(finalLoc.getCode()) .setAnfme(logItem.getReviseQty()) // 如果batch不为空,同时设置splrBatch,以便查询时能匹配 .setSplrBatch(StringUtils.isNotBlank(logItem.getBatch()) ? logItem.getBatch() : null) .setUpdateBy(loginUserId) .setId(null) .setCreateBy(loginUserId); if (!locItemService.save(locDetl)) { throw new CoolException("库存明细保存失败!!"); } // 为库存调整产生的库存创建对应的WkOrderItem // 遍历所有未完成的出库单,检查是否需要这些物料 for (WkOrder outOrder : outOrders) { // 从预加载的索引中查找该出库单的明细 List orderItems = finalOrderItemsMap.getOrDefault(outOrder.getId(), new ArrayList<>()); // 检查该出库单是否已有匹配的明细 boolean hasMatchingItem = orderItems.stream().anyMatch(item -> { // 物料ID必须匹配 if (!Objects.equals(item.getMatnrId(), logItem.getMatnrId())) { return false; } // 如果有批次信息,需要匹配批次 if (StringUtils.isNotBlank(logItem.getBatch())) { boolean batchMatch = Objects.equals(item.getBatch(), logItem.getBatch()) || Objects.equals(item.getSplrBatch(), logItem.getBatch()); if (!batchMatch) { return false; } } // 如果有字段索引,需要匹配 if (StringUtils.isNotBlank(logItem.getFieldsIndex())) { if (!Objects.equals(item.getFieldsIndex(), logItem.getFieldsIndex())) { return false; } } return true; }); // 如果该出库单需要这个物料但没有对应的明细,则创建 if (!hasMatchingItem) { // 为所有未完成的出库单创建WkOrderItem,即使出库单没有其他明细 // 因为库存调整的物料可能需要出库 WkOrderItem newOrderItem = new WkOrderItem(); newOrderItem.setOrderId(outOrder.getId()) .setOrderCode(outOrder.getCode()) .setMatnrId(logItem.getMatnrId()) .setMatnrCode(logItem.getMatnrCode()) .setMaktx(logItem.getMaktx()) .setBatch(logItem.getBatch()) // 如果batch不为空,同时设置splrBatch,以便查询时能匹配 .setSplrBatch(StringUtils.isNotBlank(logItem.getBatch()) ? logItem.getBatch() : null) .setFieldsIndex(logItem.getFieldsIndex()) .setAnfme(logItem.getReviseQty()) .setWorkQty(0.0) .setStockUnit(logItem.getUnit()) .setPurUnit(logItem.getUnit()) .setSpec(logItem.getSpec()) .setModel(logItem.getModel()) .setCreateBy(loginUserId) .setUpdateBy(loginUserId) .setCreateTime(new Date()) .setUpdateTime(new Date()); newOrderItems.add(newOrderItem); } } }); }); // 批量保存WkOrderItem,减少数据库操作次数 if (!newOrderItems.isEmpty()) { if (!outStockItemService.saveBatch(newOrderItems)) { throw new CoolException("库存调整单据明细创建失败!!"); } } revise.setExceStatus(CommonExceStatus.COMMON_EXCE_STATUS_TASK_DONE.val); if (!locReviseService.updateById(revise)) { throw new CoolException("调整单修改失败!!"); } return R.ok(); } }