Commit b96d20e6163747426bee2871b17f2a2742362345
1 parent
e10af3d0
✨ feat(mes): 添加批次追溯功能,支持向前和向后查询批次列表
Showing
7 changed files
with
269 additions
and
2 deletions
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<MesWmBatchDO> { |
| 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> | ... | ... |