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 1 package cn.iocoder.yudao.module.mes.controller.admin.md.item;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageParam;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 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 14 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemPageReqVO;
11 15 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemRespVO;
12 16 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
13 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 19 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
  20 +import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
15 21 import io.swagger.v3.oas.annotations.Operation;
16 22 import io.swagger.v3.oas.annotations.Parameter;
  23 +import io.swagger.v3.oas.annotations.Parameters;
17 24 import io.swagger.v3.oas.annotations.tags.Tag;
18 25 import jakarta.annotation.Resource;
19 26 import jakarta.servlet.http.HttpServletResponse;
... ... @@ -21,13 +28,17 @@ import jakarta.validation.Valid;
21 28 import org.springframework.security.access.prepost.PreAuthorize;
22 29 import org.springframework.validation.annotation.Validated;
23 30 import org.springframework.web.bind.annotation.*;
  31 +import org.springframework.web.multipart.MultipartFile;
24 32  
25 33 import java.io.IOException;
  34 +import java.util.Collections;
26 35 import java.util.List;
  36 +import java.util.Map;
27 37  
28 38 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
29 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
30 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 43 @Tag(name = "管理后台 - MES 物料产品")
33 44 @RestController
... ... @@ -38,6 +49,9 @@ public class MesMdItemController {
38 49 @Resource
39 50 private MesMdItemService itemService;
40 51  
  52 + @Resource
  53 + private MesMdItemTypeService itemTypeService;
  54 +
41 55 @PostMapping("/create")
42 56 @Operation(summary = "创建物料产品")
43 57 @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
... ... @@ -75,14 +89,15 @@ public class MesMdItemController {
75 89 @Operation(summary = "获得物料产品分页")
76 90 @PreAuthorize("@ss.hasPermission('mes:md-item:query')")
77 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 96 @GetMapping("/simple-list")
82 97 @Operation(summary = "获得物料产品精简列表", description = "只包含被开启的物料,主要用于前端的下拉选项")
83 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 101 .setId(item.getId()).setName(item.getName()).setCode(item.getCode())
87 102 .setItemTypeId(item.getItemTypeId()).setItemTypeName(item.getItemTypeName())
88 103 .setUnitOfMeasure(item.getUnitOfMeasure())));
... ... @@ -95,12 +110,52 @@ public class MesMdItemController {
95 110 public void exportItemExcel(@Valid MesMdItemPageReqVO pageReqVO,
96 111 HttpServletResponse response) throws IOException {
97 112 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
98   - PageResult<MesMdItemRespVO> pageResult = itemService.getItemVOPage(pageReqVO);
  113 + PageResult<MesMdItemDO> pageResult = itemService.getItemPage(pageReqVO);
99 114 // 导出 Excel
100 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 1 package cn.iocoder.yudao.module.mes.controller.admin.md.item;
2 2  
3   -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
4 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 6 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
9 7 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeRespVO;
10 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 12 import io.swagger.v3.oas.annotations.Parameter;
15 13 import io.swagger.v3.oas.annotations.tags.Tag;
16 14 import jakarta.annotation.Resource;
17   -import jakarta.servlet.http.HttpServletResponse;
18 15 import jakarta.validation.Valid;
19 16 import org.springframework.security.access.prepost.PreAuthorize;
20 17 import org.springframework.validation.annotation.Validated;
21 18 import org.springframework.web.bind.annotation.*;
22 19  
23   -import java.io.IOException;
24 20 import java.util.List;
25 21  
26   -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
27 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
28 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
29 24  
... ... @@ -86,17 +81,4 @@ public class MesMdItemTypeController {
86 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 7 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
  10 +import java.util.Collection;
10 11 import java.util.List;
11 12  
12 13 /**
... ... @@ -17,11 +18,11 @@ import java.util.List;
17 18 @Mapper
18 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 22 return selectPage(reqVO, new LambdaQueryWrapperX<MesMdItemDO>()
22 23 .likeIfPresent(MesMdItemDO::getCode, reqVO.getCode())
23 24 .likeIfPresent(MesMdItemDO::getName, reqVO.getName())
24   - .eqIfPresent(MesMdItemDO::getItemTypeId, reqVO.getItemTypeId())
  25 + .inIfPresent(MesMdItemDO::getItemTypeId, itemTypeIds)
25 26 .eqIfPresent(MesMdItemDO::getStatus, reqVO.getStatus())
26 27 .betweenIfPresent(MesMdItemDO::getCreateTime, reqVO.getCreateTime())
27 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 21  
22 22 // ========== MES 基础数据-计量单位(1-040-101-000) ==========
23 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 26 // ========== MES 基础数据-物料(1-040-102-000) ==========
26 27 ErrorCode MD_ITEM_NOT_EXISTS = new ErrorCode(1_040_102_000, "物料不存在");
27 28 ErrorCode MD_ITEM_CODE_DUPLICATE = new ErrorCode(1_040_102_001, "物料编码已存在");
28 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 32 // ========== MES 基础数据-客户(1-040-103-000) ==========
31 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 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2  
3 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 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 7 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
7 8 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
8 9 import jakarta.validation.Valid;
... ... @@ -51,37 +52,37 @@ public interface MesMdItemService {
51 52 MesMdItemDO getItem(Long id);
52 53  
53 54 /**
54   - * 获得物料产品 VO 分页
  55 + * 获得物料产品分页
55 56 *
56 57 * @param pageReqVO 分页查询
57 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 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 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 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 93 */
93 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 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
6 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 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 11 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
10 12 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
11 13 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
12 14 import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
13 15 import jakarta.annotation.Resource;
14 16 import org.springframework.stereotype.Service;
  17 +import org.springframework.transaction.annotation.Transactional;
15 18 import org.springframework.validation.annotation.Validated;
16 19  
17 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 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
... ... @@ -51,10 +51,8 @@ public class MesMdItemServiceImpl implements MesMdItemService {
51 51  
52 52 // 插入
53 53 MesMdItemDO item = BeanUtils.toBean(createReqVO, MesMdItemDO.class);
54   - // 如果未启用安全库存,清零库存上下限
55   - clearStockIfNotSafe(item);
  54 + clearStockIfNotSafe(item); // 如果未启用安全库存,清零库存上下限
56 55 itemMapper.insert(item);
57   - // 返回
58 56 return item.getId();
59 57 }
60 58  
... ... @@ -71,8 +69,7 @@ public class MesMdItemServiceImpl implements MesMdItemService {
71 69  
72 70 // 更新
73 71 MesMdItemDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemDO.class);
74   - // 如果未启用安全库存,清零库存上下限
75   - clearStockIfNotSafe(updateObj);
  72 + clearStockIfNotSafe(updateObj); // 如果未启用安全库存,清零库存上下限
76 73 itemMapper.updateById(updateObj);
77 74 }
78 75  
... ... @@ -129,8 +126,7 @@ public class MesMdItemServiceImpl implements MesMdItemService {
129 126 if (Boolean.TRUE.equals(item.getSafeStockFlag())) {
130 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 132 @Override
... ... @@ -139,39 +135,30 @@ public class MesMdItemServiceImpl implements MesMdItemService {
139 135 }
140 136  
141 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 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 156 @Override
154   - public List<MesMdItemRespVO> getItemVOList(Collection<Long> ids) {
  157 + public List<MesMdItemDO> getItemList(Collection<Long> ids) {
155 158 if (CollUtil.isEmpty(ids)) {
156 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 164 @Override
... ... @@ -179,4 +166,80 @@ public class MesMdItemServiceImpl implements MesMdItemService {
179 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 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 1 package cn.iocoder.yudao.module.mes.service.md.item;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
5 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 11 import org.springframework.stereotype.Service;
11 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 16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 17 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
... ... @@ -45,7 +44,6 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
45 44 // 插入
46 45 MesMdItemTypeDO itemType = BeanUtils.toBean(createReqVO, MesMdItemTypeDO.class);
47 46 itemTypeMapper.insert(itemType);
48   - // 返回
49 47 return itemType.getId();
50 48 }
51 49  
... ... @@ -77,6 +75,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
77 75 if (itemService.getItemCountByItemTypeId(id) > 0) {
78 76 throw exception(MD_ITEM_TYPE_EXITS_ITEM);
79 77 }
  78 +
80 79 // 2. 删除
81 80 itemTypeMapper.deleteById(id);
82 81 }
... ... @@ -126,11 +125,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
126 125 if (itemType == null) {
127 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 129 throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);
135 130 }
136 131 }
... ... @@ -140,11 +135,7 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
140 135 if (itemType == null) {
141 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 139 throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);
149 140 }
150 141 }
... ... @@ -164,4 +155,25 @@ public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
164 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 }
... ...