Commit dfa61b7d7e9d8dbb3c2bc3aa68d31d1ff29b0c24

Authored by YunaiV
1 parent 92a5a2bd

✨ feat(wm): 添加物料消耗记录及相关服务实现

Showing 15 changed files with 449 additions and 40 deletions
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java
... ... @@ -7,6 +7,7 @@ package cn.iocoder.yudao.module.infra.api.config;
7 7 */
8 8 public interface ConfigApi {
9 9  
  10 +
10 11 /**
11 12 * 根据参数键查询参数值
12 13 *
... ...
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java
... ... @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
11 11 */
12 12 public interface WebSocketSenderApi {
13 13  
  14 +
14 15 /**
15 16 * 发送消息给指定用户
16 17 *
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/MesProFeedbackController.java
... ... @@ -48,6 +48,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPOR
48 48 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
49 49 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
50 50 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  51 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
51 52  
52 53 @Tag(name = "管理后台 - MES 生产报工")
53 54 @RestController
... ... @@ -156,7 +157,7 @@ public class MesProFeedbackController {
156 157 @Parameter(name = "id", description = "编号", required = true)
157 158 @PreAuthorize("@ss.hasPermission('mes:pro-feedback:approve')")
158 159 public CommonResult<Boolean> approveFeedback(@RequestParam("id") Long id) {
159   - return success(feedbackService.approveFeedback(id));
  160 + return success(feedbackService.approveFeedback(id, getLoginUserId()));
160 161 }
161 162  
162 163 // ==================== 拼接 VO ====================
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/itemconsume/MesWmItemConsumeDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
  8 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
  9 +import com.baomidou.mybatisplus.annotation.KeySequence;
  10 +import com.baomidou.mybatisplus.annotation.TableId;
  11 +import com.baomidou.mybatisplus.annotation.TableName;
  12 +import lombok.*;
  13 +
  14 +import java.time.LocalDateTime;
  15 +
  16 +/**
  17 + * MES 物料消耗记录 DO
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@TableName("mes_wm_item_consume")
  22 +@KeySequence("mes_wm_item_consume_seq")
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@NoArgsConstructor
  28 +@AllArgsConstructor
  29 +public class MesWmItemConsumeDO extends BaseDO {
  30 +
  31 + /**
  32 + * 编号
  33 + */
  34 + @TableId
  35 + private Long id;
  36 + /**
  37 + * 生产工单编号
  38 + *
  39 + * 关联 {@link MesProWorkOrderDO#getId()}
  40 + */
  41 + private Long workOrderId;
  42 + /**
  43 + * 生产任务编号
  44 + *
  45 + * 关联 {@link MesProTaskDO#getId()}
  46 + */
  47 + private Long taskId;
  48 + /**
  49 + * 工作站编号
  50 + *
  51 + * 关联 {@link MesMdWorkstationDO#getId()}
  52 + */
  53 + private Long workstationId;
  54 + /**
  55 + * 工序编号
  56 + *
  57 + * 关联 {@link MesProProcessDO#getId()}
  58 + */
  59 + private Long processId;
  60 + /**
  61 + * 报工记录编号
  62 + *
  63 + * 关联 {@link MesProFeedbackDO#getId()}
  64 + */
  65 + private Long feedbackId;
  66 + /**
  67 + * 消耗日期
  68 + */
  69 + private LocalDateTime consumeDate;
  70 + // DONE @AI:搞个独立的枚举类;
  71 + /**
  72 + * 状态
  73 + *
  74 + * 枚举 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum}
  75 + */
  76 + private Integer status;
  77 + /**
  78 + * 备注
  79 + */
  80 + private String remark;
  81 +
  82 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/itemconsume/MesWmItemConsumeLineDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +import java.math.BigDecimal;
  11 +
  12 +/**
  13 + * MES 物料消耗记录行 DO
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@TableName("mes_wm_item_consume_line")
  18 +@KeySequence("mes_wm_item_consume_line_seq")
  19 +@Data
  20 +@EqualsAndHashCode(callSuper = true)
  21 +@ToString(callSuper = true)
  22 +@Builder
  23 +@NoArgsConstructor
  24 +@AllArgsConstructor
  25 +public class MesWmItemConsumeLineDO extends BaseDO {
  26 +
  27 + /**
  28 + * 编号
  29 + */
  30 + @TableId
  31 + private Long id;
  32 + /**
  33 + * 消耗记录编号
  34 + *
  35 + * 关联 {@link MesWmItemConsumeDO#getId()}
  36 + */
  37 + private Long consumeId;
  38 + /**
  39 + * 物料编号
  40 + *
  41 + * 关联 {@link MesMdItemDO#getId()}
  42 + */
  43 + private Long itemId;
  44 + /**
  45 + * 消耗数量
  46 + */
  47 + private BigDecimal quantity;
  48 + /**
  49 + * 批次编号
  50 + *
  51 + * 关联 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO#getId()}
  52 + */
  53 + private Long batchId;
  54 + /**
  55 + * 批次号
  56 + *
  57 + * 冗余 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO#getCode()}
  58 + */
  59 + private String batchCode;
  60 + /**
  61 + * 备注
  62 + */
  63 + private String remark;
  64 +
  65 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeLineMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * MES 物料消耗记录行 Mapper
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Mapper
  15 +public interface MesWmItemConsumeLineMapper extends BaseMapperX<MesWmItemConsumeLineDO> {
  16 +
  17 + default List<MesWmItemConsumeLineDO> selectListByConsumeId(Long consumeId) {
  18 + return selectList(MesWmItemConsumeLineDO::getConsumeId, consumeId);
  19 + }
  20 +
  21 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/itemconsume/MesWmItemConsumeMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +/**
  8 + * MES 物料消耗记录 Mapper
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Mapper
  13 +public interface MesWmItemConsumeMapper extends BaseMapperX<MesWmItemConsumeDO> {
  14 +
  15 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -250,6 +250,7 @@ public interface ErrorCodeConstants {
250 250 ErrorCode PRO_FEEDBACK_TASK_OR_ORDER_FINISHED = new ErrorCode(1_040_506_009, "当前生产任务或工单已完成,不能继续报工");
251 251 ErrorCode PRO_FEEDBACK_QUANTITY_MUST_POSITIVE = new ErrorCode(1_040_506_010, "报工数量必须大于 0");
252 252 ErrorCode PRO_FEEDBACK_QUALIFIED_UNQUALIFIED_REQUIRED = new ErrorCode(1_040_506_011, "请输入合格品和不良品数量,且合计须大于 0");
  253 + ErrorCode PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS = new ErrorCode(1_040_506_012, "当前报工单未完成检验(待检数量:{}),无法执行报工");
253 254  
254 255 // ========== MES 生产管理-生产流转卡(1-040-507-000) ==========
255 256 ErrorCode PRO_CARD_NOT_EXISTS = new ErrorCode(1_040_507_000, "生产流转卡不存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmItemConsumeStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.enums.wm;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import cn.iocoder.yudao.module.mes.enums.MesOrderStatusConstants;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Getter;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * MES 物料消耗记录状态枚举
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Getter
  16 +@AllArgsConstructor
  17 +public enum MesWmItemConsumeStatusEnum implements ArrayValuable<Integer> {
  18 +
  19 + /**
  20 + * 草稿
  21 + */
  22 + PREPARE(MesOrderStatusConstants.PREPARE, "草稿"),
  23 + /**
  24 + * 已完成
  25 + */
  26 + FINISHED(MesOrderStatusConstants.FINISHED, "已完成");
  27 +
  28 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmItemConsumeStatusEnum::getStatus).toArray(Integer[]::new);
  29 +
  30 + /**
  31 + * 状态值
  32 + */
  33 + private final Integer status;
  34 + /**
  35 + * 状态名
  36 + */
  37 + private final String name;
  38 +
  39 + @Override
  40 + public Integer[] array() {
  41 + return ARRAYS;
  42 + }
  43 +
  44 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java
... ... @@ -68,10 +68,11 @@ public interface MesProFeedbackService {
68 68 /**
69 69 * 审批报工(审批中 -> 已完成 或 待检验)
70 70 *
71   - * @param id 编号
  71 + * @param id 编号
  72 + * @param userId 当前操作用户编号
72 73 * @return true=已完成, false=待检验
73 74 */
74   - boolean approveFeedback(Long id);
  75 + boolean approveFeedback(Long id, Long userId);
75 76  
76 77 }
77 78  
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
1 1 package cn.iocoder.yudao.module.mes.service.pro.feedback;
2 2  
3   -import cn.hutool.core.util.BooleanUtil;
4 3 import cn.hutool.core.util.ObjUtil;
5 4 import cn.hutool.core.util.ObjectUtil;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -9,6 +8,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedba
9 8 import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
10 9 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
11 10 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
12 12 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
13 13 import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
14 14 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
... ... @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationServic
16 16 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
17 17 import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
18 18 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
  19 +import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService;
19 20 import jakarta.annotation.Resource;
20 21 import org.springframework.context.annotation.Lazy;
21 22 import org.springframework.stereotype.Service;
... ... @@ -54,6 +55,9 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
54 55 @Lazy // 避免循环依赖
55 56 private MesProTaskService taskService;
56 57  
  58 + @Resource
  59 + private MesWmItemConsumeService itemConsumeService;
  60 +
57 61 @Override
58 62 public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) {
59 63 // 1. 校验
... ... @@ -121,43 +125,55 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
121 125 .setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus()));
122 126 }
123 127  
124   - // DONE @AI:改成 boolean:true 表示已完成;false 表示待检验,前端需提示"报工成功,请等待质量检验完成!"
125 128 @Override
126 129 @Transactional(rollbackFor = Exception.class)
127   - public boolean approveFeedback(Long id) {
128   - // TODO @AI: 逻辑需要继续跟进;
129   - // 1.1 校验存在 + 审批中状态
  130 + public boolean approveFeedback(Long id, Long userId) {
  131 + // 1.1.a 校验存在 + 审批中状态
130 132 MesProFeedbackDO feedback = validateFeedbackStatusApproving(id);
131   - // 1.2 校验报工数量 > 0
  133 + // 1.1.b 校验报工数量 > 0
132 134 if (feedback.getFeedbackQuantity() == null
133 135 || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
134 136 throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
135 137 }
136   - // 1.3 校验任务未完成
  138 + // 1.2.a 校验任务未完成
137 139 taskService.validateTaskNotFinished(feedback.getTaskId());
138   - // 1.4 仍有待检数量时不能执行
  140 + // 1.2.b 仍有待检数量时不能执行
139 141 if (feedback.getUncheckQuantity() != null
140 142 && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
141   - throw exception(PRO_FEEDBACK_NOT_APPROVING); // TODO @芋艿:新增一个专用错误码,例如 PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS
  143 + throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
142 144 }
143 145  
144   - // 2.1 查询工序的 checkFlag,决定目标状态
145   - boolean checkFlag = getCheckFlag(feedback.getRouteId(), feedback.getProcessId());
146   - Integer targetStatus = checkFlag
147   - ? MesProFeedbackStatusEnum.UNCHECK.getStatus() // 需要检验 → 待检验
148   - : MesProFeedbackStatusEnum.FINISHED.getStatus(); // 无需检验 → 已完成
149   - // 2.2 更新状态 + 审核人
150   - feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
151   - .setStatus(targetStatus)
152   - .setApproveUserId(getLoginUserId()));
153   -
154   - // 3. 如果不需要检验(直接完成),执行后续操作
155   - if (BooleanUtil.isFalse(checkFlag)) {
156   - // TODO @芋艿:WM 物料消耗(WmItemConsumeService)未迁移,此处需补充物料消耗逻辑
157   - // TODO @芋艿:WM 产品产出(WmProductProduceService)未迁移,此处需补充产品产出逻辑
158   - // TODO @芋艿:updateProTaskAndWorkorder 待 pro_task 服务迁移后补充级联更新任务/工单的已生产数量
  146 + // 2. 物料消耗:根据工序 BOM 生成消耗记录
  147 + MesWmItemConsumeDO itemConsume = itemConsumeService.generateItemConsume(feedback);
  148 + if (itemConsume != null) {
  149 + itemConsumeService.finishItemConsume(itemConsume.getId());
  150 + }
  151 +
  152 + // 3. 查询工序的 keyFlag + checkFlag
  153 + MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
  154 + feedback.getRouteId(), feedback.getProcessId());
  155 + boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
  156 + boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
  157 +
  158 + // 4. 情况一:关键工序 + 需要检验 → 待检验状态(等质检完成后入库)
  159 + if (keyFlag && checkFlag) {
  160 + feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
  161 + .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())
  162 + .setApproveUserId(userId));
  163 + // TODO @芋艿:WM 产品产出(WmProductProduceService)待补充 generateProductProduce(质量状态=待检验)
  164 + return false;
  165 + }
  166 +
  167 + // 4.1 情况二:关键工序 + 无需检验 → 直接完成
  168 + if (keyFlag) {
  169 + // TODO @芋艿:WM 产品产出(WmProductProduceService)待补充 generateProductProduce + executeProductProduce
  170 + // TODO @芋艿:updateProTaskAndWorkorder 待补充级联更新任务/工单的已生产数量
159 171 }
160   - return BooleanUtil.isFalse(checkFlag); // true=已完成, false=待检验
  172 + // 4.2 更新状态为已完成 + 审核人
  173 + feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
  174 + .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
  175 + .setApproveUserId(userId));
  176 + return true; // 已完成
161 177 }
162 178  
163 179 // ==================== 校验方法 ====================
... ... @@ -226,16 +242,4 @@ public class MesProFeedbackServiceImpl implements MesProFeedbackService {
226 242 return taskService.validateTaskNotFinished(reqVO.getTaskId());
227 243 }
228 244  
229   - /**
230   - * 获取指定工艺路线中指定工序的 checkFlag
231   - *
232   - * @param routeId 工艺路线编号
233   - * @param processId 工序编号
234   - * @return 是否需要检验
235   - */
236   - private boolean getCheckFlag(Long routeId, Long processId) {
237   - MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId);
238   - return routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
239   - }
240   -
241 245 }
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeLineService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * MES 物料消耗记录行 Service 接口
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface MesWmItemConsumeLineService {
  13 +
  14 + /**
  15 + * 批量创建消耗行
  16 + *
  17 + * @param lines 消耗行列表
  18 + */
  19 + void createItemConsumeLineBatch(List<MesWmItemConsumeLineDO> lines);
  20 +
  21 + /**
  22 + * 根据消耗记录编号查询消耗行
  23 + *
  24 + * @param consumeId 消耗记录编号
  25 + * @return 消耗行列表
  26 + */
  27 + List<MesWmItemConsumeLineDO> getItemConsumeLineListByConsumeId(Long consumeId);
  28 +
  29 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeLineServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO;
  4 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemconsume.MesWmItemConsumeLineMapper;
  5 +import jakarta.annotation.Resource;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.validation.annotation.Validated;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * MES 物料消耗记录行 Service 实现类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Service
  17 +@Validated
  18 +public class MesWmItemConsumeLineServiceImpl implements MesWmItemConsumeLineService {
  19 +
  20 + @Resource
  21 + private MesWmItemConsumeLineMapper itemConsumeLineMapper;
  22 +
  23 + @Override
  24 + public void createItemConsumeLineBatch(List<MesWmItemConsumeLineDO> lines) {
  25 + itemConsumeLineMapper.insertBatch(lines);
  26 + }
  27 +
  28 + @Override
  29 + public List<MesWmItemConsumeLineDO> getItemConsumeLineListByConsumeId(Long consumeId) {
  30 + return itemConsumeLineMapper.selectListByConsumeId(consumeId);
  31 + }
  32 +
  33 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
  2 +
  3 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
  5 +
  6 +/**
  7 + * MES 物料消耗记录 Service 接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface MesWmItemConsumeService {
  12 +
  13 + /**
  14 + * 根据报工单生成物料消耗记录
  15 + *
  16 + * 1. 查询当前工序的 BOM 物料配置
  17 + * 2. 生成消耗单头 + 行(消耗数量 = BOM 用料比例 × 报工数量)
  18 + *
  19 + * @param feedback 报工记录
  20 + * @return 生成的消耗记录,无 BOM 时返回 null
  21 + */
  22 + MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback);
  23 +
  24 + /**
  25 + * 完成物料消耗(库存扣减)
  26 + *
  27 + * 遍历消耗行,扣减线边库库存,更新消耗单状态为已完成
  28 + *
  29 + * @param consumeId 消耗记录编号
  30 + */
  31 + void finishItemConsume(Long consumeId);
  32 +
  33 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.wm.itemconsume;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO;
  8 +import cn.iocoder.yudao.module.mes.dal.mysql.wm.itemconsume.MesWmItemConsumeMapper;
  9 +import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemConsumeStatusEnum;
  10 +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.time.LocalDateTime;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  19 +
  20 +/**
  21 + * MES 物料消耗记录 Service 实现类
  22 + *
  23 + * 参考 KTG {@code WmItemConsumeServiceImpl#generateItemConsume}
  24 + *
  25 + * @author 芋道源码
  26 + */
  27 +@Service
  28 +@Validated
  29 +public class MesWmItemConsumeServiceImpl implements MesWmItemConsumeService {
  30 +
  31 + @Resource
  32 + private MesWmItemConsumeMapper itemConsumeMapper;
  33 + @Resource
  34 + private MesWmItemConsumeLineService itemConsumeLineService;
  35 + @Resource
  36 + private MesProRouteProductBomService routeProductBomService;
  37 +
  38 + // TODO @芋艿:待 MaterialStockService 补充 decreaseStock 后,注入并调用
  39 + // @Resource
  40 + // private MesWmMaterialStockService materialStockService;
  41 +
  42 + @Override
  43 + public MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback) {
  44 + // 1. 查询当前工序的 BOM 物料配置
  45 + List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
  46 + feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
  47 + if (CollUtil.isEmpty(boms)) {
  48 + return null;
  49 + }
  50 +
  51 + // 2.1 生成消耗单头
  52 + MesWmItemConsumeDO consume = MesWmItemConsumeDO.builder()
  53 + .workOrderId(feedback.getWorkOrderId()).taskId(feedback.getTaskId())
  54 + .workstationId(feedback.getWorkstationId()).processId(feedback.getProcessId())
  55 + .feedbackId(feedback.getId()).consumeDate(LocalDateTime.now())
  56 + .status(MesWmItemConsumeStatusEnum.PREPARE.getStatus()).build();
  57 + itemConsumeMapper.insert(consume);
  58 + // 2.2 批量生成消耗行(消耗数量 = BOM 用料比例 × 报工数量)
  59 + List<MesWmItemConsumeLineDO> lines = convertList(boms, bom -> MesWmItemConsumeLineDO.builder()
  60 + .consumeId(consume.getId()).itemId(bom.getItemId())
  61 + .quantity(bom.getQuantity().multiply(feedback.getFeedbackQuantity())).build());
  62 + itemConsumeLineService.createItemConsumeLineBatch(lines);
  63 + return consume;
  64 + }
  65 +
  66 + @Override
  67 + public void finishItemConsume(Long consumeId) {
  68 + // TODO @芋艿:待 MaterialStockService 补充 decreaseStock 后实现库存扣减
  69 + // List<MesWmItemConsumeLineDO> lines = itemConsumeLineService.getItemConsumeLineListByConsumeId(consumeId);
  70 + // for (MesWmItemConsumeLineDO line : lines) {
  71 + // materialStockService.decreaseStock(line.getItemId(), ..., line.getQuantity());
  72 + // }
  73 + // 更新消耗单状态为已完成
  74 + itemConsumeMapper.updateById(MesWmItemConsumeDO.builder()
  75 + .id(consumeId).status(MesWmItemConsumeStatusEnum.FINISHED.getStatus()).build());
  76 + }
  77 +
  78 +}
... ...