1. 物料新增统一展平属性的接口

2. 修正物料错误的名称
This commit is contained in:
chenbowen
2025-12-25 17:33:15 +08:00
parent 80128e275d
commit 529728fc0f
14 changed files with 419 additions and 50 deletions

View File

@@ -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')")

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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);
}
}
}

View File

@@ -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,