feat(delivery): 新增钢卷绑定信息查询功能并优化发货单明细校验

- 在 WmsDeliveryWaybillDetailMapper 中新增按钢卷ID批量查询和单个查询绑定来源信息的方法
- 在 Mapper XML 文件中新增 WmsCoilBindInfoResult 结果映射和两个查询SQL
- 在 WmsDeliveryWaybillDetailServiceImpl 的数据校验方法中实现钢卷重复绑定检查
- 新增 WmsCoilBindInfoVo 类用于封装钢卷绑定信息
- 在 WmsMaterialCoilBo 中新增 excludeBound 和 includeBindInfo 参数控制绑定逻辑
- 在 WmsMaterialCoilServiceImpl 中实现钢卷列表的绑定信息查询和排除逻辑
- 在 WmsMaterialCoilVo 中新增绑定相关字段用于前端显示
- 优化发货单明细保存前的数据校验,防止钢卷重复绑定并提供详细的绑定来源提示
This commit is contained in:
2026-01-27 14:47:31 +08:00
parent 0fb7aebf67
commit ad74b9df01
7 changed files with 198 additions and 1 deletions

View File

@@ -189,6 +189,17 @@ public class WmsMaterialCoilBo extends BaseEntity {
private Boolean onlyUnshippedAndUnplanned;
/**
* 是否排除已被发货单明细绑定的钢卷true=列表不返回已绑定钢卷)
*/
private Boolean excludeBound;
/**
* 是否在列表中返回“发货单明细绑定信息”true=返回 bound + 绑定来源信息)
* 默认不返回,避免不需要的场景变慢。
*/
private Boolean includeBindInfo;
//销售id
private Long saleId;

View File

@@ -0,0 +1,31 @@
package com.klp.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 钢卷绑定信息(发货单明细 -> 发货单 -> 发货计划)
* 用于快速标记钢卷是否已被发货单明细占用,并返回占用来源信息。
*/
@Data
@ExcelIgnoreUnannotated
public class WmsCoilBindInfoVo implements Serializable {
private static final long serialVersionUID = 1L;
private Long coilId;
private Long detailId;
private Long waybillId;
private String waybillNo;
private String waybillName;
private Long planId;
private String planName;
private Date planDate;
}

View File

@@ -333,5 +333,24 @@ public class WmsMaterialCoilVo extends BaseEntity {
* 独占状态0=未独占1=特殊分卷中)
*/
private Integer exclusiveStatus;
// ========== 发货绑定信息(由发货单明细占用) ==========
/**
* 是否已被发货单明细绑定true=不可再次绑定)
*/
private Boolean bound;
private Long bindDetailId;
private Long bindWaybillId;
private String bindWaybillNo;
private String bindWaybillName;
private Long bindPlanId;
private String bindPlanName;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date bindPlanDate;
}

View File

@@ -1,8 +1,13 @@
package com.klp.mapper;
import com.klp.domain.WmsDeliveryWaybillDetail;
import com.klp.domain.vo.WmsCoilBindInfoVo;
import com.klp.domain.vo.WmsDeliveryWaybillDetailVo;
import com.klp.common.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
/**
* 发货单明细Mapper接口
@@ -12,4 +17,13 @@ import com.klp.common.core.mapper.BaseMapperPlus;
*/
public interface WmsDeliveryWaybillDetailMapper extends BaseMapperPlus<WmsDeliveryWaybillDetailMapper, WmsDeliveryWaybillDetail, WmsDeliveryWaybillDetailVo> {
/**
* 按钢卷ID批量查询绑定来源信息明细->发货单->发货计划)
*/
List<WmsCoilBindInfoVo> selectBindInfoByCoilIds(@Param("coilIds") Collection<Long> coilIds);
/**
* 按钢卷ID查询绑定来源信息明细->发货单->发货计划)
*/
WmsCoilBindInfoVo selectBindInfoByCoilId(@Param("coilId") Long coilId);
}

View File

@@ -7,11 +7,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.klp.common.utils.StringUtils;
import com.klp.common.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.klp.domain.bo.WmsDeliveryWaybillDetailBo;
import com.klp.domain.vo.WmsDeliveryWaybillDetailVo;
import com.klp.domain.WmsDeliveryWaybillDetail;
import com.klp.domain.vo.WmsCoilBindInfoVo;
import com.klp.mapper.WmsDeliveryWaybillDetailMapper;
import com.klp.service.IWmsDeliveryWaybillDetailService;
@@ -105,7 +107,32 @@ public class WmsDeliveryWaybillDetailServiceImpl implements IWmsDeliveryWaybillD
* 保存前的数据校验
*/
private void validEntityBeforeSave(WmsDeliveryWaybillDetail entity){
//TODO 做一些数据校验,如唯一约束
if (entity == null || entity.getCoilId() == null) {
return;
}
// 同一个钢卷只能被一个发货单明细绑定(排除当前记录)
LambdaQueryWrapper<WmsDeliveryWaybillDetail> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsDeliveryWaybillDetail::getCoilId, entity.getCoilId());
lqw.eq(WmsDeliveryWaybillDetail::getDelFlag, 0);
lqw.ne(entity.getDetailId() != null, WmsDeliveryWaybillDetail::getDetailId, entity.getDetailId());
lqw.last("LIMIT 1");
WmsDeliveryWaybillDetail exists = baseMapper.selectOne(lqw);
if (exists == null) {
return;
}
// 拼接提示:在哪个计划/发货单下已被绑定
WmsCoilBindInfoVo bindInfo = baseMapper.selectBindInfoByCoilId(entity.getCoilId());
if (bindInfo != null) {
throw new ServiceException(
"该钢卷已被绑定,不能重复选择;" +
"发货计划:" + bindInfo.getPlanName() +
",发货单:" + bindInfo.getWaybillNo() +
"" + bindInfo.getWaybillName() + ""
);
}
throw new ServiceException("该钢卷已被绑定,不能重复选择");
}
/**

View File

@@ -70,6 +70,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
private final WmsDeliveryPlanMapper deliveryPlanMapper;
private final WmsProductMapper productMapper;
private final WmsRawMaterialMapper rawMaterialMapper;
private final WmsDeliveryWaybillDetailMapper deliveryWaybillDetailMapper;
/**
* 查询钢卷物料表
@@ -305,6 +306,47 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
return TableDataInfo.build(result);
}
// 批量查询当前页钢卷的“发货单明细绑定”信息(用于禁选/置灰/提示来源)
// 仅当前端明确需要时才查询,避免不必要的性能开销
if (Boolean.TRUE.equals(bo.getIncludeBindInfo())) {
try {
List<Long> coilIds = records.stream()
.map(WmsMaterialCoilVo::getCoilId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!coilIds.isEmpty()) {
Map<Long, WmsCoilBindInfoVo> bindMap = deliveryWaybillDetailMapper
.selectBindInfoByCoilIds(coilIds)
.stream()
.collect(Collectors.toMap(
WmsCoilBindInfoVo::getCoilId,
v -> v,
(a, b) -> a
));
for (WmsMaterialCoilVo vo : records) {
WmsCoilBindInfoVo bind = bindMap.get(vo.getCoilId());
if (bind != null) {
vo.setBound(Boolean.TRUE);
vo.setBindDetailId(bind.getDetailId());
vo.setBindWaybillId(bind.getWaybillId());
vo.setBindWaybillNo(bind.getWaybillNo());
vo.setBindWaybillName(bind.getWaybillName());
vo.setBindPlanId(bind.getPlanId());
vo.setBindPlanName(bind.getPlanName());
vo.setBindPlanDate(bind.getPlanDate());
} else {
vo.setBound(Boolean.FALSE);
}
}
}
} catch (Exception ignore) {
// 绑定信息属于增强字段,查询失败时不影响钢卷列表主流程
}
}
Set<String> userNames = records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getUpdateBy(), v.getExportBy()))
.filter(StringUtils::isNotBlank)
@@ -408,6 +450,11 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
qw.in("mc.quality_status", java.util.Arrays.asList("C+", "C", "C-", "D+", "D", "D-"));
}
// 排除已被发货单明细绑定的钢卷SQL级过滤分页总数准确
if (Boolean.TRUE.equals(bo.getExcludeBound())) {
qw.apply("NOT EXISTS (SELECT 1 FROM wms_delivery_waybill_detail d WHERE d.del_flag = 0 AND d.coil_id = mc.coil_id)");
}
// 组合 item_id 条件:改为使用 EXISTS 子查询,替代预查询 + IN
boolean hasSelectType = StringUtils.isNotBlank(bo.getSelectType());
boolean hasAnyItemFilter = StringUtils.isNotBlank(bo.getItemMaterial())

View File

@@ -27,5 +27,53 @@
<result property="updateBy" column="update_by"/>
</resultMap>
<resultMap type="com.klp.domain.vo.WmsCoilBindInfoVo" id="WmsCoilBindInfoResult">
<result property="coilId" column="coil_id"/>
<result property="detailId" column="detail_id"/>
<result property="waybillId" column="waybill_id"/>
<result property="waybillNo" column="waybill_no"/>
<result property="waybillName" column="waybill_name"/>
<result property="planId" column="plan_id"/>
<result property="planName" column="plan_name"/>
<result property="planDate" column="plan_date"/>
</resultMap>
<select id="selectBindInfoByCoilIds" resultMap="WmsCoilBindInfoResult">
SELECT
d.coil_id,
d.detail_id,
w.waybill_id,
w.waybill_no,
w.waybill_name,
p.plan_id,
p.plan_name,
p.plan_date
FROM wms_delivery_waybill_detail d
INNER JOIN wms_delivery_waybill w ON w.waybill_id = d.waybill_id AND w.del_flag = 0
INNER JOIN wms_delivery_plan p ON p.plan_id = w.plan_id AND p.del_flag = 0
WHERE d.del_flag = 0
AND d.coil_id IN
<foreach collection="coilIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<select id="selectBindInfoByCoilId" resultMap="WmsCoilBindInfoResult">
SELECT
d.coil_id,
d.detail_id,
w.waybill_id,
w.waybill_no,
w.waybill_name,
p.plan_id,
p.plan_name,
p.plan_date
FROM wms_delivery_waybill_detail d
INNER JOIN wms_delivery_waybill w ON w.waybill_id = d.waybill_id AND w.del_flag = 0
INNER JOIN wms_delivery_plan p ON p.plan_id = w.plan_id AND p.del_flag = 0
WHERE d.del_flag = 0
AND d.coil_id = #{coilId}
LIMIT 1
</select>
</mapper>