Files
klp-oa/klp-wms/src/main/java/com/klp/service/impl/WmsFurnacePlanServiceImpl.java
Joshi a037c56122 feat(wms): 完善退火计划钢卷绑定和解绑逻辑
- 添加钢卷映射缓存以提高查询效率
- 增加钢卷排他状态检查防止并发操作冲突
- 实现绑定计划时自动释放实际库区锁定功能
- 优化解绑操作增加数据验证和异常处理
- 修复解绑后钢卷状态更新确保一致性
- 移除冗余的批量释放库区操作提升性能
2026-06-14 12:19:48 +08:00

526 lines
21 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.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.exception.ServiceException;
import com.klp.common.helper.LoginHelper;
import com.klp.common.utils.StringUtils;
import com.klp.domain.*;
import com.klp.domain.bo.WmsFurnacePlanBo;
import com.klp.domain.bo.WmsFurnacePlanCoilBo;
import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.vo.WmsFurnacePlanCoilVo;
import com.klp.domain.vo.WmsFurnacePlanVo;
import com.klp.domain.vo.WmsMaterialCoilVo;
import com.klp.mapper.*;
import com.klp.service.IWmsFurnacePlanService;
import com.klp.service.IWmsMaterialCoilService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 退火计划Service业务层处理
*
* @author klp
* @date 2026-03-14
*/
@RequiredArgsConstructor
@Service
public class WmsFurnacePlanServiceImpl implements IWmsFurnacePlanService {
private final WmsFurnacePlanMapper baseMapper;
private final WmsFurnacePlanCoilMapper planCoilMapper;
private final WmsFurnaceMapper furnaceMapper;
private final WmsMaterialCoilMapper materialCoilMapper;
private final WmsActualWarehouseMapper actualWarehouseMapper;
private final WmsWarehouseMapper warehouseMapper;
private final IWmsMaterialCoilService materialCoilService;
@Override
public WmsFurnacePlanVo queryById(Long planId) {
WmsFurnacePlanVo plan = baseMapper.selectVoById(planId);
if (plan != null) {
plan.setCoilIds(queryPlanCoils(planId).stream()
.map(WmsFurnacePlanCoilVo::getCoilId)
.collect(Collectors.toList()));
fillFurnaceNames(java.util.Collections.singletonList(plan));
}
return plan;
}
@Override
public TableDataInfo<WmsFurnacePlanVo> queryPageList(WmsFurnacePlanBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<WmsFurnacePlan> lqw = buildQueryWrapper(bo);
Page<WmsFurnacePlanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
fillFurnaceNames(result.getRecords());
return TableDataInfo.build(result);
}
@Override
public List<WmsFurnacePlanVo> queryList(WmsFurnacePlanBo bo) {
LambdaQueryWrapper<WmsFurnacePlan> lqw = buildQueryWrapper(bo);
List<WmsFurnacePlanVo> list = baseMapper.selectVoList(lqw);
fillFurnaceNames(list);
return list;
}
private LambdaQueryWrapper<WmsFurnacePlan> buildQueryWrapper(WmsFurnacePlanBo bo) {
LambdaQueryWrapper<WmsFurnacePlan> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getPlanNo()), WmsFurnacePlan::getPlanNo, bo.getPlanNo());
lqw.eq(bo.getTargetFurnaceId() != null, WmsFurnacePlan::getTargetFurnaceId, bo.getTargetFurnaceId());
lqw.eq(bo.getStatus() != null, WmsFurnacePlan::getStatus, bo.getStatus());
lqw.ge(bo.getPlanStartTime() != null, WmsFurnacePlan::getPlanStartTime, bo.getPlanStartTime());
lqw.orderByDesc(WmsFurnacePlan::getPlanStartTime);
return lqw;
}
private void fillFurnaceNames(List<WmsFurnacePlanVo> plans) {
if (plans == null || plans.isEmpty()) {
return;
}
List<Long> furnaceIds = plans.stream()
.map(WmsFurnacePlanVo::getTargetFurnaceId)
.filter(id -> id != null)
.distinct()
.collect(Collectors.toList());
if (furnaceIds.isEmpty()) {
return;
}
List<WmsFurnace> furnaces = furnaceMapper.selectBatchIds(furnaceIds);
java.util.Map<Long, String> nameMap = furnaces.stream()
.collect(Collectors.toMap(WmsFurnace::getFurnaceId, WmsFurnace::getFurnaceName, (a, b) -> a));
plans.forEach(plan -> {
if (plan.getTargetFurnaceId() != null) {
plan.setTargetFurnaceName(nameMap.get(plan.getTargetFurnaceId()));
}
});
}
@Override
public Boolean insertByBo(WmsFurnacePlanBo bo) {
WmsFurnacePlan add = BeanUtil.toBean(bo, WmsFurnacePlan.class);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setPlanId(add.getPlanId());
}
return flag;
}
@Override
public Boolean updateByBo(WmsFurnacePlanBo bo) {
WmsFurnacePlan update = BeanUtil.toBean(bo, WmsFurnacePlan.class);
validEntityBeforeSave(update, false);
return baseMapper.updateById(update) > 0;
}
private void validEntityBeforeSave(WmsFurnacePlan entity, boolean isNew) {
LambdaQueryWrapper<WmsFurnacePlan> planNoQuery = Wrappers.lambdaQuery();
planNoQuery.eq(WmsFurnacePlan::getPlanNo, entity.getPlanNo());
if (!isNew) {
planNoQuery.ne(WmsFurnacePlan::getPlanId, entity.getPlanId());
}
if (baseMapper.selectCount(planNoQuery) > 0) {
throw new ServiceException("计划号已存在");
}
WmsFurnace furnace = furnaceMapper.selectById(entity.getTargetFurnaceId());
if (furnace == null || furnace.getDelFlag() != null && furnace.getDelFlag() == 1) {
throw new ServiceException("目标炉子不存在");
}
}
@Override
public Boolean updateStatus(Long planId, Integer status) {
WmsFurnacePlan plan = baseMapper.selectById(planId);
if (plan == null) {
throw new ServiceException("计划不存在");
}
WmsFurnacePlan update = new WmsFurnacePlan();
update.setPlanId(planId);
update.setStatus(status);
boolean updated = baseMapper.updateById(update) > 0;
if (updated && plan.getTargetFurnaceId() != null) {
if (status != null && (status == 2)) {
updateFurnaceBusy(plan.getTargetFurnaceId(), 1);
}
if (status != null && (status == 3 || status == 4)) {
updateFurnaceBusy(plan.getTargetFurnaceId(), 0);
}
}
return updated;
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid && ids != null && !ids.isEmpty()) {
for (Long planId : ids) {
if (planCoilMapper.selectCount(Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, planId)) > 0) {
throw new ServiceException("计划下存在钢卷,请先解绑");
}
}
}
return baseMapper.deleteBatchIds(ids) > 0;
}
@Override
public List<WmsFurnacePlanCoilVo> queryPlanCoils(Long planId) {
// 1. 查询计划钢卷关联记录
List<WmsFurnacePlanCoilVo> planCoils = planCoilMapper.selectVoList(
Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, planId)
);
if (planCoils == null || planCoils.isEmpty()) {
return planCoils;
}
// 2. 收集钢卷ID并批量查询钢卷信息
List<Long> coilIds = planCoils.stream()
.map(WmsFurnacePlanCoilVo::getCoilId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (coilIds.isEmpty()) {
return planCoils;
}
String coilIdsStr = coilIds.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
WmsMaterialCoilBo coilBo = new WmsMaterialCoilBo();
coilBo.setCoilIds(coilIdsStr);
List<WmsMaterialCoilVo> materialCoils = materialCoilService.queryList(coilBo);
Map<Long, WmsMaterialCoilVo> coilMap = materialCoils.stream()
.collect(Collectors.toMap(WmsMaterialCoilVo::getCoilId, item -> item, (a, b) -> a));
// 3. 批量查询逻辑库区名称
Map<Long, String> logicWarehouseMap = buildLogicWarehouseNameMap(
planCoils.stream()
.map(WmsFurnacePlanCoilVo::getLogicWarehouseId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList())
);
// 4. 填充钢卷信息
planCoils.forEach(planCoil -> fillCoilInfo(planCoil, coilMap, logicWarehouseMap));
return planCoils;
}
/**
* 构建逻辑库区ID到名称的映射
*/
private Map<Long, String> buildLogicWarehouseNameMap(List<Long> warehouseIds) {
if (warehouseIds.isEmpty()) {
return Collections.emptyMap();
}
return warehouseMapper.selectBatchIds(warehouseIds).stream()
.collect(Collectors.toMap(WmsWarehouse::getWarehouseId,
WmsWarehouse::getWarehouseName, (a, b) -> a));
}
/**
* 填充钢卷信息
*/
private void fillCoilInfo(
WmsFurnacePlanCoilVo planCoil,
Map<Long, WmsMaterialCoilVo> coilMap,
Map<Long, String> logicWarehouseMap) {
// 填充钢卷基本信息(实际库区名称已在钢卷信息中)
WmsMaterialCoilVo coil = coilMap.get(planCoil.getCoilId());
if (coil != null) {
planCoil.setEnterCoilNo(coil.getEnterCoilNo());
planCoil.setCoil(coil);
}
// 填充逻辑库区名称
if (planCoil.getLogicWarehouseId() != null) {
planCoil.setLogicWarehouseName(logicWarehouseMap.get(planCoil.getLogicWarehouseId()));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean bindPlanCoils(WmsFurnacePlanCoilBo bo) {
if (bo.getPlanId() == null) {
throw new ServiceException("计划ID不能为空");
}
List<Long> coilIds = parseCoilIds(bo);
if (coilIds.isEmpty()) {
throw new ServiceException("请至少选择一条钢卷");
}
WmsFurnacePlan plan = ensurePlanExist(bo.getPlanId());
if (plan.getStatus() != null && plan.getStatus() == 2) {
throw new ServiceException("计划进行中,无法再领料");
}
// 校验钢卷必须为现存钢卷dataType=1历史钢卷不能被加工
List<WmsMaterialCoil> coils = materialCoilMapper.selectBatchIds(coilIds);
Set<Long> existingIds = coils.stream().map(WmsMaterialCoil::getCoilId).collect(Collectors.toSet());
java.util.Map<Long, WmsMaterialCoil> coilMap = coils.stream()
.collect(Collectors.toMap(WmsMaterialCoil::getCoilId, c -> c, (a, b) -> a));
for (Long coilId : coilIds) {
if (!existingIds.contains(coilId)) {
throw new ServiceException("钢卷被删除无法执行退火");
}
}
for (WmsMaterialCoil coil : coils) {
if (coil.getDataType() == null || coil.getDataType() != 1) {
throw new ServiceException("钢卷" + coil.getEnterCoilNo() + "为历史钢卷,不能被加工");
}
}
for (Long coilId : coilIds) {
if (planCoilMapper.selectCount(Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, bo.getPlanId())
.eq(WmsFurnacePlanCoil::getCoilId, coilId)) > 0) {
continue;
}
// 检查钢卷是否已被其他操作锁定exclusiveStatus != 0
// exclusiveStatus=1: 分卷中, exclusiveStatus=2: 已在其他计划中
WmsMaterialCoil coil = coilMap.get(coilId);
if (coil != null && coil.getExclusiveStatus() != null && coil.getExclusiveStatus() != 0) {
throw new ServiceException("钢卷" + coil.getEnterCoilNo() + "正在进行其他操作,无法绑定");
}
WmsFurnacePlanCoil entity = new WmsFurnacePlanCoil();
entity.setPlanId(bo.getPlanId());
entity.setCoilId(coilId);
entity.setLogicWarehouseId(bo.getLogicWarehouseId());
entity.setFurnaceLevel(bo.getFurnaceLevel());
planCoilMapper.insert(entity);
// 绑定计划时立即释放实际库区(锁卷)
releaseActualWarehouse(coilId);
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean unbindPlanCoil(WmsFurnacePlanCoilBo bo) {
if (bo.getPlanId() == null || bo.getCoilId() == null) {
throw new ServiceException("计划ID和钢卷ID不能为空");
}
int deleted = planCoilMapper.delete(Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, bo.getPlanId())
.eq(WmsFurnacePlanCoil::getCoilId, bo.getCoilId()));
if (deleted <= 0) {
throw new ServiceException("该钢卷未绑定到此计划,无需解绑");
}
// 解绑后解锁钢卷
materialCoilMapper.update(null, Wrappers.<WmsMaterialCoil>lambdaUpdate()
.eq(WmsMaterialCoil::getCoilId, bo.getCoilId())
.set(WmsMaterialCoil::getExclusiveStatus, 0));
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean inFurnace(Long planId) {
WmsFurnacePlan plan = baseMapper.selectById(planId);
if (plan == null) {
throw new ServiceException("计划不存在");
}
if (plan.getActualStartTime() != null) {
throw new ServiceException("计划已入炉");
}
if (planCoilMapper.selectCount(Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, planId)) <= 0) {
throw new ServiceException("计划未绑定钢卷");
}
Date now = new Date();
WmsFurnacePlan update = new WmsFurnacePlan();
update.setPlanId(planId);
update.setActualStartTime(now);
update.setStatus(2);
baseMapper.updateById(update);
updateFurnaceBusy(plan.getTargetFurnaceId(), 1);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean completePlan(Long planId, List<com.klp.domain.bo.WmsFurnacePlanLocationItemBo> locations) {
WmsFurnacePlan plan = baseMapper.selectById(planId);
if (plan == null) {
throw new ServiceException("计划不存在");
}
if (plan.getStatus() == null || plan.getStatus() != 2) {
throw new ServiceException("计划未进行中,无法完成");
}
List<WmsFurnacePlanCoilVo> coils = queryPlanCoils(planId);
if (coils == null || coils.isEmpty()) {
throw new ServiceException("计划未绑定钢卷");
}
if (locations == null || locations.isEmpty()) {
throw new ServiceException("请先分配逻辑库位");
}
java.util.Map<Long, Long> locationMap = locations.stream()
.collect(java.util.stream.Collectors.toMap(com.klp.domain.bo.WmsFurnacePlanLocationItemBo::getCoilId,
com.klp.domain.bo.WmsFurnacePlanLocationItemBo::getWarehouseId, (a, b) -> a));
for (WmsFurnacePlanCoilVo coil : coils) {
Long targetLocation = locationMap.get(coil.getCoilId());
if (targetLocation == null) {
throw new ServiceException("钢卷" + coil.getEnterCoilNo() + "未分配库位");
}
WmsMaterialCoil oldCoil = materialCoilMapper.selectById(coil.getCoilId());
if (oldCoil == null) {
throw new ServiceException("钢卷不存在: " + coil.getCoilId());
}
WmsMaterialCoilBo updateBo = new WmsMaterialCoilBo();
// 复制老钢卷的所有信息
BeanUtils.copyProperties(oldCoil, updateBo);
// 只覆盖需要修改的字段
updateBo.setCoilId(coil.getCoilId());
updateBo.setWarehouseId(targetLocation);
updateBo.setActualWarehouseId(-1L);
updateBo.setCreateTime(new Date());
updateBo.setUpdateTime(new Date());
updateBo.setExclusiveStatus(0);
updateBo.setCreateBy(LoginHelper.getUsername());
updateBo.setUpdateBy(LoginHelper.getUsername());
updateBo.setNextWarehouseId(null);
updateBo.setStatus(0);
updateBo.setExportBy(null);
updateBo.setExportTime(null);
materialCoilService.updateByBo(updateBo, "annealing");
WmsFurnacePlanCoil wmsFurnacePlanCoil = planCoilMapper.selectOne(Wrappers.<WmsFurnacePlanCoil>lambdaQuery()
.eq(WmsFurnacePlanCoil::getPlanId, planId)
.eq(WmsFurnacePlanCoil::getCoilId, coil.getCoilId())
.eq(WmsFurnacePlanCoil::getDelFlag, 0));
if (wmsFurnacePlanCoil != null && wmsFurnacePlanCoil.getPlanCoilId() != null) {
planCoilMapper.update(null, Wrappers.<WmsFurnacePlanCoil>lambdaUpdate()
.eq(WmsFurnacePlanCoil::getPlanCoilId, wmsFurnacePlanCoil.getPlanCoilId())
.set(WmsFurnacePlanCoil::getLogicWarehouseId, targetLocation));
}
}
Date now = new Date();
WmsFurnacePlan update = new WmsFurnacePlan();
update.setPlanId(planId);
update.setStatus(3);
update.setEndTime(now);
baseMapper.updateById(update);
updateFurnaceBusy(plan.getTargetFurnaceId(), 0);
return true;
}
private void releaseActualWarehouse(Long coilId) {
WmsMaterialCoil coil = materialCoilMapper.selectById(coilId);
if (coil == null) {
return;
}
Long actualWarehouseId = coil.getActualWarehouseId();
if (actualWarehouseId != null) {
WmsActualWarehouse warehouse = new WmsActualWarehouse();
warehouse.setActualWarehouseId(actualWarehouseId);
warehouse.setIsEnabled(1);
actualWarehouseMapper.updateById(warehouse);
}
materialCoilMapper.update(null, Wrappers.<WmsMaterialCoil>lambdaUpdate()
.eq(WmsMaterialCoil::getCoilId, coilId)
.set(WmsMaterialCoil::getActualWarehouseId, null)
.set(WmsMaterialCoil::getExclusiveStatus, 2));
}
private List<Long> parseCoilIds(WmsFurnacePlanCoilBo bo) {
List<Long> coilIds = new ArrayList<>();
if (bo.getCoilId() != null) {
coilIds.add(bo.getCoilId());
}
if (StringUtils.isNotBlank(bo.getCoilIds())) {
String[] parts = bo.getCoilIds().split(",");
for (String part : parts) {
if (StringUtils.isNotBlank(part)) {
coilIds.add(Long.parseLong(part.trim()));
}
}
}
if (StringUtils.isNotBlank(bo.getEnterCoilNos())) {
coilIds.addAll(resolveCoilIdsByEnterNos(bo.getEnterCoilNos()));
}
if (StringUtils.isNotBlank(bo.getCurrentCoilNos())) {
coilIds.addAll(resolveCoilIdsByCurrentNos(bo.getCurrentCoilNos()));
}
return coilIds.stream().distinct().collect(Collectors.toList());
}
private List<Long> resolveCoilIdsByEnterNos(String enterCoilNos) {
List<String> nos = splitCommaValues(enterCoilNos);
if (nos.isEmpty()) {
return new ArrayList<>();
}
return materialCoilMapper.selectList(Wrappers.<WmsMaterialCoil>lambdaQuery()
.in(WmsMaterialCoil::getEnterCoilNo, nos)
.eq(WmsMaterialCoil::getDelFlag, 0))
.stream()
.map(WmsMaterialCoil::getCoilId)
.collect(Collectors.toList());
}
private List<Long> resolveCoilIdsByCurrentNos(String currentCoilNos) {
List<String> nos = splitCommaValues(currentCoilNos);
if (nos.isEmpty()) {
return new ArrayList<>();
}
return materialCoilMapper.selectList(Wrappers.<WmsMaterialCoil>lambdaQuery()
.in(WmsMaterialCoil::getCurrentCoilNo, nos)
.eq(WmsMaterialCoil::getDelFlag, 0))
.stream()
.map(WmsMaterialCoil::getCoilId)
.collect(Collectors.toList());
}
private List<String> splitCommaValues(String values) {
List<String> result = new ArrayList<>();
if (StringUtils.isBlank(values)) {
return result;
}
String[] parts = values.split(",");
for (String part : parts) {
if (StringUtils.isNotBlank(part)) {
result.add(part.trim());
}
}
return result;
}
private WmsFurnacePlan ensurePlanExist(Long planId) {
WmsFurnacePlan plan = baseMapper.selectById(planId);
if (plan == null) {
throw new ServiceException("计划不存在");
}
return plan;
}
private void updateFurnaceBusy(Long furnaceId, Integer busyFlag) {
if (furnaceId == null) {
return;
}
WmsFurnace update = new WmsFurnace();
update.setFurnaceId(furnaceId);
update.setBusyFlag(busyFlag);
furnaceMapper.updateById(update);
}
}