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 | package cn.iocoder.yudao.module.mes.dal.mysql.wm.batch; | 1 | package cn.iocoder.yudao.module.mes.dal.mysql.wm.batch; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; | 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| 6 | -import cn.hutool.core.util.ObjUtil; | ||
| 7 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 7 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 8 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 9 | ||
| @@ -105,4 +105,20 @@ public interface MesWmBatchMapper extends BaseMapperX<MesWmBatchDO> { | @@ -105,4 +105,20 @@ public interface MesWmBatchMapper extends BaseMapperX<MesWmBatchDO> { | ||
| 105 | return CollUtil.getFirst(list); | 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,6 +3,8 @@ package cn.iocoder.yudao.module.mes.service.wm.batch; | ||
| 3 | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; | 3 | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; |
| 4 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; | 4 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| 5 | 5 | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 6 | /** | 8 | /** |
| 7 | * 批次管理 Service 接口 | 9 | * 批次管理 Service 接口 |
| 8 | * | 10 | * |
| @@ -20,4 +22,24 @@ public interface MesWmBatchService { | @@ -20,4 +22,24 @@ public interface MesWmBatchService { | ||
| 20 | */ | 22 | */ |
| 21 | MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO); | 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 | package cn.iocoder.yudao.module.mes.service.wm.batch; | 1 | package cn.iocoder.yudao.module.mes.service.wm.batch; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.hutool.core.util.ObjUtil; | 4 | import cn.hutool.core.util.ObjUtil; |
| 4 | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; | 5 | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; |
| 5 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO; | 6 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemBatchConfigDO; |
| @@ -18,6 +19,9 @@ import org.springframework.stereotype.Service; | @@ -18,6 +19,9 @@ import org.springframework.stereotype.Service; | ||
| 18 | import org.springframework.transaction.annotation.Transactional; | 19 | import org.springframework.transaction.annotation.Transactional; |
| 19 | import org.springframework.validation.annotation.Validated; | 20 | import org.springframework.validation.annotation.Validated; |
| 20 | 21 | ||
| 22 | +import java.util.ArrayList; | ||
| 23 | +import java.util.List; | ||
| 24 | + | ||
| 21 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 25 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 22 | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | 26 | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| 23 | 27 | ||
| @@ -171,4 +175,32 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | @@ -171,4 +175,32 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | ||
| 171 | return batch; | 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,7 +163,7 @@ public class MesWmItemReceiptLineServiceImpl implements MesWmItemReceiptLineServ | ||
| 163 | MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO(); | 163 | MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO(); |
| 164 | // 从入库单行获取 | 164 | // 从入库单行获取 |
| 165 | batchReqVO.setItemId(reqVO.getItemId()).setProduceDate(reqVO.getProductionDate()) | 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 | batchReqVO.setVendorId(receipt.getVendorId()).setReceiptDate(receipt.getReceiptDate()); | 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> |