商城系统的一个难点就是商品规格的设计,如何设计一个可拓展,灵活性高的呢,这是一个难点和痛点。下面直接上干货!

首先是数据库设计

1.SKU表

CREATE TABLE "TB_PEM_PRODUCT_SKU" (
"ID" VARCHAR2(32 BYTE) NOT NULL ,
"PRODUCT_ID" VARCHAR2(32 BYTE) NOT NULL ,
"ATTRS_VALUE_ID" VARCHAR2(200 BYTE) NULL ,
"ATTRS_VALUE" VARCHAR2(512 BYTE) NULL ,
"PROMOTION_PRICE" NUMBER(14) DEFAULT 0  NULL ,
"ORIGINAL_PRICE" NUMBER(14) DEFAULT 0  NULL ,
"LIMIT_COUNT" NUMBER(22) DEFAULT 0  NULL ,
"STOCK" NUMBER(22) DEFAULT 0  NULL ,
"PRICE" NUMBER(14) DEFAULT 0  NULL ,
"DELETE_STATUS" NUMBER(2) DEFAULT 1  NULL ,
"CREATE_TIME" DATE NULL ,
"CREATE_ID" VARCHAR2(32 BYTE) NULL ,
"CREATE_NAME" VARCHAR2(32 BYTE) NULL ,
"UPDATE_TIME" DATE NULL ,
"UPDATE_ID" VARCHAR2(32 BYTE) NULL ,
"UPDATE_NAME" VARCHAR2(32 BYTE) NULL ,
"EXCHANGE_AMOUNT" NUMBER(14) DEFAULT 0  NULL ,
"EXCHANGE_INTEGRAL" NUMBER(14) DEFAULT 0  NULL ,
"GOODS_STOCK" NUMBER(22) DEFAULT 0  NULL ,
"STORE_ID" VARCHAR2(32 BYTE) DEFAULT 11183  NULL ,
"INTEGRAL_USE_TYPE" NUMBER(2) DEFAULT 1  NULL ,
"SORT" NUMBER(5) DEFAULT 1  NULL
)
LOGGING
NOCOMPRESS
NOCACHE;
COMMENT ON TABLE "TB_PEM_PRODUCT_SKU" IS 'sku表';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."ID" IS 'ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."PRODUCT_ID" IS '商品ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."ATTRS_VALUE_ID" IS '销售属性ID,用-隔开';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."ATTRS_VALUE" IS '销售属性,用;隔开';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."PROMOTION_PRICE" IS '促销价/当前售价';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."ORIGINAL_PRICE" IS '成本价';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."LIMIT_COUNT" IS '限购数量';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."STOCK" IS '库存';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."PRICE" IS '划线价';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."DELETE_STATUS" IS '0:删除,1:正常';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."CREATE_ID" IS '创建人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."CREATE_NAME" IS '创建人';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."UPDATE_ID" IS '更新人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."UPDATE_NAME" IS '更新人';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."EXCHANGE_AMOUNT" IS 'EXCHANGE_TYPE为2时填写';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."EXCHANGE_INTEGRAL" IS '兑换积分';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."GOODS_STOCK" IS '积分商品可兑换数量';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."STORE_ID" IS '商店ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."INTEGRAL_USE_TYPE" IS '1:未配置积分商城 2:已配置积分商城';
COMMENT ON COLUMN "TB_PEM_PRODUCT_SKU"."SORT" IS '排序';-- ----------------------------
-- Indexes structure for table TB_PEM_PRODUCT_SKU
-- ----------------------------
CREATE INDEX "PRODUCT_ID_SKU"
ON "TB_PEM_PRODUCT_SKU" ("PRODUCT_ID" ASC)
LOGGING
VISIBLE;-- ----------------------------
-- Checks structure for table TB_PEM_PRODUCT_SKU
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_SKU" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_SKU" ADD CHECK ("PRODUCT_ID" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_SKU" ADD CHECK ("ID" IS NOT NULL);-- ----------------------------
-- Primary Key structure for table TB_PEM_PRODUCT_SKU
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_SKU" ADD PRIMARY KEY ("ID");

2.销售属性表

CREATE TABLE "TB_PEM_PRODUCT_ATTR" (
"ID" VARCHAR2(32 BYTE) NOT NULL ,
"NAME" VARCHAR2(32 BYTE) NOT NULL ,
"DELETE_STATUS" NUMBER(2) DEFAULT 1  NULL ,
"CREATE_TIME" DATE NULL ,
"CREATE_ID" VARCHAR2(32 BYTE) NULL ,
"CREATE_NAME" VARCHAR2(32 BYTE) NULL ,
"UPDATE_TIME" DATE NULL ,
"UPDATE_ID" VARCHAR2(32 BYTE) NULL ,
"UPDATE_NAME" VARCHAR2(32 BYTE) NULL
)
LOGGING
NOCOMPRESS
NOCACHE;
COMMENT ON TABLE "TB_PEM_PRODUCT_ATTR" IS '销售属性表';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."NAME" IS '销售属性名称';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."DELETE_STATUS" IS '0:删除,1:正常';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."CREATE_ID" IS '创建人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."CREATE_NAME" IS '创建人';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."UPDATE_ID" IS '更新人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR"."UPDATE_NAME" IS '更新人';-- ----------------------------
-- Indexes structure for table TB_PEM_PRODUCT_ATTR
-- ------------------------------ ----------------------------
-- Checks structure for table TB_PEM_PRODUCT_ATTR
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_ATTR" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_ATTR" ADD CHECK ("NAME" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_ATTR" ADD CHECK ("ID" IS NOT NULL);-- ----------------------------
-- Primary Key structure for table TB_PEM_PRODUCT_ATTR
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_ATTR" ADD PRIMARY KEY ("ID");

3.销售属性值表

CREATE TABLE "TB_PEM_PRODUCT_ATTR_VALUE" (
"ID" VARCHAR2(32 BYTE) NOT NULL ,
"ATTR_ID" VARCHAR2(32 BYTE) NOT NULL ,
"VALUE" VARCHAR2(50 BYTE) NOT NULL ,
"DELETE_STATUS" NUMBER(2) DEFAULT 1  NULL ,
"CREATE_TIME" DATE NULL ,
"CREATE_ID" VARCHAR2(32 BYTE) NULL ,
"CREATE_NAME" VARCHAR2(32 BYTE) NULL ,
"UPDATE_TIME" DATE NULL ,
"UPDATE_ID" VARCHAR2(32 BYTE) NULL ,
"UPDATE_NAME" VARCHAR2(32 BYTE) NULL
)
LOGGING
NOCOMPRESS
NOCACHE;
COMMENT ON TABLE "TB_PEM_PRODUCT_ATTR_VALUE" IS '销售属性值表';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."ID" IS '销售属性值ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."ATTR_ID" IS '销售属性ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."VALUE" IS '销售属性值';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."DELETE_STATUS" IS '0:删除,1:正常';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."CREATE_ID" IS '创建人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."CREATE_NAME" IS '创建人';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."UPDATE_ID" IS '更新人ID';
COMMENT ON COLUMN "TB_PEM_PRODUCT_ATTR_VALUE"."UPDATE_NAME" IS '更新人';-- ----------------------------
-- Indexes structure for table TB_PEM_PRODUCT_ATTR_VALUE
-- ------------------------------ ----------------------------
-- Checks structure for table TB_PEM_PRODUCT_ATTR_VALUE
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_ATTR_VALUE" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_ATTR_VALUE" ADD CHECK ("ATTR_ID" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_ATTR_VALUE" ADD CHECK ("VALUE" IS NOT NULL);
ALTER TABLE "TB_PEM_PRODUCT_ATTR_VALUE" ADD CHECK ("ID" IS NOT NULL);-- ----------------------------
-- Primary Key structure for table TB_PEM_PRODUCT_ATTR_VALUE
-- ----------------------------
ALTER TABLE "TB_PEM_PRODUCT_ATTR_VALUE" ADD PRIMARY KEY ("ID");

4.SKU冗余表(为了扩展性所加的表)

CREATE TABLE "TB_PEM_SPU_SKU_ATTR_REL" (
"ID" VARCHAR2(32 BYTE) NOT NULL ,
"PRODUCT_ID" VARCHAR2(32 BYTE) NULL ,
"SKU_ID" VARCHAR2(32 BYTE) NULL ,
"ATTR_ID" VARCHAR2(32 BYTE) NULL ,
"ATTR_NAME" VARCHAR2(32 BYTE) NULL ,
"ATTR_SORT" NUMBER(5) DEFAULT 1  NULL ,
"ATTR_VALUE_ID" VARCHAR2(32 BYTE) NULL ,
"ATTR_VALUE" VARCHAR2(50 BYTE) NULL ,
"ATTR_VALUE_SORT" NUMBER(5) DEFAULT 1  NULL ,
"DELETE_STATUS" NUMBER(2) DEFAULT 1  NULL ,
"CREATE_TIME" DATE NULL ,
"CREATE_ID" VARCHAR2(32 BYTE) NULL ,
"CREATE_NAME" VARCHAR2(32 BYTE) NULL ,
"UPDATE_TIME" DATE NULL ,
"UPDATE_ID" VARCHAR2(32 BYTE) NULL ,
"UPDATE_NAME" VARCHAR2(32 BYTE) NULL
)
LOGGING
NOCOMPRESS
NOCACHE;
COMMENT ON TABLE "TB_PEM_SPU_SKU_ATTR_REL" IS '商品SPUSKU属性关联表';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ID" IS 'ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."PRODUCT_ID" IS '商品ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."SKU_ID" IS 'SKUID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_ID" IS '销售属性ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_NAME" IS '销售属性名称';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_SORT" IS '销售属性排序';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_VALUE_ID" IS '销售属性值ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_VALUE" IS '销售属性值';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."ATTR_VALUE_SORT" IS '销售属性值排序';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."DELETE_STATUS" IS '0:删除,1:正常';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."CREATE_ID" IS '创建人ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."CREATE_NAME" IS '创建人';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."UPDATE_ID" IS '更新人ID';
COMMENT ON COLUMN "TB_PEM_SPU_SKU_ATTR_REL"."UPDATE_NAME" IS '更新人';-- ----------------------------
-- Indexes structure for table TB_PEM_SPU_SKU_ATTR_REL
-- ----------------------------
CREATE INDEX "PRODUCT_ID_SKU_ID_REL"
ON "TB_PEM_SPU_SKU_ATTR_REL" ("PRODUCT_ID" ASC, "SKU_ID" ASC)
LOGGING
VISIBLE;
CREATE INDEX "PU_SKU_ATTR_REL_ATTR_VALUE_ID"
ON "TB_PEM_SPU_SKU_ATTR_REL" ("ATTR_VALUE_ID" ASC)
LOGGING
VISIBLE;
CREATE INDEX "TB_PEM_SPU_SKU_ATTR_REL"
ON "TB_PEM_SPU_SKU_ATTR_REL" ("ATTR_ID" ASC)
LOGGING
VISIBLE;-- ----------------------------
-- Checks structure for table TB_PEM_SPU_SKU_ATTR_REL
-- ----------------------------
ALTER TABLE "TB_PEM_SPU_SKU_ATTR_REL" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "TB_PEM_SPU_SKU_ATTR_REL" ADD CHECK ("ID" IS NOT NULL);-- ----------------------------
-- Primary Key structure for table TB_PEM_SPU_SKU_ATTR_REL
-- ----------------------------
ALTER TABLE "TB_PEM_SPU_SKU_ATTR_REL" ADD PRIMARY KEY ("ID");

下面直接上代码

Service接口层

    /*** 查询商品对应的sku列表*       [{*         "attrId": "112",*       "attrName": "颜色",*      "data": [{*           "attrValueId": "123",*          "attrValue":红色 " ,*                }, {*          "attrValueId": "123",*          "attrValue"  :蓝色 "  ,*        }]* @param productId 商品ID 查询条件* @return ResultMap*/ResultMap<List<ProductSkuListDto>> listSkuForAdmin(String productId);/*** 新增/编辑sku* @param product* @param productDto* @return*/List<ProductSku> saveOrUpdateSku(Product product, ProductAdminDto productDto);

Service实现层

    @Overridepublic ResultMap<List<ProductSkuListDto>> listSkuForAdmin(String productId) {List<SpuSkuAttrRel> spuSkuAttrRelList = spuSkuAttrRelService.selectList(new EntityWrapper<SpuSkuAttrRel>().eq("PRODUCT_ID", productId).orderAsc(Arrays.asList("ATTR_SORT", "ATTR_VALUE_SORT")));//根据属性值去重spuSkuAttrRelList = spuSkuAttrRelList.stream().filter(distinctByKey(SpuSkuAttrRel::getAttrValueId)).collect(Collectors.toList());//根据属性去重List<SpuSkuAttrRel> spuSkuAttrRelList1 = spuSkuAttrRelList.stream().filter(distinctByKey(SpuSkuAttrRel::getAttrId)).collect(Collectors.toList());List<ProductSkuListDto> productSkuListDtoList = new ArrayList<>();//构造数据for (SpuSkuAttrRel spuSkuAttrRel : spuSkuAttrRelList1) {ProductSkuListDto productSkuListDto = new ProductSkuListDto();List<ProductSkuListDto.SkuList> data = new ArrayList<>();BeanUtils.copyProperties(spuSkuAttrRel, productSkuListDto);for (SpuSkuAttrRel skuAttrRel : spuSkuAttrRelList) {if (spuSkuAttrRel.getAttrId().equals(skuAttrRel.getAttrId())) {ProductSkuListDto.SkuList sku = new ProductSkuListDto.SkuList();BeanUtils.copyProperties(skuAttrRel, sku);data.add(sku);}}productSkuListDto.setData(data);productSkuListDtoList.add(productSkuListDto);}return ResultUtil.success(productSkuListDtoList);}@Override@Transactional(rollbackFor = Exception.class)public List<ProductSku> saveOrUpdateSku(Product product, ProductAdminDto productDto) {if (productDto.getSkuDetail().size() > 625) {throw new ServiceException("超过最大可设置规格");}List<ProductAttr> productAttrList = saveOrUpdateAttr(productDto.getSkuList());List<ProductAttrValue> productAttrValueList = saveOrUpdateAttrValue(productDto.getSkuList(), productAttrList);List<ProductSku> productSkuList = saveOrUpdateSkuDetail(productDto.getSkuDetail(), productAttrList, productAttrValueList, product);return productSkuList;}@Overridepublic boolean deleteSku(String productId) {boolean skuDeleteFlag = false;List<ProductSku> productSkuList = productSkuService.selectList(new EntityWrapper<ProductSku>().eq("PRODUCT_ID", productId));if (CollectionUtil.isNotEmpty(productSkuList)){boolean flag1 = productSkuService.delete(new EntityWrapper<ProductSku>().eq("PRODUCT_ID", productId));boolean flag2 = spuSkuAttrRelService.delete(new EntityWrapper<SpuSkuAttrRel>().eq("PRODUCT_ID", productId));List<String> deleteSkuIds = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());if (flag1 && flag2){skuStatusRedis(deleteSkuIds,productId);skuDeleteFlag = true;}}return skuDeleteFlag;}/*** 保存/修改属性** @param skuList* @return*/private List<ProductAttr> saveOrUpdateAttr(List<ProductSkuListDto> skuList) {List<ProductAttr> productAttrList = new ArrayList<>();skuList.forEach(attr -> {String attrId = attr.getAttrId();ProductAttr productAttr = new ProductAttr();if (StringUtils.isBlank(attrId)) {productAttr.setId(MyIdUtil.getId());} else {productAttr.setId(attrId);}productAttr.setName(attr.getAttrName());productAttrList.add(productAttr);});productAttrService.insertOrUpdateBatch(productAttrList);return productAttrList;}/*** 保存/修改属性值** @param skuList* @return*/private List<ProductAttrValue> saveOrUpdateAttrValue(List<ProductSkuListDto> skuList, List<ProductAttr> productAttrList) {List<ProductAttrValue> productAttrValueList = new ArrayList<>();for (int i = 0; i < skuList.size(); i++) {for (ProductSkuListDto.SkuList attrValue : skuList.get(i).getData()) {String attrValueId = attrValue.getAttrValueId();ProductAttrValue productAttrValue = new ProductAttrValue();if (StringUtils.isBlank(attrValueId)) {productAttrValue.setId(MyIdUtil.getId());} else {productAttrValue.setId(attrValueId);}productAttrValue.setAttrId(productAttrList.get(i).getId());productAttrValue.setValue(attrValue.getAttrValue());productAttrValueList.add(productAttrValue);}}productAttrValueService.insertOrUpdateBatch(productAttrValueList);return productAttrValueList;}/*** 保存/修改sku** @param skuDetail* @return*/private List<ProductSku> saveOrUpdateSkuDetail(List<ProductSkuDetailDto> skuDetail, List<ProductAttr> productAttrList, List<ProductAttrValue> productAttrValueList, Product product) {List<ProductSku> productSkuList = new ArrayList<>();//需删除的skuIdMap<String,Object> map  = deleteSkuId(product.getId(), skuDetail);List<String> deleteSkuIds = (List<String>) map.get("skuIdList");if (CollectionUtil.isNotEmpty(deleteSkuIds)) {productSkuService.deleteBatchIds(deleteSkuIds);skuStatusRedis(deleteSkuIds,product.getId());}cartesian(productAttrValueList, skuDetail);for (int i = 0; i < skuDetail.size(); i++) {String skuId = skuDetail.get(i).getSkuId();ProductSku productSku = new ProductSku();if (StringUtils.isBlank(skuId)) {productSku.setId(MyIdUtil.getId());} else {productSku.setId(skuId);}BeanUtils.copyProperties(skuDetail.get(i), productSku);BeanUtils.copyProperties(skuDetail.get(i).getDetail(), productSku);//value根据;拆分成字符串String attrsValue = String.join(";", skuDetail.get(i).getAttrsValue());productSku.setAttrsValue(attrsValue);productSku.setProductId(product.getId());productSku.setSort(i + 1);productSkuList.add(productSku);}productSkuService.insertOrUpdateBatch(productSkuList);List<ProductSku> tempDtoList = productSkuList.stream().sorted(Comparator.comparing(ProductSku::getPromotionPrice)).collect(Collectors.toList());Long stock = productSkuList.stream().mapToLong(ProductSku::getStock).sum();product.setStock(stock);if (tempDtoList.isEmpty()) {product.setPrice(0L);product.setPromotionPrice(0L);} else {//获取sku列表的划线价格最低值product.setPrice(tempDtoList.get(0).getPrice());//获取sku列表的销售价格最低值product.setPromotionPrice(tempDtoList.get(0).getPromotionPrice());}saveOrUpdateSkuRel(productAttrList,productAttrValueList, productSkuList, product.getId(), map);return productSkuList;}/*** 保存/修改spu_sku_attr_rel 映射关系** @param productAttrList* @return*/private boolean saveOrUpdateSkuRel(List<ProductAttr> productAttrList,List<ProductAttrValue> productAttrValueList, List<ProductSku> productSkuList, String productId, Map<String,Object> map) {List<SpuSkuAttrRel> spuSkuAttrRelList1 = spuSkuAttrRelService.selectList(new EntityWrapper<SpuSkuAttrRel>().eq("PRODUCT_ID", productId));if (CollectionUtil.isNotEmpty(spuSkuAttrRelList1)){Integer status = (Integer) map.get("status");//status = 1 ,表示所有SkuId都相等if (status == 1){boolean flag =  isAttrAndAttrValueEquals(productAttrList,productAttrValueList,spuSkuAttrRelList1);//flag为true 值没变动,无需操作,直接返回if (flag){return true;}}spuSkuAttrRelService.delete(new EntityWrapper<SpuSkuAttrRel>().eq("PRODUCT_ID", productId));}List<SpuSkuAttrRel> spuSkuAttrRelList = new ArrayList<>();for (int i = 0; i < productAttrList.size(); i++) {for (int j = 0; j < productSkuList.size(); j++) {SpuSkuAttrRel spuSkuAttrRel = new SpuSkuAttrRel();spuSkuAttrRel.setId(MyIdUtil.getId());spuSkuAttrRel.setProductId(productId);spuSkuAttrRel.setSkuId(productSkuList.get(j).getId());spuSkuAttrRel.setAttrId(productAttrList.get(i).getId());spuSkuAttrRel.setAttrName(productAttrList.get(i).getName());spuSkuAttrRel.setAttrSort(i + 1);List<String> attrsValue = Arrays.asList(productSkuList.get(j).getAttrsValue().split(";")).stream().map(s -> s.trim()).collect(Collectors.toList());List<String> attrsValueId = Arrays.asList(productSkuList.get(j).getAttrsValueId().split("-")).stream().map(s -> s.trim()).collect(Collectors.toList());spuSkuAttrRel.setAttrValue(attrsValue.get(i));spuSkuAttrRel.setAttrValueId(attrsValueId.get(i));spuSkuAttrRel.setAttrValueSort(j + 1);spuSkuAttrRelList.add(spuSkuAttrRel);}}return spuSkuAttrRelService.insertOrUpdateBatch(spuSkuAttrRelList);}/*** 根据指定key去重** @param keyExtractor* @param <T>* @return*/private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {Set<Object> seen = ConcurrentHashMap.newKeySet();return t -> seen.add(keyExtractor.apply(t));}/*** sku被删除后存进redis*/private void skuStatusRedis(List<String> skuIds,String productId) {//0表示被删除int status = 0;skuIds.forEach(skuId -> {String key = String.format("%s%s", CacheConstants.SKU_STATUS, skuId);redisTemplate.opsForValue().set(key, status, 30, TimeUnit.DAYS);productCacheService.deleteSkuStockLimitCount(ProductCacheDto.builder().productId(productId).skuId(skuId).build());
//            integralGoodsCacheFeignClient.deleteSkuStockLimitCountExchangedCount(IntegralGoodsCachePropertyDto
//                    .builder().productId(productId).skuId(skuId).build());});}/*** 笛卡尔积计算规格对应关系*/private void cartesian(List<ProductAttrValue> productAttrValueList, List<ProductSkuDetailDto> skuDetail) {//根据属性ID去重List<ProductAttrValue> productAttrValueList1 = productAttrValueList.stream().filter(distinctByKey(ProductAttrValue::getAttrId)).collect(Collectors.toList());List<ImmutableSet<String>> immutableSetList = new ArrayList<>();productAttrValueList1.forEach(attrValue1 -> {List<String> attrValueIds = new ArrayList<>();productAttrValueList.forEach(attrValue -> {if (attrValue.getAttrId().equals(attrValue1.getAttrId())) {attrValueIds.add(attrValue.getId());}});ImmutableSet<String> charList = ImmutableSet.copyOf(attrValueIds);immutableSetList.add(charList);});Set<List<String>> set = Sets.cartesianProduct(immutableSetList);int i = 0;for (List<String> strings : set) {List<String> ids = new ArrayList<>();for (String string : strings) {ids.add(string);}//id根据-拆分成字符串String attrsValueId = String.join("-", ids);skuDetail.get(i).setAttrsValueId(attrsValueId);i++;}}/*** 筛选出需要删除的skuID* 属性名称修改的情况,全部skuId删除* 属性值修改,存在的skuId修改,不存在的sku筛选出来* status记录是否需要删除关联表,1,表示不用,0表示需要** @param productId* @return*/private Map<String, Object> deleteSkuId(String productId, List<ProductSkuDetailDto> skuDetail) {Map map = new HashMap<String, Object>(16);List<String> deleteSkuIds = new ArrayList<>();map.put("status", 0);List<ProductSku> productSkuList = productSkuService.selectList(new EntityWrapper<ProductSku>().eq("PRODUCT_ID", productId));if (CollectionUtil.isEmpty(productSkuList)) {map.put("skuIdList", deleteSkuIds);return map;}//数据库已保存的skuIdList<String> skuId = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());//前端传入的skuIdList<String> skuId1 = skuDetail.stream().map(ProductSkuDetailDto::getSkuId).collect(Collectors.toList());//筛选出skuId不同的集合// 差集 (skuId - skuId1)deleteSkuIds = skuId.stream().filter(item -> !skuId1.contains(item)).collect(toList());//如果数据库已保存的skuId和前端传入的skuId都相等,说明没有修改规格,此时status记录1,不用删除关联表if (CollectionUtil.isEmpty(deleteSkuIds) && skuId.size() == skuId1.size()) {map.put("status", 1);}map.put("skuIdList", deleteSkuIds);return map;}/*** 当skuId全部相同时,判断关联表里面的属性名称和属性值是否有变动* @return true 值没变动  flase 值有变动*/private boolean isAttrAndAttrValueEquals(List<ProductAttr> productAttrList,List<ProductAttrValue> productAttrValueList,List<SpuSkuAttrRel> spuSkuAttrRelList){List<String> isEquals = new ArrayList<>();List<String> isEquals1 = new ArrayList<>();boolean flag = false;List<String> attrList = productAttrList.stream().map(ProductAttr::getName).collect(Collectors.toList());List<String> attrValueList = productAttrValueList.stream().map(ProductAttrValue::getValue).collect(Collectors.toList());List<String> attrList1 = spuSkuAttrRelList.stream().filter(distinctByKey(SpuSkuAttrRel::getAttrName)).map(SpuSkuAttrRel::getAttrName).collect(Collectors.toList());List<String> attrValueList1 = spuSkuAttrRelList.stream().filter(distinctByKey(SpuSkuAttrRel::getAttrValue)).map(SpuSkuAttrRel::getAttrValue).collect(Collectors.toList());isEquals = attrList.stream().filter(item -> !attrList1.contains(item)).collect(toList());isEquals1 = attrValueList.stream().filter(item -> !attrValueList1.contains(item)).collect(toList());if (CollectionUtil.isEmpty(isEquals) && CollectionUtil.isEmpty(isEquals1)){flag = true;}return flag;}

dto层

@Data
public class ProductSkuListDto extends SuperEntity {private static final long serialVersionUID = 1L;/*** 销售属性Id*/private String attrId;/*** 销售属性名称*/@Pattern(regexp = RegexConstants.ATTR_NAME, message = "商品规格名称中含有敏感字符或长度过长")private String attrName;@Validprivate List<SkuList> data;@Datapublic static class  SkuList {/*** 销售属性值ID*/private String attrValueId;/*** 销售属性值*/@Pattern(regexp = RegexConstants.ATTR_VALUE, message = "商品规格值中含有敏感字符或长度过长")private String attrValue;}}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("TB_PEM_PRODUCT_SKU")
public class ProductSku extends SuperEntity {private static final long serialVersionUID = 1L;/*** ID*/private String id;/*** 商品ID*/private String productId;/*** 销售属性ID,用-隔开*/private String attrsValueId;/*** 销售属性,用;隔开*/private String attrsValue;/*** 促销价/当前售价*/private Long promotionPrice;/*** 成本价*/private Long originalPrice;/*** 限购数量*/private Long limitCount;/*** 库存*/private Long stock;/*** 划线价*/private Long price;/*** 0:删除,1:正常*/private Integer deleteStatus;/*** 创建时间*/private Date createTime;/*** 创建人ID*/private String createId;/*** 创建人*/private String createName;/*** 更新时间*/private Date updateTime;/*** 更新人ID*/private String updateId;/*** 更新人*/private String updateName;/*** EXCHANGE_TYPE为2时填写*/private Long exchangeAmount;/*** 兑换积分*/private Long exchangeIntegral;/*** 积分商品可兑换数量*/private Long goodsStock;/*** 商店ID*/private String storeId;/*** 排序*/private Integer sort;/*** 是否在积分商城配置(1:未配置积分商城 2:已配置积分商城)*/private Integer integralUseType;}
@Data
public class ProductAdminDto extends SuperEntity {private static final long serialVersionUID = 1L;/*** 用户id*/private String id;/*** 商品分组名称*///@NotEmpty(message = "商品分组名不能为空" )private List<String> productGroupNames;/*** 商品分组id*/@NotEmpty(message = "商品分组id不能为空" )private List<String>  productGroupIds;/*** 商品标签名称*/private List<String> productLabelNames;/*** 商品标签id*/private List<String> productLabelIds;/*** SEOid*/private List<String> productSeoIds;/*** 商品类型 0实物,1电子券*/@NotNull(message = "商品类型不能为空")private Integer productType;/*** 商品名字*/@NotBlank(message = "商品名称不能为空")@Pattern(regexp = RegexConstants.PRODUCT_NAME_ALLOW, message = "商品名称中含有敏感字符或长度过长")private String name;/*** 商品封面图片*/private String pic;/*** 货号*/private String productNo;/*** 物流配送方式 0->快递发货;1->自取提货 ;*/private Integer deliveryWay;/*** 配送范围选中 0无限制 1设置配送范围 2配送模板选中*/private Integer deliveryRound;/*** 商品属性状态 0自营店  1加盟店*/private Integer productAttrStatus;/*** 加盟店名字*/private String addShop;/*** 采购状态 0否   1是*/private Integer isbuyStatus;/*** 采购数量*/@Max(value = 999999999, message = "采购数量不能超过9位数")private Long buyCount;/***  删除状态:0->未删除;1->已删除*/private Integer deleteStatus;/*** 上架状态 0->下架;1->上架*/private Integer publishStatus;/*** 新品状态 0->不是新品;1->新品*/private Integer newStatus;/*** 推荐状态 0->不推荐;1->推荐*/private Integer recommandStatus;/*** 0->未审核;1->审核通过*/private Integer verifyStatus;/*** 排序*/private Long sort;/*** 销量*/private Long sale;/*** 销售价格(原价)*/@Max(value = 999999999, message = "销售价格不能超过9位数")private Long price;/*** 浏览量*/private Long visitCount;/*** 访客数*/private Long visitUsrCount;/*** 促销价格(打折)*/@Max(value = 999999999, message = "促销价格不能超过9位数")private Long promotionPrice;/*** 商家卖点*/@Pattern(regexp = RegexConstants.PRODUCT_SUB_NAME_ALLOW, message = "商品短标题中含有敏感字符或长度过长")private String shopSell;/*** 积分*/private Long giftGrowth;private Long giftPoint;/*** 赠送的成长值*/private Long usePointLimit;/*** 副标题*/private String subTitle;/*** 分享描述*/private String description;/*** 折扣方式 0折扣  1特价*/private Integer discountWay;/*** 多少折*/private Long discount;/*** 规格名*/@Length(max = 100,message = "规格名字不能超过100个字")private String spces;/*** 成本价*/@Max(value = 999999999, message = "成本价不能超过9位数")private Long originalPrice;/*** 库存*/@Max(value = 999999999, message = "库存不能超过9位数")private Long stock;/*** 库存预警值*/private Long lowStock;/*** 单位克,斤等*/private String unit;/*** 商品重量*/private Long weight;/*** 是否为预告商品0->不是;1->是*/private Integer previewStatus;/*** SEO关键字*/private String keywords;/*** 产品图片限制为5张,以逗号分割*/private List<String> albumPicList;/*** 详情标题*/private String detailTitle;/*** 详情描述*/private String detailDesc;/*** 产品详情网页内容*/private String detailHtml;/*** 移动端网页详情*/private String detailMobileHtml;/*** 详情图片*/private List<String> detailPicList;/*** 促销开始时间*/private Date promotionStartTime;/*** 限购数量*/@Max(value = 999999999, message = "限购数量不能超过9位数")private Long limitCount;/*** 供应商id*/private String supplyId;/*** 供应商名字*/@Length(max = 100,message = "供应商名字不能超过100个字")private String supplyName;/*** 商品配送模板id*/private String templateId;/*** 商品配送模板名字*/@Length(max = 100,message = "商品配送模板名字不能超过100个字")private String templateName;/*** 是否在主商城售卖  1: 在主商城售卖  2 : 不在主商城售卖*/private Integer showType;/*** 优惠券ID*/private String cardId;/*** 优惠券名称*/private String title;/*** 虚拟商品类型(暂实现1:优惠券 )*/private Integer virtualProductType;/*** 优惠券发放状态(1: 未发放, 2: 正在发放, 3: 已停止发放)*/private Integer couponCardProvideStatus;/*** sku列表*/@Validprivate List<ProductSkuListDto> skuList;/*** sku详情*/@Validprivate List<ProductSkuDetailDto> skuDetail;private Integer skuType;
}

商城系统开发从0到1(sku之设计与实现)相关推荐

  1. 分销APP联盟商家入驻商城系统开发

    分销APP联盟商家入驻商城系统开发,分销APP联盟商家入驻商城系统软件(陈琦:138-2848-7919可微)分销APP联盟商家入驻商城系统平台,分销APP联盟商家入驻商城系统模式,APP分销联盟商家 ...

  2. 联盟商家入驻三级分销商城系统开发app

    分销APP联盟商家入驻商城系统开发,分销APP联盟商家入驻商城系统软件(陈琦:138-2848-7919可微)分销APP联盟商家入驻商城系统平台,分销APP联盟商家入驻商城系统模式,APP分销联盟商家 ...

  3. 小程序拼团商城系统开发

    最近了解到目前,随着电子商务产业的快速发展,网上商城系统成本低,速度快等优势为众多企业带来了机遇,营销功能丰富,商品系统完善,用户体验良好. PHP程序快速开发,运行速度快,技术本身可以快速学习.嵌入 ...

  4. B2B2C分销商城系统开发解析-首篇

    B2B2C分销商城大系统是一款基于移动互联网的电商应用服务产品,通过在微信中建立购物商城,实现在线购物功能的一款系统软件. b2b2c分销系统,第一个B指广义的卖方(即成品.半成品.材料提供商等),第 ...

  5. 即拼商城系统开发PHP源码小程序

    导语: 拼购模式很火吗?电商巨头对拼购不会视而不见的,近来,淘宝上线了特价版APP,京东也开展了"拼购节",中国最大的两家电商均在"拼购"上发力,由此可见拼购到 ...

  6. 微信小程序购物商城系统开发系列-工具篇

    微信小程序购物商城系统开发系列-工具篇 微信小程序开放公测以来,一夜之间在各种技术社区中就火起来啦.对于它 估计大家都不陌生了,对于它未来的价值就不再赘述,简单一句话:可以把小程序简单理解为一个新的操 ...

  7. 区块链app源码_区块链app商城系统开发适用于哪些企业

    区块链商城系统开发,区块链技术让人们对于软件应用开发有了全新的想象,现在也有越来越多的企业开始布局区块链业务,区块链应用开渐渐成为市场应用的宠儿,随着区块链技术的不断发展,未来也会有越来越多的区块链应 ...

  8. 二级分销商城系统开发软件

    二级分销商城系统开发咨询[林先生:130微7827电7929],二级分销商城系统开发模式,二级分销商城系统开发软件,二级分销商城系统开发APP,二级分销商城系统现成源码,二级分销商城系统开发平台. 传 ...

  9. b2b2c商城系统开发

    B2B2C 是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个BUSINESS ,并不仅仅局限于品牌供应商.影视制作公司和图书出版商,任何的商品供应商 ...

最新文章

  1. PostgreSQL ODBC问题与探索SQLSpecialColumns
  2. Ubuntu开发Java教程_Ubuntu下Java环境的搭建基础教程
  3. PAT (Basic Level) 1091 N-自守数(模拟+stl)
  4. Objective-C之category
  5. 怎么测试ajax get请求,为什么我的AJAX在运行测试时请求dev中的PUT请求,但请求了GET请求?...
  6. html5网页自动滚动,Html5 滚动穿透的方法
  7. 力扣题目——88. 合并两个有序数组
  8. 消息中间件的研究 (一)
  9. 拓端tecdat|R语言广义相加(加性)模型(GAMs)与光滑函数可视化
  10. GlobalMapper小白学习日记
  11. (01)ORB-SLAM2源码无死角解析-(18) SVD奇异值分解→求解Homography,Fundamental矩阵,了解矩阵自由度
  12. 海龟作图python等边三角形_python 海龟作图
  13. python编程怎么画三角形的外接圆_用MATLAB画三角形外接圆
  14. 内网建站 NAT穿透 局域网穿透
  15. python profile 性能分析
  16. springboot+shiro前后端分离过程中跨域问题、sessionId问题、302鉴权失败问题
  17. 大家有哪些舍不得拿出来分享的网站?你有吗?这几个抓紧收藏起来
  18. mysql客户端报错1366_mysql 错误提示1366 Incorrect string value怎么解决?
  19. 024 Rust死灵书之Send和Sync
  20. 如何在读卡器中增加SAM

热门文章

  1. 测试人员如何保证业务安全性?
  2. 为什么觉得苦瓜的苦没有生活苦?
  3. 浅浅仿制一个APP首页
  4. Zabbix Windows自定义监控
  5. 蓝牙耳机怎么挑选?工程师盘点目前最值得入手的蓝牙耳机
  6. Android ColorUtils
  7. 品牌网站建设的制作方法和思路
  8. Verilog实现---1/x任意整数分频器通用代码
  9. web-sorrow
  10. 计算机教师线下研修方式与内容,网络研修线下心得体会