Commit b96d20e6163747426bee2871b17f2a2742362345

Authored by YunaiV
1 parent e10af3d0

✨ feat(mes): 添加批次追溯功能,支持向前和向后查询批次列表

yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/batch/MesWmBatchController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.batch;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchRespVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
  7 +import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
  8 +import io.swagger.v3.oas.annotations.Operation;
  9 +import io.swagger.v3.oas.annotations.Parameter;
  10 +import io.swagger.v3.oas.annotations.tags.Tag;
  11 +import jakarta.annotation.Resource;
  12 +import jakarta.validation.Valid;
  13 +import org.springframework.security.access.prepost.PreAuthorize;
  14 +import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.GetMapping;
  16 +import org.springframework.web.bind.annotation.RequestMapping;
  17 +import org.springframework.web.bind.annotation.RequestParam;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +
  24 +@Tag(name = "管理后台 - MES 批次管理")
  25 +@RestController
  26 +@RequestMapping("/mes/wm/batch")
  27 +@Validated
  28 +public class MesWmBatchController {
  29 +
  30 + @Resource
  31 + private MesWmBatchService batchService;
  32 +
  33 + @GetMapping("/forward-list")
  34 + @Operation(summary = "批次向前追溯")
  35 + @Parameter(name = "code", description = "批次编码", required = true, example = "BATCH20250314001")
  36 + @PreAuthorize("@ss.hasPermission('mes:wm-batch:query')")
  37 + public CommonResult<List<MesWmBatchRespVO>> getForwardList(@RequestParam("code") @Valid String code) {
  38 + List<MesWmBatchDO> list = batchService.getForwardBatchList(code);
  39 + return success(BeanUtils.toBean(list, MesWmBatchRespVO.class));
  40 + }
  41 +
  42 + @GetMapping("/backward-list")
  43 + @Operation(summary = "批次向后追溯")
  44 + @Parameter(name = "code", description = "批次编码", required = true, example = "BATCH20250314001")
  45 + @PreAuthorize("@ss.hasPermission('mes:wm-batch:query')")
  46 + public CommonResult<List<MesWmBatchRespVO>> getBackwardList(@RequestParam("code") @Valid String code) {
  47 + List<MesWmBatchDO> list = batchService.getBackwardBatchList(code);
  48 + return success(BeanUtils.toBean(list, MesWmBatchRespVO.class));
  49 + }
  50 +
  51 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/batch/vo/MesWmBatchRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - MES 批次管理 Response VO")
  9 +@Data
  10 +public class MesWmBatchRespVO {
  11 +
  12 + @Schema(description = "批次 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  13 + private Long id;
  14 +
  15 + @Schema(description = "批次编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "BATCH20250314001")
  16 + private String code;
  17 +
  18 + @Schema(description = "物料 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  19 + private Long itemId;
  20 +
  21 + @Schema(description = "物料编码", example = "ITEM001")
  22 + private String itemCode;
  23 +
  24 + @Schema(description = "物料名称", example = "产品A")
  25 + private String itemName;
  26 +
  27 + @Schema(description = "规格型号", example = "100*200")
  28 + private String itemSpecification;
  29 +
  30 + @Schema(description = "单位名称", example = "个")
  31 + private String unitName;
  32 +
  33 + @Schema(description = "生产日期")
  34 + private LocalDateTime produceDate;
  35 +
  36 + @Schema(description = "有效期")
  37 + private LocalDateTime expireDate;
  38 +
  39 + @Schema(description = "入库日期")
  40 + private LocalDateTime receiptDate;
  41 +
  42 + @Schema(description = "供应商 ID", example = "1")
  43 + private Long vendorId;
  44 +
  45 + @Schema(description = "供应商编码", example = "V001")
  46 + private String vendorCode;
  47 +
  48 + @Schema(description = "供应商名称", example = "供应商A")
  49 + private String vendorName;
  50 +
  51 + @Schema(description = "客户 ID", example = "1")
  52 + private Long clientId;
  53 +
  54 + @Schema(description = "客户编码", example = "C001")
  55 + private String clientCode;
  56 +
  57 + @Schema(description = "客户名称", example = "客户A")
  58 + private String clientName;
  59 +
  60 + @Schema(description = "采购订单编号", example = "PO20250314001")
  61 + private String purchaseOrderCode;
  62 +
  63 + @Schema(description = "销售订单编号", example = "SO20250314001")
  64 + private String salesOrderCode;
  65 +
  66 + @Schema(description = "生产工单 ID", example = "1")
  67 + private Long workOrderId;
  68 +
  69 + @Schema(description = "生产工单编号", example = "WO20250314001")
  70 + private String workOrderCode;
  71 +
  72 + @Schema(description = "生产任务 ID", example = "1")
  73 + private Long taskId;
  74 +
  75 + @Schema(description = "工作站 ID", example = "1")
  76 + private Long workstationId;
  77 +
  78 + @Schema(description = "工作站编码", example = "WS001")
  79 + private String workstationCode;
  80 +
  81 + @Schema(description = "工具 ID", example = "1")
  82 + private Long toolId;
  83 +
  84 + @Schema(description = "模具 ID", example = "1")
  85 + private Long moldId;
  86 +
  87 + @Schema(description = "生产批号", example = "LOT20250314001")
  88 + private String lotNumber;
  89 +
  90 + @Schema(description = "质量状态", example = "1")
  91 + private String qualityStatus;
  92 +
  93 + @Schema(description = "备注", example = "备注")
  94 + private String remark;
  95 +
  96 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  97 + private LocalDateTime createTime;
  98 +
  99 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/batch/MesWmBatchMapper.java
1 1 package cn.iocoder.yudao.module.mes.dal.mysql.wm.batch;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 6 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
6   -import cn.hutool.core.util.ObjUtil;
7 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
... ... @@ -105,4 +105,20 @@ public interface MesWmBatchMapper extends BaseMapperX&lt;MesWmBatchDO&gt; {
105 105 return CollUtil.getFirst(list);
106 106 }
107 107  
  108 + /**
  109 + * 查询向前追溯批次列表
  110 + * <p>
  111 + * 查询当前批次被哪些工单的哪些批次产品消耗
  112 + * SQL 逻辑:从领料明细 -> 领料单 -> 报工记录 -> 生产入库单 -> 生产入库行
  113 + */
  114 + List<MesWmBatchDO> selectListByForward(String batchCode);
  115 +
  116 + /**
  117 + * 查询向后追溯批次列表
  118 + * <p>
  119 + * 查询当前批次的产品使用了哪些批次的物资
  120 + * SQL 逻辑:从生产入库明细 -> 生产入库单 -> 领料单 -> 领料明细
  121 + */
  122 + List<MesWmBatchDO> selectListByBackward(String batchCode);
  123 +
108 124 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchService.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.mes.service.wm.batch;
3 3 import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
4 4 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
5 5  
  6 +import java.util.List;
  7 +
6 8 /**
7 9 * 批次管理 Service 接口
8 10 *
... ... @@ -20,4 +22,24 @@ public interface MesWmBatchService {
20 22 */
21 23 MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO);
22 24  
  25 + /**
  26 + * 批次向前追溯(递归查询)
  27 + * <p>
  28 + * 查询当前批次被哪些工单的哪些批次产品消耗
  29 + *
  30 + * @param code 批次编码
  31 + * @return 批次列表
  32 + */
  33 + List<MesWmBatchDO> getForwardBatchList(String code);
  34 +
  35 + /**
  36 + * 批次向后追溯(递归查询)
  37 + * <p>
  38 + * 查询当前批次的产品使用了哪些批次的物资
  39 + *
  40 + * @param code 批次编码
  41 + * @return 批次列表
  42 + */
  43 + List<MesWmBatchDO> getBackwardBatchList(String code);
  44 +
23 45 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.wm.batch;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
5 6 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO;
... ... @@ -18,6 +19,9 @@ import org.springframework.stereotype.Service;
18 19 import org.springframework.transaction.annotation.Transactional;
19 20 import org.springframework.validation.annotation.Validated;
20 21  
  22 +import java.util.ArrayList;
  23 +import java.util.List;
  24 +
21 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 26 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
23 27  
... ... @@ -171,4 +175,32 @@ public class MesWmBatchServiceImpl implements MesWmBatchService {
171 175 return batch;
172 176 }
173 177  
  178 + @Override
  179 + public List<MesWmBatchDO> getForwardBatchList(String code) {
  180 + List<MesWmBatchDO> list = batchMapper.selectListByForward(code);
  181 + if (CollUtil.isEmpty(list)) {
  182 + return list;
  183 + }
  184 + // 继续递归查询下游批次
  185 + List<MesWmBatchDO> results = new ArrayList<>(list);
  186 + for (MesWmBatchDO batch : list) {
  187 + results.addAll(getForwardBatchList(batch.getCode()));
  188 + }
  189 + return results;
  190 + }
  191 +
  192 + @Override
  193 + public List<MesWmBatchDO> getBackwardBatchList(String code) {
  194 + List<MesWmBatchDO> list = batchMapper.selectListByBackward(code);
  195 + if (CollUtil.isEmpty(list)) {
  196 + return list;
  197 + }
  198 + // 继续递归查询上游批次
  199 + List<MesWmBatchDO> results = new ArrayList<>(list);
  200 + for (MesWmBatchDO batch : list) {
  201 + results.addAll(getBackwardBatchList(batch.getCode()));
  202 + }
  203 + return results;
  204 + }
  205 +
174 206 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptLineServiceImpl.java
... ... @@ -163,7 +163,7 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ
163 163 MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO();
164 164 // 从入库单行获取
165 165 batchReqVO.setItemId(reqVO.getItemId()).setProduceDate(reqVO.getProductionDate())
166   - .setExpireDate(reqVO.getExpireDate()).setLotNumber(reqVO.getProductionBatchNumber());
  166 + .setExpireDate(reqVO.getExpireDate()).setLotNumber(reqVO.getLotNumber());
167 167 // 从父单据获取
168 168 batchReqVO.setVendorId(receipt.getVendorId()).setReceiptDate(receipt.getReceiptDate());
169 169 // 从到货通知单获取采购订单号(如果关联了到货通知单)
... ...
yudao-module-mes/src/main/resources/mapper/wm/batch/MesWmBatchMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.wm.batch.MesWmBatchMapper">
  4 +
  5 + <resultMap id="BatchTraceResult" type="cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO">
  6 + <result property="workOrderCode" column="workorder_code"/>
  7 + <result property="itemId" column="item_id"/>
  8 + <result property="batchId" column="batch_id"/>
  9 + <result property="batchCode" column="batch_code"/>
  10 + </resultMap>
  11 +
  12 + <!-- 向前追溯:查询当前批次被哪些工单的哪些批次产品消耗 -->
  13 + <!-- TODO @AI:看看能不能写成 mybatis plus join -->
  14 + <!-- TODO @AI:看看能不能直接使用 MesWmBatchDO 返回;或者直接使用目前的 batchrespVO 返回把; -->
  15 + <!-- TODO @AI:查询到 pf.work_order_id,pf.item_id, ppl.batch_id;然后 controller 补全这些信息 -->
  16 + <!-- TODO @芋艿(暂不调整,暂未实现):
  17 + mes_wm_production_issue_detail => wm_item_consume_detail
  18 + mes_wm_production_issue =》wm_item_consume
  19 + -->
  20 + <select id="selectListByForward" parameterType="String" resultMap="BatchTraceResult">
  21 + SELECT DISTINCT pf.workorder_id AS workorder_code, pf.item_id, ppl.batch_id, ppl.batch_code
  22 + FROM mes_wm_production_issue_detail icl
  23 + LEFT JOIN mes_wm_production_issue ic ON icl.issue_id = ic.id
  24 + LEFT JOIN mes_pro_feedback pf ON pf.id = ic.feedback_id
  25 + LEFT JOIN mes_wm_product_produce pp ON pp.work_order_id = ic.work_order_id
  26 + LEFT JOIN mes_wm_product_produce_line ppl ON pp.id = ppl.produce_id
  27 + WHERE icl.batch_code = #{batchCode}
  28 + </select>
  29 +
  30 + <!-- 向后追溯:查询当前批次的产品使用了哪些批次的物资 -->
  31 + <!-- TODO @AI:看看能不能直接使用 MesWmBatchDO 返回;或者直接使用目前的 batchrespVO 返回把; -->
  32 + <!--- TODO @AI:work_order_id 返回就行,不用 as -->
  33 + <!--- TODO @AI:item_id 返回就行,通过 controller 在拼接更多 item 字段; -->
  34 + <!--- TODO @芋艿:mes_wm_production_issue 应该改成 wm_item_consume,还没开发;
  35 + mes_wm_production_issue_detail 应该改成 wm_item_consume_detail,还没开发;先不调整了;
  36 + -->
  37 + <select id="selectListByBackward" parameterType="String" resultMap="BatchTraceResult">
  38 + SELECT DISTINCT ic.work_order_id as workorder_code, icd.item_id, icd.batch_id, icd.batch_code
  39 + FROM mes_wm_product_produce_detail ppd
  40 + LEFT JOIN mes_wm_product_produce pp ON ppd.produce_id = pp.id
  41 + LEFT JOIN mes_wm_production_issue ic ON pp.work_order_id = ic.work_order_id
  42 + LEFT JOIN mes_wm_production_issue_detail ON ic.id = icd.issue_id
  43 + WHERE ppd.batch_code = #{batchCode}
  44 + AND icd.batch_code IS NOT NULL
  45 + </select>
  46 +
  47 +</mapper>
... ...