Commit 977fdca68a16ed6f3714707f277caa3d7f24a599

Authored by YunaiV
1 parent 6d0569bc

feat(mes): 新增物料产品分类与物料产品管理

迁移 ktg-mes 的 md_item_type 和 md_item 两张表到 yudao-module-mes,
包含完整的 CRUD、树形分类、物料分页查询、enrichment 等功能。

后端新增 17 个文件(1337 行):
- DO: MesMdItemTypeDO(树形分类)、MesMdItemDO(物料产品)
- VO: 分类 3 个(List/Save/Resp)、物料 3 个(Page/Save/Resp)
- Mapper: 2 个(BaseMapperX,纯 Java,无 XML)
- Service: 4 个(接口 + 实现)
- Controller: 2 个(/mes/md/item-type、/mes/md/item)
- ErrorCodeConstants 追加 9 个错误码

关键业务逻辑:
- 分类:父分类递归环路检测、同级名称/编码唯一性、删除保护(子分类+物料)
- 物料:编码/名称全局唯一、分类存在校验、安全库存联动清零
- enrichment:物料 RespVO 通过 Service 关联填充 itemTypeName、itemOrProduct
- 循环依赖:MesMdItemTypeServiceImpl 用 @Lazy 注入 MesMdItemService

plan: plans/swirling-stirring-metcalfe.md
Showing 17 changed files with 1337 additions and 0 deletions
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdItemController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  10 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO;
  11 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemRespVO;
  12 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
  13 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  14 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.servlet.http.HttpServletResponse;
  20 +import jakarta.validation.Valid;
  21 +import org.springframework.security.access.prepost.PreAuthorize;
  22 +import org.springframework.validation.annotation.Validated;
  23 +import org.springframework.web.bind.annotation.*;
  24 +
  25 +import java.io.IOException;
  26 +import java.util.List;
  27 +
  28 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  29 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  31 +
  32 +@Tag(name = "管理后台 - MES 物料产品")
  33 +@RestController
  34 +@RequestMapping("/mes/md/item")
  35 +@Validated
  36 +public class MesMdItemController {
  37 +
  38 + @Resource
  39 + private MesMdItemService itemService;
  40 +
  41 + @PostMapping("/create")
  42 + @Operation(summary = "创建物料产品")
  43 + @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
  44 + public CommonResult<Long> createItem(@Valid @RequestBody MesMdItemSaveReqVO createReqVO) {
  45 + return success(itemService.createItem(createReqVO));
  46 + }
  47 +
  48 + @PutMapping("/update")
  49 + @Operation(summary = "更新物料产品")
  50 + @PreAuthorize("@ss.hasPermission('mes:md-item:update')")
  51 + public CommonResult<Boolean> updateItem(@Valid @RequestBody MesMdItemSaveReqVO updateReqVO) {
  52 + itemService.updateItem(updateReqVO);
  53 + return success(true);
  54 + }
  55 +
  56 + @DeleteMapping("/delete")
  57 + @Operation(summary = "删除物料产品")
  58 + @Parameter(name = "id", description = "编号", required = true)
  59 + @PreAuthorize("@ss.hasPermission('mes:md-item:delete')")
  60 + public CommonResult<Boolean> deleteItem(@RequestParam("id") Long id) {
  61 + itemService.deleteItem(id);
  62 + return success(true);
  63 + }
  64 +
  65 + @GetMapping("/get")
  66 + @Operation(summary = "获得物料产品")
  67 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  68 + @PreAuthorize("@ss.hasPermission('mes:md-item:query')")
  69 + public CommonResult<MesMdItemRespVO> getItem(@RequestParam("id") Long id) {
  70 + MesMdItemDO item = itemService.getItem(id);
  71 + return success(BeanUtils.toBean(item, MesMdItemRespVO.class));
  72 + }
  73 +
  74 + @GetMapping("/page")
  75 + @Operation(summary = "获得物料产品分页")
  76 + @PreAuthorize("@ss.hasPermission('mes:md-item:query')")
  77 + public CommonResult<PageResult<MesMdItemRespVO>> getItemPage(@Valid MesMdItemPageReqVO pageReqVO) {
  78 + return success(itemService.getItemVOPage(pageReqVO));
  79 + }
  80 +
  81 + @GetMapping("/simple-list")
  82 + @Operation(summary = "获得物料产品精简列表", description = "只包含被开启的物料,主要用于前端的下拉选项")
  83 + public CommonResult<List<MesMdItemRespVO>> getItemSimpleList() {
  84 + List<MesMdItemRespVO> list = itemService.getItemVOListByStatus(CommonStatusEnum.ENABLE.getStatus());
  85 + return success(convertList(list, item -> new MesMdItemRespVO()
  86 + .setId(item.getId()).setName(item.getName()).setCode(item.getCode())
  87 + .setItemTypeId(item.getItemTypeId()).setItemTypeName(item.getItemTypeName())
  88 + .setUnitOfMeasure(item.getUnitOfMeasure())));
  89 + }
  90 +
  91 + @GetMapping("/export-excel")
  92 + @Operation(summary = "导出物料产品 Excel")
  93 + @PreAuthorize("@ss.hasPermission('mes:md-item:export')")
  94 + @ApiAccessLog(operateType = EXPORT)
  95 + public void exportItemExcel(@Valid MesMdItemPageReqVO pageReqVO,
  96 + HttpServletResponse response) throws IOException {
  97 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  98 + PageResult<MesMdItemRespVO> pageResult = itemService.getItemVOPage(pageReqVO);
  99 + // 导出 Excel
  100 + ExcelUtils.write(response, "物料产品.xls", "数据", MesMdItemRespVO.class,
  101 + pageResult.getList());
  102 + }
  103 +
  104 + // TODO @AI:缺少批量导入;
  105 +
  106 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdItemTypeController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
  9 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeRespVO;
  10 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  12 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
  13 +import io.swagger.v3.oas.annotations.Operation;
  14 +import io.swagger.v3.oas.annotations.Parameter;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import jakarta.annotation.Resource;
  17 +import jakarta.servlet.http.HttpServletResponse;
  18 +import jakarta.validation.Valid;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.validation.annotation.Validated;
  21 +import org.springframework.web.bind.annotation.*;
  22 +
  23 +import java.io.IOException;
  24 +import java.util.List;
  25 +
  26 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  29 +
  30 +@Tag(name = "管理后台 - MES 物料产品分类")
  31 +@RestController
  32 +@RequestMapping("/mes/md/item-type")
  33 +@Validated
  34 +public class MesMdItemTypeController {
  35 +
  36 + @Resource
  37 + private MesMdItemTypeService itemTypeService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建物料产品分类")
  41 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:create')")
  42 + public CommonResult<Long> createItemType(@Valid @RequestBody MesMdItemTypeSaveReqVO createReqVO) {
  43 + return success(itemTypeService.createItemType(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新物料产品分类")
  48 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:update')")
  49 + public CommonResult<Boolean> updateItemType(@Valid @RequestBody MesMdItemTypeSaveReqVO updateReqVO) {
  50 + itemTypeService.updateItemType(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除物料产品分类")
  56 + @Parameter(name = "id", description = "编号", required = true)
  57 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:delete')")
  58 + public CommonResult<Boolean> deleteItemType(@RequestParam("id") Long id) {
  59 + itemTypeService.deleteItemType(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得物料产品分类")
  65 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:query')")
  67 + public CommonResult<MesMdItemTypeRespVO> getItemType(@RequestParam("id") Long id) {
  68 + MesMdItemTypeDO itemType = itemTypeService.getItemType(id);
  69 + return success(BeanUtils.toBean(itemType, MesMdItemTypeRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/list")
  73 + @Operation(summary = "获得物料产品分类列表")
  74 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:query')")
  75 + public CommonResult<List<MesMdItemTypeRespVO>> getItemTypeList(@Valid MesMdItemTypeListReqVO listReqVO) {
  76 + List<MesMdItemTypeDO> list = itemTypeService.getItemTypeList(listReqVO);
  77 + return success(BeanUtils.toBean(list, MesMdItemTypeRespVO.class));
  78 + }
  79 +
  80 + @GetMapping("/simple-list")
  81 + @Operation(summary = "获得物料产品分类精简列表", description = "只包含被开启的分类,主要用于前端的下拉选项")
  82 + public CommonResult<List<MesMdItemTypeRespVO>> getItemTypeSimpleList() {
  83 + List<MesMdItemTypeDO> list = itemTypeService.getItemTypeList(
  84 + new MesMdItemTypeListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
  85 + return success(convertList(list, itemType -> new MesMdItemTypeRespVO()
  86 + .setId(itemType.getId()).setName(itemType.getName()).setParentId(itemType.getParentId())));
  87 + }
  88 +
  89 + // TODO @AI:不需要导出;
  90 + @GetMapping("/export-excel")
  91 + @Operation(summary = "导出物料产品分类 Excel")
  92 + @PreAuthorize("@ss.hasPermission('mes:md-item-type:export')")
  93 + @ApiAccessLog(operateType = EXPORT)
  94 + public void exportItemTypeExcel(@Valid MesMdItemTypeListReqVO listReqVO,
  95 + HttpServletResponse response) throws IOException {
  96 + List<MesMdItemTypeDO> list = itemTypeService.getItemTypeList(listReqVO);
  97 + // 导出 Excel
  98 + ExcelUtils.write(response, "物料产品分类.xls", "数据", MesMdItemTypeRespVO.class,
  99 + BeanUtils.toBean(list, MesMdItemTypeRespVO.class));
  100 + }
  101 +
  102 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - MES 物料产品分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class MesMdItemPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "物料编码", example = "ITEM001")
  21 + private String code;
  22 +
  23 + @Schema(description = "物料名称", example = "螺丝")
  24 + private String name;
  25 +
  26 + @Schema(description = "物料分类编号", example = "1")
  27 + private Long itemTypeId;
  28 +
  29 + @Schema(description = "状态", example = "0")
  30 + private Integer status;
  31 +
  32 + @Schema(description = "创建时间")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] createTime;
  35 +
  36 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
  6 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  7 +import cn.idev.excel.annotation.ExcelProperty;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +
  11 +import java.math.BigDecimal;
  12 +import java.time.LocalDateTime;
  13 +
  14 +@Schema(description = "管理后台 - MES 物料产品 Response VO")
  15 +@Data
  16 +@ExcelIgnoreUnannotated
  17 +public class MesMdItemRespVO {
  18 +
  19 + @Schema(description = "物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  20 + @ExcelProperty("物料编号")
  21 + private Long id;
  22 +
  23 + @Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM001")
  24 + @ExcelProperty("物料编码")
  25 + private String code;
  26 +
  27 + @Schema(description = "物料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "螺丝")
  28 + @ExcelProperty("物料名称")
  29 + private String name;
  30 +
  31 + @Schema(description = "规格型号", example = "M6*20")
  32 + @ExcelProperty("规格型号")
  33 + private String specification;
  34 +
  35 + @Schema(description = "单位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "pcs")
  36 + @ExcelProperty("单位编码")
  37 + private String unitOfMeasure;
  38 +
  39 + @Schema(description = "物料分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  40 + private Long itemTypeId;
  41 + @Schema(description = "物料分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原材料")
  42 + @ExcelProperty("物料分类")
  43 + private String itemTypeName;
  44 +
  45 + @Schema(description = "物料/产品标识", example = "ITEM")
  46 + @ExcelProperty("物料/产品标识")
  47 + private String itemOrProduct;
  48 +
  49 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  50 + @ExcelProperty(value = "状态", converter = DictConvert.class)
  51 + @DictFormat(DictTypeConstants.COMMON_STATUS)
  52 + private Integer status;
  53 +
  54 + @Schema(description = "是否启用安全库存", example = "false")
  55 + @ExcelProperty("是否启用安全库存")
  56 + private Boolean safeStockFlag;
  57 +
  58 + @Schema(description = "最低库存量", example = "100.0000")
  59 + @ExcelProperty("最低库存量")
  60 + private BigDecimal minStock;
  61 +
  62 + @Schema(description = "最高库存量", example = "10000.0000")
  63 + @ExcelProperty("最高库存量")
  64 + private BigDecimal maxStock;
  65 +
  66 + @Schema(description = "是否高值物料", example = "false")
  67 + @ExcelProperty("是否高值物料")
  68 + private Boolean highValue;
  69 +
  70 + @Schema(description = "是否启用批次管理", example = "true")
  71 + @ExcelProperty("是否启用批次管理")
  72 + private Boolean batchFlag;
  73 +
  74 + @Schema(description = "备注", example = "备注")
  75 + @ExcelProperty("备注")
  76 + private String remark;
  77 +
  78 + @Schema(description = "预留字段1")
  79 + private String attr1;
  80 +
  81 + @Schema(description = "预留字段2")
  82 + private String attr2;
  83 +
  84 + @Schema(description = "预留字段3")
  85 + private Integer attr3;
  86 +
  87 + @Schema(description = "预留字段4")
  88 + private Integer attr4;
  89 +
  90 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  91 + @ExcelProperty("创建时间")
  92 + private LocalDateTime createTime;
  93 +
  94 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.math.BigDecimal;
  9 +
  10 +@Schema(description = "管理后台 - MES 物料产品新增/修改 Request VO")
  11 +@Data
  12 +public class MesMdItemSaveReqVO {
  13 +
  14 + @Schema(description = "物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Long id;
  16 +
  17 + @Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM001")
  18 + @NotEmpty(message = "物料编码不能为空")
  19 + private String code;
  20 +
  21 + @Schema(description = "物料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "螺丝")
  22 + @NotEmpty(message = "物料名称不能为空")
  23 + private String name;
  24 +
  25 + @Schema(description = "规格型号", example = "M6*20")
  26 + private String specification;
  27 +
  28 + @Schema(description = "单位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "pcs")
  29 + @NotEmpty(message = "单位编码不能为空")
  30 + private String unitOfMeasure;
  31 +
  32 + @Schema(description = "物料分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "物料分类编号不能为空")
  34 + private Long itemTypeId;
  35 +
  36 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  37 + @NotNull(message = "状态不能为空")
  38 + private Integer status;
  39 +
  40 + @Schema(description = "是否启用安全库存", example = "false")
  41 + private Boolean safeStockFlag;
  42 +
  43 + @Schema(description = "最低库存量", example = "100.0000")
  44 + private BigDecimal minStock;
  45 +
  46 + @Schema(description = "最高库存量", example = "10000.0000")
  47 + private BigDecimal maxStock;
  48 +
  49 + @Schema(description = "是否高值物料", example = "false")
  50 + private Boolean highValue;
  51 +
  52 + @Schema(description = "是否启用批次管理", example = "true")
  53 + private Boolean batchFlag;
  54 +
  55 + @Schema(description = "备注", example = "备注")
  56 + private String remark;
  57 +
  58 + @Schema(description = "预留字段1")
  59 + private String attr1;
  60 +
  61 + @Schema(description = "预留字段2")
  62 + private String attr2;
  63 +
  64 + @Schema(description = "预留字段3")
  65 + private Integer attr3;
  66 +
  67 + @Schema(description = "预留字段4")
  68 + private Integer attr4;
  69 +
  70 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/type/MesMdItemTypeListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - MES 物料产品分类列表 Request VO")
  7 +@Data
  8 +public class MesMdItemTypeListReqVO {
  9 +
  10 + @Schema(description = "分类名称", example = "原材料")
  11 + private String name;
  12 +
  13 + @Schema(description = "状态", example = "0")
  14 + private Integer status;
  15 +
  16 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/type/MesMdItemTypeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
  6 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  7 +import cn.idev.excel.annotation.ExcelProperty;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +@Schema(description = "管理后台 - MES 物料产品分类 Response VO")
  14 +@Data
  15 +@ExcelIgnoreUnannotated
  16 +public class MesMdItemTypeRespVO {
  17 +
  18 + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @ExcelProperty("分类编号")
  20 + private Long id;
  21 +
  22 + @Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  23 + @ExcelProperty("父分类编号")
  24 + private Long parentId;
  25 +
  26 + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "RAW")
  27 + @ExcelProperty("分类编码")
  28 + private String code;
  29 +
  30 + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原材料")
  31 + @ExcelProperty("分类名称")
  32 + private String name;
  33 +
  34 + @Schema(description = "物料/产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM")
  35 + @ExcelProperty("物料/产品标识")
  36 + private String itemOrProduct;
  37 +
  38 + @Schema(description = "显示排序", example = "0")
  39 + @ExcelProperty("显示排序")
  40 + private Integer sort;
  41 +
  42 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  43 + @ExcelProperty(value = "状态", converter = DictConvert.class)
  44 + @DictFormat(DictTypeConstants.COMMON_STATUS)
  45 + private Integer status;
  46 +
  47 + @Schema(description = "备注", example = "备注")
  48 + @ExcelProperty("备注")
  49 + private String remark;
  50 +
  51 + @Schema(description = "预留字段1")
  52 + private String attr1;
  53 +
  54 + @Schema(description = "预留字段2")
  55 + private String attr2;
  56 +
  57 + @Schema(description = "预留字段3")
  58 + private Integer attr3;
  59 +
  60 + @Schema(description = "预留字段4")
  61 + private Integer attr4;
  62 +
  63 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  64 + @ExcelProperty("创建时间")
  65 + private LocalDateTime createTime;
  66 +
  67 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/type/MesMdItemTypeSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - MES 物料产品分类新增/修改 Request VO")
  9 +@Data
  10 +public class MesMdItemTypeSaveReqVO {
  11 +
  12 + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + private Long id;
  14 +
  15 + @Schema(description = "父分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  16 + @NotNull(message = "父分类编号不能为空")
  17 + private Long parentId;
  18 +
  19 + @Schema(description = "分类编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "RAW")
  20 + @NotEmpty(message = "分类编码不能为空")
  21 + private String code;
  22 +
  23 + @Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "原材料")
  24 + @NotEmpty(message = "分类名称不能为空")
  25 + private String name;
  26 +
  27 + @Schema(description = "物料/产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "ITEM")
  28 + @NotEmpty(message = "物料/产品标识不能为空")
  29 + private String itemOrProduct;
  30 +
  31 + @Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  32 + @NotNull(message = "显示排序不能为空")
  33 + private Integer sort;
  34 +
  35 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  36 + @NotNull(message = "状态不能为空")
  37 + private Integer status;
  38 +
  39 + @Schema(description = "备注", example = "备注")
  40 + private String remark;
  41 +
  42 + @Schema(description = "预留字段1")
  43 + private String attr1;
  44 +
  45 + @Schema(description = "预留字段2")
  46 + private String attr2;
  47 +
  48 + @Schema(description = "预留字段3")
  49 + private Integer attr3;
  50 +
  51 + @Schema(description = "预留字段4")
  52 + private Integer attr4;
  53 +
  54 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +import java.math.BigDecimal;
  10 +
  11 +/**
  12 + * MES 物料产品 DO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName("mes_md_item")
  17 +@KeySequence("mes_md_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class MesMdItemDO extends BaseDO {
  25 +
  26 + /**
  27 + * 物料编号
  28 + */
  29 + @TableId
  30 + private Long id;
  31 + /**
  32 + * 物料编码
  33 + */
  34 + private String code;
  35 + /**
  36 + * 物料名称
  37 + */
  38 + private String name;
  39 + /**
  40 + * 规格型号
  41 + */
  42 + private String specification;
  43 + /**
  44 + * 单位编码
  45 + */
  46 + private String unitOfMeasure;
  47 + /**
  48 + * 物料分类编号
  49 + *
  50 + * 关联 {@link MesMdItemTypeDO#getId()}
  51 + */
  52 + private Long itemTypeId;
  53 + /**
  54 + * 状态
  55 + *
  56 + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
  57 + */
  58 + private Integer status;
  59 + /**
  60 + * 是否启用安全库存
  61 + */
  62 + private Boolean safeStockFlag;
  63 + /**
  64 + * 最低库存量
  65 + */
  66 + private BigDecimal minStock;
  67 + /**
  68 + * 最高库存量
  69 + */
  70 + private BigDecimal maxStock;
  71 + /**
  72 + * 是否高值物料
  73 + */
  74 + private Boolean highValue;
  75 + /**
  76 + * 是否启用批次管理
  77 + */
  78 + private Boolean batchFlag;
  79 + /**
  80 + * 备注
  81 + */
  82 + private String remark;
  83 + /**
  84 + * 预留字段1
  85 + */
  86 + private String attr1;
  87 + /**
  88 + * 预留字段2
  89 + */
  90 + private String attr2;
  91 + /**
  92 + * 预留字段3
  93 + */
  94 + private Integer attr3;
  95 + /**
  96 + * 预留字段4
  97 + */
  98 + private Integer attr4;
  99 +
  100 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemTypeDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.dataobject.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +/**
  10 + * MES 物料产品分类 DO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@TableName("mes_md_item_type")
  15 +@KeySequence("mes_md_item_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class MesMdItemTypeDO extends BaseDO {
  23 +
  24 + public static final Long PARENT_ID_ROOT = 0L;
  25 +
  26 + /**
  27 + * 分类编号
  28 + */
  29 + @TableId
  30 + private Long id;
  31 + /**
  32 + * 分类编码
  33 + */
  34 + private String code;
  35 + /**
  36 + * 分类名称
  37 + */
  38 + private String name;
  39 + /**
  40 + * 父分类编号
  41 + */
  42 + private Long parentId;
  43 + /**
  44 + * 物料/产品标识
  45 + */
  46 + private String itemOrProduct;
  47 + /**
  48 + * 显示排序
  49 + */
  50 + private Integer sort;
  51 + /**
  52 + * 状态
  53 + *
  54 + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
  55 + */
  56 + private Integer status;
  57 + /**
  58 + * 备注
  59 + */
  60 + private String remark;
  61 + /**
  62 + * 预留字段1
  63 + */
  64 + private String attr1;
  65 + /**
  66 + * 预留字段2
  67 + */
  68 + private String attr2;
  69 + /**
  70 + * 预留字段3
  71 + */
  72 + private Integer attr3;
  73 + /**
  74 + * 预留字段4
  75 + */
  76 + private Integer attr4;
  77 +
  78 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * MES 物料产品 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface MesMdItemMapper extends BaseMapperX<MesMdItemDO> {
  19 +
  20 + default PageResult<MesMdItemDO> selectPage(MesMdItemPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<MesMdItemDO>()
  22 + .likeIfPresent(MesMdItemDO::getCode, reqVO.getCode())
  23 + .likeIfPresent(MesMdItemDO::getName, reqVO.getName())
  24 + .eqIfPresent(MesMdItemDO::getItemTypeId, reqVO.getItemTypeId())
  25 + .eqIfPresent(MesMdItemDO::getStatus, reqVO.getStatus())
  26 + .betweenIfPresent(MesMdItemDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(MesMdItemDO::getId));
  28 + }
  29 +
  30 + default MesMdItemDO selectByCode(String code) {
  31 + return selectOne(MesMdItemDO::getCode, code);
  32 + }
  33 +
  34 + default MesMdItemDO selectByName(String name) {
  35 + return selectOne(MesMdItemDO::getName, name);
  36 + }
  37 +
  38 + default Long selectCountByItemTypeId(Long itemTypeId) {
  39 + return selectCount(MesMdItemDO::getItemTypeId, itemTypeId);
  40 + }
  41 +
  42 + default List<MesMdItemDO> selectListByStatus(Integer status) {
  43 + return selectList(MesMdItemDO::getStatus, status);
  44 + }
  45 +
  46 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemTypeMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * MES 物料产品分类 Mapper
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Mapper
  17 +public interface MesMdItemTypeMapper extends BaseMapperX<MesMdItemTypeDO> {
  18 +
  19 + default List<MesMdItemTypeDO> selectList(MesMdItemTypeListReqVO reqVO) {
  20 + return selectList(new LambdaQueryWrapperX<MesMdItemTypeDO>()
  21 + .likeIfPresent(MesMdItemTypeDO::getName, reqVO.getName())
  22 + .eqIfPresent(MesMdItemTypeDO::getStatus, reqVO.getStatus())
  23 + .orderByAsc(MesMdItemTypeDO::getSort));
  24 + }
  25 +
  26 + default MesMdItemTypeDO selectByParentIdAndName(Long parentId, String name) {
  27 + return selectOne(MesMdItemTypeDO::getParentId, parentId, MesMdItemTypeDO::getName, name);
  28 + }
  29 +
  30 + default MesMdItemTypeDO selectByParentIdAndCode(Long parentId, String code) {
  31 + return selectOne(MesMdItemTypeDO::getParentId, parentId, MesMdItemTypeDO::getCode, code);
  32 + }
  33 +
  34 + default Long selectCountByParentId(Long parentId) {
  35 + return selectCount(MesMdItemTypeDO::getParentId, parentId);
  36 + }
  37 +
  38 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
... ... @@ -11,12 +11,21 @@ public interface ErrorCodeConstants {
11 11  
12 12 // ========== MES 基础数据-物料分类(1-040-100-000) ==========
13 13 ErrorCode MD_ITEM_TYPE_NOT_EXISTS = new ErrorCode(1_040_100_000, "物料分类不存在");
  14 + ErrorCode MD_ITEM_TYPE_EXITS_CHILDREN = new ErrorCode(1_040_100_001, "存在子分类,无法删除");
  15 + ErrorCode MD_ITEM_TYPE_PARENT_NOT_EXITS = new ErrorCode(1_040_100_002, "父级分类不存在");
  16 + ErrorCode MD_ITEM_TYPE_PARENT_ERROR = new ErrorCode(1_040_100_003, "不能设置自己为父分类");
  17 + ErrorCode MD_ITEM_TYPE_NAME_DUPLICATE = new ErrorCode(1_040_100_004, "同一父分类下已存在该名称的分类");
  18 + ErrorCode MD_ITEM_TYPE_CODE_DUPLICATE = new ErrorCode(1_040_100_005, "同一父分类下已存在该编码的分类");
  19 + ErrorCode MD_ITEM_TYPE_PARENT_IS_CHILD = new ErrorCode(1_040_100_006, "不能设置自己的子分类为父分类");
  20 + ErrorCode MD_ITEM_TYPE_EXITS_ITEM = new ErrorCode(1_040_100_007, "该分类下存在物料,无法删除");
14 21  
15 22 // ========== MES 基础数据-计量单位(1-040-101-000) ==========
16 23 ErrorCode MD_UNIT_MEASURE_NOT_EXISTS = new ErrorCode(1_040_101_000, "计量单位不存在");
17 24  
18 25 // ========== MES 基础数据-物料(1-040-102-000) ==========
19 26 ErrorCode MD_ITEM_NOT_EXISTS = new ErrorCode(1_040_102_000, "物料不存在");
  27 + ErrorCode MD_ITEM_CODE_DUPLICATE = new ErrorCode(1_040_102_001, "物料编码已存在");
  28 + ErrorCode MD_ITEM_NAME_DUPLICATE = new ErrorCode(1_040_102_002, "物料名称已存在");
20 29  
21 30 // ========== MES 基础数据-客户(1-040-103-000) ==========
22 31 ErrorCode MD_CLIENT_NOT_EXISTS = new ErrorCode(1_040_103_000, "客户不存在");
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemRespVO;
  6 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
  7 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  15 +
  16 +/**
  17 + * MES 物料产品 Service 接口
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +public interface MesMdItemService {
  22 +
  23 + /**
  24 + * 创建物料产品
  25 + *
  26 + * @param createReqVO 创建信息
  27 + * @return 编号
  28 + */
  29 + Long createItem(@Valid MesMdItemSaveReqVO createReqVO);
  30 +
  31 + /**
  32 + * 更新物料产品
  33 + *
  34 + * @param updateReqVO 更新信息
  35 + */
  36 + void updateItem(@Valid MesMdItemSaveReqVO updateReqVO);
  37 +
  38 + /**
  39 + * 删除物料产品
  40 + *
  41 + * @param id 编号
  42 + */
  43 + void deleteItem(Long id);
  44 +
  45 + /**
  46 + * 获得物料产品
  47 + *
  48 + * @param id 编号
  49 + * @return 物料产品
  50 + */
  51 + MesMdItemDO getItem(Long id);
  52 +
  53 + /**
  54 + * 获得物料产品 VO 分页
  55 + *
  56 + * @param pageReqVO 分页查询
  57 + * @return 物料产品分页
  58 + */
  59 + PageResult<MesMdItemRespVO> getItemVOPage(MesMdItemPageReqVO pageReqVO);
  60 +
  61 + /**
  62 + * 获得指定状态的物料产品 VO 列表
  63 + *
  64 + * @param status 状态
  65 + * @return 物料产品 VO 列表
  66 + */
  67 + List<MesMdItemRespVO> getItemVOListByStatus(Integer status);
  68 +
  69 + /**
  70 + * 获得物料产品 VO 列表
  71 + *
  72 + * @param ids 编号数组
  73 + * @return 物料产品 VO 列表
  74 + */
  75 + List<MesMdItemRespVO> getItemVOList(Collection<Long> ids);
  76 +
  77 + /**
  78 + * 获得物料产品 VO Map
  79 + *
  80 + * @param ids 编号数组
  81 + * @return 物料产品 VO Map
  82 + */
  83 + default Map<Long, MesMdItemRespVO> getItemVOMap(Collection<Long> ids) {
  84 + return convertMap(getItemVOList(ids), MesMdItemRespVO::getId);
  85 + }
  86 +
  87 + /**
  88 + * 基于物料分类编号,获得物料数量
  89 + *
  90 + * @param itemTypeId 物料分类编号
  91 + * @return 物料数量
  92 + */
  93 + Long getItemCountByItemTypeId(Long itemTypeId);
  94 +
  95 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.md.item;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemRespVO;
  9 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
  10 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  11 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  12 +import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
  13 +import jakarta.annotation.Resource;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.validation.annotation.Validated;
  16 +
  17 +import java.math.BigDecimal;
  18 +import java.util.Collection;
  19 +import java.util.Collections;
  20 +import java.util.List;
  21 +import java.util.Map;
  22 +import java.util.Objects;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  26 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  27 +
  28 +/**
  29 + * MES 物料产品 Service 实现类
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Service
  34 +@Validated
  35 +public class MesMdItemServiceImpl implements MesMdItemService {
  36 +
  37 + @Resource
  38 + private MesMdItemMapper itemMapper;
  39 +
  40 + @Resource
  41 + private MesMdItemTypeService itemTypeService;
  42 +
  43 + @Override
  44 + public Long createItem(MesMdItemSaveReqVO createReqVO) {
  45 + // 校验物料编码的唯一性
  46 + validateItemCodeUnique(null, createReqVO.getCode());
  47 + // 校验物料名称的唯一性
  48 + validateItemNameUnique(null, createReqVO.getName());
  49 + // 校验物料分类存在
  50 + validateItemTypeExists(createReqVO.getItemTypeId());
  51 +
  52 + // 插入
  53 + MesMdItemDO item = BeanUtils.toBean(createReqVO, MesMdItemDO.class);
  54 + // 如果未启用安全库存,清零库存上下限
  55 + clearStockIfNotSafe(item);
  56 + itemMapper.insert(item);
  57 + // 返回
  58 + return item.getId();
  59 + }
  60 +
  61 + @Override
  62 + public void updateItem(MesMdItemSaveReqVO updateReqVO) {
  63 + // 校验存在
  64 + validateItemExists(updateReqVO.getId());
  65 + // 校验物料编码的唯一性
  66 + validateItemCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
  67 + // 校验物料名称的唯一性
  68 + validateItemNameUnique(updateReqVO.getId(), updateReqVO.getName());
  69 + // 校验物料分类存在
  70 + validateItemTypeExists(updateReqVO.getItemTypeId());
  71 +
  72 + // 更新
  73 + MesMdItemDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemDO.class);
  74 + // 如果未启用安全库存,清零库存上下限
  75 + clearStockIfNotSafe(updateObj);
  76 + itemMapper.updateById(updateObj);
  77 + }
  78 +
  79 + @Override
  80 + public void deleteItem(Long id) {
  81 + // 校验存在
  82 + validateItemExists(id);
  83 + // 删除
  84 + itemMapper.deleteById(id);
  85 + }
  86 +
  87 + private void validateItemExists(Long id) {
  88 + if (itemMapper.selectById(id) == null) {
  89 + throw exception(MD_ITEM_NOT_EXISTS);
  90 + }
  91 + }
  92 +
  93 + private void validateItemCodeUnique(Long id, String code) {
  94 + MesMdItemDO item = itemMapper.selectByCode(code);
  95 + if (item == null) {
  96 + return;
  97 + }
  98 + if (id == null) {
  99 + throw exception(MD_ITEM_CODE_DUPLICATE);
  100 + }
  101 + if (!Objects.equals(item.getId(), id)) {
  102 + throw exception(MD_ITEM_CODE_DUPLICATE);
  103 + }
  104 + }
  105 +
  106 + private void validateItemNameUnique(Long id, String name) {
  107 + MesMdItemDO item = itemMapper.selectByName(name);
  108 + if (item == null) {
  109 + return;
  110 + }
  111 + if (id == null) {
  112 + throw exception(MD_ITEM_NAME_DUPLICATE);
  113 + }
  114 + if (!Objects.equals(item.getId(), id)) {
  115 + throw exception(MD_ITEM_NAME_DUPLICATE);
  116 + }
  117 + }
  118 +
  119 + private void validateItemTypeExists(Long itemTypeId) {
  120 + if (itemTypeService.getItemType(itemTypeId) == null) {
  121 + throw exception(MD_ITEM_TYPE_NOT_EXISTS);
  122 + }
  123 + }
  124 +
  125 + /**
  126 + * 如果未启用安全库存,清零库存上下限
  127 + */
  128 + private void clearStockIfNotSafe(MesMdItemDO item) {
  129 + if (Boolean.TRUE.equals(item.getSafeStockFlag())) {
  130 + return;
  131 + }
  132 + item.setMinStock(BigDecimal.ZERO);
  133 + item.setMaxStock(BigDecimal.ZERO);
  134 + }
  135 +
  136 + @Override
  137 + public MesMdItemDO getItem(Long id) {
  138 + return itemMapper.selectById(id);
  139 + }
  140 +
  141 + @Override
  142 + public PageResult<MesMdItemRespVO> getItemVOPage(MesMdItemPageReqVO pageReqVO) {
  143 + PageResult<MesMdItemDO> pageResult = itemMapper.selectPage(pageReqVO);
  144 + return new PageResult<>(buildItemVOList(pageResult.getList()), pageResult.getTotal());
  145 + }
  146 +
  147 + @Override
  148 + public List<MesMdItemRespVO> getItemVOListByStatus(Integer status) {
  149 + List<MesMdItemDO> list = itemMapper.selectListByStatus(status);
  150 + return buildItemVOList(list);
  151 + }
  152 +
  153 + @Override
  154 + public List<MesMdItemRespVO> getItemVOList(Collection<Long> ids) {
  155 + if (CollUtil.isEmpty(ids)) {
  156 + return Collections.emptyList();
  157 + }
  158 + List<MesMdItemDO> list = itemMapper.selectByIds(ids);
  159 + return buildItemVOList(list);
  160 + }
  161 +
  162 + private List<MesMdItemRespVO> buildItemVOList(List<MesMdItemDO> list) {
  163 + if (CollUtil.isEmpty(list)) {
  164 + return Collections.emptyList();
  165 + }
  166 + Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
  167 + convertSet(list, MesMdItemDO::getItemTypeId));
  168 + return BeanUtils.toBean(list, MesMdItemRespVO.class, item -> {
  169 + MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
  170 + itemType -> {
  171 + item.setItemTypeName(itemType.getName());
  172 + item.setItemOrProduct(itemType.getItemOrProduct());
  173 + });
  174 + });
  175 + }
  176 +
  177 + @Override
  178 + public Long getItemCountByItemTypeId(Long itemTypeId) {
  179 + return itemMapper.selectCountByItemTypeId(itemTypeId);
  180 + }
  181 +
  182 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemTypeService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.md.item;
  2 +
  3 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
  5 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  6 +import jakarta.validation.Valid;
  7 +
  8 +import java.util.Collection;
  9 +import java.util.List;
  10 +import java.util.Map;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  13 +
  14 +/**
  15 + * MES 物料产品分类 Service 接口
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public interface MesMdItemTypeService {
  20 +
  21 + /**
  22 + * 创建物料产品分类
  23 + *
  24 + * @param createReqVO 创建信息
  25 + * @return 编号
  26 + */
  27 + Long createItemType(@Valid MesMdItemTypeSaveReqVO createReqVO);
  28 +
  29 + /**
  30 + * 更新物料产品分类
  31 + *
  32 + * @param updateReqVO 更新信息
  33 + */
  34 + void updateItemType(@Valid MesMdItemTypeSaveReqVO updateReqVO);
  35 +
  36 + /**
  37 + * 删除物料产品分类
  38 + *
  39 + * @param id 编号
  40 + */
  41 + void deleteItemType(Long id);
  42 +
  43 + /**
  44 + * 获得物料产品分类
  45 + *
  46 + * @param id 编号
  47 + * @return 物料产品分类
  48 + */
  49 + MesMdItemTypeDO getItemType(Long id);
  50 +
  51 + /**
  52 + * 获得物料产品分类列表
  53 + *
  54 + * @param listReqVO 查询条件
  55 + * @return 物料产品分类列表
  56 + */
  57 + List<MesMdItemTypeDO> getItemTypeList(MesMdItemTypeListReqVO listReqVO);
  58 +
  59 + /**
  60 + * 获得物料产品分类列表
  61 + *
  62 + * @param ids 编号数组
  63 + * @return 物料产品分类列表
  64 + */
  65 + List<MesMdItemTypeDO> getItemTypeList(Collection<Long> ids);
  66 +
  67 + /**
  68 + * 获得物料产品分类 Map
  69 + *
  70 + * @param ids 编号数组
  71 + * @return 物料产品分类 Map
  72 + */
  73 + default Map<Long, MesMdItemTypeDO> getItemTypeMap(Collection<Long> ids) {
  74 + return convertMap(getItemTypeList(ids), MesMdItemTypeDO::getId);
  75 + }
  76 +
  77 +}
... ...
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemTypeServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.service.md.item;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
  6 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
  7 +import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemTypeMapper;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.context.annotation.Lazy;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import java.util.Collection;
  14 +import java.util.List;
  15 +import java.util.Objects;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
  19 +
  20 +/**
  21 + * MES 物料产品分类 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
  28 +
  29 + @Resource
  30 + private MesMdItemTypeMapper itemTypeMapper;
  31 +
  32 + @Resource
  33 + @Lazy // 延迟加载,避免循环依赖
  34 + private MesMdItemService itemService;
  35 +
  36 + @Override
  37 + public Long createItemType(MesMdItemTypeSaveReqVO createReqVO) {
  38 + // 校验父分类编号的有效性
  39 + validateParentItemType(null, createReqVO.getParentId());
  40 + // 校验分类名称的唯一性
  41 + validateItemTypeNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
  42 + // 校验分类编码的唯一性
  43 + validateItemTypeCodeUnique(null, createReqVO.getParentId(), createReqVO.getCode());
  44 +
  45 + // 插入
  46 + MesMdItemTypeDO itemType = BeanUtils.toBean(createReqVO, MesMdItemTypeDO.class);
  47 + itemTypeMapper.insert(itemType);
  48 + // 返回
  49 + return itemType.getId();
  50 + }
  51 +
  52 + @Override
  53 + public void updateItemType(MesMdItemTypeSaveReqVO updateReqVO) {
  54 + // 校验存在
  55 + validateItemTypeExists(updateReqVO.getId());
  56 + // 校验父分类编号的有效性
  57 + validateParentItemType(updateReqVO.getId(), updateReqVO.getParentId());
  58 + // 校验分类名称的唯一性
  59 + validateItemTypeNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
  60 + // 校验分类编码的唯一性
  61 + validateItemTypeCodeUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getCode());
  62 +
  63 + // 更新
  64 + MesMdItemTypeDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemTypeDO.class);
  65 + itemTypeMapper.updateById(updateObj);
  66 + }
  67 +
  68 + @Override
  69 + public void deleteItemType(Long id) {
  70 + // 1.1 校验存在
  71 + validateItemTypeExists(id);
  72 + // 1.2 校验是否有子分类
  73 + if (itemTypeMapper.selectCountByParentId(id) > 0) {
  74 + throw exception(MD_ITEM_TYPE_EXITS_CHILDREN);
  75 + }
  76 + // 1.3 校验是否有物料
  77 + if (itemService.getItemCountByItemTypeId(id) > 0) {
  78 + throw exception(MD_ITEM_TYPE_EXITS_ITEM);
  79 + }
  80 + // 2. 删除
  81 + itemTypeMapper.deleteById(id);
  82 + }
  83 +
  84 + private void validateItemTypeExists(Long id) {
  85 + if (itemTypeMapper.selectById(id) == null) {
  86 + throw exception(MD_ITEM_TYPE_NOT_EXISTS);
  87 + }
  88 + }
  89 +
  90 + private void validateParentItemType(Long id, Long parentId) {
  91 + if (parentId == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(parentId)) {
  92 + return;
  93 + }
  94 + // 1. 不能设置自己为父分类
  95 + if (Objects.equals(id, parentId)) {
  96 + throw exception(MD_ITEM_TYPE_PARENT_ERROR);
  97 + }
  98 + // 2. 父分类不存在
  99 + MesMdItemTypeDO parentItemType = itemTypeMapper.selectById(parentId);
  100 + if (parentItemType == null) {
  101 + throw exception(MD_ITEM_TYPE_PARENT_NOT_EXITS);
  102 + }
  103 + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路
  104 + if (id == null) { // id 为空,说明新增,不需要考虑环路
  105 + return;
  106 + }
  107 + for (int i = 0; i < Short.MAX_VALUE; i++) {
  108 + // 3.1 校验环路
  109 + parentId = parentItemType.getParentId();
  110 + if (Objects.equals(id, parentId)) {
  111 + throw exception(MD_ITEM_TYPE_PARENT_IS_CHILD);
  112 + }
  113 + // 3.2 继续递归下一级父分类
  114 + if (parentId == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(parentId)) {
  115 + break;
  116 + }
  117 + parentItemType = itemTypeMapper.selectById(parentId);
  118 + if (parentItemType == null) {
  119 + break;
  120 + }
  121 + }
  122 + }
  123 +
  124 + private void validateItemTypeNameUnique(Long id, Long parentId, String name) {
  125 + MesMdItemTypeDO itemType = itemTypeMapper.selectByParentIdAndName(parentId, name);
  126 + if (itemType == null) {
  127 + return;
  128 + }
  129 + // 如果 id 为空,说明不用比较是否为相同 id 的分类
  130 + if (id == null) {
  131 + throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);
  132 + }
  133 + if (!Objects.equals(itemType.getId(), id)) {
  134 + throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);
  135 + }
  136 + }
  137 +
  138 + private void validateItemTypeCodeUnique(Long id, Long parentId, String code) {
  139 + MesMdItemTypeDO itemType = itemTypeMapper.selectByParentIdAndCode(parentId, code);
  140 + if (itemType == null) {
  141 + return;
  142 + }
  143 + // 如果 id 为空,说明不用比较是否为相同 id 的分类
  144 + if (id == null) {
  145 + throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);
  146 + }
  147 + if (!Objects.equals(itemType.getId(), id)) {
  148 + throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);
  149 + }
  150 + }
  151 +
  152 + @Override
  153 + public MesMdItemTypeDO getItemType(Long id) {
  154 + return itemTypeMapper.selectById(id);
  155 + }
  156 +
  157 + @Override
  158 + public List<MesMdItemTypeDO> getItemTypeList(MesMdItemTypeListReqVO listReqVO) {
  159 + return itemTypeMapper.selectList(listReqVO);
  160 + }
  161 +
  162 + @Override
  163 + public List<MesMdItemTypeDO> getItemTypeList(Collection<Long> ids) {
  164 + return itemTypeMapper.selectByIds(ids);
  165 + }
  166 +
  167 +}
... ...