Commit dc0874b76ca47f4fbefd8f630a46c5f77f6b341d
1 parent
28d0cfd0
✨ feat(validation): 增加字段长度限制和必填校验
在多个请求对象中添加了字段长度限制和必填校验,以确保数据的有效性和一致性。具体修改包括: - MesTmToolTypeSaveReqVO 和 MesTmToolSaveReqVO 中的 code 和 name 字段增加长度限制 - MesWmItemConsumeLinePageReqVO 中的 feedbackId 字段增加必填校验 - MesMdWorkstationToolService 中新增获取指定工具类型工装夹具资源数量的方法 - ErrorCodeConstants 中新增工具类型被工作站工装资源引用的错误码 这些改动旨在提升数据的完整性和系统的健壮性
Showing
15 changed files
with
233 additions
and
17 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/MesTmToolSaveReqVO.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo; |
| 3 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import jakarta.validation.constraints.Size; | |
| 6 | 7 | import lombok.Data; |
| 7 | 8 | |
| 8 | 9 | import java.time.LocalDateTime; |
| ... | ... | @@ -16,16 +17,20 @@ public class MesTmToolSaveReqVO { |
| 16 | 17 | |
| 17 | 18 | @Schema(description = "工具编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "T-001") |
| 18 | 19 | @NotEmpty(message = "工具编码不能为空") |
| 20 | + @Size(max = 64, message = "工具编码长度不能超过 64 个字符") | |
| 19 | 21 | private String code; |
| 20 | 22 | |
| 21 | 23 | @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "5mm 铣刀") |
| 22 | 24 | @NotEmpty(message = "工具名称不能为空") |
| 25 | + @Size(max = 255, message = "工具名称长度不能超过 255 个字符") | |
| 23 | 26 | private String name; |
| 24 | 27 | |
| 25 | 28 | @Schema(description = "品牌", example = "三菱") |
| 29 | + @Size(max = 255, message = "品牌长度不能超过 255 个字符") | |
| 26 | 30 | private String brand; |
| 27 | 31 | |
| 28 | 32 | @Schema(description = "型号规格", example = "M5-100") |
| 33 | + @Size(max = 255, message = "型号规格长度不能超过 255 个字符") | |
| 29 | 34 | private String spec; |
| 30 | 35 | |
| 31 | 36 | @Schema(description = "工具类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| ... | ... | @@ -36,7 +41,8 @@ public class MesTmToolSaveReqVO { |
| 36 | 41 | @NotNull(message = "数量不能为空") |
| 37 | 42 | private Integer quantity; |
| 38 | 43 | |
| 39 | - @Schema(description = "可用数量", example = "1") | |
| 44 | + @Schema(description = "可用数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 45 | + @NotNull(message = "可用数量不能为空") | |
| 40 | 46 | private Integer availableQuantity; |
| 41 | 47 | |
| 42 | 48 | @Schema(description = "保养维护类型", example = "1") |
| ... | ... | @@ -53,6 +59,7 @@ public class MesTmToolSaveReqVO { |
| 53 | 59 | private Integer status; |
| 54 | 60 | |
| 55 | 61 | @Schema(description = "备注", example = "备注") |
| 62 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 56 | 63 | private String remark; |
| 57 | 64 | |
| 58 | - } | |
| 65 | +} | |
| 59 | 66 | \ No newline at end of file | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/tm/tool/vo/type/MesTmToolTypeSaveReqVO.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type; |
| 3 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import jakarta.validation.constraints.Size; | |
| 6 | 7 | import lombok.Data; |
| 7 | 8 | |
| 8 | 9 | @Schema(description = "管理后台 - MES 工具类型新增/修改 Request VO") |
| ... | ... | @@ -14,10 +15,12 @@ public class MesTmToolTypeSaveReqVO { |
| 14 | 15 | |
| 15 | 16 | @Schema(description = "类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "TT-001") |
| 16 | 17 | @NotEmpty(message = "类型编码不能为空") |
| 18 | + @Size(max = 64, message = "类型编码长度不能超过 64 个字符") | |
| 17 | 19 | private String code; |
| 18 | 20 | |
| 19 | 21 | @Schema(description = "类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "铣刀") |
| 20 | 22 | @NotEmpty(message = "类型名称不能为空") |
| 23 | + @Size(max = 255, message = "类型名称长度不能超过 255 个字符") | |
| 21 | 24 | private String name; |
| 22 | 25 | |
| 23 | 26 | @Schema(description = "是否编码管理", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| ... | ... | @@ -31,6 +34,7 @@ public class MesTmToolTypeSaveReqVO { |
| 31 | 34 | private Integer maintenPeriod; |
| 32 | 35 | |
| 33 | 36 | @Schema(description = "备注", example = "备注") |
| 37 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | |
| 34 | 38 | private String remark; |
| 35 | 39 | |
| 36 | - } | |
| 40 | +} | |
| 37 | 41 | \ No newline at end of file | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/itemconsume/vo/MesWmItemConsumeLinePageReqVO.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.wm.itemconsume.vo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 5 | 6 | import lombok.Data; |
| 6 | 7 | import lombok.EqualsAndHashCode; |
| 7 | 8 | import lombok.ToString; |
| ... | ... | @@ -13,6 +14,7 @@ import lombok.ToString; |
| 13 | 14 | public class MesWmItemConsumeLinePageReqVO extends PageParam { |
| 14 | 15 | |
| 15 | 16 | @Schema(description = "报工记录编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 17 | + @NotNull(message = "报工记录编号不能为空") | |
| 16 | 18 | private Long feedbackId; |
| 17 | 19 | |
| 18 | 20 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/workstation/MesMdWorkstationToolMapper.java
| ... | ... | @@ -29,4 +29,8 @@ public interface MesMdWorkstationToolMapper extends BaseMapperX<MesMdWorkstation |
| 29 | 29 | delete(MesMdWorkstationToolDO::getWorkstationId, workstationId); |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | + default Long selectCountByToolTypeId(Long toolTypeId) { | |
| 33 | + return selectCount(MesMdWorkstationToolDO::getToolTypeId, toolTypeId); | |
| 34 | + } | |
| 35 | + | |
| 32 | 36 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
| ... | ... | @@ -217,6 +217,7 @@ public interface ErrorCodeConstants { |
| 217 | 217 | ErrorCode TM_TOOL_TYPE_CODE_DUPLICATE = new ErrorCode(1_040_400_001, "工具类型编码已存在"); |
| 218 | 218 | ErrorCode TM_TOOL_TYPE_NAME_DUPLICATE = new ErrorCode(1_040_400_002, "工具类型名称已存在"); |
| 219 | 219 | ErrorCode TM_TOOL_TYPE_HAS_TOOL = new ErrorCode(1_040_400_003, "该工具类型下存在工具,无法删除"); |
| 220 | + ErrorCode TM_TOOL_TYPE_HAS_WORKSTATION_TOOL = new ErrorCode(1_040_400_004, "该工具类型已被工作站工装资源引用,无法删除"); | |
| 220 | 221 | |
| 221 | 222 | // ========== MES 工具管理-工具台账(1-040-401-000) ========== |
| 222 | 223 | ErrorCode TM_TOOL_NOT_EXISTS = new ErrorCode(1_040_401_000, "工具不存在"); |
| ... | ... | @@ -362,6 +363,7 @@ public interface ErrorCodeConstants { |
| 362 | 363 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); |
| 363 | 364 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); |
| 364 | 365 | ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); |
| 366 | + | |
| 365 | 367 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== |
| 366 | 368 | ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); |
| 367 | 369 | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/workstation/MesMdWorkstationToolService.java
| ... | ... | @@ -50,4 +50,12 @@ public interface MesMdWorkstationToolService { |
| 50 | 50 | */ |
| 51 | 51 | void deleteWorkstationToolByWorkstationId(Long workstationId); |
| 52 | 52 | |
| 53 | + /** | |
| 54 | + * 获得指定工具类型的工装夹具资源数量 | |
| 55 | + * | |
| 56 | + * @param toolTypeId 工具类型编号 | |
| 57 | + * @return 数量 | |
| 58 | + */ | |
| 59 | + Long getWorkstationToolCountByToolTypeId(Long toolTypeId); | |
| 60 | + | |
| 53 | 61 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/workstation/MesMdWorkstationToolServiceImpl.java
| ... | ... | @@ -89,4 +89,9 @@ public class MesMdWorkstationToolServiceImpl implements MesMdWorkstationToolServ |
| 89 | 89 | workstationToolMapper.deleteByWorkstationId(workstationId); |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | + @Override | |
| 93 | + public Long getWorkstationToolCountByToolTypeId(Long toolTypeId) { | |
| 94 | + return workstationToolMapper.selectCountByToolTypeId(toolTypeId); | |
| 95 | + } | |
| 96 | + | |
| 92 | 97 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java
| ... | ... | @@ -82,11 +82,14 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ |
| 82 | 82 | @Override |
| 83 | 83 | @Transactional(rollbackFor = Exception.class) |
| 84 | 84 | public void updateIndicatorResult(MesQcIndicatorResultSaveReqVO updateReqVO) { |
| 85 | - // 1. 校验存在 | |
| 85 | + // 1.1 校验存在 | |
| 86 | 86 | validateIndicatorResultExists(updateReqVO.getId()); |
| 87 | + // 1.2 校验所有明细的 indicatorId 是否存在 | |
| 88 | + validateIndicatorIds(updateReqVO.getItems()); | |
| 87 | 89 | |
| 88 | - // 2.1 更新主表 | |
| 90 | + // 2.1 更新主表(锁定 qcId/qcType/itemId,不允许改挂到其他质检单) | |
| 89 | 91 | MesQcIndicatorResultDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorResultDO.class); |
| 92 | + updateObj.setQcId(null).setQcType(null).setItemId(null); | |
| 90 | 93 | resultMapper.updateById(updateObj); |
| 91 | 94 | // 2.2 批量更新明细 |
| 92 | 95 | List<MesQcIndicatorResultDetailDO> details = BeanUtils.toBean(updateReqVO.getItems(), | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
| ... | ... | @@ -283,7 +283,7 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { |
| 283 | 283 | * 推导工序 ID |
| 284 | 284 | * |
| 285 | 285 | * <p>通过产品 → 工艺路线产品 → 工艺路线 → 校验工位工序是否在该路线中 |
| 286 | - * <p>如果工位的工序在路线中,则返回该 processId;否则返回 null | |
| 286 | + * <p>如果工位的工序在路线中,则返回该 processId;否则返回 null(与 KTG 保持一致) | |
| 287 | 287 | * |
| 288 | 288 | * @param productId 产品 ID(工单产品) |
| 289 | 289 | * @param workstationProcessId 工位关联的工序 ID | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
| ... | ... | @@ -221,8 +221,8 @@ public class MesQcOqcServiceImpl implements MesQcOqcService { |
| 221 | 221 | return null; |
| 222 | 222 | } |
| 223 | 223 | if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_PRODUCT_SALES)) { |
| 224 | - MesWmProductSalesLineDO salesLine = productSalesLineService.getProductSalesLine(sourceLineId); | |
| 225 | - if (salesLine != null && salesLine.getSalesId() != null) { | |
| 224 | + MesWmProductSalesLineDO salesLine = productSalesLineService.validateProductSalesLineExists(sourceLineId); | |
| 225 | + if (salesLine.getSalesId() != null) { | |
| 226 | 226 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); |
| 227 | 227 | return sales != null ? sales.getCode() : null; |
| 228 | 228 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/tm/tool/MesTmToolTypeServiceImpl.java
| ... | ... | @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTyp |
| 7 | 7 | import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTypeSaveReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolTypeDO; |
| 9 | 9 | import cn.iocoder.yudao.module.mes.dal.mysql.tm.tool.MesTmToolTypeMapper; |
| 10 | +import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationToolService; | |
| 10 | 11 | import jakarta.annotation.Resource; |
| 11 | 12 | import org.springframework.context.annotation.Lazy; |
| 12 | 13 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -37,6 +38,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { |
| 37 | 38 | @Lazy |
| 38 | 39 | private MesTmToolService toolService; |
| 39 | 40 | |
| 41 | + @Resource | |
| 42 | + @Lazy | |
| 43 | + private MesMdWorkstationToolService workstationToolService; | |
| 44 | + | |
| 40 | 45 | @Override |
| 41 | 46 | public Long createToolType(MesTmToolTypeSaveReqVO createReqVO) { |
| 42 | 47 | // 校验数据 |
| ... | ... | @@ -75,6 +80,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { |
| 75 | 80 | if (toolService.getToolCountByToolTypeId(id) > 0) { |
| 76 | 81 | throw exception(TM_TOOL_TYPE_HAS_TOOL); |
| 77 | 82 | } |
| 83 | + // 校验是否被工作站工装资源引用 | |
| 84 | + if (workstationToolService.getWorkstationToolCountByToolTypeId(id) > 0) { | |
| 85 | + throw exception(TM_TOOL_TYPE_HAS_WORKSTATION_TOOL); | |
| 86 | + } | |
| 78 | 87 | |
| 79 | 88 | // 删除 |
| 80 | 89 | toolTypeMapper.deleteById(id); | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
| ... | ... | @@ -39,6 +39,11 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| 39 | 39 | @Slf4j |
| 40 | 40 | public class MesWmBatchServiceImpl implements MesWmBatchService { |
| 41 | 41 | |
| 42 | + /** | |
| 43 | + * 批次追溯最大递归深度,防止极端场景下性能问题 | |
| 44 | + */ | |
| 45 | + private static final int MAX_TRACE_DEPTH = 20; | |
| 46 | + | |
| 42 | 47 | @Resource |
| 43 | 48 | private MesWmBatchMapper batchMapper; |
| 44 | 49 | @Resource |
| ... | ... | @@ -196,11 +201,11 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { |
| 196 | 201 | |
| 197 | 202 | @Override |
| 198 | 203 | public List<MesWmBatchDO> getForwardBatchList(String code) { |
| 199 | - return getForwardBatchList(code, new HashSet<>()); | |
| 204 | + return getForwardBatchList(code, new HashSet<>(), 0); | |
| 200 | 205 | } |
| 201 | 206 | |
| 202 | - private List<MesWmBatchDO> getForwardBatchList(String code, Set<String> visited) { | |
| 203 | - if (code == null || !visited.add(code)) { | |
| 207 | + private List<MesWmBatchDO> getForwardBatchList(String code, Set<String> visited, int depth) { | |
| 208 | + if (code == null || !visited.add(code) || depth >= MAX_TRACE_DEPTH) { | |
| 204 | 209 | return new ArrayList<>(); |
| 205 | 210 | } |
| 206 | 211 | List<MesWmBatchDO> list = batchMapper.selectListByForward(code); |
| ... | ... | @@ -210,18 +215,18 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { |
| 210 | 215 | // 继续递归查询下游批次 |
| 211 | 216 | List<MesWmBatchDO> results = new ArrayList<>(list); |
| 212 | 217 | for (MesWmBatchDO batch : list) { |
| 213 | - results.addAll(getForwardBatchList(batch.getCode(), visited)); | |
| 218 | + results.addAll(getForwardBatchList(batch.getCode(), visited, depth + 1)); | |
| 214 | 219 | } |
| 215 | 220 | return results; |
| 216 | 221 | } |
| 217 | 222 | |
| 218 | 223 | @Override |
| 219 | 224 | public List<MesWmBatchDO> getBackwardBatchList(String code) { |
| 220 | - return getBackwardBatchList(code, new HashSet<>()); | |
| 225 | + return getBackwardBatchList(code, new HashSet<>(), 0); | |
| 221 | 226 | } |
| 222 | 227 | |
| 223 | - private List<MesWmBatchDO> getBackwardBatchList(String code, Set<String> visited) { | |
| 224 | - if (code == null || !visited.add(code)) { | |
| 228 | + private List<MesWmBatchDO> getBackwardBatchList(String code, Set<String> visited, int depth) { | |
| 229 | + if (code == null || !visited.add(code) || depth >= MAX_TRACE_DEPTH) { | |
| 225 | 230 | return new ArrayList<>(); |
| 226 | 231 | } |
| 227 | 232 | List<MesWmBatchDO> list = batchMapper.selectListByBackward(code); |
| ... | ... | @@ -231,7 +236,7 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { |
| 231 | 236 | // 继续递归查询上游批次 |
| 232 | 237 | List<MesWmBatchDO> results = new ArrayList<>(list); |
| 233 | 238 | for (MesWmBatchDO batch : list) { |
| 234 | - results.addAll(getBackwardBatchList(batch.getCode(), visited)); | |
| 239 | + results.addAll(getBackwardBatchList(batch.getCode(), visited, depth + 1)); | |
| 235 | 240 | } |
| 236 | 241 | return results; |
| 237 | 242 | } | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineService.java
| ... | ... | @@ -69,6 +69,14 @@ public interface MesWmProductSalesLineService { |
| 69 | 69 | PageResult<MesWmProductSalesLineDO> getProductSalesLinePage(MesWmProductSalesLinePageReqVO pageReqVO); |
| 70 | 70 | |
| 71 | 71 | /** |
| 72 | + * 校验销售出库单行存在 | |
| 73 | + * | |
| 74 | + * @param id 编号 | |
| 75 | + * @return 销售出库单行 | |
| 76 | + */ | |
| 77 | + MesWmProductSalesLineDO validateProductSalesLineExists(Long id); | |
| 78 | + | |
| 79 | + /** | |
| 72 | 80 | * OQC 检验完成后,更新销售出库单行的 OQC 质检状态 |
| 73 | 81 | * |
| 74 | 82 | * @param id 行 ID | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java
| ... | ... | @@ -124,7 +124,8 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe |
| 124 | 124 | return productSalesLineMapper.selectPage(pageReqVO); |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | - private MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { | |
| 127 | + @Override | |
| 128 | + public MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { | |
| 128 | 129 | MesWmProductSalesLineDO line = productSalesLineMapper.selectById(id); |
| 129 | 130 | if (line == null) { |
| 130 | 131 | throw exception(WM_PRODUCT_SALES_LINE_NOT_EXISTS); | ... | ... |
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImplTest.java
| ... | ... | @@ -8,6 +8,9 @@ import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; |
| 8 | 8 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 9 | 9 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 10 | 10 | import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; |
| 11 | +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; | |
| 12 | +import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; | |
| 13 | +import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService; | |
| 11 | 14 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 12 | 15 | import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; |
| 13 | 16 | import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; |
| ... | ... | @@ -54,6 +57,12 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { |
| 54 | 57 | private AdminUserApi adminUserApi; |
| 55 | 58 | @MockitoBean |
| 56 | 59 | private MesProFeedbackService feedbackService; |
| 60 | + @MockitoBean | |
| 61 | + private MesProRouteProductService routeProductService; | |
| 62 | + @MockitoBean | |
| 63 | + private MesProRouteProcessService routeProcessService; | |
| 64 | + @MockitoBean | |
| 65 | + private MesProTaskService taskService; | |
| 57 | 66 | |
| 58 | 67 | @Test |
| 59 | 68 | public void testFinishIpqc_writeBack_feedback() { |
| ... | ... | @@ -211,4 +220,153 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { |
| 211 | 220 | assertThrows(Exception.class, () -> ipqcService.finishIpqc(ipqc.getId())); |
| 212 | 221 | } |
| 213 | 222 | |
| 223 | + // ==================== processId 推导行为测试 ==================== | |
| 224 | + | |
| 225 | + /** | |
| 226 | + * 工位有工序 + 该工序在产品工艺路线中 → processId 应该被设置为工位工序 | |
| 227 | + */ | |
| 228 | + @Test | |
| 229 | + public void testCreateIpqc_processId_workstationHasProcess() { | |
| 230 | + // 准备参数 | |
| 231 | + Long workstationId = randomLongId(); | |
| 232 | + Long workOrderId = randomLongId(); | |
| 233 | + Long productId = randomLongId(); | |
| 234 | + Long processId = randomLongId(); | |
| 235 | + Long routeId = randomLongId(); | |
| 236 | + Long templateId = randomLongId(); | |
| 237 | + | |
| 238 | + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); | |
| 239 | + reqVO.setCode("IPQC-TEST-001"); | |
| 240 | + reqVO.setWorkstationId(workstationId); | |
| 241 | + reqVO.setWorkOrderId(workOrderId); | |
| 242 | + reqVO.setInspectorUserId(randomLongId()); | |
| 243 | + | |
| 244 | + // mock 工位返回有工序 | |
| 245 | + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); | |
| 246 | + workstation.setId(workstationId); | |
| 247 | + workstation.setProcessId(processId); | |
| 248 | + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); | |
| 249 | + // mock 工单 | |
| 250 | + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); | |
| 251 | + workOrder.setId(workOrderId); | |
| 252 | + workOrder.setProductId(productId); | |
| 253 | + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); | |
| 254 | + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); | |
| 255 | + // mock 模板匹配 | |
| 256 | + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); | |
| 257 | + templateItem.setTemplateId(templateId); | |
| 258 | + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); | |
| 259 | + // mock 工艺路线 | |
| 260 | + MesProRouteProductDO routeProduct = new MesProRouteProductDO(); | |
| 261 | + routeProduct.setRouteId(routeId); | |
| 262 | + when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); | |
| 263 | + // mock 工序在路线中 | |
| 264 | + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) | |
| 265 | + .thenReturn(new MesProRouteProcessDO()); | |
| 266 | + | |
| 267 | + // 调用 | |
| 268 | + Long ipqcId = ipqcService.createIpqc(reqVO); | |
| 269 | + | |
| 270 | + // 断言:processId 应该是工位工序 | |
| 271 | + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); | |
| 272 | + assertEquals(processId, ipqc.getProcessId()); | |
| 273 | + } | |
| 274 | + | |
| 275 | + /** | |
| 276 | + * 工位没有工序(processId=null)→ processId 应该留 null(与 KTG 对齐,不回退到任务工序) | |
| 277 | + */ | |
| 278 | + @Test | |
| 279 | + public void testCreateIpqc_processId_workstationNoProcess_shouldBeNull() { | |
| 280 | + // 准备参数 | |
| 281 | + Long workstationId = randomLongId(); | |
| 282 | + Long workOrderId = randomLongId(); | |
| 283 | + Long productId = randomLongId(); | |
| 284 | + Long taskId = randomLongId(); | |
| 285 | + Long templateId = randomLongId(); | |
| 286 | + | |
| 287 | + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); | |
| 288 | + reqVO.setCode("IPQC-TEST-002"); | |
| 289 | + reqVO.setWorkstationId(workstationId); | |
| 290 | + reqVO.setWorkOrderId(workOrderId); | |
| 291 | + reqVO.setTaskId(taskId); // 故意传入 taskId,验证不会回退 | |
| 292 | + reqVO.setInspectorUserId(randomLongId()); | |
| 293 | + | |
| 294 | + // mock 工位没有工序 | |
| 295 | + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); | |
| 296 | + workstation.setId(workstationId); | |
| 297 | + workstation.setProcessId(null); // 无工序 | |
| 298 | + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); | |
| 299 | + // mock 工单 | |
| 300 | + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); | |
| 301 | + workOrder.setId(workOrderId); | |
| 302 | + workOrder.setProductId(productId); | |
| 303 | + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); | |
| 304 | + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); | |
| 305 | + // mock 模板匹配 | |
| 306 | + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); | |
| 307 | + templateItem.setTemplateId(templateId); | |
| 308 | + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); | |
| 309 | + | |
| 310 | + // 调用 | |
| 311 | + Long ipqcId = ipqcService.createIpqc(reqVO); | |
| 312 | + | |
| 313 | + // 断言:processId 应该是 null,即使传入了 taskId 也不应该回退 | |
| 314 | + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); | |
| 315 | + assertNull(ipqc.getProcessId(), | |
| 316 | + "工位无工序时 processId 应为 null(与 KTG 对齐),不应回退到任务工序"); | |
| 317 | + // 断言:不应该读取任务的 processId | |
| 318 | + verify(routeProductService, never()).getRouteProductByItemId(any()); | |
| 319 | + } | |
| 320 | + | |
| 321 | + /** | |
| 322 | + * 工位有工序但该工序不在产品工艺路线中 → processId 应该留 null | |
| 323 | + */ | |
| 324 | + @Test | |
| 325 | + public void testCreateIpqc_processId_processNotInRoute() { | |
| 326 | + // 准备参数 | |
| 327 | + Long workstationId = randomLongId(); | |
| 328 | + Long workOrderId = randomLongId(); | |
| 329 | + Long productId = randomLongId(); | |
| 330 | + Long processId = randomLongId(); | |
| 331 | + Long routeId = randomLongId(); | |
| 332 | + Long templateId = randomLongId(); | |
| 333 | + | |
| 334 | + MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO(); | |
| 335 | + reqVO.setCode("IPQC-TEST-003"); | |
| 336 | + reqVO.setWorkstationId(workstationId); | |
| 337 | + reqVO.setWorkOrderId(workOrderId); | |
| 338 | + reqVO.setInspectorUserId(randomLongId()); | |
| 339 | + | |
| 340 | + // mock 工位有工序 | |
| 341 | + MesMdWorkstationDO workstation = new MesMdWorkstationDO(); | |
| 342 | + workstation.setId(workstationId); | |
| 343 | + workstation.setProcessId(processId); | |
| 344 | + when(workstationService.validateWorkstationExists(workstationId)).thenReturn(workstation); | |
| 345 | + // mock 工单 | |
| 346 | + MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); | |
| 347 | + workOrder.setId(workOrderId); | |
| 348 | + workOrder.setProductId(productId); | |
| 349 | + when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); | |
| 350 | + when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); | |
| 351 | + // mock 模板匹配 | |
| 352 | + MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); | |
| 353 | + templateItem.setTemplateId(templateId); | |
| 354 | + when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); | |
| 355 | + // mock 工艺路线存在 | |
| 356 | + MesProRouteProductDO routeProduct = new MesProRouteProductDO(); | |
| 357 | + routeProduct.setRouteId(routeId); | |
| 358 | + when(routeProductService.getRouteProductByItemId(productId)).thenReturn(routeProduct); | |
| 359 | + // mock 工序不在路线中 | |
| 360 | + when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) | |
| 361 | + .thenReturn(null); | |
| 362 | + | |
| 363 | + // 调用 | |
| 364 | + Long ipqcId = ipqcService.createIpqc(reqVO); | |
| 365 | + | |
| 366 | + // 断言:processId 应该是 null | |
| 367 | + MesQcIpqcDO ipqc = ipqcMapper.selectById(ipqcId); | |
| 368 | + assertNull(ipqc.getProcessId()); | |
| 369 | + } | |
| 370 | + | |
| 214 | 371 | } |
| 372 | + | ... | ... |