Files
klp-oa/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java

2173 lines
101 KiB
Java
Raw Normal View History

2025-10-28 12:20:20 +08:00
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;
2025-10-28 12:20:20 +08:00
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;
2025-10-29 11:17:52 +08:00
import com.klp.common.helper.LoginHelper;
2025-10-28 12:20:20 +08:00
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;
2025-10-28 12:20:20 +08:00
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
2025-10-28 12:20:20 +08:00
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;
2025-11-17 11:58:42 +08:00
import com.klp.mapper.WmsGenerateRecordMapper;
2025-10-28 12:20:20 +08:00
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;
2025-10-28 12:20:20 +08:00
import com.klp.service.IWmsRawMaterialService;
2025-10-29 11:17:52 +08:00
import com.klp.service.IWmsBomItemService;
2025-11-15 15:37:50 +08:00
import com.klp.service.IWmsProductService;
2025-10-28 12:20:20 +08:00
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
2025-10-28 12:20:20 +08:00
import java.util.stream.Collectors;
2025-10-29 14:13:06 +08:00
import java.math.BigDecimal;
2025-10-28 12:20:20 +08:00
/**
* 钢卷物料表Service业务层处理
*
* @author Joshi
* @date 2025-07-18
*/
@Slf4j
2025-10-28 12:20:20 +08:00
@RequiredArgsConstructor
@Service
public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
private final WmsMaterialCoilMapper baseMapper;
private final IWmsStockService stockService;
private final IWmsGenerateRecordService generateRecordService;
2025-11-17 11:58:42 +08:00
private final WmsGenerateRecordMapper generateRecordMapper;
2025-10-28 12:20:20 +08:00
private final IWmsWarehouseService warehouseService;
private final IWmsActualWarehouseService actualWarehouseService;
2025-10-28 12:20:20 +08:00
private final IWmsRawMaterialService rawMaterialService;
2025-10-29 11:17:52 +08:00
private final IWmsBomItemService bomItemService;
2025-11-15 15:37:50 +08:00
private final IWmsProductService productService;
private final ISysUserService userService;
private final WmsDeliveryPlanMapper deliveryPlanMapper;
private final WmsProductMapper productMapper;
private final WmsRawMaterialMapper rawMaterialMapper;
2025-10-28 12:20:20 +08:00
/**
* 查询钢卷物料表
*/
@Override
2025-10-29 15:50:46 +08:00
public WmsMaterialCoilVo queryById(Long coilId) {
2025-10-28 12:20:20 +08:00
WmsMaterialCoilVo vo = baseMapper.selectVoById(coilId);
if (vo == null) {
return null;
}
2025-10-28 12:20:20 +08:00
// 查询关联对象
fillRelatedObjects(vo);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
return vo;
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
2025-11-17 11:58:42 +08:00
* 批量填充关联对象信息优化版本避免N+1查询
*/
private void fillRelatedObjectsBatch(List<WmsMaterialCoilVo> voList) {
if (voList == null || voList.isEmpty()) {
return;
}
// 收集所有需要查询的ID
Set<Long> warehouseIds = new HashSet<>();
Set<Long> nextWarehouseIds = new HashSet<>();
Set<Long> actualWarehouseIds = new HashSet<>();
Set<Long> qrcodeRecordIds = new HashSet<>();
Set<Long> rawMaterialIds = new HashSet<>();
Set<Long> productIds = new HashSet<>();
Set<Long> 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<Long, WmsWarehouseVo> 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<Long, WmsActualWarehouseVo> actualWarehouseMap = new HashMap<>();
if (!actualWarehouseIds.isEmpty()) {
for (Long id : actualWarehouseIds) {
WmsActualWarehouseVo actualWarehouse = actualWarehouseService.queryById(id);
if (actualWarehouse != null) {
actualWarehouseMap.put(id, actualWarehouse);
}
}
}
// 批量查询二维码信息
Map<Long, WmsGenerateRecordVo> qrcodeMap = new HashMap<>();
if (!qrcodeRecordIds.isEmpty()) {
for (Long id : qrcodeRecordIds) {
WmsGenerateRecordVo qrcode = generateRecordService.queryById(id);
if (qrcode != null) {
qrcodeMap.put(id, qrcode);
}
}
}
// 批量查询原材料信息
Map<Long, WmsRawMaterialVo> 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<Long, WmsProductVo> 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<Long, List<WmsBomItemVo>> bomItemMap = new HashMap<>();
if (!bomIds.isEmpty()) {
for (Long bomId : bomIds) {
WmsBomItemBo bomItemBo = new WmsBomItemBo();
bomItemBo.setBomId(bomId);
List<WmsBomItemVo> 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()));
}
}
}
}
/**
* 填充关联对象信息单个对象保留用于兼容性
2025-10-28 12:20:20 +08:00
*/
private void fillRelatedObjects(WmsMaterialCoilVo vo) {
// 查询所在库区信息
if (vo.getWarehouseId() != null) {
WmsWarehouseVo warehouse = warehouseService.queryById(vo.getWarehouseId());
vo.setWarehouse(warehouse);
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 查询下一库区信息
if (vo.getNextWarehouseId() != null) {
WmsWarehouseVo nextWarehouse = warehouseService.queryById(vo.getNextWarehouseId());
vo.setNextWarehouse(nextWarehouse);
}
2025-10-29 11:17:52 +08:00
// 查询实际库区信息
if (vo.getActualWarehouseId() != null) {
WmsActualWarehouseVo actualWarehouse = actualWarehouseService.queryById(vo.getActualWarehouseId());
if (actualWarehouse != null) {
vo.setActualWarehouseName(actualWarehouse.getActualWarehouseName());
}
}
2025-10-28 12:20:20 +08:00
// 查询二维码信息
if (vo.getQrcodeRecordId() != null) {
WmsGenerateRecordVo qrcodeRecord = generateRecordService.queryById(vo.getQrcodeRecordId());
vo.setQrcodeRecord(qrcodeRecord);
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 查询原材料信息当itemType为raw_material时
if ("raw_material".equals(vo.getItemType()) && vo.getItemId() != null) {
WmsRawMaterialVo rawMaterial = rawMaterialService.queryById(vo.getItemId());
vo.setRawMaterial(rawMaterial);
2025-10-29 11:17:52 +08:00
// 查询原材料对应的BOM信息通过bomId查询BomItem列表
if (rawMaterial != null && rawMaterial.getBomId() != null) {
WmsBomItemBo bomItemBo = new WmsBomItemBo();
bomItemBo.setBomId(rawMaterial.getBomId());
List<WmsBomItemVo> bomItemList = bomItemService.queryList(bomItemBo);
vo.setBomItemList(bomItemList);
}
2025-10-28 12:20:20 +08:00
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 查询产品信息当itemType为product时
if ("product".equals(vo.getItemType()) && vo.getItemId() != null) {
2025-11-15 15:37:50 +08:00
WmsProductVo product = productService.queryById(vo.getItemId());
vo.setProduct(product);
2025-11-15 15:37:50 +08:00
// 查询产品对应的BOM信息通过bomId查询BomItem列表
if (product != null && product.getBomId() != null) {
2025-10-29 13:37:08 +08:00
WmsBomItemBo bomItemBo = new WmsBomItemBo();
2025-11-15 15:37:50 +08:00
bomItemBo.setBomId(product.getBomId());
2025-10-29 13:37:08 +08:00
List<WmsBomItemVo> bomItemList = bomItemService.queryList(bomItemBo);
vo.setBomItemList(bomItemList);
}
2025-10-28 12:20:20 +08:00
}
}
/**
* 查询钢卷物料表列表
*/
@Override
public TableDataInfo<WmsMaterialCoilVo> queryPageList(WmsMaterialCoilBo bo, PageQuery pageQuery) {
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
Page<WmsMaterialCoilVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
List<WmsMaterialCoilVo> records = result.getRecords();
if (records == null || records.isEmpty()) {
return TableDataInfo.build(result);
}
Set<String> userNames = records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getUpdateBy()))
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
Map<String, String> 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()));
}
}
// 从联查结果中构建产品和原材料对象(避免单独查询)
2025-11-17 11:58:42 +08:00
buildItemObjectFromJoin(vo);
2025-11-15 15:37:50 +08:00
}
2025-10-28 12:20:20 +08:00
return TableDataInfo.build(result);
}
private QueryWrapper<WmsMaterialCoil> buildQueryWrapperPlus(WmsMaterialCoilBo bo) {
QueryWrapper<WmsMaterialCoil> 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());
2025-11-11 14:57:51 +08:00
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<Long> explicitItemIds = null;
if (StringUtils.isNotBlank(bo.getItemIds())) {
String[] itemIdArray = bo.getItemIds().split(",");
List<Long> 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<Object> 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<Long> 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());
// 处理发货时间筛选逻辑(核心修改部分)
if (bo.getByExportTimeStart() != null || bo.getByExportTimeEnd() != null) {
// 开启OR条件分组满足情况1 或 情况2
qw.and(w -> {
// 情况1发货时间不为null且满足时间范围
w.nested(n -> {
n.isNotNull("mc.export_time");
if (bo.getByExportTimeStart() != null) {
n.ge("mc.export_time", bo.getByExportTimeStart());
}
if (bo.getByExportTimeEnd() != null) {
n.le("mc.export_time", bo.getByExportTimeEnd());
}
});
// 情况2状态为1且发货时间为null用更新时间匹配发货时间范围
w.or();
w.nested(n -> {
n.eq("mc.status", 1);
n.isNull("mc.export_time");
if (bo.getByExportTimeStart() != null) {
n.ge("mc.update_time", bo.getByExportTimeStart());
}
if (bo.getByExportTimeEnd() != null) {
n.le("mc.update_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<Object> args) {
if (StringUtils.isBlank(csvValues)) {
return "";
}
String[] vals = csvValues.split(",");
List<String> 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) + ')';
}
2025-10-28 12:20:20 +08:00
/**
* 查询钢卷物料表列表
*/
@Override
public List<WmsMaterialCoilVo> queryList(WmsMaterialCoilBo bo) {
QueryWrapper<WmsMaterialCoil> lqw = buildQueryWrapperPlus(bo);
2025-11-15 15:37:50 +08:00
List<WmsMaterialCoilVo> list = baseMapper.selectVoListWithDynamicJoin(lqw);
2025-11-17 11:58:42 +08:00
// 从联查结果中构建产品和原材料对象(避免单独查询)
2025-11-15 15:37:50 +08:00
for (WmsMaterialCoilVo vo : list) {
2025-11-17 11:58:42 +08:00
buildItemObjectFromJoin(vo);
2025-11-15 15:37:50 +08:00
}
2025-11-15 15:37:50 +08:00
return list;
}
2025-11-15 15:37:50 +08:00
/**
2025-11-17 11:58:42 +08:00
* 从联查结果中构建物品对象产品或原材料
* 直接从VO的临时字段中获取数据构建对象避免单独查询数据库
*/
private void buildItemObjectFromJoin(WmsMaterialCoilVo vo) {
if (vo.getItemId() == null || vo.getItemType() == null) {
return;
}
// 构建原材料对象
if ("raw_material".equals(vo.getItemType())) {
2025-11-17 11:58:42 +08:00
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());
2025-11-17 11:58:42 +08:00
vo.setRawMaterial(rawMaterial);
}
// 构建产品对象
2025-11-15 15:37:50 +08:00
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());
2025-11-15 15:37:50 +08:00
vo.setProduct(product);
}
2025-10-28 12:20:20 +08:00
}
/**
* 新增钢卷物料表
*/
@Override
@Transactional(rollbackFor = Exception.class)
public WmsMaterialCoilVo insertByBo(WmsMaterialCoilBo bo) {
2025-10-28 12:20:20 +08:00
// 1. 生成二维码
Long qrcodeRecordId = generateQrcodeForInsert(bo);
bo.setQrcodeRecordId(qrcodeRecordId);
2025-10-29 11:17:52 +08:00
2025-10-29 15:50:46 +08:00
// 2. 查找或创建stock
findOrCreateStock(bo);
2025-10-29 15:50:46 +08:00
2025-10-28 12:20:20 +08:00
// 3. 插入钢卷数据
WmsMaterialCoil add = BeanUtil.toBean(bo, WmsMaterialCoil.class);
if(bo.getDataType() != null && bo.getDataType() == 10){
add.setDataType(10);
}else {
add.setDataType(1); // 新增的钢卷默认为当前数据
}
2025-10-28 12:20:20 +08:00
validEntityBeforeSave(add);
2025-11-17 10:59:22 +08:00
int rows = baseMapper.insert(add);
if (rows <= 0) {
throw new RuntimeException("新增钢卷失败");
2025-10-28 12:20:20 +08:00
}
2025-11-17 10:59:22 +08:00
// 设置返回用的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());
2025-10-28 12:20:20 +08:00
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 生成二维码新增
*/
private Long generateQrcodeForInsert(WmsMaterialCoilBo bo) {
try {
Map<String, Object> contentMap = new HashMap<>();
String currentCoilNo = bo.getCurrentCoilNo() != null ? bo.getCurrentCoilNo() : bo.getEnterCoilNo();
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
contentMap.put("enter_coil_no", bo.getEnterCoilNo()); // 入场钢卷号(唯一不变)
contentMap.put("current_coil_no", currentCoilNo); // 当前钢卷号(可变)
2025-11-03 17:03:03 +08:00
contentMap.put("coil_id", "null"); // 初始钢卷ID新增时暂时为null插入后更新
contentMap.put("current_coil_id", "null"); // 当前有效的钢卷ID新增时暂时为null插入后更新
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 创建steps数组
List<Map<String, Object>> steps = new ArrayList<>();
Map<String, Object> step1 = new HashMap<>();
step1.put("step", 1);
step1.put("action", "新增");
step1.put("current_coil_no", currentCoilNo);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 判断是合卷还是分卷
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", "新增");
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
steps.add(step1);
contentMap.put("steps", steps);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
ObjectMapper objectMapper = new ObjectMapper();
String contentJson = objectMapper.writeValueAsString(contentMap);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo();
recordBo.setContent(contentJson);
recordBo.setSerialNumber(bo.getEnterCoilNo()); // 使用入场钢卷号作为编号
recordBo.setQrcodeType(0L);
recordBo.setIsEnabled(0L);
recordBo.setSize(200L);
recordBo.setStatus(1); // 1=当前有效码
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo);
return record.getRecordId();
} catch (Exception e) {
throw new RuntimeException("生成二维码失败: " + e.getMessage());
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 查找或创建stock
*/
private void findOrCreateStock(WmsMaterialCoilBo bo) {
2025-10-28 12:20:20 +08:00
if (bo.getItemType() == null || bo.getItemId() == null) {
throw new RuntimeException("物品类型和物品ID不能为空");
}
2025-10-29 11:17:52 +08:00
2025-10-29 15:50:46 +08:00
// 查询是否存在相同的stock匹配itemType和itemId
2025-10-28 12:20:20 +08:00
WmsStockBo stockBo = new WmsStockBo();
stockBo.setItemType(bo.getItemType());
stockBo.setItemId(bo.getItemId());
List<WmsStockVo> stockList = stockService.queryList(stockBo);
2025-10-29 11:17:52 +08:00
if (stockList.isEmpty()) {
2025-10-29 15:50:46 +08:00
// 如果没有找到匹配的stock新增一条stock记录
WmsStockBo newStockBo = new WmsStockBo();
newStockBo.setItemType(bo.getItemType());
newStockBo.setItemId(bo.getItemId());
2025-10-29 15:50:46 +08:00
// 调用stockService新增stock
Boolean insertResult = stockService.insertByBo(newStockBo);
if (!insertResult) {
throw new RuntimeException("新增库存记录失败");
}
2025-10-28 12:20:20 +08:00
}
// 如果已存在stock记录则不需要重复创建
2025-10-28 12:20:20 +08:00
}
/**
* 修改钢卷物料表
* 如果newCoils不为空则进行批量更新分卷/合卷
* 如果newCoils为空则进行单个更新
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(WmsMaterialCoilBo bo) {
// 判断是否批量更新
if (bo.getNewCoils() != null && !bo.getNewCoils().isEmpty()) {
// 批量更新逻辑(分卷/合卷)
return updateByBatch(bo);
} else {
2025-10-28 14:56:46 +08:00
// 单个更新逻辑需要coilId
if (bo.getCoilId() == null) {
throw new RuntimeException("钢卷ID不能为空");
}
2025-10-28 12:20:20 +08:00
return updateBySingle(bo);
}
}
/**
* 简单更新钢卷物料表
* 直接更新属性内容不进行历史记录处理
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateSimple(WmsMaterialCoilBo bo) {
if (bo.getCoilId() == null) {
throw new RuntimeException("钢卷ID不能为空");
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 查询原钢卷是否存在
WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId());
if (oldCoil == null) {
throw new RuntimeException("钢卷不存在");
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 直接更新钢卷属性
WmsMaterialCoil updateCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
validEntityBeforeSave(updateCoil);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 使用MyBatis-Plus的updateById方法直接更新
boolean flag = baseMapper.updateById(updateCoil) > 0;
// 特殊处理saleId字段确保null值也能被正确更新
if (bo.getSaleId() == null) {
LambdaUpdateWrapper<WmsMaterialCoil> 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);
}
2025-10-29 11:17:52 +08:00
// 更新实际库区的启用状态
if (flag) {
// 只有当新的库区ID不为空且与原库区ID不同时才更新库区状态
if (bo.getActualWarehouseId() != null && !bo.getActualWarehouseId().equals(oldCoil.getActualWarehouseId())) {
updateActualWarehouseEnableStatus(oldCoil.getActualWarehouseId(), bo.getActualWarehouseId());
}
}
2025-10-28 12:20:20 +08:00
return flag;
}
/**
2025-11-11 09:40:42 +08:00
* 单个更新
2025-10-28 12:20:20 +08:00
*/
private Boolean updateBySingle(WmsMaterialCoilBo bo) {
// 查询原钢卷
WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId());
if (oldCoil == null) {
throw new RuntimeException("原钢卷不存在");
}
2025-10-29 11:17:52 +08:00
2025-11-11 09:40:42 +08:00
// 1. 将原钢卷标记为历史数据dataType = 0
LambdaUpdateWrapper<WmsMaterialCoil> 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()); // 继续使用原二维码
2025-11-11 09:42:59 +08:00
2025-11-11 09:40:42 +08:00
// 继承原记录的关键字段
if (newCoil.getEnterCoilNo() == null) {
newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo());
2025-10-29 11:17:52 +08:00
}
2025-11-11 09:40:42 +08:00
if (newCoil.getSupplierCoilNo() == null) {
newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo());
2025-11-03 17:03:03 +08:00
}
2025-11-11 09:42:59 +08:00
2025-11-11 09:40:42 +08:00
validEntityBeforeSave(newCoil);
2025-11-11 09:42:59 +08:00
2025-11-11 09:40:42 +08:00
// 插入新记录
boolean flag = baseMapper.insert(newCoil) > 0;
2025-11-11 09:42:59 +08:00
2025-10-28 12:20:20 +08:00
if (flag) {
2025-11-11 09:40:42 +08:00
// 3. 更新二维码内容添加更新步骤并更新current_coil_id
2025-11-11 09:20:09 +08:00
if (oldCoil.getQrcodeRecordId() != null) {
2025-11-11 09:40:42 +08:00
updateQrcodeContentForNormalUpdate(oldCoil, bo, newCoil.getCoilId());
2025-11-11 09:20:09 +08:00
}
// 只有当新的库区ID不为空且与原库区ID不同时才更新库区状态
if (bo.getActualWarehouseId() != null && !bo.getActualWarehouseId().equals(oldCoil.getActualWarehouseId())) {
updateActualWarehouseEnableStatus(oldCoil.getActualWarehouseId(), bo.getActualWarehouseId());
}
2025-10-28 12:20:20 +08:00
}
2025-11-11 09:42:59 +08:00
2025-10-28 12:20:20 +08:00
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);
}
}
2025-10-28 12:20:20 +08:00
/**
* 生成二维码更新时库区变化
*/
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<String, Object> 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); // 当前钢卷号
2025-11-03 17:03:03 +08:00
contentMap.put("coil_id", String.valueOf(oldCoil.getCoilId())); // 初始钢卷ID记录最初的ID
contentMap.put("current_coil_id", "null"); // 当前钢卷ID更新时暂时为null插入后更新
// 复制原钢卷的历史steps
List<Map<String, Object>> steps = new ArrayList<>();
if (oldCoil.getQrcodeRecordId() != null) {
WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId());
if (oldRecord != null) {
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
Map<String, Object> oldContentMap = objectMapper.readValue(oldRecord.getContent(), Map.class);
@SuppressWarnings("unchecked")
List<Map<String, Object>> oldSteps = (List<Map<String, Object>>) oldContentMap.get("steps");
if (oldSteps != null) {
steps.addAll(oldSteps);
}
}
}
// 添加更新步骤(库区变化)
Map<String, Object> 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());
}
}
2025-10-28 12:20:20 +08:00
/**
* 批量更新分卷/合卷
*/
private Boolean updateByBatch(WmsMaterialCoilBo bo) {
2025-10-28 14:56:46 +08:00
// 查询原钢卷(分卷时需要,合卷时可能不需要)
WmsMaterialCoil oldCoil = null;
if (bo.getCoilId() != null) {
oldCoil = baseMapper.selectById(bo.getCoilId());
if (oldCoil == null) {
throw new RuntimeException("原钢卷不存在");
}
2025-10-28 12:20:20 +08:00
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 判断是分卷还是合卷
boolean isSplit = false;
boolean isMerge = false;
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
// 检查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) {
2025-10-28 12:20:20 +08:00
isSplit = true;
break;
}
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 1. 将原数据更新为历史数据data_type=0
2025-10-28 14:56:46 +08:00
// 注意合卷时bo的coilId可能为空因为bo是合卷后的新钢卷
if (bo.getCoilId() != null) {
LambdaUpdateWrapper<WmsMaterialCoil> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId())
2025-10-29 15:50:46 +08:00
.set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据
2025-10-28 14:56:46 +08:00
baseMapper.update(null, updateWrapper);
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 2. 插入多条新的当前数据data_type=1
List<WmsMaterialCoil> newCoils = new ArrayList<>();
List<String> allNewCoilNos = new ArrayList<>();
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 收集所有新钢卷号
for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) {
allNewCoilNos.add(newCoilBo.getCurrentCoilNo());
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
if (isSplit) {
2025-10-28 14:56:46 +08:00
// 分卷将bo作为被分卷的原始对象newCoils中的对象作为分卷后产生的新钢卷
if (oldCoil == null) {
throw new RuntimeException("分卷操作需要原钢卷信息");
}
2025-10-29 11:17:52 +08:00
// 1. 将原始钢卷的二维码标记为失效status=0
if (oldCoil.getQrcodeRecordId() != null) {
WmsGenerateRecordBo oldQrBo = new WmsGenerateRecordBo();
oldQrBo.setRecordId(oldCoil.getQrcodeRecordId());
oldQrBo.setStatus(0); // 0=失效
generateRecordService.updateByBo(oldQrBo);
}
2025-11-11 09:42:59 +08:00
2025-11-07 14:33:28 +08:00
// 2. 将原始钢卷标记为历史数据,并记录所有子卷号
// 在母卷的 parent_coil_nos 字段中记录所有子卷号(用逗号分隔)
String childCoilNos = String.join(",", allNewCoilNos);
LambdaUpdateWrapper<WmsMaterialCoil> motherUpdateWrapper = new LambdaUpdateWrapper<>();
motherUpdateWrapper.eq(WmsMaterialCoil::getCoilId, oldCoil.getCoilId())
.set(WmsMaterialCoil::getParentCoilNos, childCoilNos); // 记录子卷号
baseMapper.update(null, motherUpdateWrapper);
2025-10-29 11:17:52 +08:00
// 3. 为每个分卷后的子钢卷生成独立的二维码并插入数据库
2025-10-29 15:50:46 +08:00
for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) {
WmsMaterialCoil newCoil = BeanUtil.toBean(newCoilBo, WmsMaterialCoil.class);
newCoil.setCoilId(null);
newCoil.setDataType(1);
// 继承原钢卷的基本信息(强制继承,不能修改的字段)
2025-10-29 15:50:46 +08:00
newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo());
newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); // 保留厂家原料卷号
2025-11-11 12:21:16 +08:00
// 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());
}
2025-10-29 15:50:46 +08:00
// 如果没有指定库区,使用原库区
if (newCoil.getWarehouseId() == null) {
newCoil.setWarehouseId(oldCoil.getWarehouseId());
}
if (newCoil.getActualWarehouseId() == null){
newCoil.setActualWarehouseId(oldCoil.getActualWarehouseId());
2025-10-29 15:50:46 +08:00
}
2025-11-11 09:42:59 +08:00
2025-11-07 14:33:28 +08:00
// 在子卷的 parent_coil_nos 字段中记录母卷号
newCoil.setParentCoilNos(oldCoil.getCurrentCoilNo());
2025-10-29 15:50:46 +08:00
// 为每个子钢卷生成独立二维码
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());
2025-10-29 15:50:46 +08:00
}
2025-10-28 12:20:20 +08:00
} else if (isMerge) {
2025-10-28 14:56:46 +08:00
// 合卷将bo作为合卷后的新钢卷newCoils中的对象作为参与合卷的原始钢卷
// 1. 将参与合卷的原始钢卷的二维码标记为失效,并将钢卷标记为历史数据
2025-10-28 14:56:46 +08:00
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);
}
2025-11-11 09:42:59 +08:00
// 标记钢卷为历史数据
LambdaUpdateWrapper<WmsMaterialCoil> 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);
}
}
2025-10-28 14:56:46 +08:00
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
// 2. 生成合卷后的新钢卷二维码
Long mergedQrcodeId = generateQrcodeForMerge(bo, bo.getNewCoils());
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
// 3. 插入合卷后的新钢卷
WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
newCoil.setCoilId(null);
newCoil.setDataType(1);
2025-10-29 11:17:52 +08:00
// 从第一个参与合卷的原始钢卷获取基本信息
if (!bo.getNewCoils().isEmpty()) {
2025-10-28 14:56:46 +08:00
WmsMaterialCoil firstOriginalCoil = baseMapper.selectById(bo.getNewCoils().get(0).getCoilId());
if (firstOriginalCoil != null) {
2025-10-29 11:17:52 +08:00
// 继承基本信息
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());
}
2025-10-28 14:56:46 +08:00
}
2025-10-28 12:20:20 +08:00
}
2025-10-28 14:56:46 +08:00
newCoil.setQrcodeRecordId(mergedQrcodeId);
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
validEntityBeforeSave(newCoil);
baseMapper.insert(newCoil);
newCoils.add(newCoil);
2025-10-29 11:17:52 +08:00
2025-10-28 15:26:43 +08:00
// 更新二维码内容中的coilId
updateQrcodeCoilId(mergedQrcodeId, newCoil.getCoilId());
// 禁用新钢卷的实际库区
if (bo.getActualWarehouseId() != null) {
WmsActualWarehouseBo enableWarehouseBo = new WmsActualWarehouseBo();
enableWarehouseBo.setActualWarehouseId(bo.getActualWarehouseId());
enableWarehouseBo.setIsEnabled(0); // 设置为禁用状态
actualWarehouseService.updateByBo(enableWarehouseBo);
}
2025-10-28 12:20:20 +08:00
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
return true;
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 为分卷生成新二维码每个子钢卷一个
*/
private Long generateQrcodeForSplit(WmsMaterialCoil oldCoil, WmsMaterialCoilBo newCoilBo, List<String> allNewCoilNos) {
try {
Map<String, Object> contentMap = new HashMap<>();
contentMap.put("enter_coil_no", oldCoil.getEnterCoilNo());
contentMap.put("current_coil_no", newCoilBo.getCurrentCoilNo());
2025-11-03 17:03:03 +08:00
contentMap.put("coil_id", String.valueOf(oldCoil.getCoilId())); // 初始钢卷ID记录原钢卷的ID
contentMap.put("current_coil_id", "null"); // 当前钢卷ID分卷时暂时为null插入后更新
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 复制原钢卷的历史steps
List<Map<String, Object>> steps = new ArrayList<>();
if (oldCoil.getQrcodeRecordId() != null) {
WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId());
if (oldRecord != null) {
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
Map<String, Object> oldContentMap = objectMapper.readValue(oldRecord.getContent(), Map.class);
@SuppressWarnings("unchecked")
List<Map<String, Object>> oldSteps = (List<Map<String, Object>>) oldContentMap.get("steps");
if (oldSteps != null) {
steps.addAll(oldSteps);
}
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 添加分卷步骤
Map<String, Object> splitStep = new HashMap<>();
splitStep.put("step", steps.size() + 1);
splitStep.put("action", "更新");
splitStep.put("operation", "分卷");
splitStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo());
2025-10-28 16:05:11 +08:00
splitStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId()));
2025-10-28 12:20:20 +08:00
splitStep.put("new_current_coil_nos", String.join(",", allNewCoilNos));
splitStep.put("child_coils", allNewCoilNos);
2025-10-29 11:17:52 +08:00
splitStep.put("operator", LoginHelper.getUsername()); // 操作者
2025-10-28 12:20:20 +08:00
steps.add(splitStep);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
contentMap.put("steps", steps);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
ObjectMapper objectMapper = new ObjectMapper();
String contentJson = objectMapper.writeValueAsString(contentMap);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
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=当前有效码
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo);
return record.getRecordId();
} catch (Exception e) {
throw new RuntimeException("生成分卷二维码失败: " + e.getMessage());
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 为合卷生成新二维码合并多个父钢卷的二维码信息
*/
2025-10-28 14:56:46 +08:00
private Long generateQrcodeForMerge(WmsMaterialCoilBo mergedCoilBo, List<WmsMaterialCoilBo> originalCoils) {
2025-10-28 12:20:20 +08:00
try {
2025-10-28 14:56:46 +08:00
if (mergedCoilBo == null) {
2025-10-28 12:20:20 +08:00
throw new RuntimeException("合卷后的钢卷数据不能为空");
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
Map<String, Object> contentMap = new HashMap<>();
2025-10-28 14:56:46 +08:00
// 获取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);
2025-10-28 12:20:20 +08:00
contentMap.put("current_coil_no", mergedCoilBo.getCurrentCoilNo());
2025-11-03 17:03:03 +08:00
contentMap.put("coil_id", "null"); // 初始钢卷ID合卷时为null
contentMap.put("current_coil_id", "null"); // 当前钢卷ID合卷时暂时为null插入后更新
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
// 合并所有参与合卷的原始钢卷的历史steps
2025-10-28 12:20:20 +08:00
List<Map<String, Object>> steps = new ArrayList<>();
2025-10-29 11:17:52 +08:00
2025-10-28 14:56:46 +08:00
// 从参与合卷的原始钢卷中获取二维码信息并合并
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<String, Object> originalContentMap = objectMapper.readValue(originalQr.getContent(), Map.class);
@SuppressWarnings("unchecked")
List<Map<String, Object>> originalSteps = (List<Map<String, Object>>) originalContentMap.get("steps");
if (originalSteps != null) {
steps.addAll(originalSteps);
}
2025-10-28 12:20:20 +08:00
}
}
}
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 添加合卷步骤
Map<String, Object> mergeStep = new HashMap<>();
mergeStep.put("step", steps.size() + 1);
mergeStep.put("action", "更新");
mergeStep.put("operation", "合卷");
2025-10-29 11:17:52 +08:00
2025-10-28 15:26:43 +08:00
// 收集参与合卷的原始钢卷号和ID
2025-10-28 14:56:46 +08:00
List<String> originalCoilNos = new ArrayList<>();
2025-10-28 15:26:43 +08:00
List<String> originalCoilIds = new ArrayList<>();
2025-10-28 14:56:46 +08:00
if (originalCoils != null && !originalCoils.isEmpty()) {
for (WmsMaterialCoilBo originalCoilBo : originalCoils) {
if (originalCoilBo.getCurrentCoilNo() != null) {
originalCoilNos.add(originalCoilBo.getCurrentCoilNo());
}
2025-10-28 15:26:43 +08:00
if (originalCoilBo.getCoilId() != null) {
originalCoilIds.add(originalCoilBo.getCoilId().toString());
}
2025-10-28 14:56:46 +08:00
}
}
mergeStep.put("parent_coil_nos", String.join(",", originalCoilNos));
2025-10-28 15:26:43 +08:00
mergeStep.put("parent_coil_ids", String.join(",", originalCoilIds));
2025-10-28 12:20:20 +08:00
mergeStep.put("new_current_coil_no", mergedCoilBo.getCurrentCoilNo());
2025-10-29 11:17:52 +08:00
mergeStep.put("operator", LoginHelper.getUsername()); // 操作者
2025-10-28 12:20:20 +08:00
steps.add(mergeStep);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
contentMap.put("steps", steps);
2025-10-29 11:17:52 +08:00
// 将父钢卷ID也存储到顶层方便快速查询
contentMap.put("parent_coil_ids", String.join(",", originalCoilIds));
2025-10-28 12:20:20 +08:00
ObjectMapper objectMapper = new ObjectMapper();
String contentJson = objectMapper.writeValueAsString(contentMap);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo();
recordBo.setContent(contentJson);
2025-10-28 14:56:46 +08:00
recordBo.setSerialNumber(enterCoilNo + "-" + mergedCoilBo.getCurrentCoilNo());
2025-10-28 12:20:20 +08:00
recordBo.setQrcodeType(0L);
recordBo.setIsEnabled(0L);
recordBo.setSize(200L);
recordBo.setStatus(1); // 1=当前有效码
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo);
return record.getRecordId();
} catch (Exception e) {
throw new RuntimeException("生成合卷二维码失败: " + e.getMessage());
}
}
2025-10-29 15:50:46 +08:00
2025-10-28 15:26:43 +08:00
/**
* 更新二维码内容中的coilId
*/
private void updateQrcodeCoilId(Long qrcodeRecordId, Long coilId) {
try {
// 获取二维码记录
WmsGenerateRecordVo record = generateRecordService.queryById(qrcodeRecordId);
if (record == null) {
throw new RuntimeException("二维码记录不存在");
}
2025-10-29 11:17:52 +08:00
2025-10-28 15:26:43 +08:00
// 解析现有content
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> contentMap = objectMapper.readValue(record.getContent(), Map.class);
2025-10-29 11:17:52 +08:00
2025-11-03 17:03:03 +08:00
// 如果是第一次设置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));
2025-10-29 11:17:52 +08:00
2025-10-28 15:26:43 +08:00
// 更新二维码记录
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());
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 更新二维码内容正常更新添加step
2025-11-11 09:40:42 +08:00
* @param oldCoil 旧钢卷记录
* @param bo 更新数据
* @param newCoilId 新钢卷ID如果创建了新记录
2025-10-28 12:20:20 +08:00
*/
2025-11-11 09:40:42 +08:00
private void updateQrcodeContentForNormalUpdate(WmsMaterialCoil oldCoil, WmsMaterialCoilBo bo, Long newCoilId) {
2025-10-28 12:20:20 +08:00
try {
// 获取原二维码记录
WmsGenerateRecordVo oldRecord = generateRecordService.queryById(oldCoil.getQrcodeRecordId());
2025-10-28 12:20:20 +08:00
if (oldRecord == null) {
throw new RuntimeException("二维码记录不存在");
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 解析现有content
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
2025-10-28 12:20:20 +08:00
Map<String, Object> contentMap = objectMapper.readValue(oldRecord.getContent(), Map.class);
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
// 获取现有steps
@SuppressWarnings("unchecked")
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
if (steps == null) {
steps = new ArrayList<>();
}
2025-10-29 11:17:52 +08:00
// 添加新的step记录更新信息
2025-10-28 12:20:20 +08:00
Map<String, Object> newStep = new HashMap<>();
newStep.put("step", steps.size() + 1);
newStep.put("action", "更新");
2025-11-11 09:20:09 +08:00
newStep.put("operation", "信息更新");
newStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo());
newStep.put("new_current_coil_no", bo.getCurrentCoilNo());
2025-11-11 09:20:09 +08:00
newStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId()));
2025-11-11 09:40:42 +08:00
newStep.put("new_coil_id", String.valueOf(newCoilId));
newStep.put("operator", LoginHelper.getUsername());
2025-11-11 09:42:59 +08:00
2025-11-11 09:20:09 +08:00
// 记录具体的变更字段
List<String> 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());
}
2025-11-11 09:42:59 +08:00
2025-11-11 09:20:09 +08:00
newStep.put("changed_fields", String.join("; ", changedFields));
newStep.put("update_time", new java.util.Date());
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
steps.add(newStep);
contentMap.put("steps", steps);
2025-11-11 09:42:59 +08:00
// 更新当前钢卷号
2025-11-11 09:20:09 +08:00
if (bo.getCurrentCoilNo() != null) {
contentMap.put("current_coil_no", bo.getCurrentCoilNo());
}
2025-11-11 09:42:59 +08:00
2025-11-11 09:40:42 +08:00
// 更新 current_coil_id 为新记录的ID
if (newCoilId != null) {
contentMap.put("current_coil_id", String.valueOf(newCoilId));
}
2025-11-03 17:03:03 +08:00
2025-10-28 12:20:20 +08:00
// 更新二维码记录
String newContentJson = objectMapper.writeValueAsString(contentMap);
WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo();
updateBo.setRecordId(oldCoil.getQrcodeRecordId());
2025-10-28 12:20:20 +08:00
updateBo.setContent(newContentJson);
generateRecordService.updateByBo(updateBo);
} catch (Exception e) {
throw new RuntimeException("更新二维码失败: " + e.getMessage());
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 保存前的数据校验
*/
2025-10-29 15:50:46 +08:00
private void validEntityBeforeSave(WmsMaterialCoil entity) {
2025-10-28 12:20:20 +08:00
//TODO 做一些数据校验,如唯一约束
}
/**
* 批量删除钢卷物料表
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
2025-10-29 15:50:46 +08:00
if (isValid) {
2025-10-28 12:20:20 +08:00
//TODO 做一些业务上的校验,判断是否需要校验
}
// 获取要删除的钢卷记录
List<WmsMaterialCoil> 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);
}
}
}
2025-10-28 12:20:20 +08:00
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 钢卷溯源查询
* 根据入场钢卷号查询二维码解析content中的steps然后根据steps中的钢卷号反向查询数据库
*
2025-10-29 15:50:46 +08:00
* @param enterCoilNo 入场钢卷号
2025-10-28 12:20:20 +08:00
* @param currentCoilNo 当前钢卷号可选用于查询特定子钢卷
* @return 溯源结果包含二维码信息和数据库记录
*/
@Override
public Map<String, Object> queryTrace(String enterCoilNo, String currentCoilNo) {
try {
2025-11-17 11:58:42 +08:00
// 优化1: 使用LIKE查询替代查询所有记录大幅提升性能
2025-10-29 11:17:52 +08:00
List<WmsGenerateRecordVo> allQrRecords = new ArrayList<>();
2025-10-29 11:17:52 +08:00
// 首先查询主二维码(以入场钢卷号为序列号的)
2025-10-28 12:20:20 +08:00
WmsGenerateRecordBo qrBo = new WmsGenerateRecordBo();
qrBo.setSerialNumber(enterCoilNo);
2025-10-29 11:17:52 +08:00
List<WmsGenerateRecordVo> mainQrRecords = generateRecordService.queryList(qrBo);
allQrRecords.addAll(mainQrRecords);
2025-11-17 11:58:42 +08:00
// 优化使用LIKE查询所有以该入场钢卷号开头的二维码分卷后的二维码
// 而不是查询所有记录然后在内存中过滤
LambdaQueryWrapper<com.klp.domain.WmsGenerateRecord> splitWrapper = Wrappers.lambdaQuery();
splitWrapper.like(com.klp.domain.WmsGenerateRecord::getSerialNumber, enterCoilNo + "-");
List<WmsGenerateRecordVo> splitRecords = generateRecordMapper.selectVoList(splitWrapper);
2025-11-17 11:58:42 +08:00
// 去重使用recordId作为唯一标识
Set<Long> existingRecordIds = allQrRecords.stream()
.map(WmsGenerateRecordVo::getRecordId)
.collect(Collectors.toSet());
2025-11-17 11:58:42 +08:00
for (WmsGenerateRecordVo record : splitRecords) {
if (record.getRecordId() != null && !existingRecordIds.contains(record.getRecordId())) {
2025-10-29 11:17:52 +08:00
allQrRecords.add(record);
2025-11-17 11:58:42 +08:00
existingRecordIds.add(record.getRecordId());
2025-10-29 11:17:52 +08:00
}
}
if (allQrRecords.isEmpty()) {
2025-10-28 12:20:20 +08:00
throw new RuntimeException("未找到对应的二维码记录");
}
2025-10-29 11:17:52 +08:00
2025-11-17 11:58:42 +08:00
// 优化2: ObjectMapper在循环外创建避免重复创建
ObjectMapper objectMapper = new ObjectMapper();
2025-10-29 11:17:52 +08:00
// 2. 合并所有二维码的steps信息去重并重新编号
Map<String, Map<String, Object>> uniqueSteps = new HashMap<>(); // 用于去重
Set<String> allCoilNos = new HashSet<>();
// 收集所有操作人用户名
Set<String> operatorUsernames = new HashSet<>();
2025-10-29 11:17:52 +08:00
for (WmsGenerateRecordVo qrRecord : allQrRecords) {
@SuppressWarnings("unchecked")
2025-11-17 11:58:42 +08:00
Map<String, Object> contentMap = (Map<String, Object>) objectMapper.readValue(qrRecord.getContent(), Map.class);
2025-10-29 11:17:52 +08:00
@SuppressWarnings("unchecked")
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
2025-10-29 11:17:52 +08:00
if (steps != null) {
for (Map<String, Object> step : steps) {
// 创建唯一标识:操作类型 + 相关钢卷号
String stepKey = createStepKey(step);
2025-10-29 11:17:52 +08:00
// 如果是新的步骤,添加到唯一步骤集合中
if (!uniqueSteps.containsKey(stepKey)) {
Map<String, Object> 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());
}
2025-10-29 11:17:52 +08:00
}
2025-10-29 11:17:52 +08:00
// 提取钢卷号
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);
}
2025-10-28 12:20:20 +08:00
}
}
// 获取操作人昵称映射
Map<String, String> operatorNicknameMap = getOperatorNicknames(operatorUsernames);
2025-10-29 11:17:52 +08:00
// 转换为列表并按原始步骤号排序(保持时间顺序)
List<Map<String, Object>> allSteps = new ArrayList<>(uniqueSteps.values());
2025-10-29 11:17:52 +08:00
// 按原始步骤号排序,保持实际操作的时间顺序
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);
});
2025-10-29 11:17:52 +08:00
// 重新编号(保持连续性)
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);
}
2025-10-29 11:17:52 +08:00
}
// 3. 如果指定了当前钢卷号,过滤出相关的钢卷号
Set<String> filteredCoilNos = allCoilNos;
2025-10-28 12:20:20 +08:00
if (currentCoilNo != null && !currentCoilNo.trim().isEmpty()) {
2025-10-29 11:17:52 +08:00
final String filterValue = currentCoilNo;
filteredCoilNos = allCoilNos.stream()
2025-10-28 12:20:20 +08:00
.filter(coilNo -> coilNo.contains(filterValue))
.collect(Collectors.toSet());
}
2025-10-29 11:17:52 +08:00
2025-11-17 11:58:42 +08:00
// 优化3: 使用IN查询替代多个OR条件提升查询性能
2025-10-29 11:17:52 +08:00
// 4. 根据提取的钢卷号反向查询数据库
2025-10-28 12:20:20 +08:00
List<WmsMaterialCoilVo> result = new ArrayList<>();
if (!filteredCoilNos.isEmpty()) {
LambdaQueryWrapper<WmsMaterialCoil> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo);
2025-10-29 11:17:52 +08:00
2025-11-17 11:58:42 +08:00
// 优化使用IN查询替代多个OR条件当钢卷号数量较少时
// 如果数量太多(>1000分批查询避免SQL过长
List<String> 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<String> batch = coilNoList.subList(i, end);
LambdaQueryWrapper<WmsMaterialCoil> batchLqw = Wrappers.lambdaQuery();
batchLqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo);
batchLqw.in(WmsMaterialCoil::getCurrentCoilNo, batch);
batchLqw.orderByAsc(WmsMaterialCoil::getCreateTime);
List<WmsMaterialCoilVo> batchResult = baseMapper.selectVoList(batchLqw);
result.addAll(batchResult);
2025-10-28 12:20:20 +08:00
}
2025-11-17 11:58:42 +08:00
}
2025-10-29 11:17:52 +08:00
2025-11-17 11:58:42 +08:00
if (coilNoList.size() <= 1000) {
lqw.orderByAsc(WmsMaterialCoil::getCreateTime);
result = baseMapper.selectVoList(lqw);
}
2025-10-29 11:17:52 +08:00
2025-11-17 11:58:42 +08:00
// 优化4: 批量填充关联对象避免N+1查询
if (!result.isEmpty()) {
fillRelatedObjectsBatch(result);
2025-10-29 11:17:52 +08:00
}
2025-10-28 12:20:20 +08:00
}
2025-10-29 11:17:52 +08:00
// 如果没有找到记录,尝试查询所有相关的钢卷(包括历史数据)
if (result.isEmpty()) {
LambdaQueryWrapper<WmsMaterialCoil> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo);
lqw.orderByAsc(WmsMaterialCoil::getCreateTime);
result = baseMapper.selectVoList(lqw);
2025-11-17 11:58:42 +08:00
// 优化:批量填充关联对象
if (!result.isEmpty()) {
fillRelatedObjectsBatch(result);
2025-10-29 11:17:52 +08:00
}
}
// 5. 构建返回结果
2025-10-28 12:20:20 +08:00
Map<String, Object> resultMap = new HashMap<>();
2025-10-29 11:17:52 +08:00
resultMap.put("qrcode", allQrRecords.get(0)); // 主二维码
resultMap.put("all_qrcodes", allQrRecords); // 所有相关二维码
resultMap.put("steps", allSteps); // 所有步骤
resultMap.put("records", result); // 所有钢卷记录
2025-10-28 12:20:20 +08:00
return resultMap;
} catch (Exception e) {
throw new RuntimeException("溯源查询失败: " + e.getMessage());
}
}
2025-10-29 11:17:52 +08:00
/**
* 根据用户名获取用户昵称映射
* @param usernames 用户名集合
* @return 用户名到昵称的映射
*/
private Map<String, String> getOperatorNicknames(Set<String> usernames) {
Map<String, String> 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;
}
2025-10-29 11:17:52 +08:00
/**
* 创建步骤唯一标识
*/
private String createStepKey(Map<String, Object> step) {
StringBuilder keyBuilder = new StringBuilder();
2025-10-29 11:17:52 +08:00
// 使用操作类型和主要标识符创建唯一key
String operation = (String) step.get("operation");
keyBuilder.append(operation).append("-");
2025-10-29 11:17:52 +08:00
// 根据操作类型使用不同的标识符
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"));
}
2025-10-29 11:17:52 +08:00
return keyBuilder.toString();
}
2025-10-29 11:17:52 +08:00
2025-10-28 12:20:20 +08:00
/**
* 从step中提取钢卷号
*/
private void extractCoilNo(Map<String, Object> step, String fieldName, Set<String> 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());
}
}
}
2025-10-29 11:17:52 +08:00
2025-10-28 15:26:43 +08:00
/**
* 从step中提取钢卷ID
*/
private void extractCoilId(Map<String, Object> step, String fieldName, Set<String> 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());
}
}
}
2025-10-29 14:13:06 +08:00
/**
* 查询各个库区中不同类型的钢卷分布情况
2025-10-29 14:40:09 +08:00
* 按库区分组统计每种物品类型和物品ID的钢卷数量和重量
2025-10-29 14:13:06 +08:00
*
2025-10-29 14:40:09 +08:00
* @param itemType 物品类型可选
2025-10-29 15:50:46 +08:00
* @param itemId 物品ID可选
2025-10-29 14:40:09 +08:00
* @return 分布情况列表包含库区信息物品类型物品ID数量重量等
2025-10-29 14:13:06 +08:00
*/
@Override
2025-10-29 14:40:09 +08:00
public List<WmsMaterialCoilVo> getDistributionByWarehouse(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByWarehouse(itemType, itemId);
2025-10-29 14:13:06 +08:00
return convertMapListToVoList(mapList);
}
@Override
public List<WmsMaterialCoilVo> getDistributionByActualWarehouse(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByActualWarehouse(itemType, itemId);
return convertMapListToVoListActual(mapList);
}
private List<WmsMaterialCoilVo> convertMapListToVoListActual(List<Map<String, Object>> mapList) {
List<WmsMaterialCoilVo> voList = new ArrayList<>();
for (Map<String, Object> 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;
}
2025-10-29 14:13:06 +08:00
/**
* 查询不同类型的钢卷在不同库区的分布情况
2025-10-29 14:40:09 +08:00
* 按物品类型和物品ID分组统计每个库区的钢卷数量和重量
2025-10-29 14:13:06 +08:00
*
2025-10-29 14:40:09 +08:00
* @param itemType 物品类型可选
2025-10-29 15:50:46 +08:00
* @param itemId 物品ID可选
2025-10-29 14:40:09 +08:00
* @return 分布情况列表包含物品类型物品ID库区信息数量重量等
2025-10-29 14:13:06 +08:00
*/
@Override
2025-10-29 14:40:09 +08:00
public List<WmsMaterialCoilVo> getDistributionByItemType(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByItemType(itemType, itemId);
2025-10-29 14:13:06 +08:00
return convertMapListToVoList(mapList);
}
/**
* 将Map列表转换为WmsMaterialCoilVo列表
*/
private List<WmsMaterialCoilVo> convertMapListToVoList(List<Map<String, Object>> mapList) {
List<WmsMaterialCoilVo> voList = new ArrayList<>();
for (Map<String, Object> 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);
2025-10-29 14:40:09 +08:00
vo.setItemId(map.get("item_id") != null ? Long.valueOf(map.get("item_id").toString()) : null);
2025-10-29 14:13:06 +08:00
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);
2025-10-29 14:13:06 +08:00
}
return voList;
}
private void setSubMaterialVo(WmsMaterialCoilVo vo, Map<String, Object> map, List<WmsMaterialCoilVo> 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<WmsMaterialCoilVo> getDistributionByActualItemType(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByActualItemType(itemType, itemId);
return convertMapListToVoListActual(mapList);
}
2025-10-28 12:20:20 +08:00
/**
* 查询钢卷导出数据列表
*/
@Override
public List<WmsMaterialCoilExportVo> queryExportList(WmsMaterialCoilBo bo) {
QueryWrapper<WmsMaterialCoil> lqw = buildQueryWrapper(bo);
return baseMapper.selectExportList(lqw);
}
2025-12-02 13:28:52 +08:00
/**
* 钢卷发货
* @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<WmsMaterialCoil> buildQueryWrapper(WmsMaterialCoilBo bo) {
QueryWrapper<WmsMaterialCoil> 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<Long> 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<Long> 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<Long> coilIds, Integer status) {
if (coilIds == null || coilIds.isEmpty()) {
return false;
}
if (status == null || status < 0 || status > 2) {
throw new RuntimeException("无效的状态值状态必须在0-2之间");
}
// 构造更新条件
LambdaUpdateWrapper<WmsMaterialCoil> 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<Long, String> getUpdatedCoilIdsByOldCoilIds(List<Long> oldCoilIds) {
// 初始化返回的Mapkey=旧IDvalue=下一步ID字符串逗号分隔
Map<Long, String> 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<WmsGenerateRecord> qrWrapper = Wrappers.lambdaQuery();
qrWrapper.eq(WmsGenerateRecord::getSerialNumber, oldCoil.getEnterCoilNo())
.or()
.like(WmsGenerateRecord::getSerialNumber, oldCoil.getEnterCoilNo() + "-");
List<WmsGenerateRecordVo> 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<String, Object> contentMap;
try {
contentMap = objectMapper.readValue(qrRecord.getContent(), Map.class);
} catch (JsonProcessingException e) {
log.warn("解析二维码记录[{}]的content失败: {}", qrRecord.getRecordId(), e.getMessage());
continue;
}
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
if (steps == null || steps.isEmpty()) {
continue;
}
String oldCoilIdStr = oldCoilId.toString();
// 4. 遍历步骤找下一步ID
for (Map<String, Object> 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<WmsMaterialCoil> 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<String> childCoilNos = null;
// 解析子卷号列表
if (newCurrentCoilNos != null && !newCurrentCoilNos.isEmpty()) {
childCoilNos = Arrays.asList(newCurrentCoilNos.split(","));
} else if (step.get("child_coils") instanceof List) {
childCoilNos = (List<String>) step.get("child_coils");
}
// 批量查询所有子卷ID并拼接为逗号分隔的字符串
if (childCoilNos != null && !childCoilNos.isEmpty()) {
// 去重+trim避免空字符串/重复卷号
List<String> validChildCoilNos = childCoilNos.stream()
.map(String::trim)
.filter(no -> !no.isEmpty())
.distinct()
.collect(Collectors.toList());
if (!validChildCoilNos.isEmpty()) {
// 批量查询(性能优化,避免循环查询)
LambdaQueryWrapper<WmsMaterialCoil> coilWrapper = Wrappers.lambdaQuery();
coilWrapper.in(WmsMaterialCoil::getCurrentCoilNo, validChildCoilNos)
.eq(WmsMaterialCoil::getDataType, 1);
List<WmsMaterialCoil> 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;
}
}