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,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | import jakarta.validation.constraints.NotNull; | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import jakarta.validation.constraints.Size; | ||
| 6 | import lombok.Data; | 7 | import lombok.Data; |
| 7 | 8 | ||
| 8 | import java.time.LocalDateTime; | 9 | import java.time.LocalDateTime; |
| @@ -16,16 +17,20 @@ public class MesTmToolSaveReqVO { | @@ -16,16 +17,20 @@ public class MesTmToolSaveReqVO { | ||
| 16 | 17 | ||
| 17 | @Schema(description = "工具编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "T-001") | 18 | @Schema(description = "工具编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "T-001") |
| 18 | @NotEmpty(message = "工具编码不能为空") | 19 | @NotEmpty(message = "工具编码不能为空") |
| 20 | + @Size(max = 64, message = "工具编码长度不能超过 64 个字符") | ||
| 19 | private String code; | 21 | private String code; |
| 20 | 22 | ||
| 21 | @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "5mm 铣刀") | 23 | @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "5mm 铣刀") |
| 22 | @NotEmpty(message = "工具名称不能为空") | 24 | @NotEmpty(message = "工具名称不能为空") |
| 25 | + @Size(max = 255, message = "工具名称长度不能超过 255 个字符") | ||
| 23 | private String name; | 26 | private String name; |
| 24 | 27 | ||
| 25 | @Schema(description = "品牌", example = "三菱") | 28 | @Schema(description = "品牌", example = "三菱") |
| 29 | + @Size(max = 255, message = "品牌长度不能超过 255 个字符") | ||
| 26 | private String brand; | 30 | private String brand; |
| 27 | 31 | ||
| 28 | @Schema(description = "型号规格", example = "M5-100") | 32 | @Schema(description = "型号规格", example = "M5-100") |
| 33 | + @Size(max = 255, message = "型号规格长度不能超过 255 个字符") | ||
| 29 | private String spec; | 34 | private String spec; |
| 30 | 35 | ||
| 31 | @Schema(description = "工具类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | 36 | @Schema(description = "工具类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| @@ -36,7 +41,8 @@ public class MesTmToolSaveReqVO { | @@ -36,7 +41,8 @@ public class MesTmToolSaveReqVO { | ||
| 36 | @NotNull(message = "数量不能为空") | 41 | @NotNull(message = "数量不能为空") |
| 37 | private Integer quantity; | 42 | private Integer quantity; |
| 38 | 43 | ||
| 39 | - @Schema(description = "可用数量", example = "1") | 44 | + @Schema(description = "可用数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 45 | + @NotNull(message = "可用数量不能为空") | ||
| 40 | private Integer availableQuantity; | 46 | private Integer availableQuantity; |
| 41 | 47 | ||
| 42 | @Schema(description = "保养维护类型", example = "1") | 48 | @Schema(description = "保养维护类型", example = "1") |
| @@ -53,6 +59,7 @@ public class MesTmToolSaveReqVO { | @@ -53,6 +59,7 @@ public class MesTmToolSaveReqVO { | ||
| 53 | private Integer status; | 59 | private Integer status; |
| 54 | 60 | ||
| 55 | @Schema(description = "备注", example = "备注") | 61 | @Schema(description = "备注", example = "备注") |
| 62 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | ||
| 56 | private String remark; | 63 | private String remark; |
| 57 | 64 | ||
| 58 | - } | 65 | +} |
| 59 | \ No newline at end of file | 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,6 +3,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | import jakarta.validation.constraints.NotNull; | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import jakarta.validation.constraints.Size; | ||
| 6 | import lombok.Data; | 7 | import lombok.Data; |
| 7 | 8 | ||
| 8 | @Schema(description = "管理后台 - MES 工具类型新增/修改 Request VO") | 9 | @Schema(description = "管理后台 - MES 工具类型新增/修改 Request VO") |
| @@ -14,10 +15,12 @@ public class MesTmToolTypeSaveReqVO { | @@ -14,10 +15,12 @@ public class MesTmToolTypeSaveReqVO { | ||
| 14 | 15 | ||
| 15 | @Schema(description = "类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "TT-001") | 16 | @Schema(description = "类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "TT-001") |
| 16 | @NotEmpty(message = "类型编码不能为空") | 17 | @NotEmpty(message = "类型编码不能为空") |
| 18 | + @Size(max = 64, message = "类型编码长度不能超过 64 个字符") | ||
| 17 | private String code; | 19 | private String code; |
| 18 | 20 | ||
| 19 | @Schema(description = "类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "铣刀") | 21 | @Schema(description = "类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "铣刀") |
| 20 | @NotEmpty(message = "类型名称不能为空") | 22 | @NotEmpty(message = "类型名称不能为空") |
| 23 | + @Size(max = 255, message = "类型名称长度不能超过 255 个字符") | ||
| 21 | private String name; | 24 | private String name; |
| 22 | 25 | ||
| 23 | @Schema(description = "是否编码管理", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | 26 | @Schema(description = "是否编码管理", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| @@ -31,6 +34,7 @@ public class MesTmToolTypeSaveReqVO { | @@ -31,6 +34,7 @@ public class MesTmToolTypeSaveReqVO { | ||
| 31 | private Integer maintenPeriod; | 34 | private Integer maintenPeriod; |
| 32 | 35 | ||
| 33 | @Schema(description = "备注", example = "备注") | 36 | @Schema(description = "备注", example = "备注") |
| 37 | + @Size(max = 500, message = "备注长度不能超过 500 个字符") | ||
| 34 | private String remark; | 38 | private String remark; |
| 35 | 39 | ||
| 36 | - } | 40 | +} |
| 37 | \ No newline at end of file | 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,6 +2,7 @@ package cn.iocoder.yudao.module.mes.controller.admin.wm.itemconsume.vo; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | +import jakarta.validation.constraints.NotNull; | ||
| 5 | import lombok.Data; | 6 | import lombok.Data; |
| 6 | import lombok.EqualsAndHashCode; | 7 | import lombok.EqualsAndHashCode; |
| 7 | import lombok.ToString; | 8 | import lombok.ToString; |
| @@ -13,6 +14,7 @@ import lombok.ToString; | @@ -13,6 +14,7 @@ import lombok.ToString; | ||
| 13 | public class MesWmItemConsumeLinePageReqVO extends PageParam { | 14 | public class MesWmItemConsumeLinePageReqVO extends PageParam { |
| 14 | 15 | ||
| 15 | @Schema(description = "报工记录编号", requiredMode = Schema.RequiredMode.REQUIRED) | 16 | @Schema(description = "报工记录编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 17 | + @NotNull(message = "报工记录编号不能为空") | ||
| 16 | private Long feedbackId; | 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,4 +29,8 @@ public interface MesMdWorkstationToolMapper extends BaseMapperX<MesMdWorkstation | ||
| 29 | delete(MesMdWorkstationToolDO::getWorkstationId, workstationId); | 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,6 +217,7 @@ public interface ErrorCodeConstants { | ||
| 217 | ErrorCode TM_TOOL_TYPE_CODE_DUPLICATE = new ErrorCode(1_040_400_001, "工具类型编码已存在"); | 217 | ErrorCode TM_TOOL_TYPE_CODE_DUPLICATE = new ErrorCode(1_040_400_001, "工具类型编码已存在"); |
| 218 | ErrorCode TM_TOOL_TYPE_NAME_DUPLICATE = new ErrorCode(1_040_400_002, "工具类型名称已存在"); | 218 | ErrorCode TM_TOOL_TYPE_NAME_DUPLICATE = new ErrorCode(1_040_400_002, "工具类型名称已存在"); |
| 219 | ErrorCode TM_TOOL_TYPE_HAS_TOOL = new ErrorCode(1_040_400_003, "该工具类型下存在工具,无法删除"); | 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 | // ========== MES 工具管理-工具台账(1-040-401-000) ========== | 222 | // ========== MES 工具管理-工具台账(1-040-401-000) ========== |
| 222 | ErrorCode TM_TOOL_NOT_EXISTS = new ErrorCode(1_040_401_000, "工具不存在"); | 223 | ErrorCode TM_TOOL_NOT_EXISTS = new ErrorCode(1_040_401_000, "工具不存在"); |
| @@ -362,6 +363,7 @@ public interface ErrorCodeConstants { | @@ -362,6 +363,7 @@ public interface ErrorCodeConstants { | ||
| 362 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); | 363 | ErrorCode QC_OQC_QUANTITY_MISMATCH = new ErrorCode(1_040_607_004, "合格品与不合格品数量之和须等于检测数量"); |
| 363 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); | 364 | ErrorCode QC_OQC_NO_TEMPLATE = new ErrorCode(1_040_607_005, "当前产品未配置 OQC 检测模板"); |
| 364 | ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); | 365 | ErrorCode QC_OQC_CHECK_RESULT_EMPTY = new ErrorCode(1_040_607_006, "完成检验单前,检测结果必须填写"); |
| 366 | + | ||
| 365 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== | 367 | // ========== MES 质量管理-出货检验行(1-040-607-100) ========== |
| 366 | ErrorCode QC_OQC_LINE_NOT_EXISTS = new ErrorCode(1_040_607_100, "出货检验行不存在"); | 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,4 +50,12 @@ public interface MesMdWorkstationToolService { | ||
| 50 | */ | 50 | */ |
| 51 | void deleteWorkstationToolByWorkstationId(Long workstationId); | 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,4 +89,9 @@ public class MesMdWorkstationToolServiceImpl implements MesMdWorkstationToolServ | ||
| 89 | workstationToolMapper.deleteByWorkstationId(workstationId); | 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,11 +82,14 @@ public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultServ | ||
| 82 | @Override | 82 | @Override |
| 83 | @Transactional(rollbackFor = Exception.class) | 83 | @Transactional(rollbackFor = Exception.class) |
| 84 | public void updateIndicatorResult(MesQcIndicatorResultSaveReqVO updateReqVO) { | 84 | public void updateIndicatorResult(MesQcIndicatorResultSaveReqVO updateReqVO) { |
| 85 | - // 1. 校验存在 | 85 | + // 1.1 校验存在 |
| 86 | validateIndicatorResultExists(updateReqVO.getId()); | 86 | validateIndicatorResultExists(updateReqVO.getId()); |
| 87 | + // 1.2 校验所有明细的 indicatorId 是否存在 | ||
| 88 | + validateIndicatorIds(updateReqVO.getItems()); | ||
| 87 | 89 | ||
| 88 | - // 2.1 更新主表 | 90 | + // 2.1 更新主表(锁定 qcId/qcType/itemId,不允许改挂到其他质检单) |
| 89 | MesQcIndicatorResultDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorResultDO.class); | 91 | MesQcIndicatorResultDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorResultDO.class); |
| 92 | + updateObj.setQcId(null).setQcType(null).setItemId(null); | ||
| 90 | resultMapper.updateById(updateObj); | 93 | resultMapper.updateById(updateObj); |
| 91 | // 2.2 批量更新明细 | 94 | // 2.2 批量更新明细 |
| 92 | List<MesQcIndicatorResultDetailDO> details = BeanUtils.toBean(updateReqVO.getItems(), | 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,7 +283,7 @@ public class MesQcIpqcServiceImpl implements MesQcIpqcService { | ||
| 283 | * 推导工序 ID | 283 | * 推导工序 ID |
| 284 | * | 284 | * |
| 285 | * <p>通过产品 → 工艺路线产品 → 工艺路线 → 校验工位工序是否在该路线中 | 285 | * <p>通过产品 → 工艺路线产品 → 工艺路线 → 校验工位工序是否在该路线中 |
| 286 | - * <p>如果工位的工序在路线中,则返回该 processId;否则返回 null | 286 | + * <p>如果工位的工序在路线中,则返回该 processId;否则返回 null(与 KTG 保持一致) |
| 287 | * | 287 | * |
| 288 | * @param productId 产品 ID(工单产品) | 288 | * @param productId 产品 ID(工单产品) |
| 289 | * @param workstationProcessId 工位关联的工序 ID | 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,8 +221,8 @@ public class MesQcOqcServiceImpl implements MesQcOqcService { | ||
| 221 | return null; | 221 | return null; |
| 222 | } | 222 | } |
| 223 | if (Objects.equals(sourceDocType, MesBizTypeConstants.WM_PRODUCT_SALES)) { | 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 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); | 226 | MesWmProductSalesDO sales = productSalesService.getProductSales(salesLine.getSalesId()); |
| 227 | return sales != null ? sales.getCode() : null; | 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,6 +7,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTyp | ||
| 7 | import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTypeSaveReqVO; | 7 | import cn.iocoder.yudao.module.mes.controller.admin.tm.tool.vo.type.MesTmToolTypeSaveReqVO; |
| 8 | import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolTypeDO; | 8 | import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolTypeDO; |
| 9 | import cn.iocoder.yudao.module.mes.dal.mysql.tm.tool.MesTmToolTypeMapper; | 9 | import cn.iocoder.yudao.module.mes.dal.mysql.tm.tool.MesTmToolTypeMapper; |
| 10 | +import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationToolService; | ||
| 10 | import jakarta.annotation.Resource; | 11 | import jakarta.annotation.Resource; |
| 11 | import org.springframework.context.annotation.Lazy; | 12 | import org.springframework.context.annotation.Lazy; |
| 12 | import org.springframework.stereotype.Service; | 13 | import org.springframework.stereotype.Service; |
| @@ -37,6 +38,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { | @@ -37,6 +38,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { | ||
| 37 | @Lazy | 38 | @Lazy |
| 38 | private MesTmToolService toolService; | 39 | private MesTmToolService toolService; |
| 39 | 40 | ||
| 41 | + @Resource | ||
| 42 | + @Lazy | ||
| 43 | + private MesMdWorkstationToolService workstationToolService; | ||
| 44 | + | ||
| 40 | @Override | 45 | @Override |
| 41 | public Long createToolType(MesTmToolTypeSaveReqVO createReqVO) { | 46 | public Long createToolType(MesTmToolTypeSaveReqVO createReqVO) { |
| 42 | // 校验数据 | 47 | // 校验数据 |
| @@ -75,6 +80,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { | @@ -75,6 +80,10 @@ public class MesTmToolTypeServiceImpl implements MesTmToolTypeService { | ||
| 75 | if (toolService.getToolCountByToolTypeId(id) > 0) { | 80 | if (toolService.getToolCountByToolTypeId(id) > 0) { |
| 76 | throw exception(TM_TOOL_TYPE_HAS_TOOL); | 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 | toolTypeMapper.deleteById(id); | 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,6 +39,11 @@ import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; | ||
| 39 | @Slf4j | 39 | @Slf4j |
| 40 | public class MesWmBatchServiceImpl implements MesWmBatchService { | 40 | public class MesWmBatchServiceImpl implements MesWmBatchService { |
| 41 | 41 | ||
| 42 | + /** | ||
| 43 | + * 批次追溯最大递归深度,防止极端场景下性能问题 | ||
| 44 | + */ | ||
| 45 | + private static final int MAX_TRACE_DEPTH = 20; | ||
| 46 | + | ||
| 42 | @Resource | 47 | @Resource |
| 43 | private MesWmBatchMapper batchMapper; | 48 | private MesWmBatchMapper batchMapper; |
| 44 | @Resource | 49 | @Resource |
| @@ -196,11 +201,11 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | @@ -196,11 +201,11 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | ||
| 196 | 201 | ||
| 197 | @Override | 202 | @Override |
| 198 | public List<MesWmBatchDO> getForwardBatchList(String code) { | 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 | return new ArrayList<>(); | 209 | return new ArrayList<>(); |
| 205 | } | 210 | } |
| 206 | List<MesWmBatchDO> list = batchMapper.selectListByForward(code); | 211 | List<MesWmBatchDO> list = batchMapper.selectListByForward(code); |
| @@ -210,18 +215,18 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | @@ -210,18 +215,18 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | ||
| 210 | // 继续递归查询下游批次 | 215 | // 继续递归查询下游批次 |
| 211 | List<MesWmBatchDO> results = new ArrayList<>(list); | 216 | List<MesWmBatchDO> results = new ArrayList<>(list); |
| 212 | for (MesWmBatchDO batch : list) { | 217 | for (MesWmBatchDO batch : list) { |
| 213 | - results.addAll(getForwardBatchList(batch.getCode(), visited)); | 218 | + results.addAll(getForwardBatchList(batch.getCode(), visited, depth + 1)); |
| 214 | } | 219 | } |
| 215 | return results; | 220 | return results; |
| 216 | } | 221 | } |
| 217 | 222 | ||
| 218 | @Override | 223 | @Override |
| 219 | public List<MesWmBatchDO> getBackwardBatchList(String code) { | 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 | return new ArrayList<>(); | 230 | return new ArrayList<>(); |
| 226 | } | 231 | } |
| 227 | List<MesWmBatchDO> list = batchMapper.selectListByBackward(code); | 232 | List<MesWmBatchDO> list = batchMapper.selectListByBackward(code); |
| @@ -231,7 +236,7 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | @@ -231,7 +236,7 @@ public class MesWmBatchServiceImpl implements MesWmBatchService { | ||
| 231 | // 继续递归查询上游批次 | 236 | // 继续递归查询上游批次 |
| 232 | List<MesWmBatchDO> results = new ArrayList<>(list); | 237 | List<MesWmBatchDO> results = new ArrayList<>(list); |
| 233 | for (MesWmBatchDO batch : list) { | 238 | for (MesWmBatchDO batch : list) { |
| 234 | - results.addAll(getBackwardBatchList(batch.getCode(), visited)); | 239 | + results.addAll(getBackwardBatchList(batch.getCode(), visited, depth + 1)); |
| 235 | } | 240 | } |
| 236 | return results; | 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,6 +69,14 @@ public interface MesWmProductSalesLineService { | ||
| 69 | PageResult<MesWmProductSalesLineDO> getProductSalesLinePage(MesWmProductSalesLinePageReqVO pageReqVO); | 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 | * OQC 检验完成后,更新销售出库单行的 OQC 质检状态 | 80 | * OQC 检验完成后,更新销售出库单行的 OQC 质检状态 |
| 73 | * | 81 | * |
| 74 | * @param id 行 ID | 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,7 +124,8 @@ public class MesWmProductSalesLineServiceImpl implements MesWmProductSalesLineSe | ||
| 124 | return productSalesLineMapper.selectPage(pageReqVO); | 124 | return productSalesLineMapper.selectPage(pageReqVO); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | - private MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { | 127 | + @Override |
| 128 | + public MesWmProductSalesLineDO validateProductSalesLineExists(Long id) { | ||
| 128 | MesWmProductSalesLineDO line = productSalesLineMapper.selectById(id); | 129 | MesWmProductSalesLineDO line = productSalesLineMapper.selectById(id); |
| 129 | if (line == null) { | 130 | if (line == null) { |
| 130 | throw exception(WM_PRODUCT_SALES_LINE_NOT_EXISTS); | 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,6 +8,9 @@ import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum; | ||
| 8 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; | 8 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 9 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; | 9 | import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService; |
| 10 | import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService; | 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 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; | 14 | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| 12 | import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; | 15 | import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService; |
| 13 | import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; | 16 | import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService; |
| @@ -54,6 +57,12 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { | @@ -54,6 +57,12 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { | ||
| 54 | private AdminUserApi adminUserApi; | 57 | private AdminUserApi adminUserApi; |
| 55 | @MockitoBean | 58 | @MockitoBean |
| 56 | private MesProFeedbackService feedbackService; | 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 | @Test | 67 | @Test |
| 59 | public void testFinishIpqc_writeBack_feedback() { | 68 | public void testFinishIpqc_writeBack_feedback() { |
| @@ -211,4 +220,153 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { | @@ -211,4 +220,153 @@ public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { | ||
| 211 | assertThrows(Exception.class, () -> ipqcService.finishIpqc(ipqc.getId())); | 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 | + |