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

1144 lines
50 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.klp.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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 lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.bo.WmsGenerateRecordBo;
import com.klp.domain.vo.WmsMaterialCoilVo;
import com.klp.domain.vo.WmsGenerateRecordVo;
import com.klp.domain.vo.WmsWarehouseVo;
import com.klp.domain.vo.WmsRawMaterialVo;
import com.klp.domain.vo.WmsBomItemVo;
import com.klp.domain.bo.WmsBomItemBo;
import com.klp.domain.WmsMaterialCoil;
import com.klp.domain.WmsStock;
import com.klp.domain.bo.WmsStockBo;
import com.klp.domain.vo.WmsStockVo;
import com.klp.mapper.WmsMaterialCoilMapper;
import com.klp.mapper.WmsStockMapper;
import com.klp.service.IWmsMaterialCoilService;
import com.klp.service.IWmsStockService;
import com.klp.service.IWmsGenerateRecordService;
import com.klp.service.IWmsWarehouseService;
import com.klp.service.IWmsRawMaterialService;
import com.klp.service.IWmsProductBomService;
import com.klp.service.IWmsBomItemService;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;
import java.util.HashSet;
import java.util.Set;
import java.math.BigDecimal;
/**
* 钢卷物料表Service业务层处理
*
* @author Joshi
* @date 2025-07-18
*/
@RequiredArgsConstructor
@Service
public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
private final WmsMaterialCoilMapper baseMapper;
private final WmsStockMapper stockMapper;
private final IWmsStockService stockService;
private final IWmsGenerateRecordService generateRecordService;
private final IWmsWarehouseService warehouseService;
private final IWmsRawMaterialService rawMaterialService;
private final IWmsProductBomService productBomService;
private final IWmsBomItemService bomItemService;
/**
* 查询钢卷物料表
*/
@Override
public WmsMaterialCoilVo queryById(Long coilId) {
WmsMaterialCoilVo vo = baseMapper.selectVoById(coilId);
if (vo == null) {
return null;
}
// 查询关联对象
fillRelatedObjects(vo);
return vo;
}
/**
* 填充关联对象信息
*/
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.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<WmsBomItemVo> bomItemList = bomItemService.queryList(bomItemBo);
vo.setBomItemList(bomItemList);
}
}
// 查询产品信息当itemType为product时
if ("product".equals(vo.getItemType()) && vo.getItemId() != null) {
// 产品和原材料的查询逻辑相同都通过itemId查询
WmsRawMaterialVo rawMaterial = rawMaterialService.queryById(vo.getItemId());
// 查询原材料对应的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);
}
}
}
/**
* 查询钢卷物料表列表
*/
@Override
public TableDataInfo<WmsMaterialCoilVo> queryPageList(WmsMaterialCoilBo bo, PageQuery pageQuery) {
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
Page<WmsMaterialCoilVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
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());
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(StringUtils.isNotBlank(bo.getItemType()), "mc.item_type", bo.getItemType());
qw.eq(bo.getItemId() !=null, "mc.item_id", bo.getItemId());
//逻辑删除
qw.eq("mc.del_flag", 0);
return qw;
}
/**
* 查询钢卷物料表列表
*/
@Override
public List<WmsMaterialCoilVo> queryList(WmsMaterialCoilBo bo) {
QueryWrapper<WmsMaterialCoil> lqw = buildQueryWrapperPlus(bo);
return baseMapper.selectVoListWithDynamicJoin(lqw);
}
/**
* 新增钢卷物料表
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(WmsMaterialCoilBo bo) {
// 1. 生成二维码
Long qrcodeRecordId = generateQrcodeForInsert(bo);
bo.setQrcodeRecordId(qrcodeRecordId);
// 2. 查找或创建stock
Long warehouseId = findOrCreateStock(bo);
bo.setWarehouseId(warehouseId);
// 3. 插入钢卷数据
WmsMaterialCoil add = BeanUtil.toBean(bo, WmsMaterialCoil.class);
add.setDataType(1); // 新增的钢卷默认为当前数据
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setCoilId(add.getCoilId());
// 4. 更新二维码内容中的coilId
updateQrcodeCoilId(qrcodeRecordId, add.getCoilId());
}
return flag;
}
/**
* 生成二维码(新增)
*/
private Long generateQrcodeForInsert(WmsMaterialCoilBo bo) {
try {
Map<String, Object> 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插入后更新
// 创建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);
// 判断是合卷还是分卷
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 Long 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<WmsStockVo> stockList = stockService.queryList(stockBo);
if (!stockList.isEmpty()) {
// 如果找到相同的stock返回第一个的warehouseId
return stockList.get(0).getWarehouseId();
} else {
// 如果没有找到匹配的stock新增一条stock记录
WmsStockBo newStockBo = new WmsStockBo();
newStockBo.setItemType(bo.getItemType());
newStockBo.setItemId(bo.getItemId());
// 如果有指定warehouseId使用指定的否则为null
if (bo.getWarehouseId() != null) {
newStockBo.setWarehouseId(bo.getWarehouseId());
}
// 调用stockService新增stock
Boolean insertResult = stockService.insertByBo(newStockBo);
if (!insertResult) {
throw new RuntimeException("新增库存记录失败");
}
// 返回新创建的stock的warehouseId
return newStockBo.getWarehouseId();
}
}
/**
* 修改钢卷物料表
* 如果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;
return flag;
}
/**
* 单个更新
*/
private Boolean updateBySingle(WmsMaterialCoilBo bo) {
// 查询原钢卷
WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId());
if (oldCoil == null) {
throw new RuntimeException("原钢卷不存在");
}
// 判断warehouseId是否发生变化
boolean warehouseChanged = bo.getWarehouseId() != null &&
!bo.getWarehouseId().equals(oldCoil.getWarehouseId());
Long qrcodeRecordId;
if (warehouseChanged) {
// 如果库区发生变化,生成新的二维码
qrcodeRecordId = generateQrcodeForUpdate(oldCoil, bo);
} else {
// 如果库区未变化,更新原二维码内容
updateQrcodeContent(oldCoil.getQrcodeRecordId(), bo);
qrcodeRecordId = oldCoil.getQrcodeRecordId();
}
// 2. 将原数据更新为历史数据data_type=0
LambdaUpdateWrapper<WmsMaterialCoil> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId())
.set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据
baseMapper.update(null, updateWrapper);
// 3. 插入一条新的当前数据data_type=1
WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
newCoil.setCoilId(null); // 清空ID让数据库自动生成新的ID
newCoil.setDataType(1); // 设置为当前数据
newCoil.setQrcodeRecordId(qrcodeRecordId); // 使用新的或原有的二维码ID
// 确保关键字段不丢失(入场钢卷号始终不变)
newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); // 入场钢卷号始终不变
if (newCoil.getSupplierCoilNo() == null) {
newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); // 保留厂家原料卷号
}
if (newCoil.getItemType() == null) {
newCoil.setItemType(oldCoil.getItemType());
}
if (newCoil.getItemId() == null) {
newCoil.setItemId(oldCoil.getItemId());
}
validEntityBeforeSave(newCoil);
boolean flag = baseMapper.insert(newCoil) > 0;
if (flag) {
bo.setCoilId(newCoil.getCoilId());
// 如果生成了新二维码更新二维码中的coilId
if (warehouseChanged) {
updateQrcodeCoilId(qrcodeRecordId, newCoil.getCoilId());
}
}
return flag;
}
/**
* 生成二维码(更新时库区变化)
*/
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); // 当前钢卷号
contentMap.put("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_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<WmsMaterialCoil> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId())
.set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据
baseMapper.update(null, updateWrapper);
}
// 2. 插入多条新的当前数据data_type=1
List<WmsMaterialCoil> newCoils = new ArrayList<>();
List<String> allNewCoilNos = new ArrayList<>();
// 收集所有新钢卷号
for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) {
allNewCoilNos.add(newCoilBo.getCurrentCoilNo());
}
if (isSplit) {
// 分卷将bo作为被分卷的原始对象newCoils中的对象作为分卷后产生的新钢卷
if (oldCoil == null) {
throw new RuntimeException("分卷操作需要原钢卷信息");
}
// 1. 将原始钢卷更新为历史数据(已在上面完成)
// 2. 为每个分卷后的子钢卷生成独立的二维码并插入数据库
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()); // 保留厂家原料卷号
newCoil.setItemType(oldCoil.getItemType());
newCoil.setItemId(oldCoil.getItemId());
// 如果前端没传team使用原钢卷的team
if (newCoil.getTeam() == null) {
newCoil.setTeam(oldCoil.getTeam());
}
// 如果没有指定库区,使用原库区
if (newCoil.getWarehouseId() == null) {
newCoil.setWarehouseId(oldCoil.getWarehouseId());
}
// 为每个子钢卷生成独立二维码
Long newQrcodeId = generateQrcodeForSplit(oldCoil, newCoilBo, allNewCoilNos);
newCoil.setQrcodeRecordId(newQrcodeId);
validEntityBeforeSave(newCoil);
baseMapper.insert(newCoil);
newCoils.add(newCoil);
// 更新二维码内容中的coilId
updateQrcodeCoilId(newQrcodeId, newCoil.getCoilId());
}
} else if (isMerge) {
// 合卷将bo作为合卷后的新钢卷newCoils中的对象作为参与合卷的原始钢卷
// 1. 将参与合卷的原始钢卷更新为历史数据
for (WmsMaterialCoilBo originalCoilBo : bo.getNewCoils()) {
if (originalCoilBo.getCoilId() != null) {
LambdaUpdateWrapper<WmsMaterialCoil> originalUpdateWrapper = new LambdaUpdateWrapper<>();
originalUpdateWrapper.eq(WmsMaterialCoil::getCoilId, originalCoilBo.getCoilId())
.set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据
baseMapper.update(null, originalUpdateWrapper);
}
}
// 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());
}
return true;
}
/**
* 为分卷生成新二维码(每个子钢卷一个)
*/
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());
contentMap.put("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> 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<WmsMaterialCoilBo> originalCoils) {
try {
if (mergedCoilBo == null) {
throw new RuntimeException("合卷后的钢卷数据不能为空");
}
Map<String, Object> 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插入后更新
// 合并所有参与合卷的原始钢卷的历史steps
List<Map<String, Object>> 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<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);
}
}
}
}
}
}
// 添加合卷步骤
Map<String, Object> mergeStep = new HashMap<>();
mergeStep.put("step", steps.size() + 1);
mergeStep.put("action", "更新");
mergeStep.put("operation", "合卷");
// 收集参与合卷的原始钢卷号和ID
List<String> originalCoilNos = new ArrayList<>();
List<String> 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<String, Object> contentMap = objectMapper.readValue(record.getContent(), Map.class);
// 更新coilId
contentMap.put("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());
}
}
/**
* 更新二维码内容(单个更新)
*/
private void updateQrcodeContent(Long qrcodeRecordId, WmsMaterialCoilBo bo) {
try {
// 获取原钢卷信息
WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId());
// 获取原二维码记录
WmsGenerateRecordVo oldRecord = generateRecordService.queryById(qrcodeRecordId);
if (oldRecord == null) {
throw new RuntimeException("二维码记录不存在");
}
// 解析现有content
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> contentMap = objectMapper.readValue(oldRecord.getContent(), Map.class);
// 获取现有steps
@SuppressWarnings("unchecked")
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
if (steps == null) {
steps = new ArrayList<>();
}
// 添加新的step记录钢卷号的变化
Map<String, Object> newStep = new HashMap<>();
newStep.put("step", steps.size() + 1);
newStep.put("action", "更新");
newStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo()); // 原当前钢卷号
newStep.put("new_current_coil_no", bo.getCurrentCoilNo()); // 新当前钢卷号
newStep.put("coil_id", String.valueOf(bo.getCoilId())); // 钢卷ID
newStep.put("operator", LoginHelper.getUsername()); // 操作者
// 判断操作类型
if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) {
newStep.put("operation", "合卷");
newStep.put("parent_coil_nos", bo.getParentCoilNos());
} else if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 1) {
newStep.put("operation", "分卷");
newStep.put("new_current_coil_nos", bo.getCurrentCoilNo());
} else {
newStep.put("operation", "更新");
}
steps.add(newStep);
contentMap.put("steps", steps);
// 更新当前钢卷号到最外层(方便快速查看)
contentMap.put("current_coil_no", bo.getCurrentCoilNo());
// 更新二维码记录
String newContentJson = objectMapper.writeValueAsString(contentMap);
WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo();
updateBo.setRecordId(qrcodeRecordId);
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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 钢卷溯源查询
* 根据入场钢卷号查询二维码解析content中的steps然后根据steps中的钢卷号反向查询数据库
*
* @param enterCoilNo 入场钢卷号
* @param currentCoilNo 当前钢卷号(可选,用于查询特定子钢卷)
* @return 溯源结果(包含二维码信息和数据库记录)
*/
@Override
public Map<String, Object> queryTrace(String enterCoilNo, String currentCoilNo) {
try {
// 1. 查询所有相关的二维码记录(包括分卷后的独立二维码)
List<WmsGenerateRecordVo> allQrRecords = new ArrayList<>();
// 首先查询主二维码(以入场钢卷号为序列号的)
WmsGenerateRecordBo qrBo = new WmsGenerateRecordBo();
qrBo.setSerialNumber(enterCoilNo);
List<WmsGenerateRecordVo> mainQrRecords = generateRecordService.queryList(qrBo);
allQrRecords.addAll(mainQrRecords);
// 然后查询所有以该入场钢卷号开头的二维码(分卷后的二维码)
WmsGenerateRecordBo splitQrBo = new WmsGenerateRecordBo();
List<WmsGenerateRecordVo> allRecords = generateRecordService.queryList(splitQrBo);
for (WmsGenerateRecordVo record : allRecords) {
if (record.getSerialNumber() != null &&
record.getSerialNumber().startsWith(enterCoilNo + "-") &&
!allQrRecords.contains(record)) {
allQrRecords.add(record);
}
}
if (allQrRecords.isEmpty()) {
throw new RuntimeException("未找到对应的二维码记录");
}
// 2. 合并所有二维码的steps信息去重并重新编号
Map<String, Map<String, Object>> uniqueSteps = new HashMap<>(); // 用于去重
Set<String> allCoilNos = new HashSet<>();
for (WmsGenerateRecordVo qrRecord : allQrRecords) {
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
Map<String, Object> contentMap = objectMapper.readValue(qrRecord.getContent(), Map.class);
@SuppressWarnings("unchecked")
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
if (steps != null) {
for (Map<String, Object> step : steps) {
// 创建唯一标识:操作类型 + 相关钢卷号
String stepKey = createStepKey(step);
// 如果是新的步骤,添加到唯一步骤集合中
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);
}
// 提取钢卷号
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);
}
}
}
// 转换为列表并按原始步骤号排序(保持时间顺序)
List<Map<String, Object>> 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"));
}
// 3. 如果指定了当前钢卷号,过滤出相关的钢卷号
Set<String> filteredCoilNos = allCoilNos;
if (currentCoilNo != null && !currentCoilNo.trim().isEmpty()) {
final String filterValue = currentCoilNo;
filteredCoilNos = allCoilNos.stream()
.filter(coilNo -> coilNo.contains(filterValue))
.collect(Collectors.toSet());
}
// 4. 根据提取的钢卷号反向查询数据库
List<WmsMaterialCoilVo> result = new ArrayList<>();
if (!filteredCoilNos.isEmpty()) {
LambdaQueryWrapper<WmsMaterialCoil> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo);
// 查询包含提取出的钢卷号的记录
final Set<String> finalCoilNos = filteredCoilNos;
lqw.and(wrapper -> {
int count = 0;
for (String coilNo : finalCoilNos) {
if (count == 0) {
wrapper.eq(WmsMaterialCoil::getCurrentCoilNo, coilNo);
} else {
wrapper.or().eq(WmsMaterialCoil::getCurrentCoilNo, coilNo);
}
count++;
}
});
lqw.orderByAsc(WmsMaterialCoil::getCreateTime);
result = baseMapper.selectVoList(lqw);
// 填充每个记录的关联对象信息(如库区)
for (WmsMaterialCoilVo vo : result) {
fillRelatedObjects(vo);
}
}
// 如果没有找到记录,尝试查询所有相关的钢卷(包括历史数据)
if (result.isEmpty()) {
LambdaQueryWrapper<WmsMaterialCoil> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo);
lqw.orderByAsc(WmsMaterialCoil::getCreateTime);
result = baseMapper.selectVoList(lqw);
// 填充每个记录的关联对象信息
for (WmsMaterialCoilVo vo : result) {
fillRelatedObjects(vo);
}
}
// 5. 构建返回结果
Map<String, Object> 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());
}
}
/**
* 创建步骤唯一标识
*/
private String createStepKey(Map<String, Object> 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<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());
}
}
}
/**
* 从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());
}
}
}
/**
* 查询各个库区中不同类型的钢卷分布情况
* 按库区分组统计每种物品类型和物品ID的钢卷数量和重量
*
* @param itemType 物品类型(可选)
* @param itemId 物品ID可选
* @return 分布情况列表包含库区信息、物品类型、物品ID、数量、重量等
*/
@Override
public List<WmsMaterialCoilVo> getDistributionByWarehouse(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByWarehouse(itemType, itemId);
return convertMapListToVoList(mapList);
}
/**
* 查询不同类型的钢卷在不同库区的分布情况
* 按物品类型和物品ID分组统计每个库区的钢卷数量和重量
*
* @param itemType 物品类型(可选)
* @param itemId 物品ID可选
* @return 分布情况列表包含物品类型、物品ID、库区信息、数量、重量等
*/
@Override
public List<WmsMaterialCoilVo> getDistributionByItemType(String itemType, Long itemId) {
List<Map<String, Object>> mapList = baseMapper.getDistributionByItemType(itemType, itemId);
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);
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);
voList.add(vo);
}
return voList;
}
}