package com.klp.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.esotericsoftware.minlog.Log; import com.fasterxml.jackson.core.JsonProcessingException; import com.klp.common.core.domain.entity.SysUser; import com.klp.common.core.page.TableDataInfo; import com.klp.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.klp.common.helper.LoginHelper; import com.klp.common.utils.StringUtils; import com.klp.common.utils.spring.SpringUtils; import com.klp.domain.*; import com.klp.domain.bo.*; import com.klp.domain.vo.*; import com.klp.mapper.WmsDeliveryPlanMapper; import com.klp.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.klp.mapper.WmsMaterialCoilMapper; import com.klp.mapper.WmsStockMapper; import com.klp.mapper.WmsProductMapper; import com.klp.mapper.WmsRawMaterialMapper; import com.klp.mapper.WmsGenerateRecordMapper; import com.klp.service.IWmsMaterialCoilService; import com.klp.service.IWmsStockService; import com.klp.service.IWmsGenerateRecordService; import com.klp.service.IWmsWarehouseService; import com.klp.service.IWmsActualWarehouseService; import com.klp.service.IWmsRawMaterialService; import com.klp.service.IWmsBomItemService; import com.klp.service.IWmsProductService; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.*; import java.util.stream.Collectors; import java.math.BigDecimal; /** * 钢卷物料表Service业务层处理 * * @author Joshi * @date 2025-07-18 */ @Slf4j @RequiredArgsConstructor @Service public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { private final WmsMaterialCoilMapper baseMapper; private final IWmsStockService stockService; private final IWmsGenerateRecordService generateRecordService; private final WmsGenerateRecordMapper generateRecordMapper; private final IWmsWarehouseService warehouseService; private final IWmsActualWarehouseService actualWarehouseService; private final IWmsRawMaterialService rawMaterialService; private final IWmsBomItemService bomItemService; private final IWmsProductService productService; private final ISysUserService userService; private final WmsDeliveryPlanMapper deliveryPlanMapper; private final WmsProductMapper productMapper; private final WmsRawMaterialMapper rawMaterialMapper; /** * 查询钢卷物料表 */ @Override public WmsMaterialCoilVo queryById(Long coilId) { WmsMaterialCoilVo vo = baseMapper.selectVoById(coilId); if (vo == null) { return null; } // 查询关联对象 fillRelatedObjects(vo); return vo; } /** * 批量填充关联对象信息(优化版本,避免N+1查询) */ private void fillRelatedObjectsBatch(List voList) { if (voList == null || voList.isEmpty()) { return; } // 收集所有需要查询的ID Set warehouseIds = new HashSet<>(); Set nextWarehouseIds = new HashSet<>(); Set actualWarehouseIds = new HashSet<>(); Set qrcodeRecordIds = new HashSet<>(); Set rawMaterialIds = new HashSet<>(); Set productIds = new HashSet<>(); Set bomIds = new HashSet<>(); for (WmsMaterialCoilVo vo : voList) { if (vo.getWarehouseId() != null) warehouseIds.add(vo.getWarehouseId()); if (vo.getNextWarehouseId() != null) nextWarehouseIds.add(vo.getNextWarehouseId()); if (vo.getActualWarehouseId() != null) actualWarehouseIds.add(vo.getActualWarehouseId()); if (vo.getQrcodeRecordId() != null) qrcodeRecordIds.add(vo.getQrcodeRecordId()); if ("raw_material".equals(vo.getItemType()) && vo.getItemId() != null) { rawMaterialIds.add(vo.getItemId()); } if ("product".equals(vo.getItemType()) && vo.getItemId() != null) { productIds.add(vo.getItemId()); } } // 批量查询库区信息 Map warehouseMap = new HashMap<>(); if (!warehouseIds.isEmpty()) { for (Long id : warehouseIds) { WmsWarehouseVo warehouse = warehouseService.queryById(id); if (warehouse != null) { warehouseMap.put(id, warehouse); } } } if (!nextWarehouseIds.isEmpty()) { for (Long id : nextWarehouseIds) { if (!warehouseMap.containsKey(id)) { WmsWarehouseVo warehouse = warehouseService.queryById(id); if (warehouse != null) { warehouseMap.put(id, warehouse); } } } } // 批量查询实际库区信息 Map actualWarehouseMap = new HashMap<>(); if (!actualWarehouseIds.isEmpty()) { for (Long id : actualWarehouseIds) { WmsActualWarehouseVo actualWarehouse = actualWarehouseService.queryById(id); if (actualWarehouse != null) { actualWarehouseMap.put(id, actualWarehouse); } } } // 批量查询二维码信息 Map qrcodeMap = new HashMap<>(); if (!qrcodeRecordIds.isEmpty()) { for (Long id : qrcodeRecordIds) { WmsGenerateRecordVo qrcode = generateRecordService.queryById(id); if (qrcode != null) { qrcodeMap.put(id, qrcode); } } } // 批量查询原材料信息 Map rawMaterialMap = new HashMap<>(); if (!rawMaterialIds.isEmpty()) { for (Long id : rawMaterialIds) { WmsRawMaterialVo rawMaterial = rawMaterialService.queryById(id); if (rawMaterial != null) { rawMaterialMap.put(id, rawMaterial); if (rawMaterial.getBomId() != null) { bomIds.add(rawMaterial.getBomId()); } } } } // 批量查询产品信息 Map productMap = new HashMap<>(); if (!productIds.isEmpty()) { for (Long id : productIds) { WmsProductVo product = productService.queryById(id); if (product != null) { productMap.put(id, product); if (product.getBomId() != null) { bomIds.add(product.getBomId()); } } } } // 批量查询BOM信息 Map> bomItemMap = new HashMap<>(); if (!bomIds.isEmpty()) { for (Long bomId : bomIds) { WmsBomItemBo bomItemBo = new WmsBomItemBo(); bomItemBo.setBomId(bomId); List bomItemList = bomItemService.queryList(bomItemBo); if (bomItemList != null && !bomItemList.isEmpty()) { bomItemMap.put(bomId, bomItemList); } } } // 填充到VO对象中 for (WmsMaterialCoilVo vo : voList) { if (vo.getWarehouseId() != null && warehouseMap.containsKey(vo.getWarehouseId())) { vo.setWarehouse(warehouseMap.get(vo.getWarehouseId())); } if (vo.getNextWarehouseId() != null && warehouseMap.containsKey(vo.getNextWarehouseId())) { vo.setNextWarehouse(warehouseMap.get(vo.getNextWarehouseId())); } if (vo.getActualWarehouseId() != null && actualWarehouseMap.containsKey(vo.getActualWarehouseId())) { WmsActualWarehouseVo actualWarehouse = actualWarehouseMap.get(vo.getActualWarehouseId()); vo.setActualWarehouseName(actualWarehouse.getActualWarehouseName()); } if (vo.getQrcodeRecordId() != null && qrcodeMap.containsKey(vo.getQrcodeRecordId())) { vo.setQrcodeRecord(qrcodeMap.get(vo.getQrcodeRecordId())); } if ("raw_material".equals(vo.getItemType()) && vo.getItemId() != null && rawMaterialMap.containsKey(vo.getItemId())) { WmsRawMaterialVo rawMaterial = rawMaterialMap.get(vo.getItemId()); vo.setRawMaterial(rawMaterial); if (rawMaterial.getBomId() != null && bomItemMap.containsKey(rawMaterial.getBomId())) { vo.setBomItemList(bomItemMap.get(rawMaterial.getBomId())); } } if ("product".equals(vo.getItemType()) && vo.getItemId() != null && productMap.containsKey(vo.getItemId())) { WmsProductVo product = productMap.get(vo.getItemId()); vo.setProduct(product); if (product.getBomId() != null && bomItemMap.containsKey(product.getBomId())) { vo.setBomItemList(bomItemMap.get(product.getBomId())); } } } } /** * 填充关联对象信息(单个对象,保留用于兼容性) */ private void fillRelatedObjects(WmsMaterialCoilVo vo) { // 查询所在库区信息 if (vo.getWarehouseId() != null) { WmsWarehouseVo warehouse = warehouseService.queryById(vo.getWarehouseId()); vo.setWarehouse(warehouse); } // 查询下一库区信息 if (vo.getNextWarehouseId() != null) { WmsWarehouseVo nextWarehouse = warehouseService.queryById(vo.getNextWarehouseId()); vo.setNextWarehouse(nextWarehouse); } // 查询实际库区信息 if (vo.getActualWarehouseId() != null) { WmsActualWarehouseVo actualWarehouse = actualWarehouseService.queryById(vo.getActualWarehouseId()); if (actualWarehouse != null) { vo.setActualWarehouseName(actualWarehouse.getActualWarehouseName()); } } // 查询二维码信息 if (vo.getQrcodeRecordId() != null) { WmsGenerateRecordVo qrcodeRecord = generateRecordService.queryById(vo.getQrcodeRecordId()); vo.setQrcodeRecord(qrcodeRecord); } // 查询原材料信息(当itemType为raw_material时) if ("raw_material".equals(vo.getItemType()) && vo.getItemId() != null) { WmsRawMaterialVo rawMaterial = rawMaterialService.queryById(vo.getItemId()); vo.setRawMaterial(rawMaterial); // 查询原材料对应的BOM信息(通过bomId查询BomItem列表) if (rawMaterial != null && rawMaterial.getBomId() != null) { WmsBomItemBo bomItemBo = new WmsBomItemBo(); bomItemBo.setBomId(rawMaterial.getBomId()); List bomItemList = bomItemService.queryList(bomItemBo); vo.setBomItemList(bomItemList); } } // 查询产品信息(当itemType为product时) if ("product".equals(vo.getItemType()) && vo.getItemId() != null) { WmsProductVo product = productService.queryById(vo.getItemId()); vo.setProduct(product); // 查询产品对应的BOM信息(通过bomId查询BomItem列表) if (product != null && product.getBomId() != null) { WmsBomItemBo bomItemBo = new WmsBomItemBo(); bomItemBo.setBomId(product.getBomId()); List bomItemList = bomItemService.queryList(bomItemBo); vo.setBomItemList(bomItemList); } } } /** * 查询钢卷物料表列表 */ @Override public TableDataInfo queryPageList(WmsMaterialCoilBo bo, PageQuery pageQuery) { QueryWrapper qw = buildQueryWrapperPlus(bo); Page result = baseMapper.selectVoPagePlus(pageQuery.build(), qw); List records = result.getRecords(); if (records == null || records.isEmpty()) { return TableDataInfo.build(result); } Set userNames = records.stream() .flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getUpdateBy())) .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); Map nickMap = Collections.emptyMap(); if (!userNames.isEmpty()) { nickMap = userService.selectNickNameMapByUserNames(new ArrayList<>(userNames)); } // 单次遍历:填充创建/更新人昵称,并构建物料/产品对象 for (WmsMaterialCoilVo vo : records) { if (!nickMap.isEmpty()) { if (StringUtils.isNotBlank(vo.getCreateBy())) { vo.setCreateByName(nickMap.getOrDefault(vo.getCreateBy(), vo.getCreateBy())); } if (StringUtils.isNotBlank(vo.getUpdateBy())) { vo.setUpdateByName(nickMap.getOrDefault(vo.getUpdateBy(), vo.getUpdateBy())); } } // 从联查结果中构建产品和原材料对象(避免单独查询) buildItemObjectFromJoin(vo); } return TableDataInfo.build(result); } private QueryWrapper buildQueryWrapperPlus(WmsMaterialCoilBo bo) { QueryWrapper qw = Wrappers.query(); qw.like(StringUtils.isNotBlank(bo.getEnterCoilNo()), "mc.enter_coil_no", bo.getEnterCoilNo()); qw.like(StringUtils.isNotBlank(bo.getCurrentCoilNo()), "mc.current_coil_no", bo.getCurrentCoilNo()); qw.like(StringUtils.isNotBlank(bo.getSupplierCoilNo()), "mc.supplier_coil_no", bo.getSupplierCoilNo()); qw.eq(bo.getDataType() != null, "mc.data_type", bo.getDataType()); qw.eq(bo.getMaterialType() != null, "mc.material_type", bo.getMaterialType()); qw.eq(bo.getWarehouseId() != null, "mc.warehouse_id", bo.getWarehouseId()); qw.eq(bo.getHasMergeSplit() != null, "mc.has_merge_split", bo.getHasMergeSplit()); qw.eq(bo.getStatus() != null, "mc.status", bo.getStatus()); qw.eq(bo.getActualWarehouseId() != null, "mc.actual_warehouse_id", bo.getActualWarehouseId()); qw.eq(StringUtils.isNotBlank(bo.getItemType()), "mc.item_type", bo.getItemType()); qw.eq(StringUtils.isNotBlank(bo.getCreateBy()), "mc.create_by", bo.getCreateBy()); qw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), "mc.update_by", bo.getUpdateBy()); // 新增长度 qw.eq(bo.getLength() != null, "mc.length", bo.getLength()); // 新增查询逻辑也就是当saleId未空时候 if (bo.getSaleId() != null) { if (bo.getSaleId() == -1) { // 当saleId为-1时查询sale_id为空的记录 qw.isNull("mc.sale_id"); } else if (bo.getSaleId() > 0) { // 正常传值时依据saleId作为查询条件 qw.eq("mc.sale_id", bo.getSaleId()); } } // 组合 item_id 条件:改为使用 EXISTS 子查询,替代预查询 + IN boolean hasSelectType = StringUtils.isNotBlank(bo.getSelectType()); boolean hasAnyItemFilter = StringUtils.isNotBlank(bo.getItemMaterial()) || StringUtils.isNotBlank(bo.getItemManufacturer()) || StringUtils.isNotBlank(bo.getItemSurfaceTreatmentDesc()) || StringUtils.isNotBlank(bo.getItemZincLayer()) || StringUtils.isNotBlank(bo.getItemName()) || StringUtils.isNotBlank(bo.getItemSpecification()); // 解析显式 itemIds 或单个 itemId List explicitItemIds = null; if (StringUtils.isNotBlank(bo.getItemIds())) { String[] itemIdArray = bo.getItemIds().split(","); List tmp = new ArrayList<>(); for (String itemIdStr : itemIdArray) { if (StringUtils.isNotBlank(itemIdStr)) { try { tmp.add(Long.parseLong(itemIdStr.trim())); } catch (NumberFormatException ignore) {} } } if (!tmp.isEmpty()) { explicitItemIds = tmp.stream().distinct().collect(Collectors.toList()); } } else if (bo.getItemId() != null) { explicitItemIds = Collections.singletonList(bo.getItemId()); } // 使用 EXISTS 针对 selectType 的细粒度筛选(使用参数占位符防注入) if (hasSelectType && hasAnyItemFilter) { StringBuilder existsSql = new StringBuilder(); List existsArgs = new ArrayList<>(); if ("product".equals(bo.getSelectType())) { existsSql.append(" EXISTS (SELECT 1 FROM wms_product p WHERE p.del_flag = 0 AND p.product_id = mc.item_id"); String clause; clause = buildOrLikeClause("p.product_name", bo.getItemName(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("p.material", bo.getItemMaterial(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("p.manufacturer", bo.getItemManufacturer(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("p.surface_treatment_desc", bo.getItemSurfaceTreatmentDesc(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("p.zinc_layer", bo.getItemZincLayer(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("p.specification", bo.getItemSpecification(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); existsSql.append(")"); } else if ("raw_material".equals(bo.getSelectType())) { existsSql.append(" EXISTS (SELECT 1 FROM wms_raw_material r WHERE r.del_flag = 0 AND r.raw_material_id = mc.item_id"); String clause; clause = buildOrLikeClause("r.raw_material_name", bo.getItemName(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("r.material", bo.getItemMaterial(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("r.manufacturer", bo.getItemManufacturer(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("r.surface_treatment_desc", bo.getItemSurfaceTreatmentDesc(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("r.zinc_layer", bo.getItemZincLayer(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); clause = buildOrLikeClause("r.specification", bo.getItemSpecification(), existsArgs); if (!clause.isEmpty()) existsSql.append(" AND ").append(clause); existsSql.append(")"); } if (existsSql.length() > 0) { qw.apply(existsSql.toString(), existsArgs.toArray()); } } // 显式 itemId 条件:与 EXISTS 共存时,语义为交集 if (CollectionUtils.isNotEmpty(explicitItemIds)) { qw.isNotNull("mc.item_id"); qw.in("mc.item_id", explicitItemIds); } else if (explicitItemIds != null && explicitItemIds.isEmpty()) { qw.apply("1 = 0"); return qw; } // 添加coilIds查询条件,支持逗号分隔的多个coilId查询 if (StringUtils.isNotBlank(bo.getCoilIds())) { String[] coilIdArray = bo.getCoilIds().split(","); List coilIdList = new ArrayList<>(); for (String coilIdStr : coilIdArray) { if (StringUtils.isNotBlank(coilIdStr)) { try { coilIdList.add(Long.parseLong(coilIdStr.trim())); } catch (NumberFormatException e) { // 忽略无效的ID格式 } } } if (!coilIdList.isEmpty()) { qw.in("mc.coil_id", coilIdList); } } // 仅查询未发货且未绑定在发货计划里的钢卷 if (Boolean.TRUE.equals(bo.getOnlyUnshippedAndUnplanned())) { // 未发货:排除状态=1(已出库/已发货) qw.ne("mc.status", 1); // 未绑定在任一有效发货计划:计划未删除,coil 字段包含当前 coil_id // 这里使用 NOT EXISTS + FIND_IN_SET,避免将所有计划加载到内存 qw.apply("NOT EXISTS (SELECT 1 FROM wms_delivery_plan dp " + "WHERE dp.del_flag = 0 AND dp.coil IS NOT NULL AND dp.coil <> '' " + "AND FIND_IN_SET(CAST(mc.coil_id AS CHAR), dp.coil))"); } //逻辑删除 qw.eq("mc.del_flag", 0); //把team字段作为筛选条件 qw.eq(StringUtils.isNotBlank(bo.getTeam()), "mc.team", bo.getTeam()); //根据开始时间和结束时间筛选修改时间 qw.ge(bo.getStartTime() != null, "mc.update_time", bo.getStartTime()); qw.le(bo.getEndTime() != null, "mc.update_time", bo.getEndTime()); qw.ge(bo.getByCreateTimeStart() != null, "mc.create_time", bo.getByCreateTimeStart()); qw.le(bo.getByCreateTimeEnd() != null, "mc.create_time", bo.getByCreateTimeEnd()); // 根据发货开始和结束筛选发货时间 qw.ge(bo.getByExportTimeStart() != null, "mc.export_time", bo.getByExportTimeStart()); qw.le(bo.getByExportTimeEnd() != null, "mc.export_time", bo.getByExportTimeEnd()); //根据异常数量筛选(大于等于指定值) if (bo.getMinAbnormalCount() != null) { qw.apply("COALESCE(ca.abnormal_count, 0) >= {0}", bo.getMinAbnormalCount()); } //根据创建时间倒叙 qw.orderByDesc("mc.create_time"); return qw; } /** * 构建 OR 连接的 LIKE 子句,使用 MyBatis-Plus apply 的 {index} 占位符并将参数加入 args。 * 例如:column = "p.product_name", values = "A,B" -> 返回 "(p.product_name LIKE {0} OR p.product_name LIKE {1})" * 同时往 args 追加 "%A%", "%B%"。 */ private String buildOrLikeClause(String column, String csvValues, List args) { if (StringUtils.isBlank(csvValues)) { return ""; } String[] vals = csvValues.split(","); List parts = new ArrayList<>(); for (String raw : vals) { if (raw == null) continue; String v = raw.trim(); if (v.isEmpty()) continue; int idx = args.size(); parts.add(column + " LIKE {" + idx + "}"); args.add('%' + v + '%'); } if (parts.isEmpty()) { return ""; } return '(' + String.join(" OR ", parts) + ')'; } /** * 查询钢卷物料表列表 */ @Override public List queryList(WmsMaterialCoilBo bo) { QueryWrapper lqw = buildQueryWrapperPlus(bo); List list = baseMapper.selectVoListWithDynamicJoin(lqw); // 从联查结果中构建产品和原材料对象(避免单独查询) for (WmsMaterialCoilVo vo : list) { buildItemObjectFromJoin(vo); } return list; } /** * 从联查结果中构建物品对象(产品或原材料) * 直接从VO的临时字段中获取数据构建对象,避免单独查询数据库 */ private void buildItemObjectFromJoin(WmsMaterialCoilVo vo) { if (vo.getItemId() == null || vo.getItemType() == null) { return; } // 构建原材料对象 if ("raw_material".equals(vo.getItemType())) { WmsRawMaterialVo rawMaterial = new WmsRawMaterialVo(); rawMaterial.setRawMaterialId(vo.getItemId()); rawMaterial.setRawMaterialCode(vo.getItemCode()); rawMaterial.setRawMaterialName(vo.getItemName()); rawMaterial.setSpecification(vo.getSpecification()); rawMaterial.setMaterial(vo.getMaterial()); rawMaterial.setManufacturer(vo.getManufacturer()); rawMaterial.setSurfaceTreatmentDesc(vo.getSurfaceTreatmentDesc()); rawMaterial.setZincLayer(vo.getZincLayer()); vo.setRawMaterial(rawMaterial); } // 构建产品对象 if ("product".equals(vo.getItemType())) { WmsProductVo product = new WmsProductVo(); product.setProductId(vo.getItemId()); product.setProductCode(vo.getItemCode()); product.setProductName(vo.getItemName()); product.setSpecification(vo.getSpecification()); product.setMaterial(vo.getMaterial()); product.setManufacturer(vo.getManufacturer()); product.setSurfaceTreatmentDesc(vo.getSurfaceTreatmentDesc()); product.setZincLayer(vo.getZincLayer()); vo.setProduct(product); } } /** * 新增钢卷物料表 */ @Override @Transactional(rollbackFor = Exception.class) public WmsMaterialCoilVo insertByBo(WmsMaterialCoilBo bo) { // 1. 生成二维码 Long qrcodeRecordId = generateQrcodeForInsert(bo); bo.setQrcodeRecordId(qrcodeRecordId); // 2. 查找或创建stock findOrCreateStock(bo); // 3. 插入钢卷数据 WmsMaterialCoil add = BeanUtil.toBean(bo, WmsMaterialCoil.class); if(bo.getDataType() != null && bo.getDataType() == 10){ add.setDataType(10); }else { add.setDataType(1); // 新增的钢卷默认为当前数据 } validEntityBeforeSave(add); int rows = baseMapper.insert(add); if (rows <= 0) { throw new RuntimeException("新增钢卷失败"); } // 设置返回用的ID并更新二维码内容中的coilId bo.setCoilId(add.getCoilId()); updateQrcodeCoilId(qrcodeRecordId, add.getCoilId()); // 如果提供了actualWarehouseId,则更新对应的实际库区为禁用状态 if (bo.getActualWarehouseId() != null) { WmsActualWarehouseBo actualWarehouseBo = new WmsActualWarehouseBo(); actualWarehouseBo.setActualWarehouseId(bo.getActualWarehouseId()); actualWarehouseBo.setIsEnabled(0); // 设置为禁用状态 actualWarehouseService.updateByBo(actualWarehouseBo); } // 4. 返回完整的 VO(包含关联对象) return queryById(add.getCoilId()); } /** * 生成二维码(新增) */ private Long generateQrcodeForInsert(WmsMaterialCoilBo bo) { try { Map contentMap = new HashMap<>(); String currentCoilNo = bo.getCurrentCoilNo() != null ? bo.getCurrentCoilNo() : bo.getEnterCoilNo(); contentMap.put("enter_coil_no", bo.getEnterCoilNo()); // 入场钢卷号(唯一不变) contentMap.put("current_coil_no", currentCoilNo); // 当前钢卷号(可变) contentMap.put("coil_id", "null"); // 初始钢卷ID(新增时暂时为null,插入后更新) contentMap.put("current_coil_id", "null"); // 当前有效的钢卷ID(新增时暂时为null,插入后更新) // 创建steps数组 List> steps = new ArrayList<>(); Map step1 = new HashMap<>(); step1.put("step", 1); step1.put("action", "新增"); step1.put("current_coil_no", currentCoilNo); // 判断是合卷还是分卷 if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) { // 合卷:父编号字符串用逗号分隔 step1.put("operation", "合卷"); step1.put("parent_coil_nos", bo.getParentCoilNos()); } else if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 1) { // 分卷:多个当前钢卷号用逗号分隔 step1.put("operation", "分卷"); step1.put("current_coil_nos", currentCoilNo); } else { // 默认:当前钢卷号 step1.put("operation", "新增"); } steps.add(step1); contentMap.put("steps", steps); ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(bo.getEnterCoilNo()); // 使用入场钢卷号作为编号 recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); recordBo.setStatus(1); // 1=当前有效码 WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成二维码失败: " + e.getMessage()); } } /** * 查找或创建stock */ private void findOrCreateStock(WmsMaterialCoilBo bo) { if (bo.getItemType() == null || bo.getItemId() == null) { throw new RuntimeException("物品类型和物品ID不能为空"); } // 查询是否存在相同的stock(匹配itemType和itemId) WmsStockBo stockBo = new WmsStockBo(); stockBo.setItemType(bo.getItemType()); stockBo.setItemId(bo.getItemId()); List stockList = stockService.queryList(stockBo); if (stockList.isEmpty()) { // 如果没有找到匹配的stock,新增一条stock记录 WmsStockBo newStockBo = new WmsStockBo(); newStockBo.setItemType(bo.getItemType()); newStockBo.setItemId(bo.getItemId()); // 调用stockService新增stock Boolean insertResult = stockService.insertByBo(newStockBo); if (!insertResult) { throw new RuntimeException("新增库存记录失败"); } } // 如果已存在stock记录,则不需要重复创建 } /** * 修改钢卷物料表 * 如果newCoils不为空,则进行批量更新(分卷/合卷) * 如果newCoils为空,则进行单个更新 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean updateByBo(WmsMaterialCoilBo bo) { // 判断是否批量更新 if (bo.getNewCoils() != null && !bo.getNewCoils().isEmpty()) { // 批量更新逻辑(分卷/合卷) return updateByBatch(bo); } else { // 单个更新逻辑,需要coilId if (bo.getCoilId() == null) { throw new RuntimeException("钢卷ID不能为空"); } return updateBySingle(bo); } } /** * 简单更新钢卷物料表 * 直接更新属性内容,不进行历史记录处理 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean updateSimple(WmsMaterialCoilBo bo) { if (bo.getCoilId() == null) { throw new RuntimeException("钢卷ID不能为空"); } // 查询原钢卷是否存在 WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId()); if (oldCoil == null) { throw new RuntimeException("钢卷不存在"); } // 直接更新钢卷属性 WmsMaterialCoil updateCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); validEntityBeforeSave(updateCoil); // 使用MyBatis-Plus的updateById方法直接更新 boolean flag = baseMapper.updateById(updateCoil) > 0; // 特殊处理saleId字段,确保null值也能被正确更新 if (bo.getSaleId() == null) { LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId()); updateWrapper.set(WmsMaterialCoil::getSaleId, (Long)null); baseMapper.update(null, updateWrapper); } if (bo.getStatus() != null && bo.getStatus().equals(1)) { WmsActualWarehouseBo disableWarehouseBo = new WmsActualWarehouseBo(); disableWarehouseBo.setActualWarehouseId(oldCoil.getActualWarehouseId()); disableWarehouseBo.setIsEnabled(1); // 设置为启用状态 actualWarehouseService.updateByBo(disableWarehouseBo); } // 更新实际库区的启用状态 if (flag) { // 只有当新的库区ID不为空且与原库区ID不同时才更新库区状态 if (bo.getActualWarehouseId() != null && !bo.getActualWarehouseId().equals(oldCoil.getActualWarehouseId())) { updateActualWarehouseEnableStatus(oldCoil.getActualWarehouseId(), bo.getActualWarehouseId()); } } return flag; } /** * 单个更新 */ private Boolean updateBySingle(WmsMaterialCoilBo bo) { // 查询原钢卷 WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId()); if (oldCoil == null) { throw new RuntimeException("原钢卷不存在"); } // 1. 将原钢卷标记为历史数据(dataType = 0) LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(WmsMaterialCoil::getCoilId, oldCoil.getCoilId()) .set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据 baseMapper.update(null, updateWrapper); // 2. 创建新记录 WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); newCoil.setCoilId(null); // 清空ID,让数据库自动生成新ID newCoil.setDataType(1); // 设置为当前数据 newCoil.setQrcodeRecordId(oldCoil.getQrcodeRecordId()); // 继续使用原二维码 // 继承原记录的关键字段 if (newCoil.getEnterCoilNo() == null) { newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); } if (newCoil.getSupplierCoilNo() == null) { newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); } validEntityBeforeSave(newCoil); // 插入新记录 boolean flag = baseMapper.insert(newCoil) > 0; if (flag) { // 3. 更新二维码内容(添加更新步骤并更新current_coil_id) if (oldCoil.getQrcodeRecordId() != null) { updateQrcodeContentForNormalUpdate(oldCoil, bo, newCoil.getCoilId()); } // 只有当新的库区ID不为空且与原库区ID不同时才更新库区状态 if (bo.getActualWarehouseId() != null && !bo.getActualWarehouseId().equals(oldCoil.getActualWarehouseId())) { updateActualWarehouseEnableStatus(oldCoil.getActualWarehouseId(), bo.getActualWarehouseId()); } } return flag; } /** * 更新实际库区的启用状态 * @param oldActualWarehouseId 原来的实际库区ID * @param newActualWarehouseId 新的实际库区ID */ private void updateActualWarehouseEnableStatus(Long oldActualWarehouseId, Long newActualWarehouseId) { // 如果实际库区没有变化,则无需更新 if (Objects.equals(oldActualWarehouseId, newActualWarehouseId)) { return; } // 启用原来的库区 if (oldActualWarehouseId != null) { WmsActualWarehouseBo oldWarehouseBo = new WmsActualWarehouseBo(); oldWarehouseBo.setActualWarehouseId(oldActualWarehouseId); oldWarehouseBo.setIsEnabled(1); // 设置为启用状态 actualWarehouseService.updateByBo(oldWarehouseBo); } // 禁用新的库区 if (newActualWarehouseId != null) { WmsActualWarehouseBo newWarehouseBo = new WmsActualWarehouseBo(); newWarehouseBo.setActualWarehouseId(newActualWarehouseId); newWarehouseBo.setIsEnabled(0); // 设置为禁用状态 actualWarehouseService.updateByBo(newWarehouseBo); } } /** * 生成二维码(更新时库区变化) */ private Long generateQrcodeForUpdate(WmsMaterialCoil oldCoil, WmsMaterialCoilBo bo) { try { // 1. 将原二维码标记为历史码(status = 0) if (oldCoil.getQrcodeRecordId() != null) { WmsGenerateRecordBo oldQrBo = new WmsGenerateRecordBo(); oldQrBo.setRecordId(oldCoil.getQrcodeRecordId()); oldQrBo.setStatus(0); // 0=历史码 generateRecordService.updateByBo(oldQrBo); } Map contentMap = new HashMap<>(); String currentCoilNo = bo.getCurrentCoilNo() != null ? bo.getCurrentCoilNo() : oldCoil.getCurrentCoilNo(); contentMap.put("enter_coil_no", oldCoil.getEnterCoilNo()); // 入场钢卷号(始终不变) contentMap.put("current_coil_no", currentCoilNo); // 当前钢卷号 contentMap.put("coil_id", String.valueOf(oldCoil.getCoilId())); // 初始钢卷ID(记录最初的ID) contentMap.put("current_coil_id", "null"); // 当前钢卷ID(更新时暂时为null,插入后更新) // 复制原钢卷的历史steps List> steps = new ArrayList<>(); if (oldCoil.getQrcodeRecordId() != null) { WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId()); if (oldRecord != null) { ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") Map oldContentMap = objectMapper.readValue(oldRecord.getContent(), Map.class); @SuppressWarnings("unchecked") List> oldSteps = (List>) oldContentMap.get("steps"); if (oldSteps != null) { steps.addAll(oldSteps); } } } // 添加更新步骤(库区变化) Map updateStep = new HashMap<>(); updateStep.put("step", steps.size() + 1); updateStep.put("action", "更新"); updateStep.put("operation", "库区变更"); updateStep.put("old_warehouse_id", String.valueOf(oldCoil.getWarehouseId())); updateStep.put("new_warehouse_id", String.valueOf(bo.getWarehouseId())); //放入真实库区 updateStep.put("old_actual_warehouse_id", String.valueOf(oldCoil.getActualWarehouseId())); updateStep.put("new_actual_warehouse_id", String.valueOf(bo.getActualWarehouseId())); updateStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId())); updateStep.put("current_coil_no", currentCoilNo); updateStep.put("operator", LoginHelper.getUsername()); // 操作者 steps.add(updateStep); contentMap.put("steps", steps); ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); // 2. 生成新的二维码(status = 1) WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(oldCoil.getEnterCoilNo() + "-W" + bo.getWarehouseId()); // 使用入场钢卷号+库区ID作为编号 recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); recordBo.setStatus(1); // 1=当前有效码 WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成更新二维码失败: " + e.getMessage()); } } /** * 批量更新(分卷/合卷) */ private Boolean updateByBatch(WmsMaterialCoilBo bo) { // 查询原钢卷(分卷时需要,合卷时可能不需要) WmsMaterialCoil oldCoil = null; if (bo.getCoilId() != null) { oldCoil = baseMapper.selectById(bo.getCoilId()); if (oldCoil == null) { throw new RuntimeException("原钢卷不存在"); } } // 判断是分卷还是合卷 boolean isSplit = false; boolean isMerge = false; // 检查bo本身是否为合卷 if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) { isMerge = true; } else if (bo.getNewCoils() != null && !bo.getNewCoils().isEmpty()) { // 检查newCoils中是否有分卷 for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { if (newCoilBo.getHasMergeSplit() != null && newCoilBo.getHasMergeSplit() == 1) { isSplit = true; break; } } } // 1. 将原数据更新为历史数据(data_type=0) // 注意:合卷时bo的coilId可能为空,因为bo是合卷后的新钢卷 if (bo.getCoilId() != null) { LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId()) .set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据 baseMapper.update(null, updateWrapper); } // 2. 插入多条新的当前数据(data_type=1) List newCoils = new ArrayList<>(); List allNewCoilNos = new ArrayList<>(); // 收集所有新钢卷号 for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { allNewCoilNos.add(newCoilBo.getCurrentCoilNo()); } if (isSplit) { // 分卷:将bo作为被分卷的原始对象,newCoils中的对象作为分卷后产生的新钢卷 if (oldCoil == null) { throw new RuntimeException("分卷操作需要原钢卷信息"); } // 1. 将原始钢卷的二维码标记为失效(status=0) if (oldCoil.getQrcodeRecordId() != null) { WmsGenerateRecordBo oldQrBo = new WmsGenerateRecordBo(); oldQrBo.setRecordId(oldCoil.getQrcodeRecordId()); oldQrBo.setStatus(0); // 0=失效 generateRecordService.updateByBo(oldQrBo); } // 2. 将原始钢卷标记为历史数据,并记录所有子卷号 // 在母卷的 parent_coil_nos 字段中记录所有子卷号(用逗号分隔) String childCoilNos = String.join(",", allNewCoilNos); LambdaUpdateWrapper motherUpdateWrapper = new LambdaUpdateWrapper<>(); motherUpdateWrapper.eq(WmsMaterialCoil::getCoilId, oldCoil.getCoilId()) .set(WmsMaterialCoil::getParentCoilNos, childCoilNos); // 记录子卷号 baseMapper.update(null, motherUpdateWrapper); // 3. 为每个分卷后的子钢卷生成独立的二维码并插入数据库 for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { WmsMaterialCoil newCoil = BeanUtil.toBean(newCoilBo, WmsMaterialCoil.class); newCoil.setCoilId(null); newCoil.setDataType(1); // 继承原钢卷的基本信息(强制继承,不能修改的字段) newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); // 保留厂家原料卷号 // materialType, itemType 和 itemId 使用前端传递的值,不强制继承 if (newCoil.getItemType() == null) { newCoil.setItemType(oldCoil.getItemType()); } if (newCoil.getItemId() == null) { newCoil.setItemId(oldCoil.getItemId()); } // 如果前端没传team,使用原钢卷的team if (newCoil.getTeam() == null) { newCoil.setTeam(oldCoil.getTeam()); } // 如果没有指定库区,使用原库区 if (newCoil.getWarehouseId() == null) { newCoil.setWarehouseId(oldCoil.getWarehouseId()); } if (newCoil.getActualWarehouseId() == null){ newCoil.setActualWarehouseId(oldCoil.getActualWarehouseId()); } // 在子卷的 parent_coil_nos 字段中记录母卷号 newCoil.setParentCoilNos(oldCoil.getCurrentCoilNo()); // 为每个子钢卷生成独立二维码 Long newQrcodeId = generateQrcodeForSplit(oldCoil, newCoilBo, allNewCoilNos); newCoil.setQrcodeRecordId(newQrcodeId); validEntityBeforeSave(newCoil); baseMapper.insert(newCoil); newCoils.add(newCoil); // 更新二维码内容中的coilId updateQrcodeCoilId(newQrcodeId, newCoil.getCoilId()); // 更新实际库区的启用状态 updateActualWarehouseEnableStatus(oldCoil.getActualWarehouseId(), newCoilBo.getActualWarehouseId()); } } else if (isMerge) { // 合卷:将bo作为合卷后的新钢卷,newCoils中的对象作为参与合卷的原始钢卷 // 1. 将参与合卷的原始钢卷的二维码标记为失效,并将钢卷标记为历史数据 for (WmsMaterialCoilBo originalCoilBo : bo.getNewCoils()) { if (originalCoilBo.getCoilId() != null) { WmsMaterialCoil originalCoil = baseMapper.selectById(originalCoilBo.getCoilId()); if (originalCoil != null) { // 标记二维码为失效 if (originalCoil.getQrcodeRecordId() != null) { WmsGenerateRecordBo oldQrBo = new WmsGenerateRecordBo(); oldQrBo.setRecordId(originalCoil.getQrcodeRecordId()); oldQrBo.setStatus(0); // 0=失效 generateRecordService.updateByBo(oldQrBo); } // 标记钢卷为历史数据 LambdaUpdateWrapper originalUpdateWrapper = new LambdaUpdateWrapper<>(); originalUpdateWrapper.eq(WmsMaterialCoil::getCoilId, originalCoilBo.getCoilId()) .set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据 baseMapper.update(null, originalUpdateWrapper); // 启用原始钢卷的实际库区 if (originalCoil.getActualWarehouseId() != null) { WmsActualWarehouseBo disableWarehouseBo = new WmsActualWarehouseBo(); disableWarehouseBo.setActualWarehouseId(originalCoil.getActualWarehouseId()); disableWarehouseBo.setIsEnabled(1); // 设置为启用状态 actualWarehouseService.updateByBo(disableWarehouseBo); } } } } // 2. 生成合卷后的新钢卷二维码 Long mergedQrcodeId = generateQrcodeForMerge(bo, bo.getNewCoils()); // 3. 插入合卷后的新钢卷 WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); newCoil.setCoilId(null); newCoil.setDataType(1); // 从第一个参与合卷的原始钢卷获取基本信息 if (!bo.getNewCoils().isEmpty()) { WmsMaterialCoil firstOriginalCoil = baseMapper.selectById(bo.getNewCoils().get(0).getCoilId()); if (firstOriginalCoil != null) { // 继承基本信息 if (newCoil.getEnterCoilNo() == null) { newCoil.setEnterCoilNo(firstOriginalCoil.getEnterCoilNo()); } if (newCoil.getSupplierCoilNo() == null) { newCoil.setSupplierCoilNo(firstOriginalCoil.getSupplierCoilNo()); // 保留厂家原料卷号 } if (newCoil.getItemType() == null) { newCoil.setItemType(firstOriginalCoil.getItemType()); } if (newCoil.getItemId() == null) { newCoil.setItemId(firstOriginalCoil.getItemId()); } if (newCoil.getTeam() == null) { newCoil.setTeam(firstOriginalCoil.getTeam()); } } } newCoil.setQrcodeRecordId(mergedQrcodeId); validEntityBeforeSave(newCoil); baseMapper.insert(newCoil); newCoils.add(newCoil); // 更新二维码内容中的coilId updateQrcodeCoilId(mergedQrcodeId, newCoil.getCoilId()); // 禁用新钢卷的实际库区 if (bo.getActualWarehouseId() != null) { WmsActualWarehouseBo enableWarehouseBo = new WmsActualWarehouseBo(); enableWarehouseBo.setActualWarehouseId(bo.getActualWarehouseId()); enableWarehouseBo.setIsEnabled(0); // 设置为禁用状态 actualWarehouseService.updateByBo(enableWarehouseBo); } } return true; } /** * 为分卷生成新二维码(每个子钢卷一个) */ private Long generateQrcodeForSplit(WmsMaterialCoil oldCoil, WmsMaterialCoilBo newCoilBo, List allNewCoilNos) { try { Map contentMap = new HashMap<>(); contentMap.put("enter_coil_no", oldCoil.getEnterCoilNo()); contentMap.put("current_coil_no", newCoilBo.getCurrentCoilNo()); contentMap.put("coil_id", String.valueOf(oldCoil.getCoilId())); // 初始钢卷ID(记录原钢卷的ID) contentMap.put("current_coil_id", "null"); // 当前钢卷ID(分卷时暂时为null,插入后更新) // 复制原钢卷的历史steps List> steps = new ArrayList<>(); if (oldCoil.getQrcodeRecordId() != null) { WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId()); if (oldRecord != null) { ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") Map oldContentMap = objectMapper.readValue(oldRecord.getContent(), Map.class); @SuppressWarnings("unchecked") List> oldSteps = (List>) oldContentMap.get("steps"); if (oldSteps != null) { steps.addAll(oldSteps); } } } // 添加分卷步骤 Map splitStep = new HashMap<>(); splitStep.put("step", steps.size() + 1); splitStep.put("action", "更新"); splitStep.put("operation", "分卷"); splitStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo()); splitStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId())); splitStep.put("new_current_coil_nos", String.join(",", allNewCoilNos)); splitStep.put("child_coils", allNewCoilNos); splitStep.put("operator", LoginHelper.getUsername()); // 操作者 steps.add(splitStep); contentMap.put("steps", steps); ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(oldCoil.getEnterCoilNo() + "-" + newCoilBo.getCurrentCoilNo()); recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); recordBo.setStatus(1); // 1=当前有效码 WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成分卷二维码失败: " + e.getMessage()); } } /** * 为合卷生成新二维码(合并多个父钢卷的二维码信息) */ private Long generateQrcodeForMerge(WmsMaterialCoilBo mergedCoilBo, List originalCoils) { try { if (mergedCoilBo == null) { throw new RuntimeException("合卷后的钢卷数据不能为空"); } Map contentMap = new HashMap<>(); // 获取enterCoilNo,优先使用mergedCoilBo的,如果没有则从原始钢卷中获取 String enterCoilNo = mergedCoilBo.getEnterCoilNo(); if (enterCoilNo == null && originalCoils != null && !originalCoils.isEmpty()) { WmsMaterialCoil firstOriginalCoil = baseMapper.selectById(originalCoils.get(0).getCoilId()); if (firstOriginalCoil != null) { enterCoilNo = firstOriginalCoil.getEnterCoilNo(); } } contentMap.put("enter_coil_no", enterCoilNo); contentMap.put("current_coil_no", mergedCoilBo.getCurrentCoilNo()); contentMap.put("coil_id", "null"); // 初始钢卷ID(合卷时为null) contentMap.put("current_coil_id", "null"); // 当前钢卷ID(合卷时暂时为null,插入后更新) // 合并所有参与合卷的原始钢卷的历史steps List> steps = new ArrayList<>(); // 从参与合卷的原始钢卷中获取二维码信息并合并 if (originalCoils != null && !originalCoils.isEmpty()) { for (WmsMaterialCoilBo originalCoilBo : originalCoils) { if (originalCoilBo.getCoilId() != null) { // 查询原始钢卷的二维码信息 WmsMaterialCoil originalCoil = baseMapper.selectById(originalCoilBo.getCoilId()); if (originalCoil != null && originalCoil.getQrcodeRecordId() != null) { WmsGenerateRecordVo originalQr = generateRecordService.queryById(originalCoil.getQrcodeRecordId()); if (originalQr != null) { ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") Map originalContentMap = objectMapper.readValue(originalQr.getContent(), Map.class); @SuppressWarnings("unchecked") List> originalSteps = (List>) originalContentMap.get("steps"); if (originalSteps != null) { steps.addAll(originalSteps); } } } } } } // 添加合卷步骤 Map mergeStep = new HashMap<>(); mergeStep.put("step", steps.size() + 1); mergeStep.put("action", "更新"); mergeStep.put("operation", "合卷"); // 收集参与合卷的原始钢卷号和ID List originalCoilNos = new ArrayList<>(); List originalCoilIds = new ArrayList<>(); if (originalCoils != null && !originalCoils.isEmpty()) { for (WmsMaterialCoilBo originalCoilBo : originalCoils) { if (originalCoilBo.getCurrentCoilNo() != null) { originalCoilNos.add(originalCoilBo.getCurrentCoilNo()); } if (originalCoilBo.getCoilId() != null) { originalCoilIds.add(originalCoilBo.getCoilId().toString()); } } } mergeStep.put("parent_coil_nos", String.join(",", originalCoilNos)); mergeStep.put("parent_coil_ids", String.join(",", originalCoilIds)); mergeStep.put("new_current_coil_no", mergedCoilBo.getCurrentCoilNo()); mergeStep.put("operator", LoginHelper.getUsername()); // 操作者 steps.add(mergeStep); contentMap.put("steps", steps); // 将父钢卷ID也存储到顶层,方便快速查询 contentMap.put("parent_coil_ids", String.join(",", originalCoilIds)); ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(enterCoilNo + "-" + mergedCoilBo.getCurrentCoilNo()); recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); recordBo.setStatus(1); // 1=当前有效码 WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成合卷二维码失败: " + e.getMessage()); } } /** * 更新二维码内容中的coilId */ private void updateQrcodeCoilId(Long qrcodeRecordId, Long coilId) { try { // 获取二维码记录 WmsGenerateRecordVo record = generateRecordService.queryById(qrcodeRecordId); if (record == null) { throw new RuntimeException("二维码记录不存在"); } // 解析现有content ObjectMapper objectMapper = new ObjectMapper(); Map contentMap = objectMapper.readValue(record.getContent(), Map.class); // 如果是第一次设置coilId(从"null"变为实际ID),则同时设置coil_id和current_coil_id if ("null".equals(contentMap.get("coil_id"))) { contentMap.put("coil_id", String.valueOf(coilId)); // 初始ID,不再改变 } // 始终更新current_coil_id为最新的钢卷ID contentMap.put("current_coil_id", String.valueOf(coilId)); // 更新二维码记录 String newContentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo(); updateBo.setRecordId(qrcodeRecordId); updateBo.setContent(newContentJson); generateRecordService.updateByBo(updateBo); } catch (Exception e) { throw new RuntimeException("更新二维码coilId失败: " + e.getMessage()); } } /** * 更新二维码内容(正常更新,添加step) * @param oldCoil 旧钢卷记录 * @param bo 更新数据 * @param newCoilId 新钢卷ID(如果创建了新记录) */ private void updateQrcodeContentForNormalUpdate(WmsMaterialCoil oldCoil, WmsMaterialCoilBo bo, Long newCoilId) { try { // 获取原二维码记录 WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId()); if (oldRecord == null) { throw new RuntimeException("二维码记录不存在"); } // 解析现有content ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") Map contentMap = objectMapper.readValue(oldRecord.getContent(), Map.class); // 获取现有steps @SuppressWarnings("unchecked") List> steps = (List>) contentMap.get("steps"); if (steps == null) { steps = new ArrayList<>(); } // 添加新的step,记录更新信息 Map newStep = new HashMap<>(); newStep.put("step", steps.size() + 1); newStep.put("action", "更新"); newStep.put("operation", "信息更新"); newStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo()); newStep.put("new_current_coil_no", bo.getCurrentCoilNo()); newStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId())); newStep.put("new_coil_id", String.valueOf(newCoilId)); newStep.put("operator", LoginHelper.getUsername()); // 记录具体的变更字段 List changedFields = new ArrayList<>(); if (bo.getCurrentCoilNo() != null && !bo.getCurrentCoilNo().equals(oldCoil.getCurrentCoilNo())) { changedFields.add("钢卷号: " + oldCoil.getCurrentCoilNo() + " → " + bo.getCurrentCoilNo()); } if (bo.getTeam() != null && !bo.getTeam().equals(oldCoil.getTeam())) { changedFields.add("班组: " + oldCoil.getTeam() + " → " + bo.getTeam()); } if (bo.getWarehouseId() != null && !bo.getWarehouseId().equals(oldCoil.getWarehouseId())) { changedFields.add("逻辑库区ID: " + oldCoil.getWarehouseId() + " → " + bo.getWarehouseId()); } if (bo.getActualWarehouseId() != null && !bo.getActualWarehouseId().equals(oldCoil.getActualWarehouseId())) { changedFields.add("真实库区ID: " + oldCoil.getActualWarehouseId() + " → " + bo.getActualWarehouseId()); } if (bo.getGrossWeight() != null && !bo.getGrossWeight().equals(oldCoil.getGrossWeight())) { changedFields.add("毛重: " + oldCoil.getGrossWeight() + " → " + bo.getGrossWeight()); } if (bo.getNetWeight() != null && !bo.getNetWeight().equals(oldCoil.getNetWeight())) { changedFields.add("净重: " + oldCoil.getNetWeight() + " → " + bo.getNetWeight()); } if (bo.getRemark() != null && !bo.getRemark().equals(oldCoil.getRemark())) { changedFields.add("备注: " + oldCoil.getRemark() + " → " + bo.getRemark()); } if (bo.getQualityStatus() != null && !bo.getQualityStatus().equals(oldCoil.getQualityStatus())) { changedFields.add("质量状态: " + oldCoil.getQualityStatus() + " → " + bo.getQualityStatus()); } if (bo.getTrimmingRequirement() != null && !bo.getTrimmingRequirement().equals(oldCoil.getTrimmingRequirement())) { changedFields.add("切边要求: " + oldCoil.getTrimmingRequirement() + " → " + bo.getTrimmingRequirement()); } if (bo.getPackingStatus() != null && !bo.getPackingStatus().equals(oldCoil.getPackingStatus())) { changedFields.add("打包状态: " + oldCoil.getPackingStatus() + " → " + bo.getPackingStatus()); } if (bo.getPackagingRequirement() != null && !bo.getPackagingRequirement().equals(oldCoil.getPackagingRequirement())) { changedFields.add("包装要求: " + oldCoil.getPackagingRequirement() + " → " + bo.getPackagingRequirement()); } newStep.put("changed_fields", String.join("; ", changedFields)); newStep.put("update_time", new java.util.Date()); steps.add(newStep); contentMap.put("steps", steps); // 更新当前钢卷号 if (bo.getCurrentCoilNo() != null) { contentMap.put("current_coil_no", bo.getCurrentCoilNo()); } // 更新 current_coil_id 为新记录的ID if (newCoilId != null) { contentMap.put("current_coil_id", String.valueOf(newCoilId)); } // 更新二维码记录 String newContentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo(); updateBo.setRecordId(oldCoil.getQrcodeRecordId()); updateBo.setContent(newContentJson); generateRecordService.updateByBo(updateBo); } catch (Exception e) { throw new RuntimeException("更新二维码失败: " + e.getMessage()); } } /** * 保存前的数据校验 */ private void validEntityBeforeSave(WmsMaterialCoil entity) { //TODO 做一些数据校验,如唯一约束 } /** * 批量删除钢卷物料表 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } // 获取要删除的钢卷记录 List coilList = baseMapper.selectBatchIds(ids); if (coilList != null && !coilList.isEmpty()) { // 遍历每个要删除的钢卷,释放其占用的实际库区 for (WmsMaterialCoil coil : coilList) { if (coil.getActualWarehouseId() != null) { WmsActualWarehouseBo actualWarehouseBo = new WmsActualWarehouseBo(); actualWarehouseBo.setActualWarehouseId(coil.getActualWarehouseId()); actualWarehouseBo.setIsEnabled(1); // 设置为启用状态,释放库区 actualWarehouseService.updateByBo(actualWarehouseBo); } } } return baseMapper.deleteBatchIds(ids) > 0; } /** * 钢卷溯源查询 * 根据入场钢卷号查询二维码,解析content中的steps,然后根据steps中的钢卷号反向查询数据库 * * @param enterCoilNo 入场钢卷号 * @param currentCoilNo 当前钢卷号(可选,用于查询特定子钢卷) * @return 溯源结果(包含二维码信息和数据库记录) */ @Override public Map queryTrace(String enterCoilNo, String currentCoilNo) { try { // 优化1: 使用LIKE查询替代查询所有记录,大幅提升性能 List allQrRecords = new ArrayList<>(); // 首先查询主二维码(以入场钢卷号为序列号的) WmsGenerateRecordBo qrBo = new WmsGenerateRecordBo(); qrBo.setSerialNumber(enterCoilNo); List mainQrRecords = generateRecordService.queryList(qrBo); allQrRecords.addAll(mainQrRecords); // 优化:使用LIKE查询所有以该入场钢卷号开头的二维码(分卷后的二维码) // 而不是查询所有记录然后在内存中过滤 LambdaQueryWrapper splitWrapper = Wrappers.lambdaQuery(); splitWrapper.like(com.klp.domain.WmsGenerateRecord::getSerialNumber, enterCoilNo + "-"); List splitRecords = generateRecordMapper.selectVoList(splitWrapper); // 去重:使用recordId作为唯一标识 Set existingRecordIds = allQrRecords.stream() .map(WmsGenerateRecordVo::getRecordId) .collect(Collectors.toSet()); for (WmsGenerateRecordVo record : splitRecords) { if (record.getRecordId() != null && !existingRecordIds.contains(record.getRecordId())) { allQrRecords.add(record); existingRecordIds.add(record.getRecordId()); } } if (allQrRecords.isEmpty()) { throw new RuntimeException("未找到对应的二维码记录"); } // 优化2: ObjectMapper在循环外创建,避免重复创建 ObjectMapper objectMapper = new ObjectMapper(); // 2. 合并所有二维码的steps信息,去重并重新编号 Map> uniqueSteps = new HashMap<>(); // 用于去重 Set allCoilNos = new HashSet<>(); // 收集所有操作人用户名 Set operatorUsernames = new HashSet<>(); for (WmsGenerateRecordVo qrRecord : allQrRecords) { @SuppressWarnings("unchecked") Map contentMap = (Map) objectMapper.readValue(qrRecord.getContent(), Map.class); @SuppressWarnings("unchecked") List> steps = (List>) contentMap.get("steps"); if (steps != null) { for (Map step : steps) { // 创建唯一标识:操作类型 + 相关钢卷号 String stepKey = createStepKey(step); // 如果是新的步骤,添加到唯一步骤集合中 if (!uniqueSteps.containsKey(stepKey)) { Map uniqueStep = new HashMap<>(step); uniqueStep.put("qrcode_serial", qrRecord.getSerialNumber()); uniqueStep.put("qrcode_id", qrRecord.getRecordId()); uniqueSteps.put(stepKey, uniqueStep); // 收集操作人用户名 Object operator = step.get("operator"); if (operator != null) { operatorUsernames.add(operator.toString()); } } // 提取钢卷号 extractCoilNo(step, "current_coil_no", allCoilNos); extractCoilNo(step, "new_current_coil_no", allCoilNos); extractCoilNo(step, "old_current_coil_no", allCoilNos); extractCoilNo(step, "new_current_coil_nos", allCoilNos); extractCoilNo(step, "merged_from", allCoilNos); extractCoilNo(step, "parent_coil_nos", allCoilNos); extractCoilId(step, "coil_id", allCoilNos); extractCoilId(step, "old_coil_id", allCoilNos); extractCoilId(step, "parent_coil_ids", allCoilNos); } } } // 获取操作人昵称映射 Map operatorNicknameMap = getOperatorNicknames(operatorUsernames); // 转换为列表并按原始步骤号排序(保持时间顺序) List> allSteps = new ArrayList<>(uniqueSteps.values()); // 按原始步骤号排序,保持实际操作的时间顺序 allSteps.sort((a, b) -> { Integer stepA = (Integer) a.get("step"); Integer stepB = (Integer) b.get("step"); if (stepA == null) stepA = 0; if (stepB == null) stepB = 0; return stepA.compareTo(stepB); }); // 重新编号(保持连续性) for (int i = 0; i < allSteps.size(); i++) { allSteps.get(i).put("display_step", i + 1); // 保留原始步骤号用于调试 allSteps.get(i).put("original_step", allSteps.get(i).get("step")); // 替换操作人为昵称 Object operator = allSteps.get(i).get("operator"); if (operator != null) { String username = operator.toString(); String nickname = operatorNicknameMap.get(username); allSteps.get(i).put("operator_nickname", nickname != null ? nickname : username); } } // 3. 如果指定了当前钢卷号,过滤出相关的钢卷号 Set filteredCoilNos = allCoilNos; if (currentCoilNo != null && !currentCoilNo.trim().isEmpty()) { final String filterValue = currentCoilNo; filteredCoilNos = allCoilNos.stream() .filter(coilNo -> coilNo.contains(filterValue)) .collect(Collectors.toSet()); } // 优化3: 使用IN查询替代多个OR条件,提升查询性能 // 4. 根据提取的钢卷号反向查询数据库 List result = new ArrayList<>(); if (!filteredCoilNos.isEmpty()) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo); // 优化:使用IN查询替代多个OR条件(当钢卷号数量较少时) // 如果数量太多(>1000),分批查询避免SQL过长 List coilNoList = new ArrayList<>(filteredCoilNos); if (coilNoList.size() <= 1000) { lqw.in(WmsMaterialCoil::getCurrentCoilNo, coilNoList); } else { // 分批查询 int batchSize = 1000; for (int i = 0; i < coilNoList.size(); i += batchSize) { int end = Math.min(i + batchSize, coilNoList.size()); List batch = coilNoList.subList(i, end); LambdaQueryWrapper batchLqw = Wrappers.lambdaQuery(); batchLqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo); batchLqw.in(WmsMaterialCoil::getCurrentCoilNo, batch); batchLqw.orderByAsc(WmsMaterialCoil::getCreateTime); List batchResult = baseMapper.selectVoList(batchLqw); result.addAll(batchResult); } } if (coilNoList.size() <= 1000) { lqw.orderByAsc(WmsMaterialCoil::getCreateTime); result = baseMapper.selectVoList(lqw); } // 优化4: 批量填充关联对象,避免N+1查询 if (!result.isEmpty()) { fillRelatedObjectsBatch(result); } } // 如果没有找到记录,尝试查询所有相关的钢卷(包括历史数据) if (result.isEmpty()) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo); lqw.orderByAsc(WmsMaterialCoil::getCreateTime); result = baseMapper.selectVoList(lqw); // 优化:批量填充关联对象 if (!result.isEmpty()) { fillRelatedObjectsBatch(result); } } // 5. 构建返回结果 Map resultMap = new HashMap<>(); resultMap.put("qrcode", allQrRecords.get(0)); // 主二维码 resultMap.put("all_qrcodes", allQrRecords); // 所有相关二维码 resultMap.put("steps", allSteps); // 所有步骤 resultMap.put("records", result); // 所有钢卷记录 return resultMap; } catch (Exception e) { throw new RuntimeException("溯源查询失败: " + e.getMessage()); } } /** * 根据用户名获取用户昵称映射 * @param usernames 用户名集合 * @return 用户名到昵称的映射 */ private Map getOperatorNicknames(Set usernames) { Map nicknameMap = new HashMap<>(); if (usernames.isEmpty()) { return nicknameMap; } for (String username : usernames) { try { // 通过用户名查找用户 SysUser user = userService.selectUserByUserName(username); if (user != null) { nicknameMap.put(username, user.getNickName()); } else { nicknameMap.put(username, username); // 找不到则使用原用户名 } } catch (Exception e) { // 出现异常时使用原用户名 nicknameMap.put(username, username); } } return nicknameMap; } /** * 创建步骤唯一标识 */ private String createStepKey(Map step) { StringBuilder keyBuilder = new StringBuilder(); // 使用操作类型和主要标识符创建唯一key String operation = (String) step.get("operation"); keyBuilder.append(operation).append("-"); // 根据操作类型使用不同的标识符 if ("分卷".equals(operation)) { // 分卷:使用原钢卷号 + 分卷列表 keyBuilder.append(step.get("old_current_coil_no")).append("->"); keyBuilder.append(step.get("new_current_coil_nos")); } else if ("合卷".equals(operation)) { // 合卷:使用父钢卷列表 + 新钢卷号 keyBuilder.append(step.get("parent_coil_nos")).append("->"); keyBuilder.append(step.get("new_current_coil_no")); } else if ("新增".equals(operation)) { // 新增:使用当前钢卷号 keyBuilder.append(step.get("current_coil_no")); } else { // 其他更新:使用原钢卷号 -> 新钢卷号 keyBuilder.append(step.get("old_current_coil_no")).append("->"); keyBuilder.append(step.get("new_current_coil_no")); } return keyBuilder.toString(); } /** * 从step中提取钢卷号 */ private void extractCoilNo(Map step, String fieldName, Set coilNos) { Object value = step.get(fieldName); if (value != null) { String strValue = value.toString(); if (strValue.contains(",")) { // 如果是逗号分隔的多个钢卷号,分割后添加 String[] coilArray = strValue.split(","); for (String coilNo : coilArray) { coilNos.add(coilNo.trim()); } } else { coilNos.add(strValue.trim()); } } } /** * 从step中提取钢卷ID */ private void extractCoilId(Map step, String fieldName, Set coilNos) { Object value = step.get(fieldName); if (value != null) { String strValue = value.toString(); if (strValue.contains(",")) { // 如果是逗号分隔的多个钢卷ID,分割后添加 String[] coilArray = strValue.split(","); for (String coilId : coilArray) { coilNos.add(coilId.trim()); } } else { coilNos.add(strValue.trim()); } } } /** * 查询各个库区中不同类型的钢卷分布情况 * 按库区分组,统计每种物品类型和物品ID的钢卷数量和重量 * * @param itemType 物品类型(可选) * @param itemId 物品ID(可选) * @return 分布情况列表,包含库区信息、物品类型、物品ID、数量、重量等 */ @Override public List getDistributionByWarehouse(String itemType, Long itemId) { List> mapList = baseMapper.getDistributionByWarehouse(itemType, itemId); return convertMapListToVoList(mapList); } @Override public List getDistributionByActualWarehouse(String itemType, Long itemId) { List> mapList = baseMapper.getDistributionByActualWarehouse(itemType, itemId); return convertMapListToVoListActual(mapList); } private List convertMapListToVoListActual(List> mapList) { List voList = new ArrayList<>(); for (Map map : mapList) { WmsMaterialCoilVo vo = new WmsMaterialCoilVo(); vo.setActualWarehouseId(map.get("actual_warehouse_id") != null ? Long.valueOf(map.get("actual_warehouse_id").toString()) : null); vo.setActualWarehouseName(map.get("actual_warehouse_name") != null ? map.get("actual_warehouse_name").toString() : null); vo.setItemType(map.get("item_type") != null ? map.get("item_type").toString() : null); vo.setItemId(map.get("item_id") != null ? Long.valueOf(map.get("item_id").toString()) : null); vo.setCoilCount(map.get("coil_count") != null ? Long.valueOf(map.get("coil_count").toString()) : 0L); vo.setTotalGrossWeight(map.get("total_gross_weight") != null ? new BigDecimal(map.get("total_gross_weight").toString()) : BigDecimal.ZERO); vo.setTotalNetWeight(map.get("total_net_weight") != null ? new BigDecimal(map.get("total_net_weight").toString()) : BigDecimal.ZERO); setSubMaterialVo(vo, map, voList); } return voList; } /** * 查询不同类型的钢卷在不同库区的分布情况 * 按物品类型和物品ID分组,统计每个库区的钢卷数量和重量 * * @param itemType 物品类型(可选) * @param itemId 物品ID(可选) * @return 分布情况列表,包含物品类型、物品ID、库区信息、数量、重量等 */ @Override public List getDistributionByItemType(String itemType, Long itemId) { List> mapList = baseMapper.getDistributionByItemType(itemType, itemId); return convertMapListToVoList(mapList); } /** * 将Map列表转换为WmsMaterialCoilVo列表 */ private List convertMapListToVoList(List> mapList) { List voList = new ArrayList<>(); for (Map map : mapList) { WmsMaterialCoilVo vo = new WmsMaterialCoilVo(); vo.setWarehouseId(map.get("warehouse_id") != null ? Long.valueOf(map.get("warehouse_id").toString()) : null); vo.setWarehouseName(map.get("warehouse_name") != null ? map.get("warehouse_name").toString() : null); vo.setItemType(map.get("item_type") != null ? map.get("item_type").toString() : null); vo.setItemId(map.get("item_id") != null ? Long.valueOf(map.get("item_id").toString()) : null); vo.setCoilCount(map.get("coil_count") != null ? Long.valueOf(map.get("coil_count").toString()) : 0L); vo.setTotalGrossWeight(map.get("total_gross_weight") != null ? new BigDecimal(map.get("total_gross_weight").toString()) : BigDecimal.ZERO); vo.setTotalNetWeight(map.get("total_net_weight") != null ? new BigDecimal(map.get("total_net_weight").toString()) : BigDecimal.ZERO); setSubMaterialVo(vo, map, voList); } return voList; } private void setSubMaterialVo(WmsMaterialCoilVo vo, Map map, List voList) { String itemType = vo.getItemType(); if (itemType == null) { voList.add(vo); return; } if ("raw_material".equals(itemType)) { WmsRawMaterialVo rawMaterial = new WmsRawMaterialVo(); rawMaterial.setRawMaterialName(map.get("itemName") != null ? map.get("itemName").toString() : null); rawMaterial.setRawMaterialCode(map.get("itemCode") != null ? map.get("itemCode").toString() : null); rawMaterial.setSpecification(map.get("specification") != null ? map.get("specification").toString() : null); rawMaterial.setMaterial(map.get("material") != null ? map.get("material").toString() : null); rawMaterial.setSurfaceTreatmentDesc(map.get("surfaceTreatmentDesc") != null ? map.get("surfaceTreatmentDesc").toString() : null); rawMaterial.setZincLayer(map.get("zincLayer") != null ? map.get("zincLayer").toString() : null); rawMaterial.setManufacturer(map.get("manufacturer") != null ? map.get("manufacturer").toString() : null); vo.setRawMaterial(rawMaterial); } else if ("product".equals(itemType)) { WmsProductVo product = new WmsProductVo(); product.setProductName(map.get("itemName") != null ? map.get("itemName").toString() : null); product.setProductCode(map.get("itemCode") != null ? map.get("itemCode").toString() : null); product.setSpecification(map.get("specification") != null ? map.get("specification").toString() : null); product.setMaterial(map.get("material") != null ? map.get("material").toString() : null); product.setSurfaceTreatmentDesc(map.get("surfaceTreatmentDesc") != null ? map.get("surfaceTreatmentDesc").toString() : null); product.setZincLayer(map.get("zincLayer") != null ? map.get("zincLayer").toString() : null); product.setManufacturer(map.get("manufacturer") != null ? map.get("manufacturer").toString() : null); vo.setProduct(product); } voList.add(vo); } @Override public List getDistributionByActualItemType(String itemType, Long itemId) { List> mapList = baseMapper.getDistributionByActualItemType(itemType, itemId); return convertMapListToVoListActual(mapList); } /** * 查询钢卷导出数据列表 */ @Override public List queryExportList(WmsMaterialCoilBo bo) { QueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectExportList(lqw); } /** * 钢卷发货 * @param coilId * @return */ @Override public int exportCoil(Long coilId) { WmsMaterialCoilVo wmsMaterialCoilVo = queryById(coilId); wmsMaterialCoilVo.setExportTime(new Date()); wmsMaterialCoilVo.setStatus(1); return baseMapper.updateById(BeanUtil.toBean(wmsMaterialCoilVo,WmsMaterialCoil.class)); } /** * 钢卷发货撤回 * @param coilId * @return */ @Override @Transactional public int withdrawExportCoil(Long coilId) { // 参数校验 if (coilId == null) { throw new IllegalArgumentException("钢卷ID不能为空"); } WmsMaterialCoilVo wmsMaterialCoilVo = queryById(coilId); if (wmsMaterialCoilVo == null) { throw new RuntimeException("钢卷不存在"); } // 检查当前状态是否为已发货状态 if (wmsMaterialCoilVo.getStatus() != 1) { throw new RuntimeException("该钢卷并没有发货,无法撤回"); } // 调用自定义的Mapper方法,只更新发货时间和状态字段 return baseMapper.updateForWithdrawExport(coilId, 0); } /** * 构建查询条件 */ private QueryWrapper buildQueryWrapper(WmsMaterialCoilBo bo) { QueryWrapper lqw = Wrappers.query(); // 基础字段筛选 lqw.eq(bo.getCoilId() != null, "mc.coil_id", bo.getCoilId()); lqw.eq(StringUtils.isNotBlank(bo.getEnterCoilNo()), "mc.enter_coil_no", bo.getEnterCoilNo()); lqw.like(StringUtils.isNotBlank(bo.getCurrentCoilNo()), "mc.current_coil_no", bo.getCurrentCoilNo()); lqw.like(StringUtils.isNotBlank(bo.getSupplierCoilNo()), "mc.supplier_coil_no", bo.getSupplierCoilNo()); lqw.eq(bo.getDataType() != null, "mc.data_type", bo.getDataType()); lqw.eq(bo.getWarehouseId() != null, "mc.warehouse_id", bo.getWarehouseId()); lqw.eq(bo.getActualWarehouseId() != null, "mc.actual_warehouse_id", bo.getActualWarehouseId()); lqw.eq(StringUtils.isNotBlank(bo.getItemType()), "mc.item_type", bo.getItemType()); lqw.eq(bo.getHasMergeSplit() != null, "mc.has_merge_split", bo.getHasMergeSplit()); // 修改itemId筛选逻辑,支持逗号分隔的多个ID查询 if (StringUtils.isNotBlank(bo.getItemIds())) { String[] itemIdArray = bo.getItemIds().split(","); List itemIdList = new ArrayList<>(); for (String itemIdStr : itemIdArray) { if (StringUtils.isNotBlank(itemIdStr)) { try { itemIdList.add(Long.parseLong(itemIdStr.trim())); } catch (NumberFormatException e) { // 忽略无效的ID格式 } } } if (!itemIdList.isEmpty()) { lqw.in("mc.item_id", itemIdList); } } else if (bo.getItemId() != null) { // 兼容原来的itemId单值查询 lqw.eq("mc.item_id", bo.getItemId()); } // 添加coilIds查询条件,支持逗号分隔的多个coilId查询 if (StringUtils.isNotBlank(bo.getCoilIds())) { String[] coilIdArray = bo.getCoilIds().split(","); List coilIdList = new ArrayList<>(); for (String coilIdStr : coilIdArray) { if (StringUtils.isNotBlank(coilIdStr)) { try { coilIdList.add(Long.parseLong(coilIdStr.trim())); } catch (NumberFormatException e) { // 忽略无效的ID格式 } } } if (!coilIdList.isEmpty()) { lqw.in("mc.coil_id", coilIdList); } } lqw.eq(bo.getStatus() != null, "mc.status", bo.getStatus()); lqw.eq(StringUtils.isNotBlank(bo.getMaterialType()), "mc.material_type", bo.getMaterialType()); lqw.eq(StringUtils.isNotBlank(bo.getQualityStatus()), "mc.quality_status", bo.getQualityStatus()); lqw.eq(StringUtils.isNotBlank(bo.getPackingStatus()), "mc.packing_status", bo.getPackingStatus()); // 把team字段作为筛选条件 lqw.eq(StringUtils.isNotBlank(bo.getTeam()), "mc.team", bo.getTeam()); // 根据开始时间和结束时间筛选修改时间 lqw.ge(bo.getStartTime() != null, "mc.update_time", bo.getStartTime()); lqw.le(bo.getEndTime() != null, "mc.update_time", bo.getEndTime()); // 根据发货开始和结束筛选发货时间 lqw.ge(bo.getByExportTimeStart() != null, "mc.export_time", bo.getByExportTimeStart()); lqw.le(bo.getByExportTimeEnd() != null, "mc.export_time", bo.getByExportTimeEnd()); // 逻辑删除 lqw.eq("mc.del_flag", 0); // 根据创建时间倒序 lqw.orderByDesc("mc.create_time"); return lqw; } /** * 批量更新钢卷发货状态 * * @param coilIds 钢卷ID列表 * @param status 目标状态 (0=在库, 1=在途, 2=已出库) * @return 是否更新成功 */ @Override public Boolean batchUpdateDeliveryStatus(List coilIds, Integer status) { if (coilIds == null || coilIds.isEmpty()) { return false; } if (status == null || status < 0 || status > 2) { throw new RuntimeException("无效的状态值,状态必须在0-2之间"); } // 构造更新条件 LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.in(WmsMaterialCoil::getCoilId, coilIds); updateWrapper.set(WmsMaterialCoil::getStatus, status); // 如果是设置为已出库状态,同时更新出库时间 if (status == 1) { updateWrapper.set(WmsMaterialCoil::getExportTime, new Date()); } return baseMapper.update(null, updateWrapper) > 0; } /** * 根据更新前的钢卷ID列表获取对应的直接下一步钢卷ID * 分卷场景返回所有子卷ID(逗号分隔),合卷/普通更新返回单个ID * * @param oldCoilIds 更新前的钢卷ID列表 * @return Map<旧钢卷ID, 下一步钢卷ID字符串>: * - 普通更新/合卷:value为单个ID字符串(如"1002") * - 分卷:value为逗号分隔的子卷ID字符串(如"1003,1004,1005") * - 无下一步:value为null */ public Map getUpdatedCoilIdsByOldCoilIds(List oldCoilIds) { // 初始化返回的Map,key=旧ID,value=下一步ID字符串(逗号分隔) Map old2NextCoilIdMap = new LinkedHashMap<>(); if (oldCoilIds == null || oldCoilIds.isEmpty()) { return old2NextCoilIdMap; } // 先将所有旧ID放入Map,默认value为null for (Long oldCoilId : oldCoilIds) { old2NextCoilIdMap.put(oldCoilId, null); } ObjectMapper objectMapper = new ObjectMapper(); // 遍历每个旧ID,解析对应的下一步ID for (Long oldCoilId : oldCoilIds) { String nextCoilIdsStr = null; // 存储最终返回的ID字符串(单个/逗号分隔) try { // 1. 查询旧钢卷记录 WmsMaterialCoil oldCoil = baseMapper.selectById(oldCoilId); if (oldCoil == null || oldCoil.getQrcodeRecordId() == null) { old2NextCoilIdMap.put(oldCoilId, null); continue; } // 2. 查询关联的二维码记录 LambdaQueryWrapper qrWrapper = Wrappers.lambdaQuery(); qrWrapper.eq(WmsGenerateRecord::getSerialNumber, oldCoil.getEnterCoilNo()) .or() .like(WmsGenerateRecord::getSerialNumber, oldCoil.getEnterCoilNo() + "-"); List qrRecords = generateRecordMapper.selectVoList(qrWrapper); if (qrRecords.isEmpty()) { old2NextCoilIdMap.put(oldCoilId, null); continue; } // 3. 解析每个二维码的步骤 for (WmsGenerateRecordVo qrRecord : qrRecords) { if (qrRecord.getContent() == null || qrRecord.getContent().trim().isEmpty()) { continue; } Map contentMap; try { contentMap = objectMapper.readValue(qrRecord.getContent(), Map.class); } catch (JsonProcessingException e) { log.warn("解析二维码记录[{}]的content失败: {}", qrRecord.getRecordId(), e.getMessage()); continue; } List> steps = (List>) contentMap.get("steps"); if (steps == null || steps.isEmpty()) { continue; } String oldCoilIdStr = oldCoilId.toString(); // 4. 遍历步骤找下一步ID for (Map step : steps) { String stepOldCoilIdStr = step.get("old_coil_id") != null ? step.get("old_coil_id").toString() : null; String newCoilIdStr = step.get("new_coil_id") != null ? step.get("new_coil_id").toString() : null; // 普通更新场景:单个ID if (stepOldCoilIdStr != null && stepOldCoilIdStr.equals(oldCoilIdStr) && newCoilIdStr != null && !"null".equals(newCoilIdStr)) { nextCoilIdsStr = newCoilIdStr; break; } // 合卷场景:单个ID String parentCoilIds = step.get("parent_coil_ids") != null ? step.get("parent_coil_ids").toString() : null; if (parentCoilIds != null && parentCoilIds.contains(oldCoilIdStr)) { String newCurrentCoilNo = step.get("new_current_coil_no") != null ? step.get("new_current_coil_no").toString() : null; if (newCurrentCoilNo != null) { LambdaQueryWrapper coilWrapper = Wrappers.lambdaQuery(); coilWrapper.eq(WmsMaterialCoil::getCurrentCoilNo, newCurrentCoilNo) .eq(WmsMaterialCoil::getDataType, 1); WmsMaterialCoil nextCoil = baseMapper.selectOne(coilWrapper); if (nextCoil != null) { nextCoilIdsStr = nextCoil.getCoilId().toString(); break; } } } // 分卷场景:所有子卷ID(逗号分隔) String operation = step.get("operation") != null ? step.get("operation").toString() : ""; if ("分卷".equals(operation) && oldCoilIdStr.equals(stepOldCoilIdStr)) { String newCurrentCoilNos = step.get("new_current_coil_nos") != null ? step.get("new_current_coil_nos").toString() : null; List childCoilNos = null; // 解析子卷号列表 if (newCurrentCoilNos != null && !newCurrentCoilNos.isEmpty()) { childCoilNos = Arrays.asList(newCurrentCoilNos.split(",")); } else if (step.get("child_coils") instanceof List) { childCoilNos = (List) step.get("child_coils"); } // 批量查询所有子卷ID并拼接为逗号分隔的字符串 if (childCoilNos != null && !childCoilNos.isEmpty()) { // 去重+trim,避免空字符串/重复卷号 List validChildCoilNos = childCoilNos.stream() .map(String::trim) .filter(no -> !no.isEmpty()) .distinct() .collect(Collectors.toList()); if (!validChildCoilNos.isEmpty()) { // 批量查询(性能优化,避免循环查询) LambdaQueryWrapper coilWrapper = Wrappers.lambdaQuery(); coilWrapper.in(WmsMaterialCoil::getCurrentCoilNo, validChildCoilNos) .eq(WmsMaterialCoil::getDataType, 1); List childCoils = baseMapper.selectList(coilWrapper); // 拼接子卷ID为逗号分隔的字符串 if (!childCoils.isEmpty()) { nextCoilIdsStr = childCoils.stream() .map(coil -> coil.getCoilId().toString()) .collect(Collectors.joining(",")); break; } } } } } if (nextCoilIdsStr != null) { break; // 找到结果,跳出二维码循环 } } // 5. 将结果放入Map old2NextCoilIdMap.put(oldCoilId, nextCoilIdsStr); } catch (Exception e) { log.error("解析钢卷ID[{}]的下一步ID失败", oldCoilId, e); old2NextCoilIdMap.put(oldCoilId, null); } } return old2NextCoilIdMap; } }