商品中心—1.B端建品和C端缓存的技术文档一

大纲

1.商品中心的专业术语

2.商品中心的基本业务系统

3.商品中心整体架构设计以及运行流程

4.商品B端---商品编码生成逻辑

5.商品B端---商品核心数据模型

6.商品B端---转换建品请求数据为商品模型数据

7.商品B端---商品建品时商品编号补全与审核配置

8.商品B端---商品审核前的草稿数据保存逻辑

9.商品B端---不需审核的建品流程持久化逻辑

10.商品B端---审核工单分页列表和商品草稿查询

11.商品B端---商品审核时的敏感字段diff计算逻辑

12.商品B端---对草稿中的商品进行审核的逻辑

13.商品B端---商品属性+买手+品类的数据维护

14.商品C端---通用缓存读写组件的实现逻辑

15.商品C端---接口代码实现逻辑

1.商品中心的专业术语

一.ITEM

商品售卖展示单位,仅⽤于销售时展示使⽤。

二.SKU

SKU是Stock Keeping Unit(库存单位),即库存进出计量的单位。可以是以件、盒、托盘等为单位,如:iPhoneX + ⿊⾊ + 256G。

三.SPU

SPU是Standard Product Unit标准化产品单元,是对某一类标准产品的共同特征属性的描述。SPU是商品信息聚合的最⼩单位,如:iPhoneX就是SPU。SPU的出现是为了满足在叶子类目下对商品进行进一步抽象的需求。比如手机就是叶子类目,虽然可以添加苹果手机或者华为手机这样的类目,但这样添加就比较麻烦了,可能会导致类目树就会变得非常庞大。所以SPU是一个介于叶子类目和商品之间的概念,是对类目的细化。因此SPU通常由"后台类目 + 关键属性"唯一确定。

四.CSPU

CSPU也就是子标准化产品单元,即SPU的细分,Child SPU。CSPU通常由"后台类目 + 关键属性 + 销售属性"唯一确定。比如手机类型下,品牌和型号这两个属性可以确定一个SPU,但还不能确定一个CSPU,需要额外的销售属性才能确定一个CSPU。以苹果手机为例,品牌是iPhone、型号是X、颜色为黑色、存储为256G,两个关键属性是品牌和型号,两个销售属性为颜色和存储。

五.运营品类

运营品类是⼀种抽象的概念,例如:运动裤、⼿机。每一个商品都会有所属的品类,比如iPhone X这个SPU会属于手机这个品类。不同的电商平台会对品类进行不同的划分,手机品类在有的平台是一级品类,在有的平台是电子产品品类的子品类。

六.前台类⽬

多级品类可以构成前台类⽬,例如:男T恤 + 男短裤可归属到男装类⽬。电商网站首页里,左侧都会有一颗类目树,这个类目树就是前台类目。

七.SKU规格

⽤来区分单品的主要指标。例如⼿机商品由颜⾊、内存两种规格区分单品,每个规格有多个可选值。从每个规格选出一个值,拼凑起来的组合就可以唯一确定一款商品SKU。颜色规格:白色、黑色、粉色、天蓝色;内存:128G、256G、512G。

八.原料商品

只采购不销售的商品,只有采购属性如包材或原材料,例如:吸管、开瓶器。

九.普通商品

⼜采购⼜销售的商品,有库存和销售属性。

十.组套商品

不采购只销售的商品,共享库存和销售属性,例如:原料商品 + 普通商品组合为⼀个商品。开瓶器是原料商品,红酒是普通商品,开瓶器 + 红酒就是一个组套商品。开瓶器不能单卖但需要采购,用户购买红酒时不用关注开瓶器,开瓶器会和红酒打包在一起进行展示和售卖。

十一.虚拟商品

不采购只销售,只有虚拟库存,只有销售属性,例如:会员卡、虚拟卡、购物卡、游戏点卡。这些虚拟商品没有必要去进行采购,用户支付后也不需要履约签收。用户完成对虚拟商品的支付后,商品直接可以展示在用户的会员中心里。

十二.售卖区

商品可以在哪⾥卖,售卖范围配置:按城市配置、按卖家组配置。有的商品只能在部分城市可以售卖,部分城市是没法售卖的。在某些区域里,商品的库存不好发货,可能会显示该区域无货。

仓库会分成两种:微仓和大仓,微仓就是微型的小仓库,大仓就是大型的大仓库。大仓可以辐射很大一片区域的发货,仓库容量很大,里面可以放很多商品。微仓也叫前置仓,在一个城市里,可以设置微仓。可以将该城市经常购买的,库存量消耗比较大的商品,放到多个微仓里。这样距离消费者就会更近一些,发货也可以更快一些。

十三.卖家类型

类型一:⾃营,类型二:POP。自营就是商品是由平台自己来采购、入仓、售卖,POP(Platform Open Plan)意思是平台开放计划,POP就是第三方卖家入驻平台开店售卖自己的商品。

十四.商品状态

可售:商品配置了售卖区并且状态为可售

可补:商品可售且微仓可补货状态

可采:商品可售且⼤仓可采货状态

准备上架:建品后为此状态,表示可采和可补

试销上架:上架状态,表示处于试销阶段

上架:正式上架售卖

预下架:售完不展示商品,表示不可采和可补

下架:不可采和不可补

停售:永久下架,已淘汰

十五.商品价格

商城价:⾮会员⽤户购买商品的价格

会员价:会员⽤户购买的价格

营销价:促销活动价

秒杀价:秒杀活动价格,⼀⼝价

2.商品中心的基本业务系统

(1)商品基础服务

(2)商品类型与采购销售之间的关系

(3)商品中心的业务系统

(1)商品基础服务

**服务一:**提供从建品到下架期间可采可补可售管理的商品全流程服务

**服务二:**对商品基本信息、品牌信息、运营品类、前台类⽬、仓配信息、标签信息、品控信息、销售信息、推⼴信息等进⾏精细化管理与运营

**服务三:**通过权限收敛,可以很好把控并记录⽤户操作⾏为,使流程更加规范

**服务四:**通过提效⼯具,业务⽅可以批量处理商品相关⼯作,降低⼈⼒成本

(2)商品类型与采购销售之间的关系

(3)商品中心的业务系统

商品中心的系统主要会分为两类:一个是面向B端,一个是面向C端。面向B端的系统,主要由公司运营来使用,对商品进行精细化管理。面向C端的系统,则会对C端用户提供各种商品浏览和查询的接口。

一.价格中心系统

商品价格管理,提供全流程价格管控和分析。⽀持功能:价格查询、价格设置、审核流程、历史价格查询与趋势分析等。

二.商品卖家系统

商品售卖⽅,这⾥将卖家定义为卖家树,⽤户可以定位到多个卖家。商品基于卖家售卖,⽤户在当前覆盖的区域内可浏览到相应卖家的商品。将多个卖家合并为⼀个⼤的卖家称为卖家组,也称为售卖区,售卖区之间的逻辑处理称为售卖区管理(可售区域)。

三.商品⽣命周期系统

商品的状态分为:准备上架、试销上架、上架、预下架、下架、停售。为了更好的管理商品,需要对商品进⾏⼀套⽣命周期管理。⽤于考核商品,降低滞销率、资⾦成本以及影响商品的可采可补逻辑。

四.商品库存系统

商品库存需要分卖家设置,卖家 + 商品 + 库存关系定位具体商品库存数量。

五.商品标签系统

需要打上特殊标签的商品,例如:爆款,后台进⾏标签 + 标签组 + 商品管理。

六.属性库系统

商品关联的属性,涉及四种属性:关键属性、销售属性、⾮关键属性、导购属性。

七.商品品控系统

把控商品质量,在商品⼊库前进⾏取样检测,给出质检报告。合格商品允许⼊库,不合格商品不允许⼊库。将可售卖商品关联上质检报告,展示给⽤户。

3.商品中心整体架构设计以及运行流程

(1)商品中心整体架构

(2)商品新建编辑流程

(1)商品中心整体架构

(2)商品新建编辑流程

复制代码
//商品服务
@DubboService(version = "1.0.0", interfaceClass = ProductApi.class, retries = 0)
public class ProductApiImpl implements ProductApi {
    @Autowired
    private ProductService productService;

    //建品/编辑商品接口
    @Override
    public JsonResult<ProductDTO> product(ProductRequest request) {
        try {
            ProductDTO productDTO = productService.product(request);
            return JsonResult.buildSuccess(productDTO);
        } catch (ProductBizException e) {
            log.error("biz error: request={}", JSON.toJSONString(request), e);
            return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());
        } catch (Exception e) {
            log.error("system error: request={}", JSON.toJSONString(request), e);
            return JsonResult.buildError(e.getMessage());
        }
    }
    ...
}

4.商品B端---商品编码生成逻辑

复制代码
//商品编码
@Service
public class ProductNoManagerImpl implements ProductNoManager {
    //6位序列号
    private static final int width = 6;

    @Autowired
    private ProductAutoNoMapper productAutoNoMapper;

    //生成商品编码
    @Override
    public String generateProductNo(Integer sourceType) {
        ProductTypeEnum productTypeEnum = ProductTypeEnum.getByCode(sourceType);
        if (productTypeEnum == null) {
            throw new ProductBizException(ProductErrorCodeEnum.PARAM_ERROR);
        }
        return getProductNo(productTypeEnum.getValue());
    }

    //获取组装后的商品编码,商品的prefixNo是100000
    private String getProductNo(String prefixNo) {
        //有一张ProductAutoNo表专门用于生成商品ID
        //分库分表也可以利用此来实现基于数据库的内存缓存分段的发号器
        ProductAutoNoDO productAutoNoDO = new ProductAutoNoDO();
        productAutoNoMapper.insert(productAutoNoDO);
        Long autoNo = productAutoNoDO.getId();//获取自增ID
        return prefixNo + IDUtils.genId(autoNo, width);//数字混淆算法
    }
}

5.商品B端---商品核心数据模型

复制代码
//建品/编辑商品请求入参
@Data
public class ProductRequest implements Serializable {
    //商品基本信息
    private ItemBaseRequest itemBaseRequest;
    //存储信息
    private ItemStorageRequest itemStorageRequest;
    //品控信息
    private ShelfLifeRequest shelfLifeRequest;
    //图文信息
    private List<ItemVideoImgRequest> itemVideoImgRequestList;
    //销售信息
    private ItemSaleRequest itemSaleRequest;
    //推广信息
    private ItemPopularizeRequest itemPopularizeRequest;
    //操作人
    @NotNull(message = "操作人[operateUser]不能为空")
    private Integer operatorUser;

    //商品基本信息
    @Data
    public static class ItemBaseRequest implements Serializable {
        //商品ID
        private String itemId;
        //商品名称
        private String itemName;
        //渠道(1-每日生鲜、2-美团、3-饿了么、4-淘鲜达、5-招商银行)
        private Integer channel;
        //卖家类型(1-自营、2-POP)
        private Integer sellerType;
        //商品状态
        private Integer itemStatus;
        //商品类型
        private Integer itemType;
        //品牌ID
        private Integer brandId;
        //产地ID
        private Integer producingAreaId;
        //成本价(单位:分)
        private Integer basePrice;
        //末级品类ID
        private Integer lastCategoryId;
        //一级品类ID
        private Integer oneCategoryId;
        //二级品类ID
        private Integer twoCategoryId;
        //三级品类ID
        private Integer threeCategoryId;
    }

    //存储信息
    @Data
    public static class ItemStorageRequest implements Serializable {
        //存储条件
        private Integer storeConditionType;
        //ITEM维度规格值(多个规格集合):key=颜色,value=蓝色;key=颜色,value=红色;key=内存,value=128g;key=内存,value=256g
        private List<ProductSpcesValue> productSpcesValueList;
    }

    //规格信息
    @Data
    public static class ProductSpcesValue implements Serializable {
        //规格关键字
        private String key;
        //规格值
        private String value;
        //排序
        private Integer sort;
    }

    //品控信息
    @Data
    public static class ShelfLifeRequest implements Serializable {

    //保质期(单位:小时)
    private Integer shelfLife;
        //Map<key=保质期类型,value=保质期时间(单位:小时)>:acceptLife 允收期,shelfLife 货架期
        private Map<String, Integer> shelfLifeMap;
    }

    //图文信息
    @Data
    public static class ItemVideoImgRequest implements Serializable {
        //内容类型(1-主图,2-轮播图、3-详情图、4-视频)
        private Integer contentType;
        //链接地址
        private String contentUrl;
        //排序(正整数,数字越小越靠前)
        private Integer contentSort;
    }

    //销售信息
    @Data
    public static class ItemSaleRequest implements Serializable {
        //sku信息
        private List<SkuInfoRequest> skuInfoRequestList;
    }

    //sku信息
    @Data
    public static class SkuInfoRequest implements Serializable {
        //商品itemId
        private String itemId;
        //商品skuId
        private String skuId;
        //商品SKU名称
        private String skuName;
        //商城价
        private Integer basePrice;
        //会员价
        private Integer vipPrice;
        //商品分级(ABC标签,运营归类处理)
        private Integer skuGrade;
        //69码,条形码
        private String barCode;
        //SKU维度规格值(单个):key=颜色,value=蓝色;key=内存,value=128g
        private List<ProductSpcesValue> productSpcesValueList;
        //sku匹配的spu信息
        private Long cspuId;
    }

    //推广信息
    @Data
    public static class ItemPopularizeRequest implements Serializable {
        //推荐语
        private String recommend;
        //亮点
        private List<HighlightsRequest> highlightsRequestList;
        //卖点
        private List<SellingPointRequest> sellingPointRequestList;
        //质检报告
        private List<QualityControlRequest> qualityControlRequestList;
    }

    //亮点
    @Data
    public static class HighlightsRequest implements Serializable {
        //亮点文案
        private String highlights;
        //排序(正整数,数字越小越靠前)
        private Integer sort;
    }

    //卖点
    @Data
    public static class SellingPointRequest implements Serializable {
        //卖点文案
        private String sellingPoint;
        //排序(正整数,数字越小越靠前)
        private Integer sort;
    }

    //质检报告
    @Data
    public static class QualityControlRequest implements Serializable {
        //商品skuId
        private String skuId;
        //质检报告名称
        private String qcName;
        //材料图片链接
        private String qcImgUrl;
        //排序(正整数,数字越小越靠前)
        private Integer qcSort;
    }
}

6.商品B端---转换建品请求数据为商品模型数据

前端的建品请求数据比较复杂,需要和后端的商品模型数据匹配起来,所以需要进行数据转换。这种数据转换,通常会用Builder模式来实现。

复制代码
@Service
public class ProductServiceImpl implements ProductService {
    ...
    //建品/编辑商品
    @Transactional(rollbackFor = Exception.class)
    @Override
    @ParamsValidate
    public ProductDTO product(ProductRequest productRequest) {
        //入参检查
        checkProductRequestParam(productRequest);
        //商品数据处理
        ProductDTO productDTO = handleProduct(productRequest);
        //返回商品信息
        return productDTO;
    }

    //建品/编辑商品入参检查
    private void checkProductRequestParam(ProductRequest productRequest) {
        ParamCheckUtil.checkObjectNonNull(productRequest);
        //商品基本信息
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        ParamCheckUtil.checkObjectNonNull(itemBaseRequest);
        //存储信息
        ProductRequest.ItemStorageRequest itemStorageRequest = productRequest.getItemStorageRequest();
        ParamCheckUtil.checkObjectNonNull(itemStorageRequest);
        //品控信息
        ProductRequest.ShelfLifeRequest shelfLifeRequest = productRequest.getShelfLifeRequest();
        ParamCheckUtil.checkObjectNonNull(shelfLifeRequest);
        //图文信息
        List<ProductRequest.ItemVideoImgRequest> itemVideoImgRequestList = productRequest.getItemVideoImgRequestList();
        ParamCheckUtil.checkObjectNonNull(itemVideoImgRequestList);
        //销售信息
        ProductRequest.ItemSaleRequest itemSaleRequest = productRequest.getItemSaleRequest();
        ParamCheckUtil.checkObjectNonNull(itemSaleRequest);
        //推广信息
        ProductRequest.ItemPopularizeRequest itemPopularizeRequest = productRequest.getItemPopularizeRequest();
        ParamCheckUtil.checkObjectNonNull(itemPopularizeRequest);
    }

    //商品数据处理
    private ProductDTO handleProduct(ProductRequest productRequest) {
        //构建商品的全量信息
        FullProductData fullProductData = buildProduct(productRequest);
        //是否构建填充 itemId
        Boolean createFlag = whetherBuildProductItemId(fullProductData);
        //判断是否需要审核,根据配置的sku或者item或者品类信息,如果需要审核,只处理草稿表中的数据,正式表中的数据不动
        if (productAuditRepository.needAudit(fullProductData, createFlag)) {
            //需要审核,则正式表中的数据不变更,只新增草稿表记录
            FullDraftData fullDraftData = buildDraft(fullProductData, AuditTypeEnum.GOODS.getCode());
            //保存草稿信息
            productAuditRepository.saveDraft(fullDraftData);
            return new ProductDTO(null, null);
        }
        //如果不需要审核,则保存商品信息
        this.saveOrUpdateDBProduct(fullProductData, createFlag);
        //发送消息通知订阅方
        sendUpdateProductMessage(fullProductData);
        //返回商品返回结果
        return new ProductDTO(fullProductData.getItemInfoDO().getItemId(), buildProductSkuIds(fullProductData));
    }

    //前端建品请求数据到后端商品数据模型的转换
    private FullProductData buildProduct(ProductRequest productRequest) {
        ProductBuilder productBuilder = new ProductBuilder(productRequest);
        FullProductData fullProductData = productBuilder.buildItemInfo()
            .buildItemShelfLife()
            .buildItemVideoImgList()
            .buildSkuInfoList()
            .buildSkuBarCodeRelationList()
            .buildCspuSkuRelation()
            .buildAttributeExtend()
            .buildQualityControl()
            .build();
        return fullProductData;
    }
    ...
}

//全量商品数据
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FullProductData {
    //ITEM信息
    private ItemInfoDO itemInfoDO;
    //保质期信息
    private ItemShelfLifeDO itemShelfLifeDO;
    //视频图片信息
    private List<ItemVideoImgDO> itemVideoImgDOList;
    //SKU信息
    private List<SkuInfoDO> skuInfoDOList;
    //69码关系
    private List<SkuBarCodeRelationDO> skuBarCodeRelationDOList;
    //CSPU与SKU关系
    private List<CspuSkuRelationDO> cspuSkuRelationDOList;
    //ITEM或SKU扩展属性
    private AttributeExtendDO attributeExtendDO;
    //品控信息
    private List<QualityControlDO> qualityControlDOList;

    public FullProductData(ItemInfoDO itemInfoDO, List<SkuInfoDO> skuInfoDOList) {
        this.itemInfoDO = itemInfoDO;
        this.skuInfoDOList = skuInfoDOList;
    }
}

//全量商品数据
public class ProductBuilder {
    //商品入参
    private ProductRequest productRequest;
    //全量商品数据
    private FullProductData fullProductData;

    public ProductBuilder(ProductRequest productRequest) {
        this.productRequest = productRequest;
        this.fullProductData = new FullProductData();
    }

    public ProductBuilder buildItemInfo() {
        ItemInfoDO itemInfoDO = new ItemInfoDO();
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        itemInfoDO.setItemId(itemBaseRequest.getItemId());
        itemInfoDO.setItemName(itemBaseRequest.getItemName());
        ...
        fullProductData.setItemInfoDO(itemInfoDO);
        return this;
    }

    public ProductBuilder buildItemShelfLife() {
        ItemShelfLifeDO itemShelfLifeDO = new ItemShelfLifeDO();
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        ProductRequest.ShelfLifeRequest shelfLifeRequest = productRequest.getShelfLifeRequest();
        itemShelfLifeDO.setItemId(itemBaseRequest.getItemId());
        itemShelfLifeDO.setShelfLifeContent(JSON.toJSONString(shelfLifeRequest.getShelfLife()));
        itemShelfLifeDO.setDelFlag(DelFlagEnum.EFFECTIVE.getCode());
        ...
        fullProductData.setItemShelfLifeDO(itemShelfLifeDO);
        return this;
    }

    public ProductBuilder buildItemVideoImgList() {
        List<ItemVideoImgDO> itemVideoImgDOList = new ArrayList<>(16);
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        List<ProductRequest.ItemVideoImgRequest> itemVideoImgRequestList = productRequest.getItemVideoImgRequestList();
        for (ProductRequest.ItemVideoImgRequest itemVideoImgRequest : itemVideoImgRequestList) {
            ItemVideoImgDO itemVideoImgDO = new ItemVideoImgDO();
            itemVideoImgDO.setItemId(itemBaseRequest.getItemId());
            ...
            itemVideoImgDOList.add(itemVideoImgDO);
        }
        fullProductData.setItemVideoImgDOList(itemVideoImgDOList);
        return this;
    }

    public ProductBuilder buildSkuInfoList() {
        List<SkuInfoDO> skuInfoDOList = new ArrayList<>(16);
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        List<ProductRequest.SkuInfoRequest> skuInfoRequestList = productRequest.getItemSaleRequest().getSkuInfoRequestList();
        for (ProductRequest.SkuInfoRequest skuInfoRequest : skuInfoRequestList) {
            SkuInfoDO skuInfoDO = new SkuInfoDO();
            skuInfoDO.setItemId(skuInfoRequest.getItemId());
            skuInfoDO.setSkuId(skuInfoRequest.getSkuId());
            skuInfoDO.setSkuName(skuInfoRequest.getSkuName());
            ...
            skuInfoDOList.add(skuInfoDO);
        }
        fullProductData.setSkuInfoDOList(skuInfoDOList);
        return this;
    }

    public ProductBuilder buildSkuBarCodeRelationList() {
        List<SkuBarCodeRelationDO> skuBarCodeRelationDOList = new ArrayList<>(16);
        List<ProductRequest.SkuInfoRequest> skuInfoRequestList = productRequest.getItemSaleRequest().getSkuInfoRequestList();
        for (ProductRequest.SkuInfoRequest skuInfoRequest : skuInfoRequestList) {
            SkuBarCodeRelationDO skuBarCodeRelationDO = new SkuBarCodeRelationDO();
            skuBarCodeRelationDO.setSkuId(skuInfoRequest.getSkuId());
            skuBarCodeRelationDO.setBarCode(skuInfoRequest.getBarCode());
            ...
            skuBarCodeRelationDOList.add(skuBarCodeRelationDO);
        }
        fullProductData.setSkuBarCodeRelationDOList(skuBarCodeRelationDOList);
        return this;
    }

    public ProductBuilder buildCspuSkuRelation() {
        List<CspuSkuRelationDO> cspuSkuRelationDOList = new ArrayList<>(16);
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        List<ProductRequest.SkuInfoRequest> skuInfoRequestList = productRequest.getItemSaleRequest().getSkuInfoRequestList();
        for (ProductRequest.SkuInfoRequest skuInfoRequest : skuInfoRequestList) {
            CspuSkuRelationDO cspuSkuRelationDO = new CspuSkuRelationDO();
            cspuSkuRelationDO.setSkuId(skuInfoRequest.getSkuId());
            cspuSkuRelationDO.setCspuId(skuInfoRequest.getCspuId());
            ...
            cspuSkuRelationDOList.add(cspuSkuRelationDO);
        }
        fullProductData.setCspuSkuRelationDOList(cspuSkuRelationDOList);
        return this;
    }

    public ProductBuilder buildAttributeExtend() {
        AttributeExtendDO attributeExtendDO = new AttributeExtendDO();
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        ProductRequest.ItemPopularizeRequest itemPopularizeRequest = productRequest.getItemPopularizeRequest();
        attributeExtendDO.setParticipateId(itemBaseRequest.getItemId());
        ...
        fullProductData.setAttributeExtendDO(attributeExtendDO);
        return this;
    }

    public ProductBuilder buildQualityControl() {
        List<QualityControlDO> qualityControlDOList = new ArrayList<>(16);
        ProductRequest.ItemBaseRequest itemBaseRequest = productRequest.getItemBaseRequest();
        ProductRequest.ItemPopularizeRequest itemPopularizeRequest = productRequest.getItemPopularizeRequest();
        List<ProductRequest.QualityControlRequest> qualityControlRequestList = itemPopularizeRequest.getQualityControlRequestList();
        for (ProductRequest.QualityControlRequest qualityControlRequest : qualityControlRequestList) {
            QualityControlDO qualityControlDO = new QualityControlDO();
            qualityControlDO.setItemId(itemBaseRequest.getItemId());
            qualityControlDO.setSkuId(qualityControlRequest.getSkuId());
            ...
            qualityControlDOList.add(qualityControlDO);
        }
        fullProductData.setQualityControlDOList(qualityControlDOList);
        return this;
    }

    public FullProductData build() {
        return this.fullProductData;
    }
}

7.商品B端---商品建品时商品编号补全与审核配置

复制代码
@Service
public class ProductServiceImpl implements ProductService {
    ...
    //建品/编辑商品
    @Transactional(rollbackFor = Exception.class)
    @Override
    @ParamsValidate
    public ProductDTO product(ProductRequest productRequest) {
        //入参检查
        checkProductRequestParam(productRequest);
        //商品数据处理
        ProductDTO productDTO = handleProduct(productRequest);
        //返回商品信息
        return productDTO;
    }
    ...

    //商品数据处理
    private ProductDTO handleProduct(ProductRequest productRequest) {
        //构建商品的全量信息
        FullProductData fullProductData = buildProduct(productRequest);
        //是否构建填充itemId
        Boolean createFlag = whetherBuildProductItemId(fullProductData);
        //判断是否需要审核,根据配置的sku或者item或者品类信息,如果需要审核,只处理草稿表中的数据,正式表中的数据不动
        if (productAuditRepository.needAudit(fullProductData, createFlag)) {
            //需要审核,则正式表中的数据不变更,只新增草稿表记录
            FullDraftData fullDraftData = buildDraft(fullProductData, AuditTypeEnum.GOODS.getCode());
            //保存草稿信息
            productAuditRepository.saveDraft(fullDraftData);
            return new ProductDTO(null, null);
        }
        //如果不需要审核,则保存商品信息
        this.saveOrUpdateDBProduct(fullProductData, createFlag);
        //发送消息通知订阅方
        sendUpdateProductMessage(fullProductData);
        //返回商品返回结果
        return new ProductDTO(fullProductData.getItemInfoDO().getItemId(), buildProductSkuIds(fullProductData));
    }

    //是否需要构建商品的ItemId
    private Boolean whetherBuildProductItemId(FullProductData fullProductData) {
        //ITEM信息
        ItemInfoDO itemInfoDO = fullProductData.getItemInfoDO();
        //新增
        if (StringUtils.isEmpty(itemInfoDO.getItemId())) {
            //保质期
            ItemShelfLifeDO itemShelfLifeDO = fullProductData.getItemShelfLifeDO();
            //生成Item的Id
            String itemId = createItemId();
            //赋值itemId
            itemInfoDO.setItemId(itemId);
            itemShelfLifeDO.setItemId(itemId);
            //SKU信息
            List<SkuInfoDO> skuInfoDOList = fullProductData.getSkuInfoDOList();
            for (SkuInfoDO skuInfoDO : skuInfoDOList) {
                //对每个SKU也生成ID
                String skuId = productNoManager.generateProductNo(ProductTypeEnum.SKU.getCode());
                skuInfoDO.setSkuId(skuId);
                skuInfoDO.setItemId(itemId);
            }
            //视频图片
            List<ItemVideoImgDO> itemVideoImgDOList = fullProductData.getItemVideoImgDOList();
            for (ItemVideoImgDO itemVideoImgDO : itemVideoImgDOList) {
                itemVideoImgDO.setItemId(itemId);
            }
            //属性扩展
            AttributeExtendDO attributeExtendDO = fullProductData.getAttributeExtendDO();
            attributeExtendDO.setParticipateId(itemInfoDO.getItemId());
            attributeExtendDO.setParticipateType(ProductTypeEnum.ITEM.getCode());
            return true;
        }
        return false;
    }

    //创建ItemId
    private String createItemId() {
        String itemId = productNoManager.generateProductNo(ProductTypeEnum.ITEM.getCode());
        return itemId;
    }
    ...
}

//商品审核 资源管理
@Repository
public class ProductAuditRepository {
    ...
    //验证是否需要审核
    public Boolean needAudit(FullProductData fullProductData, Boolean createFlag) {
        ItemInfoDO itemInfoDO = fullProductData.getItemInfoDO();
        Integer count = 0;
        if (!createFlag) {
            //1.首先判断 商品审核内容配置表 中是否有对应的skuId
            List<String> skuIds = fullProductData.getSkuInfoDOList().stream().map(SkuInfoDO::getSkuId).collect(Collectors.toList());
            count = countByCustomIds(skuIds, AuditCustomTypeEnum.SKU);
            if (count > 0) {
                return true;
            }
            //2.是否有对应的item
            count = countByCustomIds(Collections.singletonList(itemInfoDO.getItemId()), AuditCustomTypeEnum.ITEM);
            if (count > 0) {
                return true;
            }
        }
        //3.验证是否有对应的categoryId
        List<Integer> categoryIds = Arrays.asList(itemInfoDO.getFirstCategoryId(), itemInfoDO.getSecondCategoryId(), itemInfoDO.getThirdCategoryId());
        count = countByCustomIds(categoryIds, AuditCustomTypeEnum.CATEGORY);
        //当商品审核内容配置表中有相应的品类数据,则需要审核,否则不需要审核
        return count > 0;
    }
    ...
}

8.商品B端---商品审核前的草稿数据保存逻辑

复制代码
@Service
public class ProductServiceImpl implements ProductService {
    ...
    //建品/编辑商品
    @Transactional(rollbackFor = Exception.class)
    @Override
    @ParamsValidate
    public ProductDTO product(ProductRequest productRequest) {
        //入参检查
        checkProductRequestParam(productRequest);
        //商品数据处理
        ProductDTO productDTO = handleProduct(productRequest);
        //返回商品信息
        return productDTO;
    }
    ...

    //商品数据处理
    private ProductDTO handleProduct(ProductRequest productRequest) {
        //构建商品的全量信息
        FullProductData fullProductData = buildProduct(productRequest);
        //是否构建填充itemId
        Boolean createFlag = whetherBuildProductItemId(fullProductData);
        //判断是否需要审核,根据配置的sku或者item或者品类信息,如果需要审核,只处理草稿表中的数据,正式表中的数据不动
        if (productAuditRepository.needAudit(fullProductData, createFlag)) {
            //需要审核,则正式表中的数据不变更,只新增草稿表记录
            FullDraftData fullDraftData = buildDraft(fullProductData, AuditTypeEnum.GOODS.getCode());
            //保存草稿信息
            productAuditRepository.saveDraft(fullDraftData);
            return new ProductDTO(null, null);
        }
        //如果不需要审核,则保存商品信息
        this.saveOrUpdateDBProduct(fullProductData, createFlag);
        //发送消息通知订阅方
        sendUpdateProductMessage(fullProductData);
        //返回商品返回结果
        return new ProductDTO(fullProductData.getItemInfoDO().getItemId(), buildProductSkuIds(fullProductData));
    }
    ...

    //根据商品数据构建商品草稿数据
    private FullDraftData buildDraft(FullProductData fullProductData, Integer auditType) {
        ProductDraftBuilder productDraftBuilder = new ProductDraftBuilder(fullProductData);
        FullDraftData fullDraftData = productDraftBuilder.buildDraftMain(auditType)
            .buildDraftImgList()
            .build();
        return fullDraftData;
    }
    ...
}

//商品审核 资源管理
@Repository
public class ProductAuditRepository {
    ...
    //保存草稿信息
    public void saveDraft(FullDraftData fullDraftData) {
        //1.保存工单信息
        AuditInfoDO auditInfoDO = saveAudit(fullDraftData);
        //2.保存工单审核历史信息
        saveAuditHistory(auditInfoDO);
        //3.保存草稿信息
        saveDraftMain(fullDraftData, auditInfoDO.getId());
        //4.保存草稿图片信息
        saveDraftImgBatch(fullDraftData);
    }

    //保存工单信息
    private AuditInfoDO saveAudit(FullDraftData fullDraftData) {
        AuditInfoDO auditInfoDO = auditConverter.converterDO(fullDraftData.getDraftMainDO());
        auditInfoDO.initCommon();
        int count = auditInfoMapper.insert(auditInfoDO);
        if (count <= 0) {
            throw new ProductBizException(AuditExceptionCode.AUDIT_SQL.getErrorCode(), "保存工单失败");
        }
        return auditInfoDO;
    }

    //保存工单审核历史信息
    private void saveAuditHistory(AuditInfoDO auditInfoDO) {
        AuditHistoryDO auditHistoryDO = auditConverter.converterHistoryDO(auditInfoDO);
        int count = this.auditHistoryMapper.insert(auditHistoryDO);
        if (count <= 0) {
            throw new ProductBizException(AuditExceptionCode.AUDIT_SQL.getErrorCode(), "保存工单审核历史信息失败");
        }
    }

    //保存草稿信息
    private void saveDraftMain(FullDraftData fullDraftData, Long auditId) {
        DraftMainDO draftMainDO = fullDraftData.getDraftMainDO();
        draftMainDO.setTicketId(auditId);
        int count = draftMainMapper.insert(draftMainDO);
        if (count <= 0) {
            throw new ProductBizException(AuditExceptionCode.AUDIT_SQL.getErrorCode(), "保存草稿审核信息失败");
        }
    }

    //保存草稿图片信息
    private void saveDraftImgBatch(FullDraftData fullDraftData) {
        List<DraftImgDO> draftImgDOS = fullDraftData.getDraftImgDOS();
        if (!CollectionUtils.isEmpty(draftImgDOS)) {
            for (DraftImgDO draftImgDO : draftImgDOS) {
                draftImgDO.setDraftId(fullDraftData.getDraftMainDO().getId());
            }
            draftImgMapper.saveBatch(draftImgDOS);
        }
    }
    ...
}

9.商品B端---不需审核的建品流程持久化逻辑

复制代码
@Service
public class ProductServiceImpl implements ProductService {
    ...
    //商品数据处理
    private ProductDTO handleProduct(ProductRequest productRequest) {
        //构建商品的全量信息
        FullProductData fullProductData = buildProduct(productRequest);
        //是否构建填充itemId
        Boolean createFlag = whetherBuildProductItemId(fullProductData);
        //判断是否需要审核,根据配置的sku或者item或者品类信息,如果需要审核,只处理草稿表中的数据,正式表中的数据不动
        if (productAuditRepository.needAudit(fullProductData, createFlag)) {
            //需要审核,则正式表中的数据不变更,只新增草稿表记录
            FullDraftData fullDraftData = buildDraft(fullProductData, AuditTypeEnum.GOODS.getCode());
            //保存草稿信息
            productAuditRepository.saveDraft(fullDraftData);
            return new ProductDTO(null, null);
        }
        //如果不需要审核,则保存商品信息
        this.saveOrUpdateDBProduct(fullProductData, createFlag);
        //发送消息通知订阅方
        sendUpdateProductMessage(fullProductData);
        //返回商品返回结果
        return new ProductDTO(fullProductData.getItemInfoDO().getItemId(), buildProductSkuIds(fullProductData));
    }

    //新增或者修改商品相关信息
    @Override
    public void saveOrUpdateDBProduct(FullProductData fullProductData, Boolean createFlag) {
        if (createFlag) {
            //新增
            productInfoRepository.saveItemInfo(fullProductData);
        } else {
            //修改
            productInfoRepository.updateItemInfo(fullProductData);
        }
    }
    ...
}

//商品 资源管理
@Repository
public class ProductInfoRepository {
    ...
    //保存商品的明细信息
    public void saveItemInfo(FullProductData fullProductData) {
        //保存商品Item的信息
        saveItemInfo(fullProductData.getItemInfoDO());
        //保存商品保质期信息
        saveItemShelfLife(fullProductData.getItemShelfLifeDO());
        //批量保存商品图片视频信息
        saveBatchVideoImg(fullProductData.getItemVideoImgDOList());
        //批量保存商品sku信息
        saveBatchSkuInfo(fullProductData.getSkuInfoDOList());
        //批量保存69码信息
        saveBatchSkuBarCodeRelation(fullProductData.getSkuBarCodeRelationDOList());
        //批量保存CSPU与SKU关系
        saveBatchCspuSkuRelation(fullProductData.getCspuSkuRelationDOList());
        //批量保存品控信息
        saveBatchQualityControl(fullProductData.getQualityControlDOList());
        //保存ITEM或SKU扩展属性
        saveAttributeExtend(fullProductData.getAttributeExtendDO());
    }

    //修改商品的明细信息
    public void updateItemInfo(FullProductData fullProductData) {
        //更新商品item信息
        updateItemInfo(fullProductData.getItemInfoDO());
        //更新商品保质期信息
        updateItemShelfLife(fullProductData.getItemShelfLifeDO());
        //更新商品扩展信息
        updateAttributeExtend(fullProductData.getAttributeExtendDO());
        //更新商品的视频图片信息
        batchUpdateVideoImg(fullProductData.getItemVideoImgDOList());
        //批量更新商品sku信息
        batchUpdateSkuInfo(fullProductData.getSkuInfoDOList());
        //批量更新商品的69规格
        batchUpdateSkuBarCodeRelation(fullProductData.getSkuBarCodeRelationDOList());
        //批量更新 CSPU与SKU关系
        batchUpdateCspuSkuRelation(fullProductData.getCspuSkuRelationDOList());
        //批量更新品控信息
        batchUpdateQualityControl(fullProductData.getQualityControlDOList());
    }
    ...

    //保存商品Item的信息
    private void saveItemInfo(ItemInfoDO itemInfoDO) {
        int count = itemInfoMapper.insert(itemInfoDO);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //保存商品保质期信息
    private void saveItemShelfLife(ItemShelfLifeDO itemShelfLifeDO) {
        int count = itemShelfLifeMapper.insert(itemShelfLifeDO);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //批量保存商品图片视频信息
    private void saveBatchVideoImg(List<ItemVideoImgDO> itemVideoImgDOList) {
        int count = itemVideoImgMapper.saveBatch(itemVideoImgDOList);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //批量保存商品sku信息
    private void saveBatchSkuInfo(List<SkuInfoDO> skuInfoDOList) {
        int count = skuInfoMapper.saveBatch(skuInfoDOList);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //批量保存69码信息
    private void saveBatchSkuBarCodeRelation(List<SkuBarCodeRelationDO> skuBarCodeRelationDOList) {
        int count = skuBarCodeRelationMapper.saveBatch(skuBarCodeRelationDOList);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //批量保存CSPU与SKU关系
    private void saveBatchCspuSkuRelation(List<CspuSkuRelationDO> cspuSkuRelationDOList) {
        int count = cspuSkuRelationMapper.saveBatch(cspuSkuRelationDOList);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //批量保存品控信息
    private void saveBatchQualityControl(List<QualityControlDO> qualityControlDOList) {
        int count = qualityControlMapper.saveBatch(qualityControlDOList);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }

    //保存 ITEM或SKU扩展属性
    private void saveAttributeExtend(AttributeExtendDO attributeExtendDO) {
        int count = attributeExtendMapper.insert(attributeExtendDO);
        if (count <= 0) {
            throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);
        }
    }
    ...
}
相关推荐
2301_7930868714 小时前
Redis 04 Reactor
数据库·redis·缓存
1892280486118 小时前
NY243NY253美光固态闪存NY257NY260
大数据·网络·人工智能·缓存
青鱼入云19 小时前
redis怎么做rehash的
redis·缓存
FFF-X19 小时前
Vue3 路由缓存实战:从基础到进阶的完整指南
vue.js·spring boot·缓存
夜影风2 天前
Nginx反向代理与缓存实现
运维·nginx·缓存
编程(变成)小辣鸡2 天前
Redis 知识点与应用场景
数据库·redis·缓存
菜菜子爱学习2 天前
Nginx学习笔记(八)—— Nginx缓存集成
笔记·学习·nginx·缓存·运维开发
魏波.2 天前
常用缓存软件分类及详解
缓存
yh云想3 天前
《多级缓存架构设计与实现全解析》
缓存·junit
白仑色3 天前
Redis 如何保证数据安全?
数据库·redis·缓存·集群·主从复制·哨兵·redis 管理工具