瀏覽代碼

路由器实物规格优化

zoujiajian 3 年之前
父節點
當前提交
96901eae26

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/GoodsDonSpec.java

@@ -21,7 +21,7 @@ public class GoodsDonSpec extends BaseEntity {
     /**
      * 规格属性值最大id
      */
-    private Integer maxId;
+    private Long maxId;
 
     /**
      * 商品规格数量

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/request/ReqGoodsSkuInsert.java

@@ -14,6 +14,8 @@ import java.util.List;
 @Getter
 @Setter
 public class ReqGoodsSkuInsert {
+    private Long id;
+
     @NotNull(message = "请指定商品spu.")
     private Long goodsId;
 

+ 8 - 1
netflix-dao/src/main/java/com/cyksj/model/manage/request/ReqGoodsSkuSpec.java

@@ -3,6 +3,7 @@ package com.cyksj.model.manage.request;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -12,6 +13,8 @@ import java.util.List;
 @Getter
 @Setter
 public class ReqGoodsSkuSpec {
+    private Long id;
+
     private String key;
 
     private List<Node> values;
@@ -19,8 +22,12 @@ public class ReqGoodsSkuSpec {
     @Getter
     @Setter
     public static class Node {
-        private Integer id;
+        private Long id;
 
         private String value;
+
+        private BigDecimal price;
+
+        private String benefitsList;
     }
 }

+ 5 - 2
netflix-dao/src/main/java/com/cyksj/model/manage/request/ReqGoodsSpecSingle.java

@@ -20,7 +20,10 @@ public class ReqGoodsSpecSingle {
     private Long goodsId;
 
     @NotNull(message = "缺少规格属性id")
-    private Integer id;
+    private Long id;
+
+    @NotNull(message = "缺少总规格属性id")
+    private Long keyId;
 
     @NotBlank(message = "缺少规格名")
     private String key;
@@ -28,5 +31,5 @@ public class ReqGoodsSpecSingle {
     @NotBlank(message = "缺少属性名.")
     private String name;
 
-    private Integer maxId;
+    private Long maxId;
 }

+ 23 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CombinationSkuReq.java

@@ -0,0 +1,23 @@
+package com.cyksj.model.request;
+
+import com.cyksj.model.manage.request.ReqGoodsSkuSpec;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CombinationSkuReq
+ *创建者: JavaZou
+ *创建时间:2023/7/25 9:46
+ */
+@Getter
+@Setter
+public class CombinationSkuReq {
+	private Long id;
+
+	private String key;
+
+	private List<ReqGoodsSkuSpec.Node> values;
+}

+ 13 - 4
netflix-service/src/main/java/com/cyksj/service/mange/CmsGoodsDonSpecService.java

@@ -1,11 +1,12 @@
 package com.cyksj.service.mange;
 
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.cyksj.mapper.GoodsDonSpecMapper;
+import com.cyksj.model.entity.GoodsDon;
 import com.cyksj.model.entity.GoodsDonSpec;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
+import com.cyksj.model.manage.request.ReqGoodsSkuInsert;
+import com.cyksj.model.request.CombinationSkuReq;
+
+import java.util.List;
 
 /**
  * @author chan
@@ -13,4 +14,12 @@ import org.springframework.stereotype.Service;
  */
 public interface CmsGoodsDonSpecService extends IService<GoodsDonSpec> {
 
+	/**
+	 * 新增或编辑规格spec
+	 */
+	void insertOrUpdateSkuSpec(ReqGoodsSkuInsert insert) throws Exception;
+
+	void skuAppend(ReqGoodsSkuInsert insert, GoodsDon goods) throws Exception;
+
+	void combinationSku(Long goodsId, List<CombinationSkuReq> combinations) throws Exception;
 }

+ 329 - 2
netflix-service/src/main/java/com/cyksj/service/mange/goods/CmsGoodsDonSpecServiceImpl.java

@@ -1,19 +1,346 @@
 package com.cyksj.service.mange.goods;
 
-import com.baomidou.mybatisplus.extension.service.IService;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.snowflake.Sequence;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.GoodsDonMapper;
+import com.cyksj.mapper.GoodsDonSkuMapper;
 import com.cyksj.mapper.GoodsDonSpecMapper;
+import com.cyksj.model.entity.GoodsDon;
+import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.GoodsDonSpec;
+import com.cyksj.model.manage.request.ReqGoodsSkuInsert;
+import com.cyksj.model.manage.request.ReqGoodsSkuSpec;
+import com.cyksj.model.manage.request.ReqGoodsSpecSingle;
+import com.cyksj.model.request.CombinationSkuReq;
 import com.cyksj.service.mange.CmsGoodsDonSpecService;
+import com.cyksj.service.sys.SysConfigService;
+import com.fasterxml.jackson.databind.JavaType;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
 /**
  * @author chan
  * @date 2022/9/27 5:19 PM
  */
 @Slf4j
 @Service
-public class CmsGoodsDonSpecServiceImpl extends ServiceImpl<GoodsDonSpecMapper,GoodsDonSpec> implements CmsGoodsDonSpecService {
+@RequiredArgsConstructor
+public class CmsGoodsDonSpecServiceImpl extends ServiceImpl<GoodsDonSpecMapper, GoodsDonSpec> implements CmsGoodsDonSpecService {
+	private final static Sequence SEQUENCE = new Sequence(0);
+
+	private final GoodsDonMapper goodsDonMapper;
+
+	private final GoodsDonSkuMapper goodsDonSkuMapper;
+
+	private final SysConfigService sysConfigService;
+
+	@Override
+	public void insertOrUpdateSkuSpec(ReqGoodsSkuInsert insert) throws Exception {
+		GoodsDon goods = goodsDonMapper.selectById(insert.getGoodsId());
+		if (null == goods) {
+			throw new RuntimeException("找不到商品spu.");
+		}
+		GoodsDonSpec loveSpec = this.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId, insert.getGoodsId()));
+		if (null != loveSpec) {
+			throw new RuntimeException("该商品已新增总规格, 请点击编辑并追加该商品的sku.");
+		}
+
+		List<ReqGoodsSkuSpec> specs = insert.getSpecs();
+		if (null == specs || specs.isEmpty()) {
+			throw new RuntimeException("缺少商品总规格.");
+		}
+
+		// 属性最大id
+		Long maxId = 0l;
+		// 统计总属性数
+		Long total = 0l;
+		// 判断id是否有重复
+		Map<Long, ReqGoodsSpecSingle> map = null;
+		// 规格数量
+		int specNumber = specs.size();
+
+		for (ReqGoodsSkuSpec spec : specs) {
+			if (spec.getId() == null) {
+				spec.setId(SEQUENCE.nextId());
+			}
+			if (StringUtils.isBlank(spec.getKey())) {
+				throw new RuntimeException("存在空白规格名.");
+			}
+
+			List<ReqGoodsSkuSpec.Node> values = spec.getValues();
+			if (null == values || values.isEmpty()) {
+				throw new RuntimeException("规格[" + spec.getKey() + "]中缺少属性.");
+			}
+			total += values.size();
+			for (ReqGoodsSkuSpec.Node e : values) {
+				if (null == e.getId() || e.getId() < 0) {
+					e.setId(SEQUENCE.nextId());
+				}
+				if (StringUtils.isBlank(e.getValue())) {
+					throw new RuntimeException("规格[" + spec.getKey() + "]中存在空白属性.");
+				}
+				if (e.getId() > maxId) {
+					maxId = e.getId();
+				}
+
+				if (goods.getType() == 2 && (e.getPrice() == null || e.getPrice().compareTo(BigDecimal.ZERO) < 0)) {
+					throw BusinessRuntimeException.getInstance("实物规格:{0}对应价格错误", e.getValue());
+				}
+
+				if (null == map) {
+					map = new HashMap<>(16);
+				}
+
+				ReqGoodsSpecSingle single = new ReqGoodsSpecSingle();
+				single.setId(e.getId());
+				single.setKeyId(spec.getId());
+				single.setKey(spec.getKey());
+				single.setName(e.getValue());
+				map.put(e.getId(), single);
+			}
+		}
+
+		if (map.size() != total) {
+			throw new RuntimeException("规格中存在重复id.");
+		}
+
+		GoodsDonSpec spec = new GoodsDonSpec();
+		spec.setGoodsId(insert.getGoodsId());
+		spec.setMaxId(maxId);
+		spec.setSpecsJson(Jsons.toJson(specs));
+		spec.setSpecNumber(specNumber);
+		spec.setSpecMapping(Jsons.toJson(map));
+		if (insert.getId() != null) {
+			this.updateById(spec);
+			return;
+		}
+		this.save(spec);
+	}
+
+	@Override
+	public void skuAppend(ReqGoodsSkuInsert insert, GoodsDon goods) throws Exception {
+
+		GoodsDonSpec loveSpec = this.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId, insert.getGoodsId()));
+		if (null == loveSpec) {
+			throw new RuntimeException("该商品未设置总规格, 请设置总规格.");
+		}
+
+		List<ReqGoodsSkuInsert.Sku> skus = insert.getSkus();
+		if (null == skus || skus.isEmpty()) {
+			throw new RuntimeException("参数错误.");
+		}
+		int specNumber = loveSpec.getSpecNumber();
+
+		JavaType type = Jsons.javaType().constructMapType(HashMap.class, Integer.class, ReqGoodsSpecSingle.class);
+		Map<Integer, ReqGoodsSpecSingle> map = Jsons.parseObject(loveSpec.getSpecMapping(), type);
+
+		StringBuilder builder = new StringBuilder();
+		List<ReqGoodsSpecSingle> specList = null;
+		GoodsDonSku goodsDonSku = null;
+
+		// 判断属性是否属于同一个key
+		Set<String> set = null;
+
+		for (ReqGoodsSkuInsert.Sku sku : insert.getSkus()) {
+			// 跳过空值的sku
+			// 解析sku的规格属性
+			final String specIds = sku.getSpecIds();
+			if (StringUtils.isBlank(specIds)) {
+				throw new RuntimeException("请选择商品的规格.");
+			}
+			int[] ids = Jsons.parseObject(specIds, int[].class);
+//            if (ids.length != specNumber) {
+//                throw new RuntimeException("sku规格数量 与 总规格数量 不一致.");
+//            }
+
+			// init
+			builder.delete(0, builder.length());
+			if (null == specList) {
+				specList = new ArrayList<>(specNumber);
+			} else {
+				specList.clear();
+			}
+			if (null == set) {
+				int capacity = (specNumber + 1) * 4 / 3 + 1;
+				set = new HashSet<>(capacity);
+			} else {
+				set.clear();
+			}
+
+			for (int id : ids) {
+				ReqGoodsSpecSingle single = map.get(id);
+				if (null == single) {
+					throw new RuntimeException("总规格属性id中 不存在 sku中的id.");
+				}
+				builder.append(single.getName()).append(';');
+				specList.add(single);
+				set.add(single.getKey());
+			}
+//            if (set.size() != specNumber) {
+//                throw new RuntimeException("sku中存在多个属性属于同一个规格.");
+//            }
+
+			String specVal = builder.toString();
+			String specJson = Jsons.toJson(specList);
+
+			if (null == goodsDonSku) {
+				goodsDonSku = new GoodsDonSku();
+				goodsDonSku.setGoodsId(insert.getGoodsId());
+			}
+			BeanUtil.copyProperties(sku, goodsDonSku);
+			GoodsDonSku specIds_exists = goodsDonSkuMapper.selectOne(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getSpecIds, specIds)
+					.eq(GoodsDonSku::getGoodsId, goods.getId())
+					.last("limit 1"));
+			if (specIds_exists != null) {
+				log.info("库中存在相同二级分类的规格,spec_ids:{}", specIds);
+				continue;
+			}
+			goodsDonSku.setSpecIds(Jsons.toJson(ids));
+			goodsDonSku.setSpecVal(specVal);
+			goodsDonSku.setSpecJson(specJson);
+			if (goods.getType() == 1) {
+				if (sku.getMonths() != null && sku.getMonths() > 0 && sku.getDays() != null && sku.getDays() > 0)
+					throw BusinessRuntimeException.getInstance("续费天数和续费月数不能同时存在");
+				if (sku.getMonths() == null && sku.getDays() == null)
+					throw BusinessRuntimeException.getInstance("请输入对应续费时间");
+			}
+			goodsDonSkuMapper.insert(goodsDonSku);
+
+		}
+	}
+
+	@Override
+	public void combinationSku(Long goodsId, List<CombinationSkuReq> combinations) throws Exception {
+		GoodsDon goodsDon = goodsDonMapper.selectById(goodsId);
+		if (goodsDon == null) throw BusinessRuntimeException.getInstance("平台不存在");
+		if (goodsDon.getType() != 2) {
+			throw BusinessRuntimeException.getInstance("暂只支持实物规格");
+		}
+		GoodsDonSpec loveSpec = this.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId, goodsId));
+		if (null == loveSpec) {
+			throw new RuntimeException("该商品未设置总规格, 请设置总规格.");
+		}
+		String specsJson = loveSpec.getSpecsJson();
+		List<ReqGoodsSkuSpec> specsList = Jsons.parseList(specsJson, ReqGoodsSkuSpec.class);
+		Map<Long, List<ReqGoodsSkuSpec>> specValueMap = specsList.stream().collect(Collectors.groupingBy(ReqGoodsSkuSpec::getId));
+		if (combinations.isEmpty()) {
+			throw BusinessRuntimeException.getInstance("请选择对应的规格组合");
+		}
+		List<ReqGoodsSkuInsert.Sku> skus = new LinkedList<>();
+		ReqGoodsSkuInsert reqGoodsSkuInsert = new ReqGoodsSkuInsert();
+		reqGoodsSkuInsert.setGoodsId(goodsId);
+		List<List<Long>> specIdLists = new LinkedList<>();
+		CombinationSkuReq request = combinations.get(0);
+		if (request.getId() == null) {
+			throw BusinessRuntimeException.getInstance("传入的总规格属性id不存在");
+		}
+		List<ReqGoodsSkuSpec> reqGoodsSkuSpecs = specValueMap.get(request.getId());
+		if (reqGoodsSkuSpecs == null) {
+			throw BusinessRuntimeException.getInstance("传入的id:{0}总规格属性不存在", request.getId());
+		}
+		Map<Long, List<ReqGoodsSkuSpec.Node>> nodeIdMap = reqGoodsSkuSpecs.get(0).getValues().stream().collect(Collectors.groupingBy(ReqGoodsSkuSpec.Node::getId));
+		if (CollUtil.isNotEmpty(request.getValues())) {
+			unifiedHandleCombinationSku(combinations, request.getValues(), specValueMap, nodeIdMap, specIdLists, skus);
+		} else {
+			ReqGoodsSkuSpec reqGoodsSkuSpec = reqGoodsSkuSpecs.get(0);
+			unifiedHandleCombinationSku(combinations,reqGoodsSkuSpec.getValues(),specValueMap, nodeIdMap,specIdLists,skus);
+		}
+		reqGoodsSkuInsert.setSkus(skus);
+		skuAppend(reqGoodsSkuInsert, goodsDon);
+	}
+
+	public void unifiedHandleCombinationSku(List<CombinationSkuReq> combinations, List<ReqGoodsSkuSpec.Node> values, Map<Long, List<ReqGoodsSkuSpec>> specValueMap, Map<Long, List<ReqGoodsSkuSpec.Node>> nodeIdMap, List<List<Long>> specIdLists, List<ReqGoodsSkuInsert.Sku> skus) throws Exception {
+		for (ReqGoodsSkuSpec.Node node_value : values) {
+			ReqGoodsSkuInsert.Sku sku = new ReqGoodsSkuInsert.Sku();
+			sku.setPrice(BigDecimal.ZERO);
+			List<Long> specIds = new LinkedList<>();
+			unifiedHandleSecondCombinationSku(node_value, nodeIdMap, sku, specIds);
+			recurCombinationSku(combinations, combinations.size(), 1, 1, specIds, specIdLists, sku, specValueMap, skus);
+		}
+	}
+
+
+	public void unifiedHandleSecondCombinationSku(ReqGoodsSkuSpec.Node temp_node, Map<Long, List<ReqGoodsSkuSpec.Node>> nodeIdMap, ReqGoodsSkuInsert.Sku sku, List<Long> specIds) {
+		List<ReqGoodsSkuSpec.Node> nodes = nodeIdMap.get(temp_node.getId());
+		if (CollUtil.isEmpty(nodes)) {
+			throw BusinessRuntimeException.getInstance("{0}规格不存在", temp_node.getValue());
+		}
+		ReqGoodsSkuSpec.Node node = nodes.get(0);
+		BigDecimal price = node.getPrice();
+		if (price == null && node.getValue().contains("无")) {
+			price = BigDecimal.ZERO;
+		}
+		if (price == null) {
+			throw BusinessRuntimeException.getInstance("该实物子规格:{0}未配置价格", node.getValue());
+		}
+		sku.setPrice(sku.getPrice().add(price));
+		specIds.add(temp_node.getId());
+		if (node.getValue().contains("套餐")) {
+			if (StrUtil.isNotEmpty(node.getBenefitsList())) {
+				sku.setIsBenefits(true);
+				sku.setBenefitsList(node.getBenefitsList());
+			}
+		}
+	}
+
+	/**
+	 * @param combinations 传入的组合数据
+	 * @param total        总共需要组合的大小
+	 * @param has          已有组合数大小
+	 * @param start        下一个组合数组下表
+	 * @param specIds      组合规格ids
+	 * @param sku          待插入库中的sku
+	 * @param specValueMap 总规格库中对应values
+	 */
+	private void recurCombinationSku(List<CombinationSkuReq> combinations, Integer total, Integer has, Integer start, List<Long> specIds, List<List<Long>> specIdLists, ReqGoodsSkuInsert.Sku sku, Map<Long, List<ReqGoodsSkuSpec>> specValueMap, List<ReqGoodsSkuInsert.Sku> skus) throws Exception {
+		if (has == total) {
+			if (specIdLists.contains(specIds)) {
+				return;
+			}
+			sku.setSpecIds(Jsons.toJson(specIds));
+			skus.add(sku);
+			return;
+		}
+		CombinationSkuReq request = combinations.get(start);
+		if (request.getId() == null) {
+			throw BusinessRuntimeException.getInstance("总规格属性id不存在");
+		}
+		List<ReqGoodsSkuSpec> reqGoodsSkuSpecs = specValueMap.get(request.getId());
+		if (reqGoodsSkuSpecs == null) {
+			throw BusinessRuntimeException.getInstance("{0}总规格属性不存在", request.getKey());
+		}
+		ReqGoodsSkuSpec reqGoodsSkuSpec = reqGoodsSkuSpecs.get(0);
+		Map<Long, List<ReqGoodsSkuSpec.Node>> nodeIdMap = reqGoodsSkuSpec.getValues().stream().collect(Collectors.groupingBy(ReqGoodsSkuSpec.Node::getId));
+		if (CollUtil.isNotEmpty(request.getValues())) {
+			createNewSkuAndRecurCombination(combinations, request.getValues(), nodeIdMap, has, start, specIds, specIdLists, sku, specValueMap, skus);
+			return;
+		}
+		createNewSkuAndRecurCombination(combinations, reqGoodsSkuSpec.getValues(), nodeIdMap, has, start, specIds, specIdLists, sku, specValueMap, skus);
+	}
 
+	/**
+	 * 新的规格 递归
+	 */
+	public void createNewSkuAndRecurCombination(List<CombinationSkuReq> combinations, List<ReqGoodsSkuSpec.Node> values, Map<Long, List<ReqGoodsSkuSpec.Node>> nodeIdMap, Integer has, Integer start, List<Long> specIds, List<List<Long>> specIdLists, ReqGoodsSkuInsert.Sku sku, Map<Long, List<ReqGoodsSkuSpec>> specValueMap, List<ReqGoodsSkuInsert.Sku> skus) throws Exception {
+		for (ReqGoodsSkuSpec.Node node_value : values) {
+			BigDecimal last_price = sku.getPrice();
+			unifiedHandleSecondCombinationSku(node_value, nodeIdMap, sku, specIds);
+			recurCombinationSku(combinations, combinations.size(), has + 1, start + 1, specIds, specIdLists, sku, specValueMap, skus);
+			specIds.remove(node_value.getId());
+			sku = new ReqGoodsSkuInsert.Sku();
+			sku.setPrice(last_price);
+		}
+	}
 }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/order/impl/UserRealOrderBenefitsImpl.java

@@ -59,8 +59,8 @@ public class UserRealOrderBenefitsImpl implements UserRealOrderBenefitsService {
 		Boolean isReceivedAll = true;
 		for (GoodsDonSku sku : skuIds) {
 			Long skuId = sku.getId();
-			Long goodsId = sku.getGoodsId();
 			sku = goodsDonSkuMapper.selectById(skuId);
+			Long goodsId = sku.getGoodsId();
 //			List<GroupsRelationView> relationViewList = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
 //					.field(GroupsRelationView::getGoodsId, goodsId)
 //					.field(GroupsRelationView::getUserId, userId)

+ 86 - 151
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonSkuController.java

@@ -1,9 +1,11 @@
 package com.cyksj.web.controller.manage.goods;
 
-import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.annotation.Log;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.snowflake.Sequence;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.BusinessType;
@@ -14,23 +16,27 @@ import com.cyksj.model.entity.GoodsDonSpec;
 import com.cyksj.model.manage.request.ReqGoodsSkuInsert;
 import com.cyksj.model.manage.request.ReqGoodsSkuSpec;
 import com.cyksj.model.manage.request.ReqGoodsSpecSingle;
+import com.cyksj.model.request.CombinationSkuReq;
 import com.cyksj.model.views.GoodsDonSkuView;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.mange.CmsGoodsDonService;
 import com.cyksj.service.mange.CmsGoodsDonSkuService;
 import com.cyksj.service.mange.CmsGoodsDonSpecService;
+import com.cyksj.service.sys.SysConfigService;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.util.MapUtils;
-import com.fasterxml.jackson.databind.JavaType;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 /**
  * @author chan
@@ -55,6 +61,10 @@ public class CmsGoodsDonSkuController {
 
     private final RedisService redisService;
 
+    private final SysConfigService sysConfigService;
+
+    private final static Sequence SEQUENCE = new Sequence(0);
+
 
     /**
      * 新增 商品spec & sku
@@ -62,75 +72,23 @@ public class CmsGoodsDonSkuController {
     @PostMapping(value = "/post/spec-sku")
     @Log(module = "平台管理/新增商品sku&spec", businessType = BusinessType.POST, isSaveRequestData = true)
     public Result<String> skuInsert(@Validated @RequestBody ReqGoodsSkuInsert insert) throws Exception{
+        goodsDonSpecService.insertOrUpdateSkuSpec(insert);
 
-        GoodsDon goods = goodsDonService.getById(insert.getGoodsId());
-        if (null == goods) {
-            throw new RuntimeException("找不到商品spu.");
-        }
-        GoodsDonSpec loveSpec = goodsDonSpecService.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId,insert.getGoodsId()));
-        if (null != loveSpec) {
-            throw new RuntimeException("该商品已新增总规格, 请点击编辑并追加该商品的sku.");
-        }
-
-        List<ReqGoodsSkuSpec> specs = insert.getSpecs();
-        if (null == specs || specs.isEmpty()) {
-            throw new RuntimeException("缺少商品总规格.");
-        }
-
-        // 属性最大id
-        int maxId = 0;
-        // 统计总属性数
-        int total = 0;
-        // 判断id是否有重复
-        Map<Integer, ReqGoodsSpecSingle> map = null;
-        // 规格数量
-        int specNumber = specs.size();
-
-        for (ReqGoodsSkuSpec spec : specs) {
-            if (StringUtils.isBlank(spec.getKey())) {
-                throw new RuntimeException("存在空白规格名.");
-            }
-
-            List<ReqGoodsSkuSpec.Node> values = spec.getValues();
-            if (null == values || values.isEmpty()) {
-                throw new RuntimeException("规格[" + spec.getKey() + "]中缺少属性.");
-            }
-            total += values.size();
-            for (ReqGoodsSkuSpec.Node e : values) {
-                if (null == e.getId() || e.getId() < 0) {
-                    throw new RuntimeException("规格[" + spec.getKey() + "]中自定义id必须大于0.");
-                }
-                if (StringUtils.isBlank(e.getValue())) {
-                    throw new RuntimeException("规格[" + spec.getKey() + "]中存在空白属性.");
-                }
-                if (e.getId() > maxId) {
-                    maxId = e.getId();
-                }
-
-                if (null == map) {
-                    map = new HashMap<>(16);
-                }
-
-                ReqGoodsSpecSingle single = new ReqGoodsSpecSingle();
-                single.setId(e.getId());
-                single.setKey(spec.getKey());
-                single.setName(e.getValue());
-                map.put(e.getId(), single);
-            }
-        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
 
-        if (map.size() != total) {
-            throw new RuntimeException("规格中存在重复id.");
+    /**
+     * 更新 spec
+     */
+    @PutMapping("/update/spec")
+    public Result<String> updateGoodsSpec(@Validated @RequestBody ReqGoodsSkuInsert insert) throws Exception {
+        Long id = insert.getId();
+        Assert.notNull(id, "规格属性id不存在");
+        GoodsDonSpec spec = goodsDonSpecService.getById(id);
+        if (spec == null || !spec.getGoodsId().equals(insert.getGoodsId())) {
+            throw BusinessRuntimeException.getInstance("规格不存在");
         }
-
-        GoodsDonSpec spec = new GoodsDonSpec();
-        spec.setGoodsId(insert.getGoodsId());
-        spec.setMaxId(maxId);
-        spec.setSpecsJson(Jsons.toJson(specs));
-        spec.setSpecNumber(specNumber);
-        spec.setSpecMapping(Jsons.toJson(map));
-        goodsDonSpecService.save(spec);
-
+        goodsDonSpecService.insertOrUpdateSkuSpec(insert);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
 
@@ -146,86 +104,7 @@ public class CmsGoodsDonSkuController {
         if (null == goods) {
             throw new RuntimeException("找不到商品spu.");
         }
-        GoodsDonSpec loveSpec = goodsDonSpecService.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId,insert.getGoodsId()));
-        if (null == loveSpec) {
-            throw new RuntimeException("该商品未设置总规格, 请设置总规格.");
-        }
-
-        List<ReqGoodsSkuInsert.Sku> skus = insert.getSkus();
-        if (null == skus || skus.isEmpty()) {
-            throw new RuntimeException("参数错误.");
-        }
-        int specNumber = loveSpec.getSpecNumber();
-
-        JavaType type = Jsons.javaType().constructMapType(HashMap.class, Integer.class, ReqGoodsSpecSingle.class);
-        Map<Integer, ReqGoodsSpecSingle> map = Jsons.parseObject(loveSpec.getSpecMapping(), type);
-
-        StringBuilder builder = new StringBuilder();
-        List<ReqGoodsSpecSingle> specList = null;
-        GoodsDonSku goodsDonSku = null;
-
-        // 判断属性是否属于同一个key
-        Set<String> set = null;
-
-        for (ReqGoodsSkuInsert.Sku sku : insert.getSkus()) {
-            // 跳过空值的sku
-            // 解析sku的规格属性
-            final String specIds = sku.getSpecIds();
-            if (StringUtils.isBlank(specIds)) {
-                throw new RuntimeException("请选择商品的规格.");
-            }
-            int[] ids = Jsons.parseObject(specIds, int[].class);
-            if (ids.length != specNumber) {
-                throw new RuntimeException("sku规格数量 与 总规格数量 不一致.");
-            }
-
-            // init
-            builder.delete(0, builder.length());
-            if (null == specList) {
-                specList = new ArrayList<>(specNumber);
-            } else {
-                specList.clear();
-            }
-            if (null == set) {
-                int capacity = (specNumber + 1) * 4 / 3 + 1;
-                set = new HashSet<>(capacity);
-            } else {
-                set.clear();
-            }
-
-            for (int id : ids) {
-                ReqGoodsSpecSingle single = map.get(id);
-                if (null == single) {
-                    throw new RuntimeException("总规格属性id中 不存在 sku中的id.");
-                }
-                builder.append(single.getName()).append(';');
-                specList.add(single);
-                set.add(single.getKey());
-            }
-            if (set.size() != specNumber) {
-                throw new RuntimeException("sku中存在多个属性属于同一个规格.");
-            }
-
-            String specVal = builder.toString();
-            String specJson = Jsons.toJson(specList);
-
-            if (null == goodsDonSku) {
-                goodsDonSku = new GoodsDonSku();
-                goodsDonSku.setGoodsId(insert.getGoodsId());
-            }
-            BeanUtil.copyProperties(sku,goodsDonSku);
-            goodsDonSku.setSpecIds(specIds);
-            goodsDonSku.setSpecVal(specVal);
-            goodsDonSku.setSpecJson(specJson);
-            if (goods.getType() == 1) {
-                if (sku.getMonths() != null && sku.getDays() != null)
-                    throw BusinessRuntimeException.getInstance("续费天数和续费月数不能同时存在");
-                if (sku.getMonths() == null && sku.getDays() == null)
-                    throw BusinessRuntimeException.getInstance("请输入对应续费时间");
-            }
-            goodsDonSkuService.save(goodsDonSku);
-
-        }
+        goodsDonSpecService.skuAppend(insert, goods);
         //给平台更新额外属性
         goodsDonService.updateGoodsSkuPrice(goods.getId(), goods);
         Set<String> keys = redisService.keys(RedisService.key.YH_HOME_PAGHE_CACHE.getName() + "*");
@@ -243,7 +122,7 @@ public class CmsGoodsDonSkuController {
         Long goodsId = sku.getGoodsId();
         GoodsDon goods = goodsDonService.getById(goodsId);
         if (goods.getType() == 1) {
-            if (sku.getMonths() != null && sku.getDays() != null)
+            if (sku.getMonths() != null && sku.getMonths() > 0 && sku.getDays() != null && sku.getDays() > 0)
                 throw BusinessRuntimeException.getInstance("续费天数和续费月数不能同时存在");
             if (sku.getMonths() == null && sku.getDays() == null)
                 throw BusinessRuntimeException.getInstance("请输入对应续费时间");
@@ -279,6 +158,16 @@ public class CmsGoodsDonSkuController {
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
 
+    /**
+     * 新 生成排列组合规格
+     */
+    @PostMapping("/post/combination/{goodsId}")
+    @Transactional(rollbackFor = Throwable.class)
+    public Result<String> postCombinationSku(@PathVariable Long goodsId, @RequestBody List<CombinationSkuReq> combinations) throws Exception {
+        goodsDonSpecService.combinationSku(goodsId, combinations);
+        return GatewayResponse.SUCCESS.newBuilder().toResult("新增规格成功");
+    }
+
     /**
      * 获取平台规格列表
      */
@@ -287,4 +176,50 @@ public class CmsGoodsDonSkuController {
         List<GoodsDonSkuView> skuViewList = beanSearcher.searchAll(GoodsDonSkuView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
         return GatewayResponse.SUCCESS.newBuilder().toResult(skuViewList);
     }
+
+    /**
+     * 同步keyid
+     */
+    @GetMapping("/sync")
+    public Result<String> syncKeyId() throws Exception {
+        GoodsDonSpec loveSpec = goodsDonSpecService.getOne(Wrappers.lambdaQuery(GoodsDonSpec.class).eq(GoodsDonSpec::getGoodsId, 15));
+        if (null == loveSpec) {
+            throw new RuntimeException("该商品未设置总规格, 请设置总规格.");
+        }
+        String specsJson = loveSpec.getSpecsJson();
+        List<ReqGoodsSkuSpec> specsList = Jsons.parseList(specsJson, ReqGoodsSkuSpec.class);
+        Map<String, List<ReqGoodsSkuSpec>> specValueMap = specsList.stream().collect(Collectors.groupingBy(ReqGoodsSkuSpec::getKey));
+
+        List<GoodsDonSku> skus = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class)
+                .eq(GoodsDonSku::getGoodsId, 15));
+        skus.forEach(sku->{
+            String specJson = sku.getSpecJson();
+            AtomicBoolean is_bol = new AtomicBoolean(false);
+            try {
+                List<ReqGoodsSpecSingle> specList = Jsons.parseList(specJson, ReqGoodsSpecSingle.class);
+                specList.forEach(spec_json->{
+                    if (spec_json.getKeyId() != null) {
+                        return;
+                    }
+                    List<ReqGoodsSkuSpec> reqGoodsSkuSpecs = specValueMap.get(spec_json.getKey());
+                    if (CollUtil.isEmpty(reqGoodsSkuSpecs)) {
+                        return;
+                    }
+                    ReqGoodsSkuSpec reqGoodsSkuSpec = reqGoodsSkuSpecs.get(0);
+                    if (reqGoodsSkuSpec.getId() == null) {
+                        log.info("未生成keyid");
+                        return;
+                    }
+                    is_bol.set(true);
+                    spec_json.setKeyId(reqGoodsSkuSpec.getId());
+                });
+                if (is_bol.get()) {
+                    sku.setSpecJson(Jsons.toJson(specList));
+                    goodsDonSkuService.updateById(sku);
+                }
+            } catch (Exception e) {
+            }
+        });
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
 }

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/CmsGoodsDonSpecController.java

@@ -72,7 +72,7 @@ public class CmsGoodsDonSpecController {
         String specsJson = Jsons.toJson(specs);
 
         JavaType type = Jsons.javaType().constructMapType(HashMap.class, Integer.class, ReqGoodsSpecSingle.class);
-        Map<Integer, ReqGoodsSpecSingle> map = Jsons.parseObject(v2spec.getSpecMapping(), type);
+        Map<Long, ReqGoodsSpecSingle> map = Jsons.parseObject(v2spec.getSpecMapping(), type);
         map.put(specSingle.getId(), specSingle);
         String specMapping = Jsons.toJson(map);