Commit 4a4a287fb70cc9cc4e5f2129b2203d2580b52f4a

Authored by YunaiV
1 parent 977fdca6

feat(mes): 完善物料分类与物料产品后端功能

1. Service 层重构:接口只返回 DO,VO 拼接移至 Controller
2. Mapper 合并两个 selectPage 为一个方法(inIfPresent 兼容空集合)
3. 新增物料批量导入功能(ImportExcelVO + ImportRespVO + Controller 端点)
4. 分类查询支持子分类递归(getItemTypeChildrenList)
5. 物料分页查询自动包含选中分类的所有子分类
6. 分类删除移除 export-excel 端点
7. 校验逻辑简化:ObjUtil.notEqual 替代双 if 判断
8. collectChildren 重命名为 collectItemTypeChildren
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdItemController.java
1 package cn.iocoder.yudao.module.mes.controller.admin.md.item; 1 package cn.iocoder.yudao.module.mes.controller.admin.md.item;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; 4 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.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 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.PageParam;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 11 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  12 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportExcelVO;
  13 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportRespVO;
10 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO; 14 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; 15 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; 16 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 17 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
  18 +import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
14 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 19 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  20 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
15 import io.swagger.v3.oas.annotations.Operation; 21 import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 22 import io.swagger.v3.oas.annotations.Parameter;
  23 +import io.swagger.v3.oas.annotations.Parameters;
17 import io.swagger.v3.oas.annotations.tags.Tag; 24 import io.swagger.v3.oas.annotations.tags.Tag;
18 import jakarta.annotation.Resource; 25 import jakarta.annotation.Resource;
19 import jakarta.servlet.http.HttpServletResponse; 26 import jakarta.servlet.http.HttpServletResponse;
@@ -21,13 +28,17 @@ import jakarta.validation.Valid; @@ -21,13 +28,17 @@ import jakarta.validation.Valid;
21 import org.springframework.security.access.prepost.PreAuthorize; 28 import org.springframework.security.access.prepost.PreAuthorize;
22 import org.springframework.validation.annotation.Validated; 29 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.*; 30 import org.springframework.web.bind.annotation.*;
  31 +import org.springframework.web.multipart.MultipartFile;
24 32
25 import java.io.IOException; 33 import java.io.IOException;
  34 +import java.util.Collections;
26 import java.util.List; 35 import java.util.List;
  36 +import java.util.Map;
27 37
28 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; 38 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 40 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  41 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
31 42
32 @Tag(name = "管理后台 - MES 物料产品") 43 @Tag(name = "管理后台 - MES 物料产品")
33 @RestController 44 @RestController
@@ -38,6 +49,9 @@ public class MesMdItemController { @@ -38,6 +49,9 @@ public class MesMdItemController {
38 @Resource 49 @Resource
39 private MesMdItemService itemService; 50 private MesMdItemService itemService;
40 51
  52 + @Resource
  53 + private MesMdItemTypeService itemTypeService;
  54 +
41 @PostMapping("/create") 55 @PostMapping("/create")
42 @Operation(summary = "创建物料产品") 56 @Operation(summary = "创建物料产品")
43 @PreAuthorize("@ss.hasPermission('mes:md-item:create')") 57 @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
@@ -75,14 +89,15 @@ public class MesMdItemController { @@ -75,14 +89,15 @@ public class MesMdItemController {
75 @Operation(summary = "获得物料产品分页") 89 @Operation(summary = "获得物料产品分页")
76 @PreAuthorize("@ss.hasPermission('mes:md-item:query')") 90 @PreAuthorize("@ss.hasPermission('mes:md-item:query')")
77 public CommonResult<PageResult<MesMdItemRespVO>> getItemPage(@Valid MesMdItemPageReqVO pageReqVO) { 91 public CommonResult<PageResult<MesMdItemRespVO>> getItemPage(@Valid MesMdItemPageReqVO pageReqVO) {
78 - return success(itemService.getItemVOPage(pageReqVO)); 92 + PageResult<MesMdItemDO> pageResult = itemService.getItemPage(pageReqVO);
  93 + return success(new PageResult<>(buildItemVOList(pageResult.getList()), pageResult.getTotal()));
79 } 94 }
80 95
81 @GetMapping("/simple-list") 96 @GetMapping("/simple-list")
82 @Operation(summary = "获得物料产品精简列表", description = "只包含被开启的物料,主要用于前端的下拉选项") 97 @Operation(summary = "获得物料产品精简列表", description = "只包含被开启的物料,主要用于前端的下拉选项")
83 public CommonResult<List<MesMdItemRespVO>> getItemSimpleList() { 98 public CommonResult<List<MesMdItemRespVO>> getItemSimpleList() {
84 - List<MesMdItemRespVO> list = itemService.getItemVOListByStatus(CommonStatusEnum.ENABLE.getStatus());  
85 - return success(convertList(list, item -> new MesMdItemRespVO() 99 + List<MesMdItemDO> list = itemService.getItemListByStatus(CommonStatusEnum.ENABLE.getStatus());
  100 + return success(convertList(buildItemVOList(list), item -> new MesMdItemRespVO()
86 .setId(item.getId()).setName(item.getName()).setCode(item.getCode()) 101 .setId(item.getId()).setName(item.getName()).setCode(item.getCode())
87 .setItemTypeId(item.getItemTypeId()).setItemTypeName(item.getItemTypeName()) 102 .setItemTypeId(item.getItemTypeId()).setItemTypeName(item.getItemTypeName())
88 .setUnitOfMeasure(item.getUnitOfMeasure()))); 103 .setUnitOfMeasure(item.getUnitOfMeasure())));
@@ -95,12 +110,52 @@ public class MesMdItemController { @@ -95,12 +110,52 @@ public class MesMdItemController {
95 public void exportItemExcel(@Valid MesMdItemPageReqVO pageReqVO, 110 public void exportItemExcel(@Valid MesMdItemPageReqVO pageReqVO,
96 HttpServletResponse response) throws IOException { 111 HttpServletResponse response) throws IOException {
97 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); 112 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
98 - PageResult<MesMdItemRespVO> pageResult = itemService.getItemVOPage(pageReqVO); 113 + PageResult<MesMdItemDO> pageResult = itemService.getItemPage(pageReqVO);
99 // 导出 Excel 114 // 导出 Excel
100 ExcelUtils.write(response, "物料产品.xls", "数据", MesMdItemRespVO.class, 115 ExcelUtils.write(response, "物料产品.xls", "数据", MesMdItemRespVO.class,
101 - pageResult.getList()); 116 + buildItemVOList(pageResult.getList()));
102 } 117 }
103 118
104 - // TODO @AI:缺少批量导入; 119 + @GetMapping("/get-import-template")
  120 + @Operation(summary = "获得物料导入模板")
  121 + public void importTemplate(HttpServletResponse response) throws IOException {
  122 + // 手动创建导出 demo
  123 + List<MesMdItemImportExcelVO> list = Collections.singletonList(
  124 + MesMdItemImportExcelVO.builder().code("ITEM001").name("螺丝").specification("M6*20")
  125 + .unitOfMeasure("PCS").itemTypeId(1L).status(0).build()
  126 + );
  127 + // 输出
  128 + ExcelUtils.write(response, "物料导入模板.xls", "物料列表", MesMdItemImportExcelVO.class, list);
  129 + }
  130 +
  131 + @PostMapping("/import")
  132 + @Operation(summary = "导入物料")
  133 + @Parameters({
  134 + @Parameter(name = "file", description = "Excel 文件", required = true),
  135 + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
  136 + })
  137 + @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
  138 + public CommonResult<MesMdItemImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
  139 + @RequestParam(value = "updateSupport", required = false,
  140 + defaultValue = "false") Boolean updateSupport) throws Exception {
  141 + List<MesMdItemImportExcelVO> list = ExcelUtils.read(file, MesMdItemImportExcelVO.class);
  142 + return success(itemService.importItemList(list, updateSupport));
  143 + }
  144 +
  145 + // ==================== 拼接 VO ====================
  146 +
  147 + private List<MesMdItemRespVO> buildItemVOList(List<MesMdItemDO> list) {
  148 + if (CollUtil.isEmpty(list)) {
  149 + return Collections.emptyList();
  150 + }
  151 + Map<Long, MesMdItemTypeDO> itemTypeMap = itemTypeService.getItemTypeMap(
  152 + convertSet(list, MesMdItemDO::getItemTypeId));
  153 + return BeanUtils.toBean(list, MesMdItemRespVO.class, item ->
  154 + MapUtils.findAndThen(itemTypeMap, item.getItemTypeId(),
  155 + itemType -> {
  156 + item.setItemTypeName(itemType.getName());
  157 + item.setItemOrProduct(itemType.getItemOrProduct());
  158 + }));
  159 + }
105 160
106 } 161 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdItemTypeController.java
1 package cn.iocoder.yudao.module.mes.controller.admin.md.item; 1 package cn.iocoder.yudao.module.mes.controller.admin.md.item;
2 2
3 -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;  
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 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; 6 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; 7 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; 8 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
@@ -14,16 +12,13 @@ import io.swagger.v3.oas.annotations.Operation; @@ -14,16 +12,13 @@ import io.swagger.v3.oas.annotations.Operation;
14 import io.swagger.v3.oas.annotations.Parameter; 12 import io.swagger.v3.oas.annotations.Parameter;
15 import io.swagger.v3.oas.annotations.tags.Tag; 13 import io.swagger.v3.oas.annotations.tags.Tag;
16 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
17 -import jakarta.servlet.http.HttpServletResponse;  
18 import jakarta.validation.Valid; 15 import jakarta.validation.Valid;
19 import org.springframework.security.access.prepost.PreAuthorize; 16 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
21 import org.springframework.web.bind.annotation.*; 18 import org.springframework.web.bind.annotation.*;
22 19
23 -import java.io.IOException;  
24 import java.util.List; 20 import java.util.List;
25 21
26 -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;  
27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
29 24
@@ -86,17 +81,4 @@ public class MesMdItemTypeController { @@ -86,17 +81,4 @@ public class MesMdItemTypeController {
86 .setId(itemType.getId()).setName(itemType.getName()).setParentId(itemType.getParentId()))); 81 .setId(itemType.getId()).setName(itemType.getName()).setParentId(itemType.getParentId())));
87 } 82 }
88 83
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 } 84 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqV @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqV
7 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 7 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.util.Collection;
10 import java.util.List; 11 import java.util.List;
11 12
12 /** 13 /**
@@ -17,11 +18,11 @@ import java.util.List; @@ -17,11 +18,11 @@ import java.util.List;
17 @Mapper 18 @Mapper
18 public interface MesMdItemMapper extends BaseMapperX<MesMdItemDO> { 19 public interface MesMdItemMapper extends BaseMapperX<MesMdItemDO> {
19 20
20 - default PageResult<MesMdItemDO> selectPage(MesMdItemPageReqVO reqVO) { 21 + default PageResult<MesMdItemDO> selectPage(MesMdItemPageReqVO reqVO, Collection<Long> itemTypeIds) {
21 return selectPage(reqVO, new LambdaQueryWrapperX<MesMdItemDO>() 22 return selectPage(reqVO, new LambdaQueryWrapperX<MesMdItemDO>()
22 .likeIfPresent(MesMdItemDO::getCode, reqVO.getCode()) 23 .likeIfPresent(MesMdItemDO::getCode, reqVO.getCode())
23 .likeIfPresent(MesMdItemDO::getName, reqVO.getName()) 24 .likeIfPresent(MesMdItemDO::getName, reqVO.getName())
24 - .eqIfPresent(MesMdItemDO::getItemTypeId, reqVO.getItemTypeId()) 25 + .inIfPresent(MesMdItemDO::getItemTypeId, itemTypeIds)
25 .eqIfPresent(MesMdItemDO::getStatus, reqVO.getStatus()) 26 .eqIfPresent(MesMdItemDO::getStatus, reqVO.getStatus())
26 .betweenIfPresent(MesMdItemDO::getCreateTime, reqVO.getCreateTime()) 27 .betweenIfPresent(MesMdItemDO::getCreateTime, reqVO.getCreateTime())
27 .orderByDesc(MesMdItemDO::getId)); 28 .orderByDesc(MesMdItemDO::getId));
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -21,11 +21,13 @@ public interface ErrorCodeConstants { @@ -21,11 +21,13 @@ public interface ErrorCodeConstants {
21 21
22 // ========== MES 基础数据-计量单位(1-040-101-000) ========== 22 // ========== MES 基础数据-计量单位(1-040-101-000) ==========
23 ErrorCode MD_UNIT_MEASURE_NOT_EXISTS = new ErrorCode(1_040_101_000, "计量单位不存在"); 23 ErrorCode MD_UNIT_MEASURE_NOT_EXISTS = new ErrorCode(1_040_101_000, "计量单位不存在");
  24 + ErrorCode MD_UNIT_MEASURE_CODE_DUPLICATE = new ErrorCode(1_040_101_001, "计量单位编码已存在");
24 25
25 // ========== MES 基础数据-物料(1-040-102-000) ========== 26 // ========== MES 基础数据-物料(1-040-102-000) ==========
26 ErrorCode MD_ITEM_NOT_EXISTS = new ErrorCode(1_040_102_000, "物料不存在"); 27 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_CODE_DUPLICATE = new ErrorCode(1_040_102_001, "物料编码已存在");
28 ErrorCode MD_ITEM_NAME_DUPLICATE = new ErrorCode(1_040_102_002, "物料名称已存在"); 29 ErrorCode MD_ITEM_NAME_DUPLICATE = new ErrorCode(1_040_102_002, "物料名称已存在");
  30 + ErrorCode MD_ITEM_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_040_102_003, "导入物料数据不能为空");
29 31
30 // ========== MES 基础数据-客户(1-040-103-000) ========== 32 // ========== MES 基础数据-客户(1-040-103-000) ==========
31 ErrorCode MD_CLIENT_NOT_EXISTS = new ErrorCode(1_040_103_000, "客户不存在"); 33 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
1 package cn.iocoder.yudao.module.mes.service.md.item; 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportExcelVO;
  5 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportRespVO;
4 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO; 6 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.controller.admin.md.item.vo.MesMdItemSaveReqVO;
7 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 8 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
@@ -51,37 +52,37 @@ public interface MesMdItemService { @@ -51,37 +52,37 @@ public interface MesMdItemService {
51 MesMdItemDO getItem(Long id); 52 MesMdItemDO getItem(Long id);
52 53
53 /** 54 /**
54 - * 获得物料产品 VO 分页 55 + * 获得物料产品分页
55 * 56 *
56 * @param pageReqVO 分页查询 57 * @param pageReqVO 分页查询
57 * @return 物料产品分页 58 * @return 物料产品分页
58 */ 59 */
59 - PageResult<MesMdItemRespVO> getItemVOPage(MesMdItemPageReqVO pageReqVO); 60 + PageResult<MesMdItemDO> getItemPage(MesMdItemPageReqVO pageReqVO);
60 61
61 /** 62 /**
62 - * 获得指定状态的物料产品 VO 列表 63 + * 获得指定状态的物料产品列表
63 * 64 *
64 * @param status 状态 65 * @param status 状态
65 - * @return 物料产品 VO 列表 66 + * @return 物料产品列表
66 */ 67 */
67 - List<MesMdItemRespVO> getItemVOListByStatus(Integer status); 68 + List<MesMdItemDO> getItemListByStatus(Integer status);
68 69
69 /** 70 /**
70 - * 获得物料产品 VO 列表 71 + * 获得物料产品列表
71 * 72 *
72 * @param ids 编号数组 73 * @param ids 编号数组
73 - * @return 物料产品 VO 列表 74 + * @return 物料产品列表
74 */ 75 */
75 - List<MesMdItemRespVO> getItemVOList(Collection<Long> ids); 76 + List<MesMdItemDO> getItemList(Collection<Long> ids);
76 77
77 /** 78 /**
78 - * 获得物料产品 VO Map 79 + * 获得物料产品 Map
79 * 80 *
80 * @param ids 编号数组 81 * @param ids 编号数组
81 - * @return 物料产品 VO Map 82 + * @return 物料产品 Map
82 */ 83 */
83 - default Map<Long, MesMdItemRespVO> getItemVOMap(Collection<Long> ids) {  
84 - return convertMap(getItemVOList(ids), MesMdItemRespVO::getId); 84 + default Map<Long, MesMdItemDO> getItemMap(Collection<Long> ids) {
  85 + return convertMap(getItemList(ids), MesMdItemDO::getId);
85 } 86 }
86 87
87 /** 88 /**
@@ -92,4 +93,13 @@ public interface MesMdItemService { @@ -92,4 +93,13 @@ public interface MesMdItemService {
92 */ 93 */
93 Long getItemCountByItemTypeId(Long itemTypeId); 94 Long getItemCountByItemTypeId(Long itemTypeId);
94 95
  96 + /**
  97 + * 批量导入物料
  98 + *
  99 + * @param importItems 导入物料列表
  100 + * @param updateSupport 是否支持更新
  101 + * @return 导入结果
  102 + */
  103 + MesMdItemImportRespVO importItemList(List<MesMdItemImportExcelVO> importItems, boolean updateSupport);
  104 +
95 } 105 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.md.item; 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 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.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportExcelVO;
  9 +import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportRespVO;
7 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO; 10 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; 11 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
10 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
12 import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper; 14 import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
13 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
14 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
  17 +import org.springframework.transaction.annotation.Transactional;
15 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
16 19
17 import java.math.BigDecimal; 20 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; 21 +import java.util.*;
  22 +import java.util.concurrent.atomic.AtomicInteger;
23 23
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -51,10 +51,8 @@ public class MesMdItemServiceImpl implements MesMdItemService { @@ -51,10 +51,8 @@ public class MesMdItemServiceImpl implements MesMdItemService {
51 51
52 // 插入 52 // 插入
53 MesMdItemDO item = BeanUtils.toBean(createReqVO, MesMdItemDO.class); 53 MesMdItemDO item = BeanUtils.toBean(createReqVO, MesMdItemDO.class);
54 - // 如果未启用安全库存,清零库存上下限  
55 - clearStockIfNotSafe(item); 54 + clearStockIfNotSafe(item); // 如果未启用安全库存,清零库存上下限
56 itemMapper.insert(item); 55 itemMapper.insert(item);
57 - // 返回  
58 return item.getId(); 56 return item.getId();
59 } 57 }
60 58
@@ -71,8 +69,7 @@ public class MesMdItemServiceImpl implements MesMdItemService { @@ -71,8 +69,7 @@ public class MesMdItemServiceImpl implements MesMdItemService {
71 69
72 // 更新 70 // 更新
73 MesMdItemDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemDO.class); 71 MesMdItemDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemDO.class);
74 - // 如果未启用安全库存,清零库存上下限  
75 - clearStockIfNotSafe(updateObj); 72 + clearStockIfNotSafe(updateObj); // 如果未启用安全库存,清零库存上下限
76 itemMapper.updateById(updateObj); 73 itemMapper.updateById(updateObj);
77 } 74 }
78 75
@@ -129,8 +126,7 @@ public class MesMdItemServiceImpl implements MesMdItemService { @@ -129,8 +126,7 @@ public class MesMdItemServiceImpl implements MesMdItemService {
129 if (Boolean.TRUE.equals(item.getSafeStockFlag())) { 126 if (Boolean.TRUE.equals(item.getSafeStockFlag())) {
130 return; 127 return;
131 } 128 }
132 - item.setMinStock(BigDecimal.ZERO);  
133 - item.setMaxStock(BigDecimal.ZERO); 129 + item.setMinStock(BigDecimal.ZERO).setMaxStock(BigDecimal.ZERO);
134 } 130 }
135 131
136 @Override 132 @Override
@@ -139,39 +135,30 @@ public class MesMdItemServiceImpl implements MesMdItemService { @@ -139,39 +135,30 @@ public class MesMdItemServiceImpl implements MesMdItemService {
139 } 135 }
140 136
141 @Override 137 @Override
142 - public PageResult<MesMdItemRespVO> getItemVOPage(MesMdItemPageReqVO pageReqVO) {  
143 - PageResult<MesMdItemDO> pageResult = itemMapper.selectPage(pageReqVO);  
144 - return new PageResult<>(buildItemVOList(pageResult.getList()), pageResult.getTotal()); 138 + public PageResult<MesMdItemDO> getItemPage(MesMdItemPageReqVO pageReqVO) {
  139 + // 查找时,如果查找某个分类编号,则包含它的子分类
  140 + Set<Long> itemTypeIds = null;
  141 + if (pageReqVO.getItemTypeId() != null) {
  142 + itemTypeIds = new HashSet<>();
  143 + itemTypeIds.add(pageReqVO.getItemTypeId());
  144 + List<MesMdItemTypeDO> children = itemTypeService.getItemTypeChildrenList(pageReqVO.getItemTypeId());
  145 + itemTypeIds.addAll(convertSet(children, MesMdItemTypeDO::getId));
  146 + }
  147 + // 分页查询
  148 + return itemMapper.selectPage(pageReqVO, itemTypeIds);
145 } 149 }
146 150
147 @Override 151 @Override
148 - public List<MesMdItemRespVO> getItemVOListByStatus(Integer status) {  
149 - List<MesMdItemDO> list = itemMapper.selectListByStatus(status);  
150 - return buildItemVOList(list); 152 + public List<MesMdItemDO> getItemListByStatus(Integer status) {
  153 + return itemMapper.selectListByStatus(status);
151 } 154 }
152 155
153 @Override 156 @Override
154 - public List<MesMdItemRespVO> getItemVOList(Collection<Long> ids) { 157 + public List<MesMdItemDO> getItemList(Collection<Long> ids) {
155 if (CollUtil.isEmpty(ids)) { 158 if (CollUtil.isEmpty(ids)) {
156 return Collections.emptyList(); 159 return Collections.emptyList();
157 } 160 }
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 - }); 161 + return itemMapper.selectByIds(ids);
175 } 162 }
176 163
177 @Override 164 @Override
@@ -179,4 +166,80 @@ public class MesMdItemServiceImpl implements MesMdItemService { @@ -179,4 +166,80 @@ public class MesMdItemServiceImpl implements MesMdItemService {
179 return itemMapper.selectCountByItemTypeId(itemTypeId); 166 return itemMapper.selectCountByItemTypeId(itemTypeId);
180 } 167 }
181 168
  169 + @Override
  170 + @Transactional(rollbackFor = Exception.class)
  171 + public MesMdItemImportRespVO importItemList(List<MesMdItemImportExcelVO> importItems, boolean updateSupport) {
  172 + // 1. 参数校验
  173 + if (CollUtil.isEmpty(importItems)) {
  174 + throw exception(MD_ITEM_IMPORT_LIST_IS_EMPTY);
  175 + }
  176 +
  177 + // 2. 遍历,逐个创建 or 更新
  178 + MesMdItemImportRespVO respVO = MesMdItemImportRespVO.builder()
  179 + .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
  180 + .failureCodes(new LinkedHashMap<>()).build();
  181 + AtomicInteger index = new AtomicInteger(1);
  182 + importItems.forEach(importItem -> {
  183 + int currentIndex = index.getAndIncrement();
  184 + // 2.1 校验字段
  185 + String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " 行");
  186 + if (StrUtil.isBlank(importItem.getCode())) {
  187 + respVO.getFailureCodes().put(key, "物料编码不能为空");
  188 + return;
  189 + }
  190 + if (StrUtil.isBlank(importItem.getName())) {
  191 + respVO.getFailureCodes().put(key, "物料名称不能为空");
  192 + return;
  193 + }
  194 + if (StrUtil.isBlank(importItem.getUnitOfMeasure())) {
  195 + respVO.getFailureCodes().put(key, "单位编码不能为空");
  196 + return;
  197 + }
  198 + if (importItem.getItemTypeId() == null) {
  199 + respVO.getFailureCodes().put(key, "物料分类编号不能为空");
  200 + return;
  201 + }
  202 + // 2.2 校验分类是否存在
  203 + try {
  204 + validateItemTypeExists(importItem.getItemTypeId());
  205 + } catch (ServiceException ex) {
  206 + respVO.getFailureCodes().put(key, ex.getMessage());
  207 + return;
  208 + }
  209 +
  210 + // 2.3 判断:创建 or 更新
  211 + MesMdItemDO existItem = itemMapper.selectByCode(importItem.getCode());
  212 + if (existItem == null) {
  213 + // 2.3.1 创建
  214 + try {
  215 + validateItemNameUnique(null, importItem.getName());
  216 + } catch (ServiceException ex) {
  217 + respVO.getFailureCodes().put(key, ex.getMessage());
  218 + return;
  219 + }
  220 + MesMdItemDO item = BeanUtils.toBean(importItem, MesMdItemDO.class);
  221 + clearStockIfNotSafe(item);
  222 + itemMapper.insert(item);
  223 + respVO.getCreateCodes().add(importItem.getCode());
  224 + } else if (updateSupport) {
  225 + // 2.3.2 更新
  226 + try {
  227 + validateItemNameUnique(existItem.getId(), importItem.getName());
  228 + } catch (ServiceException ex) {
  229 + respVO.getFailureCodes().put(key, ex.getMessage());
  230 + return;
  231 + }
  232 + MesMdItemDO updateObj = BeanUtils.toBean(importItem, MesMdItemDO.class);
  233 + updateObj.setId(existItem.getId());
  234 + clearStockIfNotSafe(updateObj);
  235 + itemMapper.updateById(updateObj);
  236 + respVO.getUpdateCodes().add(importItem.getCode());
  237 + } else {
  238 + // 不支持更新
  239 + respVO.getFailureCodes().put(key, "物料编码已存在");
  240 + }
  241 + });
  242 + return respVO;
  243 + }
  244 +
182 } 245 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemTypeService.java
@@ -74,4 +74,12 @@ public interface MesMdItemTypeService { @@ -74,4 +74,12 @@ public interface MesMdItemTypeService {
74 return convertMap(getItemTypeList(ids), MesMdItemTypeDO::getId); 74 return convertMap(getItemTypeList(ids), MesMdItemTypeDO::getId);
75 } 75 }
76 76
  77 + /**
  78 + * 获得指定分类的所有子分类列表(递归)
  79 + *
  80 + * @param parentId 父分类编号
  81 + * @return 子分类列表
  82 + */
  83 + List<MesMdItemTypeDO> getItemTypeChildrenList(Long parentId);
  84 +
77 } 85 }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemTypeServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.md.item; 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 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.MesMdItemTypeListReqVO;
5 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO; 6 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
@@ -10,9 +11,7 @@ import org.springframework.context.annotation.Lazy; @@ -10,9 +11,7 @@ import org.springframework.context.annotation.Lazy;
10 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
12 13
13 -import java.util.Collection;  
14 -import java.util.List;  
15 -import java.util.Objects; 14 +import java.util.*;
16 15
17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; 17 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -45,7 +44,6 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { @@ -45,7 +44,6 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
45 // 插入 44 // 插入
46 MesMdItemTypeDO itemType = BeanUtils.toBean(createReqVO, MesMdItemTypeDO.class); 45 MesMdItemTypeDO itemType = BeanUtils.toBean(createReqVO, MesMdItemTypeDO.class);
47 itemTypeMapper.insert(itemType); 46 itemTypeMapper.insert(itemType);
48 - // 返回  
49 return itemType.getId(); 47 return itemType.getId();
50 } 48 }
51 49
@@ -77,6 +75,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { @@ -77,6 +75,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
77 if (itemService.getItemCountByItemTypeId(id) > 0) { 75 if (itemService.getItemCountByItemTypeId(id) > 0) {
78 throw exception(MD_ITEM_TYPE_EXITS_ITEM); 76 throw exception(MD_ITEM_TYPE_EXITS_ITEM);
79 } 77 }
  78 +
80 // 2. 删除 79 // 2. 删除
81 itemTypeMapper.deleteById(id); 80 itemTypeMapper.deleteById(id);
82 } 81 }
@@ -126,11 +125,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { @@ -126,11 +125,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
126 if (itemType == null) { 125 if (itemType == null) {
127 return; 126 return;
128 } 127 }
129 - // 如果 id 为空,说明不用比较是否为相同 id 的分类  
130 - if (id == null) {  
131 - throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);  
132 - }  
133 - if (!Objects.equals(itemType.getId(), id)) { 128 + if (ObjUtil.notEqual(itemType.getId(), id)) {
134 throw exception(MD_ITEM_TYPE_NAME_DUPLICATE); 129 throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);
135 } 130 }
136 } 131 }
@@ -140,11 +135,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { @@ -140,11 +135,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
140 if (itemType == null) { 135 if (itemType == null) {
141 return; 136 return;
142 } 137 }
143 - // 如果 id 为空,说明不用比较是否为相同 id 的分类  
144 - if (id == null) {  
145 - throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);  
146 - }  
147 - if (!Objects.equals(itemType.getId(), id)) { 138 + if (ObjUtil.notEqual(itemType.getId(), id)) {
148 throw exception(MD_ITEM_TYPE_CODE_DUPLICATE); 139 throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);
149 } 140 }
150 } 141 }
@@ -164,4 +155,25 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { @@ -164,4 +155,25 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
164 return itemTypeMapper.selectByIds(ids); 155 return itemTypeMapper.selectByIds(ids);
165 } 156 }
166 157
  158 + @Override
  159 + public List<MesMdItemTypeDO> getItemTypeChildrenList(Long parentId) {
  160 + List<MesMdItemTypeDO> allList = itemTypeMapper.selectList(); // 分类总量小,全量查
  161 + // 递归收集所有子分类
  162 + List<MesMdItemTypeDO> result = new ArrayList<>();
  163 + collectItemTypeChildren(result, parentId, allList);
  164 + return result;
  165 + }
  166 +
  167 + /**
  168 + * 递归收集所有子分类
  169 + */
  170 + private void collectItemTypeChildren(List<MesMdItemTypeDO> result, Long parentId, List<MesMdItemTypeDO> allList) {
  171 + for (MesMdItemTypeDO itemType : allList) {
  172 + if (Objects.equals(itemType.getParentId(), parentId)) {
  173 + result.add(itemType);
  174 + collectItemTypeChildren(result, itemType.getId(), allList);
  175 + }
  176 + }
  177 + }
  178 +
167 } 179 }