该贴为学习记录贴,记录博主开发项目时遇到的各种问题和解决方法
需求:对商品购买增加库存功能,并且在编辑商品页面中,根据商品拥有的规格生成From表单,用户可在From表单中修改商品各个规格的库存。
在第二章中已经处理完前端传送数据给后端,后端进行保存sku的。这一章解决后端从数据库获取sku进行封装的问题
有一个关于商品规格的遗漏点:
在这里商品的规格是总规格,所有商品共用同样的规格属性。比如规格有颜色、尺码、衣长、裤长。那么A商品可以选择只有颜色和尺码,B商品也可以选择颜色和尺码外加衣长,也可以把所有规格全选了,但是商品不能有总规格总没有的规格。而规格下有具体的规格值,颜色:蓝色,黄色;尺码:120,130。
基于以上补充的条件,从数据库获取数据的时候要先考虑一个问题,如果商品已经有了sku数据,之后用户对规格进行操作,比如原来只有颜色只有黄色和蓝色,然后用户增加了一条新的规格值,让颜色有蓝色、黄色、红色三种,而商品原来有颜色这一规格sku中只有蓝色和黄色没有红色,那么就要思考:用户增删规格后,需要对sku数据进行增删,并且还要对商品下的规格值也进行增删操作。
首先根据商品下的规格ID获取所有规格值ID(注意区分规格和规格值,颜色是规格,蓝色是规格值),将所有的规格值ID保存在specificationItemIdList中(注:specificationItemIdList保存的是所有规格值,包括新增的)。
Map < Long, List < SchoolSpecificationItem >> spItemMap = new HashMap < > ();
List < Long > specificationItemIdList = new ArrayList < > ();
if (CollectionUtils.isNotEmpty(specificationIdList)) {
for (Long specificationId: specificationIdList) {
List < SchoolSpecificationItem > schoolSpecificationItemList =
schoolSpecificationItemRepository.findBySpecificationId(specificationId);
if (CollectionUtils.isNotEmpty(schoolSpecificationItemList)) {
for (SchoolSpecificationItem schoolSpecificationItem: schoolSpecificationItemList) {
spItemMap.computeIfAbsent(schoolSpecificationItem.getSpecificationId(), key->new ArrayList < > ())
.add(schoolSpecificationItem);
Long specificationItemId = schoolSpecificationItem.getId();
specificationItemIdList.add(specificationItemId);
}
}
}
}
为方便后续操作,使用Map来进行规格值的保存,key为规格ID,Value为规格值的集合,这之后将用来进行新增的sku数据封装。
Map<Long, List<Object>> spItemMap = new HashMap<>();
在获取到商品的sku数据后,根据sku的规格值来对specificationItemIdList进行remove,最后留下的是新增的规格值。
根据商品sku数据中的规格值ID来查询数据库,判断规格值是否已删除,从而删除商品下对应的规格值。
再根据specificationItemIdList来获取对应的规格值,对商品下的规格值进行增加。
开始对商品的sku表单数据进行处理:
- 根据商品ID和sku状态来获取所有的sku数据schoolCommoditySkuList进行封装;
- 删除封装后的sku数据中不存在的规格值,更改不存在的sku的状态进行逻辑删除;
- 在这里我用到了List的remove,但是在遍历List的时候对List进行remove会有错误,简单来讲就是原来List有3个,而遍历一开始就要遍历3次,remove掉一个之后第三个为空,这个时候没有对象遍历但是编译器还是要遍历,于是就产生了错误。专业讲解可以百度,解决方法可以用下面的代码。
Iterator<Object> iterator = list.iterator();
while(iterator..hasNext()){
Object obj = iterator.next();
iterator.remove();
}
- 将新增的规格值保存在Map<Long,List<Object>> newSpItemMap中
- 使用递归的方式封装新增的规格值,要注意递归封装的时候会出现重复封装,比如新增规格值为颜色:红色和尺码:130。在第一次循环的时候就已经封装了红色130,但是在循环到130的时候,又会把130和红色封装一次。这时候需要在每次封装的时候将封装过的保存在一个集合中,并且每次封装之前都判断一遍是否已经封装过。
List < String > executedSkuIdList = new ArrayList < > ();
List < SchoolCommoditySkuSpecificationVO > addSchoolCommoditySkuSpecificationVOList = new ArrayList < > ();
for (Map.Entry < Long, List < SchoolSpecificationItem >> entry: newSpItemMap.entrySet()) {
Long spId = entry.getKey();
List < SchoolSpecificationItem > values = entry.getValue();
Set < Long > executedIds = new HashSet < > ();
executedIds.add(spId);
List < SchoolCommoditySkuSpecificationVO > results =
this.combineSpecification(spItemMap, executedIds, values, new ArrayList < > (), executedSkuIdList);
addSchoolCommoditySkuSpecificationVOList.addAll(results);
}
// 递归方法
private List < SchoolCommoditySkuSpecificationVO > combineSpecification(
Map < Long, List < SchoolSpecificationItem >> spItemMap, Set < Long > executedIds,
List < SchoolSpecificationItem > specificationItems, List < SchoolCommoditySkuSpecificationVO > skuSpecifications,
List < String > excutedSkuIdList) {
List < SchoolCommoditySkuSpecificationVO > newAlls = new ArrayList < > ();
if (CollectionUtils.isNotEmpty(skuSpecifications)) {
for (SchoolCommoditySkuSpecificationVO skuSpecificationVO: skuSpecifications) {
if (CollectionUtils.isNotEmpty(specificationItems)) {
for (SchoolSpecificationItem nextItem: specificationItems) {
SchoolCommodityVO schoolCommodityVO = new SchoolCommodityVO();
Long specificationItemId = nextItem.getId();
String name = nextItem.getSchoolSpecification().getName();
String value = nextItem.getSpecifcationValue();
schoolCommodityVO.setId(specificationItemId);
schoolCommodityVO.setName(name);
schoolCommodityVO.setValue(value);
SchoolCommoditySkuSpecificationVO cloneCommoditySkuSpecificationVO =
new SchoolCommoditySkuSpecificationVO();
List < SchoolCommodityVO > specifications = new ArrayList < > ();
specifications.add(schoolCommodityVO);
List < SchoolCommodityVO > oldSchoolCommodityVos = skuSpecificationVO.getSpecifications();
specifications.addAll(oldSchoolCommodityVos);
cloneCommoditySkuSpecificationVO.setSpecifications(specifications);
newAlls.add(cloneCommoditySkuSpecificationVO);
}
}
}
} else {
if (CollectionUtils.isNotEmpty(specificationItems)) {
for (SchoolSpecificationItem nextItem: specificationItems) {
SchoolCommodityVO schoolCommodityVO = new SchoolCommodityVO();
Long specificationItemId = nextItem.getId();
String name = nextItem.getSchoolSpecification().getName();
String value = nextItem.getSpecifcationValue();
schoolCommodityVO.setId(specificationItemId);
schoolCommodityVO.setName(name);
schoolCommodityVO.setValue(value);
SchoolCommoditySkuSpecificationVO cloneCommoditySkuSpecificationVO = new SchoolCommoditySkuSpecificationVO();
List < SchoolCommodityVO > specifications = new ArrayList < > ();
specifications.add(schoolCommodityVO);
cloneCommoditySkuSpecificationVO.setSpecifications(specifications);
newAlls.add(cloneCommoditySkuSpecificationVO);
}
}
}
// 判断是否存在下一个处理节点
for (Long key: spItemMap.keySet()) {
if (!executedIds.contains(key)) {
// 如果未执行,就添加已执行列表内
executedIds.add(key);
return combineSpecification(spItemMap, executedIds, spItemMap.get(key), newAlls, excutedSkuIdList);
}
}
//遍历删除,除去重复的sku数据
Iterator < SchoolCommoditySkuSpecificationVO > iterator = newAlls.iterator();
while (iterator.hasNext()) {
SchoolCommoditySkuSpecificationVO schoolCommoditySkuSpecificationVO = iterator.next();
List < SchoolCommodityVO > specifications = schoolCommoditySkuSpecificationVO.getSpecifications();
String itemIdStr = getSkuSpecificationValues(specifications);
if (excutedSkuIdList.contains(itemIdStr)) {
iterator.remove();
} else {
excutedSkuIdList.add(itemIdStr);
}
}
return newAlls;
}
总结:
- 所有商品共用同个规格属性,所以每次获取sku都必须判断规格和规格值的增删;
- 对List遍历时需要进行remove和add操作必须使用list.iterator()避免报错;
- 根据规格值的增删来对sku进行增删,必须考虑重复封装新数据的错误问题。