1. 物料新增统一展平属性的接口
2. 修正物料错误的名称
This commit is contained in:
@@ -80,6 +80,14 @@ public class MaterialInfomationController {
|
||||
return success(materialInfomation);
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-ids")
|
||||
@Operation(summary = "按 ID 批量获得物料信息")
|
||||
@Parameter(name = "ids", description = "编号集合", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('base:material-infomation:query')")
|
||||
public CommonResult<List<MaterialInfomationRespVO>> getMaterialInfomationListByIds(@RequestParam("ids") List<Long> ids) {
|
||||
return success(materialInfomationService.getMaterialInfomationListByIds(ids));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得物料信息分页")
|
||||
@PreAuthorize("@ss.hasPermission('base:material-infomation:query')")
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zt.plat.module.base.controller.admin.base.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 展平后的物料属性(按属性编码为键)
|
||||
*/
|
||||
@Data
|
||||
public class MaterialInfomationFlatAttributeRespVO {
|
||||
|
||||
@Schema(description = "属性ID")
|
||||
private Long propertiesId;
|
||||
|
||||
@Schema(description = "属性编码")
|
||||
private String propertiesCode;
|
||||
|
||||
@Schema(description = "属性名称")
|
||||
private String propertiesName;
|
||||
|
||||
@Schema(description = "属性数据类型")
|
||||
private String dataType;
|
||||
|
||||
@Schema(description = "属性值(编码/原值)")
|
||||
private String value;
|
||||
|
||||
@Schema(description = "属性值展示(字典标签,若无字典则原值)")
|
||||
private String valueLabel;
|
||||
|
||||
@Schema(description = "字典类型ID")
|
||||
private Long dictTypeId;
|
||||
|
||||
@Schema(description = "计量单位ID")
|
||||
private Long unitId;
|
||||
|
||||
@Schema(description = "计量单位名称")
|
||||
private String unitName;
|
||||
|
||||
@Schema(description = "计量单位符号")
|
||||
private String unitSymbol;
|
||||
|
||||
@Schema(description = "是否关键属性")
|
||||
private Integer isKey;
|
||||
|
||||
@Schema(description = "是否计量定价")
|
||||
private Integer isMetering;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
private Long sort;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zt.plat.module.base.controller.admin.base.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 物料属性(持有属性)行展示 VO
|
||||
*/
|
||||
@Data
|
||||
public class MaterialInfomationPropertyRespVO {
|
||||
|
||||
@Schema(description = "属性ID")
|
||||
private Long propertiesId;
|
||||
|
||||
@Schema(description = "属性编码")
|
||||
private String propertiesCode;
|
||||
|
||||
@Schema(description = "属性名称")
|
||||
private String propertiesName;
|
||||
|
||||
@Schema(description = "属性数据类型")
|
||||
private String dataType;
|
||||
|
||||
@Schema(description = "属性值(编码/原值)")
|
||||
private String dictionaryDataValue;
|
||||
|
||||
@Schema(description = "属性值展示(字典标签,若无字典则原值)")
|
||||
private String valueLabel;
|
||||
|
||||
@Schema(description = "字典类型ID")
|
||||
private Long dictionaryTypeId;
|
||||
|
||||
@Schema(description = "计量单位ID")
|
||||
private Long unitId;
|
||||
|
||||
@Schema(description = "计量单位名称")
|
||||
private String unitName;
|
||||
|
||||
@Schema(description = "计量单位符号")
|
||||
private String unitSymbol;
|
||||
|
||||
@Schema(description = "是否关键属性")
|
||||
private Integer isKey;
|
||||
|
||||
@Schema(description = "是否计量定价")
|
||||
private Integer isMetering;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
private Long sort;
|
||||
}
|
||||
@@ -2,38 +2,56 @@ package com.zt.plat.module.base.controller.admin.base.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 物料信息 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class MaterialInfomationRespVO {
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3326")
|
||||
@ExcelProperty("主键ID")
|
||||
private Long id;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("物料编码")
|
||||
private String code;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
@ExcelProperty("物料名称")
|
||||
private String name;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "分类ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@ExcelProperty("分类ID")
|
||||
private Long classesId;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料基础字段 + 属性编码->原值的动态键值,基础字段优先,序列化时直接展开为顶层字段")
|
||||
private Map<String, Object> flatAttributes;
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getFlatAttributes() {
|
||||
return flatAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,44 @@
|
||||
package com.zt.plat.module.base.controller.admin.base.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 精简的物料信息 Response VO
|
||||
*/
|
||||
@Data
|
||||
public class MaterialInfomationSimpleRespVO {
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料信息ID", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料编码")
|
||||
private String code;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料名称")
|
||||
private String name;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "分类ID")
|
||||
private Long classesId;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(description = "物料基础字段 + 属性编码->原值的动态键值,基础字段优先,序列化时直接展开为顶层字段")
|
||||
private Map<String, Object> flatAttributes;
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getFlatAttributes() {
|
||||
return flatAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.zt.plat.module.base.controller.admin.base.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 字典值与标签映射的展示 VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MaterialInfomationValueLabelVO {
|
||||
|
||||
@Schema(description = "字典标签或原值")
|
||||
private String label;
|
||||
|
||||
@Schema(description = "字典类型ID")
|
||||
private Long dictTypeId;
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public class MdmMaterialViewDO {
|
||||
private String shortDescription;
|
||||
private String longDescription;
|
||||
private String chalcoCode;
|
||||
private String zhongtongCode;
|
||||
private String majorClassCode;
|
||||
private String majorClassName;
|
||||
private String specification;
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.zt.plat.module.base.dal.dataobject.base.MaterialInfomationDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 物料信息 Service 接口
|
||||
@@ -72,4 +73,12 @@ public interface MaterialInfomationService {
|
||||
* @return 精简分页列表
|
||||
*/
|
||||
PageResult<MaterialInfomationSimpleRespVO> getMaterialInfomationSimplePage(MaterialInfomationSimplePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 按 ID 批量查询物料信息(含持有属性)
|
||||
*
|
||||
* @param ids 物料 ID 集合
|
||||
* @return 物料信息列表
|
||||
*/
|
||||
List<MaterialInfomationRespVO> getMaterialInfomationListByIds(Collection<Long> ids);
|
||||
}
|
||||
@@ -14,9 +14,13 @@ import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimple
|
||||
import com.zt.plat.module.base.controller.admin.base.vo.MaterialInfomationSimpleRespVO;
|
||||
import com.zt.plat.module.base.dal.dao.materialclasses.MaterialClassesMapper;
|
||||
import com.zt.plat.module.base.dal.dao.materialhasclasses.MaterialHasClassesMapper;
|
||||
import com.zt.plat.module.base.dal.dao.materialhasproperties.MaterialHasPropertiesMapper;
|
||||
import com.zt.plat.module.base.dal.dao.materialproperties.MaterialPropertiesMapper;
|
||||
import com.zt.plat.module.base.dal.dataobject.base.MaterialInfomationDO;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialclasses.MaterialClassesDO;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialhasclasses.MaterialHasClassesDO;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialhasproperties.MaterialHasPropertiesDO;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialproperties.MaterialPropertiesDO;
|
||||
import com.zt.plat.module.base.dal.mysql.base.MaterialInfomationMapper;
|
||||
import com.zt.plat.module.erp.api.ErpExternalApi;
|
||||
import com.zt.plat.module.erp.api.dto.ErpProductiveVersionReqDTO;
|
||||
@@ -25,12 +29,8 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -59,6 +59,12 @@ public class MaterialInfomationServiceImpl implements MaterialInfomationService
|
||||
@Resource
|
||||
private MaterialClassesMapper materialClassesMapper;
|
||||
|
||||
@Resource
|
||||
private MaterialHasPropertiesMapper materialHasPropertiesMapper;
|
||||
|
||||
@Resource
|
||||
private MaterialPropertiesMapper materialPropertiesMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MaterialInfomationRespVO createMaterialInfomation(MaterialInfomationSaveReqVO createReqVO) {
|
||||
@@ -190,6 +196,23 @@ public class MaterialInfomationServiceImpl implements MaterialInfomationService
|
||||
return CollUtil.getFirst(buildRespList(Collections.singletonList(info)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MaterialInfomationRespVO> getMaterialInfomationListByIds(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<MaterialInfomationDO> infoList = materialInfomationMapper.selectBatchIds(ids);
|
||||
if (CollUtil.isEmpty(infoList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<MaterialHasClassesDO> relationList = materialHasClassesMapper.selectList(MaterialHasClassesDO::getInfomationId, ids);
|
||||
Map<Long, Long> infoClassMap = relationList.stream()
|
||||
.filter(item -> item.getInfomationId() != null)
|
||||
.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
|
||||
infoList.forEach(item -> item.setClassesId(infoClassMap.get(item.getId())));
|
||||
return buildRespList(infoList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MaterialInfomationRespVO> getMaterialInfomationPage(MaterialInfomationPageReqVO pageReqVO) {
|
||||
List<Long> infomationIds = null;
|
||||
@@ -282,14 +305,28 @@ public class MaterialInfomationServiceImpl implements MaterialInfomationService
|
||||
MaterialHasClassesDO::getClassesId, (existing, replacement) -> existing));
|
||||
}
|
||||
|
||||
Map<Long, MaterialPropertiesAggregate> propertyAggregateMap = buildPropertyAggregates(
|
||||
pageResult.getList().stream().map(MaterialInfomationDO::getId).collect(Collectors.toList()));
|
||||
|
||||
Map<Long, Long> finalInfoClassMap = infoClassMap;
|
||||
List<MaterialInfomationSimpleRespVO> respList = pageResult.getList().stream().map(item -> {
|
||||
MaterialInfomationSimpleRespVO vo = new MaterialInfomationSimpleRespVO();
|
||||
vo.setId(item.getId());
|
||||
vo.setCode(item.getCode());
|
||||
vo.setName(item.getName());
|
||||
vo.setRemark(item.getRemark());
|
||||
vo.setClassesId(finalInfoClassMap.get(item.getId()));
|
||||
Map<String, Object> flatMap = new LinkedHashMap<>();
|
||||
flatMap.put("id", item.getId());
|
||||
flatMap.put("code", item.getCode());
|
||||
flatMap.put("name", item.getName());
|
||||
flatMap.put("classesId", finalInfoClassMap.get(item.getId()));
|
||||
flatMap.put("remark", item.getRemark());
|
||||
|
||||
MaterialPropertiesAggregate aggregate = propertyAggregateMap.get(item.getId());
|
||||
if (aggregate != null && CollUtil.isNotEmpty(aggregate.getAttributes())) {
|
||||
aggregate.getAttributes().forEach((k, v) -> {
|
||||
if (!flatMap.containsKey(k)) {
|
||||
flatMap.put(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
vo.setFlatAttributes(flatMap);
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
@@ -300,8 +337,89 @@ public class MaterialInfomationServiceImpl implements MaterialInfomationService
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, MaterialPropertiesAggregate> propertyAggregateMap = buildPropertyAggregates(
|
||||
list.stream().map(MaterialInfomationDO::getId).filter(Objects::nonNull).collect(Collectors.toList()));
|
||||
return list.stream()
|
||||
.map(item -> BeanUtils.toBean(item, MaterialInfomationRespVO.class))
|
||||
.map(item -> {
|
||||
MaterialInfomationRespVO vo = BeanUtils.toBean(item, MaterialInfomationRespVO.class);
|
||||
Map<String, Object> flatMap = new LinkedHashMap<>();
|
||||
flatMap.put("id", item.getId());
|
||||
flatMap.put("code", item.getCode());
|
||||
flatMap.put("name", item.getName());
|
||||
flatMap.put("classesId", item.getClassesId());
|
||||
flatMap.put("remark", item.getRemark());
|
||||
flatMap.put("createTime", item.getCreateTime());
|
||||
|
||||
MaterialPropertiesAggregate aggregate = propertyAggregateMap.get(item.getId());
|
||||
if (aggregate != null && CollUtil.isNotEmpty(aggregate.getAttributes())) {
|
||||
aggregate.getAttributes().forEach((k, v) -> {
|
||||
// 基础字段优先,存在同名则跳过属性值
|
||||
if (!flatMap.containsKey(k)) {
|
||||
flatMap.put(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
vo.setFlatAttributes(flatMap);
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<Long, MaterialPropertiesAggregate> buildPropertyAggregates(Collection<Long> infoIds) {
|
||||
if (CollUtil.isEmpty(infoIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
List<MaterialHasPropertiesDO> hasPropertiesList = materialHasPropertiesMapper.selectList(
|
||||
new LambdaQueryWrapperX<MaterialHasPropertiesDO>().in(MaterialHasPropertiesDO::getInfomationId, infoIds));
|
||||
if (CollUtil.isEmpty(hasPropertiesList)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Set<Long> propertiesIds = hasPropertiesList.stream()
|
||||
.map(MaterialHasPropertiesDO::getPropertiesId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, MaterialPropertiesDO> propertiesMap = propertiesIds.isEmpty()
|
||||
? Collections.emptyMap()
|
||||
: materialPropertiesMapper.selectBatchIds(propertiesIds).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(MaterialPropertiesDO::getId, Function.identity()));
|
||||
|
||||
Map<Long, List<MaterialHasPropertiesDO>> infoPropsMap = hasPropertiesList.stream()
|
||||
.collect(Collectors.groupingBy(MaterialHasPropertiesDO::getInfomationId));
|
||||
|
||||
Map<Long, MaterialPropertiesAggregate> result = new HashMap<>();
|
||||
for (Map.Entry<Long, List<MaterialHasPropertiesDO>> entry : infoPropsMap.entrySet()) {
|
||||
Long infoId = entry.getKey();
|
||||
List<MaterialHasPropertiesDO> props = entry.getValue();
|
||||
// 按 sort 升序,保持 deterministic,重复 code 时保留首条
|
||||
props.sort(Comparator.comparing((MaterialHasPropertiesDO o) -> o.getSort() == null ? Long.MAX_VALUE : o.getSort())
|
||||
.thenComparing(MaterialHasPropertiesDO::getPropertiesId, Comparator.nullsLast(Long::compareTo))
|
||||
.thenComparing(MaterialHasPropertiesDO::getId, Comparator.nullsLast(Long::compareTo)));
|
||||
|
||||
Map<String, Object> flatMap = new LinkedHashMap<>();
|
||||
for (MaterialHasPropertiesDO item : props) {
|
||||
MaterialPropertiesDO property = propertiesMap.get(item.getPropertiesId());
|
||||
String code = property != null ? property.getCode() : null;
|
||||
if (StrUtil.isBlank(code)) {
|
||||
continue;
|
||||
}
|
||||
if (flatMap.containsKey(code)) {
|
||||
continue;
|
||||
}
|
||||
flatMap.put(code, item.getValue());
|
||||
}
|
||||
|
||||
result.put(infoId, new MaterialPropertiesAggregate(flatMap));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private record MaterialPropertiesAggregate(Map<String, Object> attributes) {
|
||||
public Map<String, Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
|
||||
private static final long ROOT_CLASS_PARENT_ID = 0L;
|
||||
|
||||
@Resource
|
||||
private MdmMaterialViewMapper mdmMaterialViewMapper;
|
||||
@Resource
|
||||
@@ -113,6 +111,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
offset += slice.size();
|
||||
report.incrementProcessedRecords(slice.size());
|
||||
final List<MdmMaterialViewDO> batch = slice;
|
||||
// 单批落库使用事务,保证本批次原子性与幂等可重试
|
||||
transactionTemplate.executeWithoutResult(status -> processBatch(batch, propertyDefinitions, report,
|
||||
materialCacheById, materialCacheByCode, categoryCacheByCode));
|
||||
}
|
||||
@@ -178,7 +177,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
.dataType(definition.getDataType())
|
||||
.remark("MDM同步自动创建")
|
||||
.build();
|
||||
pendingInsertDefinitions.add(current);
|
||||
pendingInsertDefinitions.add(current);
|
||||
report.incrementInsertedPropertyDefinitions();
|
||||
} else {
|
||||
boolean needUpdate = false;
|
||||
@@ -298,6 +297,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
}
|
||||
Map<String, MaterialInfomationDO> mapping = new LinkedHashMap<>();
|
||||
List<MaterialInfomationDO> pendingMaterialInserts = new ArrayList<>();
|
||||
List<MaterialInfomationDO> pendingMaterialUpdates = new ArrayList<>(); // 收集需批量更新的物料主数据
|
||||
for (MdmMaterialViewDO item : batch) {
|
||||
Long codeId = item.getCodeId();
|
||||
if (codeId == null || codeId <= 0) {
|
||||
@@ -309,8 +309,8 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
continue;
|
||||
}
|
||||
MaterialInfomationDO current = existingById.get(codeId);
|
||||
// 名称优先取 MDM 的长描述(desclong),若为空再回退到 desc1
|
||||
String name = normalizeValue(item.getLongDescription());
|
||||
// 名称优先取 MDM 的短描述(descshort),若为空再回退到 desc1
|
||||
String name = normalizeValue(item.getShortDescription());
|
||||
if (name == null) {
|
||||
name = normalizeValue(item.getMaterialName());
|
||||
}
|
||||
@@ -345,7 +345,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
update.setId(current.getId());
|
||||
update.setCode(current.getCode());
|
||||
update.setName(current.getName());
|
||||
materialInfomationMapper.updateById(update);
|
||||
pendingMaterialUpdates.add(update);
|
||||
report.incrementUpdatedMaterials();
|
||||
}
|
||||
mapping.put(code, current);
|
||||
@@ -354,6 +354,9 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
if (CollUtil.isNotEmpty(pendingMaterialInserts)) {
|
||||
materialInfomationMapper.insertBatch(pendingMaterialInserts);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(pendingMaterialUpdates)) {
|
||||
materialInfomationMapper.updateBatch(pendingMaterialUpdates);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@@ -374,9 +377,10 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<Long, MaterialHasClassesDO> existingRelations = materialHasClassesMapper.selectByInfoIds(infoIds)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, Function.identity(), (a, b) -> a));
|
||||
List<MaterialHasClassesDO> relationsToInsert = new ArrayList<>();
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MaterialHasClassesDO::getInfomationId, Function.identity(), (a, b) -> a));
|
||||
List<MaterialHasClassesDO> relationsToInsert = new ArrayList<>();
|
||||
List<MaterialHasClassesDO> relationsToUpdate = new ArrayList<>(); // 收集需批量更新的物料-分类关系
|
||||
for (MdmMaterialViewDO item : batch) {
|
||||
String materialCode = normalizeValue(item.getMaterialCode());
|
||||
MaterialInfomationDO info = materials.get(materialCode);
|
||||
@@ -401,7 +405,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
MaterialHasClassesDO update = new MaterialHasClassesDO();
|
||||
update.setId(relation.getId());
|
||||
update.setClassesId(cls.getId());
|
||||
materialHasClassesMapper.updateById(update);
|
||||
relationsToUpdate.add(update);
|
||||
relation.setClassesId(cls.getId());
|
||||
report.incrementUpdatedClassRelations();
|
||||
}
|
||||
@@ -409,6 +413,9 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
if (CollUtil.isNotEmpty(relationsToInsert)) {
|
||||
materialHasClassesMapper.insertBatch(relationsToInsert);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(relationsToUpdate)) {
|
||||
materialHasClassesMapper.updateBatch(relationsToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private void upsertMaterialProperties(List<MdmMaterialViewDO> batch,
|
||||
@@ -438,6 +445,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
.collect(Collectors.toMap(item -> buildRelationKey(item.getInfomationId(), item.getPropertiesId()),
|
||||
Function.identity(), (a, b) -> a));
|
||||
List<MaterialHasPropertiesDO> propertyRelationsToInsert = new ArrayList<>();
|
||||
List<MaterialHasPropertiesDO> propertyRelationsToUpdate = new ArrayList<>(); // 收集需批量更新的物料属性值
|
||||
for (MdmMaterialViewDO item : batch) {
|
||||
String materialCode = normalizeValue(item.getMaterialCode());
|
||||
MaterialInfomationDO info = materials.get(materialCode);
|
||||
@@ -473,7 +481,7 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
update.setId(relation.getId());
|
||||
update.setValue(value);
|
||||
update.setSort(entry.getKey().getSort());
|
||||
materialHasPropertiesMapper.updateById(update);
|
||||
propertyRelationsToUpdate.add(update);
|
||||
relation.setValue(value);
|
||||
relation.setSort(entry.getKey().getSort());
|
||||
report.incrementUpdatedPropertyValues();
|
||||
@@ -483,6 +491,9 @@ public class MasterDataSyncServiceImpl implements MasterDataSyncService {
|
||||
if (CollUtil.isNotEmpty(propertyRelationsToInsert)) {
|
||||
materialHasPropertiesMapper.insertBatch(propertyRelationsToInsert);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(propertyRelationsToUpdate)) {
|
||||
materialHasPropertiesMapper.updateBatch(propertyRelationsToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildRelationKey(Long infoId, Long propertyId) {
|
||||
|
||||
@@ -14,21 +14,22 @@ import java.util.function.Function;
|
||||
@RequiredArgsConstructor
|
||||
public enum MasterDataPropertyDefinition {
|
||||
|
||||
BASE_UNIT_CODE("MTRL_BASE_UNIT_CODE", "基本计量单位编码", 50L, MdmMaterialViewDO::getBaseUnitCode, "STRING"),
|
||||
BASE_UNIT_NAME("MTRL_BASE_UNIT_NAME", "基本计量单位描述", 60L, MdmMaterialViewDO::getBaseUnitName, "STRING"),
|
||||
SHORT_DESCRIPTION("MTRL_SHORT_DESC", "短描述", 70L, MdmMaterialViewDO::getShortDescription, "STRING"),
|
||||
LONG_DESCRIPTION("MTRL_LONG_DESC", "长描述", 80L, MdmMaterialViewDO::getLongDescription, "STRING"),
|
||||
CHALCO_CODE("MTRL_CHALCO_CODE", "中铝编码", 90L, MdmMaterialViewDO::getChalcoCode, "STRING"),
|
||||
SPECIFICATION("MTRL_SPECIFICATION", "规格", 100L, MdmMaterialViewDO::getSpecification, "STRING"),
|
||||
MODEL("MTRL_MODEL", "型号", 110L, MdmMaterialViewDO::getModel, "STRING"),
|
||||
TEXTURE("MTRL_TEXTURE", "材质", 120L, MdmMaterialViewDO::getTexture, "STRING"),
|
||||
DRAWING_NUMBER("MTRL_DRAWING_NUMBER", "图号", 130L, MdmMaterialViewDO::getDrawingNumber, "STRING"),
|
||||
ORDER_NUMBER("MTRL_ORDER_NUMBER", "订货号", 140L, MdmMaterialViewDO::getOrderNumber, "STRING"),
|
||||
OTHER_PARAMETERS("MTRL_OTHER_PARAMETERS", "其它参数", 150L, MdmMaterialViewDO::getOtherParameters, "STRING"),
|
||||
EQUIPMENT_CATEGORY("MTRL_EQUIPMENT_CATEGORY", "设备类别", 160L, MdmMaterialViewDO::getEquipmentCategory, "STRING"),
|
||||
MANUFACTURER("MTRL_MANUFACTURER", "主机生产商", 170L, MdmMaterialViewDO::getManufacturer, "STRING"),
|
||||
SOURCE("MTRL_SOURCE", "来源", 180L, MdmMaterialViewDO::getSource, "STRING"),
|
||||
RECORD_TIME("MTRL_RECORD_TIME", "记录时间", 190L,
|
||||
BASE_UNIT_CODE("mtrlBaseUnitCode", "基本计量单位编码", 50L, MdmMaterialViewDO::getBaseUnitCode, "STRING"),
|
||||
BASE_UNIT_NAME("mtrlBaseUnitName", "基本计量单位描述", 60L, MdmMaterialViewDO::getBaseUnitName, "STRING"),
|
||||
SHORT_DESCRIPTION("mtrlShortDesc", "短描述", 70L, MdmMaterialViewDO::getShortDescription, "STRING"),
|
||||
LONG_DESCRIPTION("mtrlLongDesc", "长描述", 80L, MdmMaterialViewDO::getLongDescription, "STRING"),
|
||||
CHALCO_CODE("mtrlChalcoCode", "中铝编码", 90L, MdmMaterialViewDO::getChalcoCode, "STRING"),
|
||||
ZHONGTONG_CODE("mtrlZhongtongCode", "中铜编码", 95L, MdmMaterialViewDO::getZhongtongCode, "STRING"),
|
||||
SPECIFICATION("mtrlSpecification", "规格", 100L, MdmMaterialViewDO::getSpecification, "STRING"),
|
||||
MODEL("mtrlModel", "型号", 110L, MdmMaterialViewDO::getModel, "STRING"),
|
||||
TEXTURE("mtrlTexture", "材质", 120L, MdmMaterialViewDO::getTexture, "STRING"),
|
||||
DRAWING_NUMBER("mtrlDrawingNumber", "图号", 130L, MdmMaterialViewDO::getDrawingNumber, "STRING"),
|
||||
ORDER_NUMBER("mtrlOrderNumber", "订货号", 140L, MdmMaterialViewDO::getOrderNumber, "STRING"),
|
||||
OTHER_PARAMETERS("mtrlOtherParameters", "其它参数", 150L, MdmMaterialViewDO::getOtherParameters, "STRING"),
|
||||
EQUIPMENT_CATEGORY("mtrlEquipmentCategory", "设备类别", 160L, MdmMaterialViewDO::getEquipmentCategory, "STRING"),
|
||||
MANUFACTURER("mtrlManufacturer", "主机生产商", 170L, MdmMaterialViewDO::getManufacturer, "STRING"),
|
||||
SOURCE("mtrlSource", "来源", 180L, MdmMaterialViewDO::getSource, "STRING"),
|
||||
RECORD_TIME("mtrlRecordTime", "记录时间", 190L,
|
||||
source -> source.getRecordTime() == null ? null : source.getRecordTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
"DATETIME");
|
||||
|
||||
|
||||
@@ -2,19 +2,27 @@ package com.zt.plat.module.base.service.materialhasproperties;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.util.*;
|
||||
import com.zt.plat.module.base.controller.admin.materialhasproperties.vo.*;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialhasproperties.MaterialHasPropertiesDO;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
|
||||
import com.zt.plat.module.base.dal.dao.materialhasproperties.MaterialHasPropertiesMapper;
|
||||
import com.zt.plat.module.base.dal.dao.materialproperties.MaterialPropertiesMapper;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialhasproperties.MaterialHasPropertiesDO;
|
||||
import com.zt.plat.module.base.dal.dataobject.materialproperties.MaterialPropertiesDO;
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -34,8 +42,12 @@ public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesSe
|
||||
@Resource
|
||||
private MaterialHasPropertiesMapper materialHasPropertiesMapper;
|
||||
|
||||
@Resource
|
||||
private MaterialPropertiesMapper materialPropertiesMapper;
|
||||
|
||||
@Override
|
||||
public MaterialHasPropertiesRespVO createMaterialHasProperties(MaterialHasPropertiesSaveReqVO createReqVO) {
|
||||
validatePropertyCodeUnique(createReqVO.getInfomationId(), createReqVO.getPropertiesId(), null);
|
||||
// 插入
|
||||
MaterialHasPropertiesDO materialHasProperties = BeanUtils.toBean(createReqVO, MaterialHasPropertiesDO.class);
|
||||
materialHasPropertiesMapper.insert(materialHasProperties);
|
||||
@@ -47,6 +59,7 @@ public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesSe
|
||||
public void updateMaterialHasProperties(MaterialHasPropertiesSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateMaterialHasPropertiesExists(updateReqVO.getId());
|
||||
validatePropertyCodeUnique(updateReqVO.getInfomationId(), updateReqVO.getPropertiesId(), updateReqVO.getId());
|
||||
// 更新
|
||||
MaterialHasPropertiesDO updateObj = BeanUtils.toBean(updateReqVO, MaterialHasPropertiesDO.class);
|
||||
materialHasPropertiesMapper.updateById(updateObj);
|
||||
@@ -110,7 +123,17 @@ public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesSe
|
||||
}
|
||||
|
||||
// 去重并按提交顺序插入
|
||||
Set<String> dedupKeys = new LinkedHashSet<>();
|
||||
Set<String> dedupCodes = new LinkedHashSet<>();
|
||||
Set<Long> propertiesIds = properties.stream()
|
||||
.map(MaterialHasPropertiesBatchItemReqVO::getPropertiesId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, MaterialPropertiesDO> propertyMap = propertiesIds.isEmpty()
|
||||
? Map.of()
|
||||
: materialPropertiesMapper.selectBatchIds(propertiesIds).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(MaterialPropertiesDO::getId, Function.identity()));
|
||||
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
MaterialHasPropertiesBatchItemReqVO item = properties.get(i);
|
||||
String propIdStr = item.getPropertiesId() == null ? null : String.valueOf(item.getPropertiesId());
|
||||
@@ -118,13 +141,18 @@ public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesSe
|
||||
resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性 ID 不能为空"));
|
||||
continue;
|
||||
}
|
||||
MaterialPropertiesDO property = propertyMap.get(item.getPropertiesId());
|
||||
String code = property != null ? property.getCode() : null;
|
||||
if (StrUtil.isBlank(code)) {
|
||||
resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性编码不能为空"));
|
||||
continue;
|
||||
}
|
||||
if (StrUtil.isBlank(item.getValue())) {
|
||||
resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "属性值不能为空"));
|
||||
continue;
|
||||
}
|
||||
String key = propIdStr;
|
||||
if (!dedupKeys.add(key)) {
|
||||
// 重复的属性直接跳过后续插入,避免唯一冲突
|
||||
if (!dedupCodes.add(code)) {
|
||||
resp.getRowErrors().add(new RowValidationErrorVO(i + 1, "同一物料下属性编码重复"));
|
||||
continue;
|
||||
}
|
||||
MaterialHasPropertiesDO entity = MaterialHasPropertiesDO.builder()
|
||||
@@ -142,4 +170,38 @@ public class MaterialHasPropertiesServiceImpl implements MaterialHasPropertiesSe
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事务内校验:同一物料下有效属性编码唯一
|
||||
*/
|
||||
private void validatePropertyCodeUnique(Long infoId, Long propertiesId, Long excludeId) {
|
||||
if (infoId == null || propertiesId == null) {
|
||||
return;
|
||||
}
|
||||
MaterialPropertiesDO property = materialPropertiesMapper.selectById(propertiesId);
|
||||
if (property == null || StrUtil.isBlank(property.getCode())) {
|
||||
return;
|
||||
}
|
||||
String code = property.getCode();
|
||||
List<MaterialPropertiesDO> sameCodeProps = materialPropertiesMapper.selectList(
|
||||
new LambdaQueryWrapperX<MaterialPropertiesDO>().eq(MaterialPropertiesDO::getCode, code));
|
||||
if (CollUtil.isEmpty(sameCodeProps)) {
|
||||
return;
|
||||
}
|
||||
Set<Long> samePropertyIds = sameCodeProps.stream()
|
||||
.map(MaterialPropertiesDO::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (samePropertyIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long exists = materialHasPropertiesMapper.selectCount(new LambdaQueryWrapperX<MaterialHasPropertiesDO>()
|
||||
.eq(MaterialHasPropertiesDO::getInfomationId, infoId)
|
||||
.in(MaterialHasPropertiesDO::getPropertiesId, samePropertyIds)
|
||||
.neIfPresent(MaterialHasPropertiesDO::getId, excludeId));
|
||||
if (exists != null && exists > 0) {
|
||||
throw exception(MATERIAL_HAS_PROPERTIES_CODE_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
a.descshort AS short_description,
|
||||
a.desclong AS long_description,
|
||||
a.desc86 AS chalco_code,
|
||||
a.code AS zhongtong_code,
|
||||
b.pur_class AS major_class_code,
|
||||
c.desc1 AS major_class_name,
|
||||
a.desc9 AS specification,
|
||||
|
||||
Reference in New Issue
Block a user