商品中心—4.商品属性与状态流转的技术文档
大纲
1.商品属性库系统简介
2.商品属性库系统数据库设计
3.商品属性库系统对品类添加属性
4.商品属性库系统添加属性值与属性组
5.商品属性库系统添加属性模版及模版内容
6.查询属性库系统数据相关接口
7.商品属性库系统与商品M端系统对接
8.商品状态变更流转架构设计与实现
9.商品状态变更流转的策略模式实现
10.商品状态变更流转的具体策略实现
1.商品属性库系统简介
(1)属性分类
(2)属性模版
(3)属性组
(4)属性库运行流程
(1)属性分类
为了⽅便管理商品,可以将属性分为三类:
一.基础属性
商品的通⽤属性,例如名称、品牌、品类等。
二.销售属性
商品的销售价格属性,不同销售属性的商品,其销售价会有所不同,例如⼿机的颜⾊、内存等。
三.搜索属性
商品搜索的属性,例如名称、品牌、品类等。
(2)属性模版
为了提⾼效率,合理化运营,会将商品的属性与品类绑定到⼀起。品类是以树形结构来管理的。既然属性与品类绑定在⼀起,为避免重复添加属性,会将每级品类的公共属性抽取出来作为⼀个属性模板。后期关联商品查询时,可以将属性最终展示在商品下⾯。
(3)属性组
由于商品的属性很多,为了⽅便⽤户浏览和数据⽐对,需要对属性进⾏分组管理。
一.电子产品属性组
二.汽车属性组
(4)属性库运行流程
一.创建属性库数据
二.查询属性库数据
三.商品对接属性库
2.商品属性库系统数据库设计
(1)属性相关表的关系
(2)属性相关表的详情
(1)属性相关表的关系
属性表管理:attribute management,简称atm。
一个品类会关联一个属性模版,一个属性模版里会有多个属性组或多个属性,一个属性组里会有多个属性,一个属性里会有多个属性值。
(2)属性相关表的详情
一.属性表
二.属性可选值表
三.属性组表
四.属性模板表
五.属性模板内容表
六.标品商品属性值表
一.属性表
create table if not exists atm_attribute_info ( id int(10) auto_increment comment '主键' primary key, category_id int(10) default null comment '品类id', attribute_name varchar(128) not null comment '属性名称', attribute_code varchar(128) null comment '属性编码', attribute_type varchar(128) not null comment '属性类型,格式为json数组(1-基础属性,2-销售属性,3-搜索属性,null-其他属性)', attribute_input_type int not null comment '属性值的输⼊⽅式(1-⽤户⾃⼰输⼊,2-单选下拉框,3-多选下拉框,4-⽇期)', attribute_comment varchar(128) default null comment '属性说明', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '属性表';
例如添加三个属性:品牌、颜色、版本。
二.属性可选值表
create table if not exists atm_attribute_value ( id int(10) auto_increment comment '主键' primary key, attribute_id int(10) not null comment '属性id', attribute_value varchar(128) not null comment '属性值', sort int not null comment '排序序号', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '属性值表';
例如给颜色属性添加了3个可选值:红色、黄色、蓝色。例如给版本属性添加了3个可选值:公开版、无线耳机套装、智能手表套装。
三.属性组表
create table if not exists atm_attribute_group ( id int(10) auto_increment comment '主键' primary key, category_id int(10) default null comment '品类id', group_name varchar(128) not null comment '属性组名称', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '属性组表';
例如添加一个属性组:基本属性组。
四.属性模板表
create table atm_attribute_template ( id int auto_increment comment '主键' primary key, template_name varchar(128) not null comment '属性模板名称', category_id int not null comment '品类id', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '属性模板表';
例如添加一个属性模版:智能手机模版;
五.属性模板内容表
create table atm_attribute_template_content ( id int auto_increment comment '主键' primary key, template_id int not null comment '属性模板id', group_id int null comment '属性组id', attribute_id int not null comment '属性id', participant_type int not null comment '参与类型(1-表示该属性是放到item上的,2-表示该属性是放到sku上的)', sort int null comment '排序序号', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '模板内容表';
例如给智能⼿机模板配置三个属性,同时指定每个属性所属的属性组,分别为品牌属性(⽆属性组)、颜⾊属性和版本属性(属于基本属性组)。另外配置品牌属性放到item上,颜⾊和版本属性放到sku上,而且属性组的顺序分别是品牌、颜⾊、版本。
六.标品商品属性值表
create table atm_item_sku_attribute_value ( id int auto_increment comment '主键' primary key, participant_id varchar(64) not null comment '标品id或商品id', participant_type int not null comment '参与类型(1-表示该属性是放到item上的,2-表示该属性是放到sku上的)', template_id int not null comment '模板id', group_id int null comment '属性组id', attribute_id int not null comment '属性id', attribute_value varchar(128) not null comment '属性值', sort int not null comment '排序序号', del_flag tinyint(1) default 0 not null comment '删除标记(1-有效,0-删除)', create_user int not null comment '创建⼈', create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', update_user int not null comment '更新⼈', update_time datetime default CURRENT_TIMESTAMP not null comment '修改时间' ) comment '标品商品属性值表';
例如item为111的标品,关联了品牌属性,值为华为。sku为222的商品关联了颜⾊属性(红色),版本属性(公开版)。sku为333的商品关联了颜⾊属性(黄色),版本属性(⽆线⽿机套装)。sku为444的商品关联了颜⾊属性(蓝色),版本属性(智能⼿表套装)。
3.商品属性库系统对品类添加属性
//属性管理相关操作API @DubboService(version = "1.0.0", interfaceClass = AttributeApi.class, retries = 0) public class AttributeApiImpl implements AttributeApi { @Autowired private AttributeService attributeService; //添加属性接口 @Override public JsonResult<AttributeInfoResultDTO> saveAttribute(AttributeInfoRequest request) { AttributeInfoResultDTO attributeResultV2DTO = attributeService.saveAttributeInfo(request); return JsonResult.buildSuccess(attributeResultV2DTO); } ... } //新增/编辑属性请求入参 @Data public class AttributeInfoRequest implements Serializable { //品类id private Long categoryId; //属性名称 private String attributeName; //属性编码 private String attributeCode; //属性类型 private String attributeType; //属性值的输入方式 private Integer attributeInputType; //属性说明 private String attributeComment; //操作人 @NotNull(message = "操作人[operateUser]不能为空") private Integer operateUser; } //属性管理相关操作API @Service public class AttributeServiceImpl implements AttributeService { @Autowired private AttributeRepository attributeRepository; //添加属性 @Override public AttributeInfoResultDTO saveAttributeInfo(AttributeInfoRequest request) { //入参检查 checkAttributeInfoRequest(request); //保存属性 Long attributeId = attributeRepository.saveAttributeInfo(request); return new AttributeInfoResultDTO(attributeId, true); } ... } @Repository public class AttributeRepository { @Resource private AttributeInfoMapper attributeInfoMapper; ... //保存属性信息 public Long saveAttributeInfo(AttributeInfoRequest request) { AttributeInfoDO attributeInfoDO = attributeConverter.convertAttributeInfoDO(request); attributeInfoDO.initCommon(); attributeInfoMapper.insert(attributeInfoDO); return attributeInfoDO.getId(); } ... }
4.商品属性库系统添加属性值与属性组
(1)添加可选的属性值
(2)添加属性组
(1)添加可选的属性值
//属性管理相关操作API @DubboService(version = "1.0.0", interfaceClass = AttributeApi.class, retries = 0) public class AttributeApiImpl implements AttributeApi { ... @Autowired private AttributeService attributeService; //给属性添加可选的属性值接口 @Override public JsonResult<AttributeValueResultDTO> saveAttributeValue(List<AttributeValueRequest> list) { AttributeValueResultDTO attributeValueResultDTO = attributeService.saveAttributeValues(list); return JsonResult.buildSuccess(attributeValueResultDTO); } ... } //属性管理相关操作API @Service public class AttributeServiceImpl implements AttributeService { @Autowired private AttributeRepository attributeRepository; ... //批量给属性添加可选的属性值 @Override public AttributeValueResultDTO saveAttributeValues(List<AttributeValueRequest> list) { attributeRepository.saveAttributeValues(list); return new AttributeValueResultDTO(true); } ... } //属性库 @Repository public class AttributeRepository { @Resource private AttributeValueMapper attributeValueV2Mapper; ... //批量保存属性值 public void saveAttributeValues(List<AttributeValueRequest> list) { //先删 LambdaQueryWrapper<AttributeValueDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AttributeValueDO::getAttributeId, list.get(0).getAttributeId()); attributeValueV2Mapper.delete(queryWrapper); //后保存 List<AttributeValueDO> attributeValueDOS = attributeConverter.convertAttributeValueV2DOList(list); attributeValueDOS.forEach(e -> { e.setUpdateUser(list.get(0).getOperateUser()); e.initCommon(); }); Integer count = attributeValueV2Mapper.insertBatch(attributeValueDOS); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
(2)添加属性组
//属性管理相关操作API @DubboService(version = "1.0.0", interfaceClass = AttributeApi.class, retries = 0) public class AttributeApiImpl implements AttributeApi { ... @Autowired private AttributeService attributeService; //添加属性组接口 @Override public JsonResult<AttributeGroupResultDTO> saveAttributeGroup(AttributeGroupRequest request) { AttributeGroupResultDTO attributeGroupResultDTO = attributeService.saveAttributeGroup(request); return JsonResult.buildSuccess(attributeGroupResultDTO); } ... } //属性管理相关操作API @Service public class AttributeServiceImpl implements AttributeService { @Autowired private AttributeRepository attributeRepository; ... //添加属性组接口 @Override public AttributeGroupResultDTO saveAttributeGroup(AttributeGroupRequest request) { //入参检查 checkAttributeGroupRequest(request); Long groupId = attributeRepository.saveAttributeGroup(request); return new AttributeGroupResultDTO(groupId, true); } ... } //属性库 @Repository public class AttributeRepository { @Resource private AttributeGroupMapper attributeGroupMapper; ... //保存属性组 public Long saveAttributeGroup(AttributeGroupRequest request) { AttributeGroupDO attributeGroupDO = attributeConverter.convertAttributeGroupDO(request); attributeGroupDO.initCommon(); attributeGroupMapper.insert(attributeGroupDO); return attributeGroupDO.getId(); } ... }
5.商品属性库系统添加属性模版及模版内容
一个品类会关联一个属性模版,一个属性模版里会有多个属性组或多个属性,一个属性组里会有多个属性,一个属性里会有多个属性值。
//属性管理相关操作API @DubboService(version = "1.0.0", interfaceClass = AttributeApi.class, retries = 0) public class AttributeApiImpl implements AttributeApi { ... @Autowired private AttributeService attributeService; //添加属性模板接口 @Override public JsonResult<AttributeTemplateResultDTO> saveAttributeTemplate(AttributeTemplateRequest request) { AttributeTemplateResultDTO attributeTemplateResultDTO = attributeService.saveAttributeTemplate(request); return JsonResult.buildSuccess(attributeTemplateResultDTO); } //给模板添加模板内容接口 @Override public JsonResult<AttributeTemplateContentResultDTO> saveAttributeTemplateContents(List<AttributeTemplateContentRequest> list) { AttributeTemplateContentResultDTO attributeTemplateContentResultDTO = attributeService.saveAttributeTemplateContents(list); return JsonResult.buildSuccess(attributeTemplateContentResultDTO); } ... } //属性管理相关操作API @Service public class AttributeServiceImpl implements AttributeService { @Autowired private AttributeRepository attributeRepository; ... //添加属性模板接口 @Override public AttributeTemplateResultDTO saveAttributeTemplate(AttributeTemplateRequest request) { //入参检查 checkAttributeTemplateRequest(request); Long templteId = attributeRepository.saveAttributeTemplate(request); return new AttributeTemplateResultDTO(templteId, true); } //添加属性模板内容接口 @Override public AttributeTemplateContentResultDTO saveAttributeTemplateContents(List<AttributeTemplateContentRequest> list) { //入参检查 checkAttributeTemplateContentRequest(list); attributeRepository.saveAttributeTemplateContents(list); return new AttributeTemplateContentResultDTO(true); } ... } //属性库 @Repository public class AttributeRepository { @Resource private AttributeTemplateMapper attributeTemplateMapper; @Resource private AttributeTemplateContentMapper attributeTemplateContentMapper; ... //保存属性模板 public Long saveAttributeTemplate(AttributeTemplateRequest request) { AttributeTemplateDO attributeTemplateDO = attributeConverter.convertAttributeTemplateDO(request); attributeTemplateDO.initCommon(); attributeTemplateMapper.insert(attributeTemplateDO); return attributeTemplateDO.getId(); } //保存属性模板内容 public void saveAttributeTemplateContents(List<AttributeTemplateContentRequest> list) { //先删 LambdaQueryWrapper<AttributeTemplateContentDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AttributeTemplateContentDO::getTemplateId, list.get(0).getTemplateId()); attributeTemplateContentMapper.delete(queryWrapper); //后保存 List<AttributeTemplateContentDO> attributeTemplateContentDOS = attributeConverter.convertAttributeTemplateContentDOList(list); attributeTemplateContentDOS.forEach(e -> { e.setUpdateUser(list.get(0).getOperateUser()); e.initCommon(); }); Integer count = attributeTemplateContentMapper.insertBatch(attributeTemplateContentDOS); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
6.查询属性库系统数据相关接口
//属性管理相关操作API @DubboService(version = "1.0.0", interfaceClass = AttributeApi.class, retries = 0) public class AttributeApiImpl implements AttributeApi { ... @Autowired private AttributeService attributeService; //属性分页查询接口 @Override public JsonResult<PageResult<AttributeInfoDTO>> pageAttributeInfo(QueryAttributeInfoRequest request) { PageResult<AttributeInfoDTO> pageResult = attributeService.pageAttributeInfo(request); return JsonResult.buildSuccess(pageResult); } //根据属性id查询属性及可选的属性值 @Override public JsonResult<List<AttributeValueDTO>> queryAttributeValueByAttributeId(Long attributeId) { List<AttributeValueDTO> attributeValueDTOS = attributeService.queryAttributeValueByAttributeId(attributeId); return JsonResult.buildSuccess(attributeValueDTOS); } //属性组分页查询接口 @Override public JsonResult<PageResult<AttributeGroupDTO>> pageAttributeGroup(QueryAttributeGroupRequest request) { PageResult<AttributeGroupDTO> pageResult = attributeService.pageAttributeGroup(request); return JsonResult.buildSuccess(pageResult); } //属性模板分页查询接口 @Override public JsonResult<PageResult<AttributeTemplateDTO>> pageAttributeTemplate(QueryAttributeTemplateRequest request) { PageResult<AttributeTemplateDTO> pageResult = attributeService.pageAttributeTemplate(request); return JsonResult.buildSuccess(pageResult); } //根据模板id查询模板内容接口 @Override public JsonResult<List<AttributeTemplateContentDTO>> queryAttributeTemplateContentByTemplateId(Long templateId) { List<AttributeTemplateContentDTO> attributeTemplateContentDTOS = attributeService.queryAttributeTemplateContentByTemplateId(templateId); return JsonResult.buildSuccess(attributeTemplateContentDTOS); } ... } //属性管理相关操作API @Service public class AttributeServiceImpl implements AttributeService { @Autowired private AttributeRepository attributeRepository; //分页查询属性 @Override public PageResult<AttributeInfoDTO> pageAttributeInfo(QueryAttributeInfoRequest request) { return attributeRepository.pageAttributeInfo(request); } //根据属性id查询属性及可选的属性值 @Override public List<AttributeValueDTO> queryAttributeValueByAttributeId(Long attributeId) { if (attributeId == null || attributeId < 0) { return null; } return attributeRepository.queryAttributeValueByAttributeId(attributeId); } //分页查询属性组 @Override public PageResult<AttributeGroupDTO> pageAttributeGroup(QueryAttributeGroupRequest request) { return attributeRepository.pageAttributeGroup(request); } //分页查询属性模板 @Override public PageResult<AttributeTemplateDTO> pageAttributeTemplate(QueryAttributeTemplateRequest request) { return attributeRepository.pageAttributeTemplate(request); } //根据属性模板id查询属性模板的内容 @Override public List<AttributeTemplateContentDTO> queryAttributeTemplateContentByTemplateId(Long templateId) { if (templateId == null || templateId < 0) { return null; } return attributeRepository.queryAttributeTemplateContentByTemplateId(templateId); } ... } //属性库 @Repository public class AttributeRepository { @Resource private AttributeGroupMapper attributeGroupMapper; //分页查询属性 public PageResult<AttributeInfoDTO> pageAttributeInfo(QueryAttributeInfoRequest request) { LambdaQueryWrapper<AttributeInfoDO> queryWrapper = Wrappers.lambdaQuery(); if (request.getCategoryId() != null && request.getCategoryId() > 0) { queryWrapper.eq(AttributeInfoDO::getCategoryId, request.getCategoryId()); } if (StringUtils.isNotBlank(request.getAttributeName())) { queryWrapper.like(AttributeInfoDO::getAttributeName, request.getAttributeName()); } if (StringUtils.isNotBlank(request.getAttributeCode())) { queryWrapper.like(AttributeInfoDO::getAttributeCode, request.getAttributeCode()); } if (request.getAttributeType() != null) { queryWrapper.eq(AttributeInfoDO::getAttributeType, request.getAttributeType()); } if (request.getAttributeInputType() != null) { queryWrapper.eq(AttributeInfoDO::getAttributeInputType, request.getAttributeInputType()); } Page<AttributeInfoDO> page = new Page<>(request.getPageNum(), request.getPageSize()); Page<AttributeInfoDO> pageResult = attributeInfoMapper.selectPage(page, queryWrapper); return attributeConverter.convertAttributeInfoDTOPageResult(pageResult); } //根据属性id查询属性值 public List<AttributeValueDTO> queryAttributeValueByAttributeId(Long attributeId) { LambdaQueryWrapper<AttributeValueDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AttributeValueDO::getAttributeId, attributeId); List<AttributeValueDO> attributeValueDOS = attributeValueV2Mapper.selectList(queryWrapper); return attributeConverter.convertAttributeValueV2DTOList(attributeValueDOS); } //分页查询属性组 public PageResult<AttributeGroupDTO> pageAttributeGroup(QueryAttributeGroupRequest request) { LambdaQueryWrapper<AttributeGroupDO> queryWrapper = Wrappers.lambdaQuery(); if (request.getCategoryId() != null && request.getCategoryId() > 0) { queryWrapper.eq(AttributeGroupDO::getCategoryId, request.getCategoryId()); } if (StringUtils.isNotBlank(request.getGroupName())) { queryWrapper.like(AttributeGroupDO::getGroupName, request.getGroupName()); } Page<AttributeGroupDO> page = new Page<>(request.getPageNum(), request.getPageSize()); Page<AttributeGroupDO> pageResult = attributeGroupMapper.selectPage(page, queryWrapper); return attributeConverter.convertAttributeGroupDTOPageResult(pageResult); } //分页查询属性模板 public PageResult<AttributeTemplateDTO> pageAttributeTemplate(QueryAttributeTemplateRequest request) { LambdaQueryWrapper<AttributeTemplateDO> queryWrapper = Wrappers.lambdaQuery(); if (request.getCategoryId() != null && request.getCategoryId() > 0) { queryWrapper.eq(AttributeTemplateDO::getCategoryId, request.getCategoryId()); } if (StringUtils.isNotBlank(request.getTemplateName())) { queryWrapper.like(AttributeTemplateDO::getTemplateName, request.getTemplateName()); } Page<AttributeTemplateDO> page = new Page<>(request.getPageNum(), request.getPageSize()); Page<AttributeTemplateDO> pageResult = attributeTemplateMapper.selectPage(page, queryWrapper); return attributeConverter.convertAttributeTemplateDTOPageResult(pageResult); } //根据属性模板id查询属性模板内容 public List<AttributeTemplateContentDTO> queryAttributeTemplateContentByTemplateId(Long templateId) { LambdaQueryWrapper<AttributeTemplateContentDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AttributeTemplateContentDO::getTemplateId, templateId); List<AttributeTemplateContentDO> attributeTemplateContentDOS = attributeTemplateContentMapper.selectList(queryWrapper); return attributeConverter.convertAttributeTemplateContentDTOList(attributeTemplateContentDOS); } ... }
7.商品属性库系统与商品M端系统对接
//商品属性相关操作API @DubboService(version = "1.0.0", interfaceClass = ItemSkuAttributeApi.class, retries = 0) public class ItemSkuAttributeApiImpl implements ItemSkuAttributeApi { @Autowired private ItemSkuAttributeService itemSkuAttributeService; //保存标品和商品属性接口 @Override public JsonResult<ItemSkuAttributeValueResultDTO> saveItemSkuAttributeValue(List<ItemSkuAttributeValueRequest> list) { ItemSkuAttributeValueResultDTO itemSkuAttributeValueResultDTO = itemSkuAttributeService.saveItemSkuAttributeValue(list); return JsonResult.buildSuccess(itemSkuAttributeValueResultDTO); } //根据商品id品类id查询聚合商品属性值接口 @Override public JsonResult<List<SkuAttributeValueDTO>> queryItemSkuAttributeValueList(QueryItemSkuAttributeValueRequest request) { List<SkuAttributeValueDTO> skuAttributeValueDTOS = itemSkuAttributeService.queryItemSkuAttributeValueList(request); return JsonResult.buildSuccess(skuAttributeValueDTOS); } //根据属性查询条件搜索商品接口 @Override public JsonResult<List<String>> querySkuIdsByAttribute(QuerySkuIdsByAttributeRequest request) { List<String> skuIds = itemSkuAttributeService.querySkuIdsByAttributes(request); return JsonResult.buildSuccess(skuIds); } } //商品属性相关操作API @Service public class ItemSkuAttributeServiceImpl implements ItemSkuAttributeService { @Autowired private AttributeRepository attributeRepository; //保存标品和商品属性 @Override public ItemSkuAttributeValueResultDTO saveItemSkuAttributeValue(List<ItemSkuAttributeValueRequest> list) { //入参检查 checkItemSkuAttributeValueRequest(list); attributeRepository.saveItemSkuAttributeValue(list); return new ItemSkuAttributeValueResultDTO(true); } //根据商品id类目id查询聚合商品属性值接口 @Override public List<SkuAttributeValueDTO> queryItemSkuAttributeValueList(QueryItemSkuAttributeValueRequest request) { //1.根据skuId和itemId查询出商品所有的属性 List<ItemSkuAttributeValueDTO> itemSkuAttributeValueDTOS = attributeRepository.queryItemSkuAttributeValueList(request); //2.找出使用这个sku使用的是哪个模板 Long templateId = itemSkuAttributeValueDTOS.get(0).getTemplateId(); //3.查询出模板内容 List<AttributeTemplateContentDTO> attributeTemplateContentDTOS = attributeRepository.queryAttributeTemplateContentByTemplateId(templateId); //4.查询出相关的属性组 List<Long> groupIds = attributeTemplateContentDTOS.stream() .map(AttributeTemplateContentDTO::getGroupId) .filter(Objects::nonNull) .collect(Collectors.toList()); List<AttributeGroupDTO> groupDTOS = attributeRepository.queryGroups(groupIds); Map<Long, String> groupIdAndNameMap = groupDTOS.stream().collect(Collectors.toMap(AttributeGroupDTO::getId, AttributeGroupDTO::getGroupName)); //5.查询出相关的属性 List<Long> attributeIds = attributeTemplateContentDTOS.stream() .map(AttributeTemplateContentDTO::getAttributeId) .collect(Collectors.toList()); List<AttributeInfoDTO> attributeInfoDTOS = attributeRepository.queryAttributeInfos(attributeIds); Map<Long, String> attributeIdAndNameMap = attributeInfoDTOS.stream().collect(Collectors.toMap(AttributeInfoDTO::getId, AttributeInfoDTO::getAttributeName)); //6.拼装结果 return buildSkuAttributeValueDTOS(itemSkuAttributeValueDTOS, groupIdAndNameMap, attributeIdAndNameMap); } //根据属性查询条件搜索商品接口 @Override public List<String> querySkuIdsByAttributes(QuerySkuIdsByAttributeRequest request) { return attributeRepository.querySkuIdsByAttribute(request); } ... } //属性库 @Repository public class AttributeRepository { ... //保存标品和商品属性 public void saveItemSkuAttributeValue(List<ItemSkuAttributeValueRequest> list) { //先删 LambdaQueryWrapper<ItemSkuAttributeValueDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(ItemSkuAttributeValueDO::getParticipantId, list.get(0).getParticipantId()); itemSkuAttributeValueMapper.delete(queryWrapper); //后保存 List<ItemSkuAttributeValueDO> itemSkuAttributeValueDOS = attributeConverter.convertItemSkuAttributeValueDOList(list); itemSkuAttributeValueDOS.forEach(e -> { e.setUpdateUser(list.get(0).getOperateUser()); e.initCommon(); }); Integer count = itemSkuAttributeValueMapper.insertBatch(itemSkuAttributeValueDOS); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } } //根据商品id类目id查询聚合商品属性值接口 public List<ItemSkuAttributeValueDTO> queryItemSkuAttributeValueList(QueryItemSkuAttributeValueRequest request) { LambdaQueryWrapper<ItemSkuAttributeValueDO> queryWrapper = Wrappers.lambdaQuery(); List<String> participantIds = new ArrayList<>(); if (StringUtils.isNotBlank(request.getItemId())) { participantIds.add(request.getItemId()); } if (StringUtils.isNotBlank(request.getSkuId())) { participantIds.add(request.getSkuId()); } queryWrapper.in(ItemSkuAttributeValueDO::getParticipantId, participantIds.stream().distinct().collect(Collectors.toList())); List<ItemSkuAttributeValueDO> itemSkuAttributeValueDOS = itemSkuAttributeValueMapper.selectList(queryWrapper); return attributeConverter.convertItemSkuAttributeValueDTO(itemSkuAttributeValueDOS); } //根据属性模板id查询属性模板内容 public List<AttributeTemplateContentDTO> queryAttributeTemplateContentByTemplateId(Long templateId) { LambdaQueryWrapper<AttributeTemplateContentDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(AttributeTemplateContentDO::getTemplateId, templateId); List<AttributeTemplateContentDO> attributeTemplateContentDOS = attributeTemplateContentMapper .selectList(queryWrapper); return attributeConverter.convertAttributeTemplateContentDTOList(attributeTemplateContentDOS); } //根据id批量查询属性组 public List<AttributeGroupDTO> queryGroups(List<Long> groupIds) { List<AttributeGroupDO> attributeGroupDOS = attributeGroupMapper.selectBatchIds(groupIds); return attributeConverter.convertAttributeGroupDTOList(attributeGroupDOS); } //根据id批量查询属性 public List<AttributeInfoDTO> queryAttributeInfos(List<Long> attributeIds) { List<AttributeInfoDO> attributeInfoDOS = attributeInfoMapper.selectBatchIds(attributeIds); return attributeConverter.convertAttributeInfoDTOList(attributeInfoDOS); } //根据属性查询条件搜索商品接口 public List<String> querySkuIdsByAttribute(QuerySkuIdsByAttributeRequest request) { LambdaQueryWrapper<ItemSkuAttributeValueDO> queryWrapper = Wrappers.lambdaQuery(); for (QuerySkuIdsByAttributeRequest.AttributeCriteria attributeCriteria : request.getAttributeCriterias()) { queryWrapper.or(wrapper -> wrapper .eq(ItemSkuAttributeValueDO::getAttributeId, attributeCriteria.getAttributeId()) .like(ItemSkuAttributeValueDO::getAttributeValue, attributeCriteria.getAttributeValue()) ); } List<ItemSkuAttributeValueDO> itemSkuAttributeValueDOS = itemSkuAttributeValueMapper.selectList(queryWrapper); return itemSkuAttributeValueDOS.stream().map(ItemSkuAttributeValueDO::getParticipantId).collect(Collectors.toList()); } ... }
8.商品状态变更流转架构设计
(1)状态流转策略表
(2)商品状态变更流向图
(3)接口实现流程图
(1)状态流转策略表
CREATE TABLE `product_status_strategy` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `start_status` int(10) NOT NULL DEFAULT '0' COMMENT '起始状态', `end_status` int(10) NOT NULL DEFAULT '0' COMMENT '截⽌状态', `strategy_code` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '策略映射码', `business_code` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '业务编码', `version_id` int(10) NOT NULL DEFAULT '0' COMMENT '版本号', `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记(1-有效,0-删除)', `create_user` int(10) NOT NULL DEFAULT '0' COMMENT '创建⼈', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_user` int(10) NOT NULL DEFAULT '0' COMMENT '更新⼈', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='状态流转策略表';
(2)商品状态变更流向图
说明:严格按照箭头方向执行,除了可重新上架,其他状态变更均不可逆。
(3)接口实现流程图
9.商品状态变更流转的策略模式实现
具体实现在商品M端系统:
一.验证⼊参必填(商品ID和变更状态)
二.验证商品是否存在,停售状态禁⽌变更状态
三.获取状态流转策略表,根据商品的当前状态和变更状态获取配置的映射策略类的实例名称
四.实现关于每个状态变更的具体逻辑
//商品状态变更请求入参 @Data public class ProductStatusRequest implements Serializable { //商品ID private String itemId; //商品变更状态(1-准备上架、2-试销上架、3-上架、4-预下架、5-下架、6-停售) private Integer itemStatus; } @Service public class ProductServiceImpl implements ProductService { ... //修改商品状态 @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.验证入参 checkUpdateProductStatusParam(productStatusRequest); //2.获取商品对应的信息 ItemInfoDO itemInfoDO = productInfoRepository.getItemByItemId(productStatusRequest.getItemId()); //停售状态不允许操作 if (itemInfoDO.getItemStatus().equals(ProductStatusEnum.STOP_SALE.getCode())) { throw new BaseBizException(ProductExceptionCode.PRODUCT_STATUS_ERROR); } //3.获取商品状态变更配置策略 String productStatusStrategy = productInfoRepository.getProductStatusStrategy(itemInfoDO.getItemStatus(), productStatusRequest.getItemStatus(), ProductConstants.PRODUCT_UPDATE_STATUS); //4.策略调用具体变更实现类 updateProductStatus(productStatusRequest, productStatusStrategy); } //通过策略模式更新商品状态 private void updateProductStatus(ProductStatusRequest productStatusRequest, String productStatusStrategy) { AbstractStatusStrategy strategy = productStatusStrategyFactory.getStrategy(productStatusStrategy); if (null == strategy) { throw new BaseBizException(ProductExceptionCode.PRODUCT_STRATEGY_ERROR); } //通过策略模式,调用底层对应的状态变更方法 strategy.updateProductStatus(productStatusRequest); } ... } @Repository public class ProductInfoRepository { ... //根据itemId查询item信息 public ItemInfoDO getItemByItemId(String itemId) { ItemInfoDO itemInfoDO = itemInfoMapper.selectByItemId(itemId); if (Objects.isNull(itemInfoDO)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } return itemInfoDO; } //获取对应的修改状态流程策略映射码 public String getProductStatusStrategy(Integer startStatus, Integer endStatus, String businessCode) { LambdaQueryWrapper<ProductStatusStrategyDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(ProductStatusStrategyDO::getStartStatus, startStatus); queryWrapper.eq(ProductStatusStrategyDO::getEndStatus, endStatus); queryWrapper.eq(ProductStatusStrategyDO::getBusinessCode, businessCode); ProductStatusStrategyDO productStatusStrategy = productStatusStrategyMapper.selectOne(queryWrapper); if (Objects.isNull(productStatusStrategy)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_STRATEGY_NULL); } return productStatusStrategy.getStrategyCode(); } ... } //商品状态变更策略工厂 @Component public class ProductStatusStrategyFactory { //@Autowired会把所有继承了AbstractStatusStrategy抽象类的Bean实例,加载到productStatusMap中 @Autowired private Map<String, AbstractStatusStrategy> productStatusMap = new ConcurrentHashMap<>(16); //获取 商品状态变更 策略实例 public AbstractStatusStrategy getStrategy(String productStatusStrategy) { return productStatusMap.get(productStatusStrategy); } } //商品状态变更抽象策略 public abstract class AbstractStatusStrategy { //商品状态流转 public abstract void updateProductStatus(ProductStatusRequest productStatusRequest); }
10.商品状态变更流转的具体策略实现
(1)商品试销上架的实现
(2)商品上架与预下架状态流转实现
(3)商品下架和停售状态流转实现
(4)流转到上架的状态变更
(1)商品试销上架的实现
一.商品试销上架时对前端销售数据校验
//试销上架状态变更 @Service("trialSaleStatus") public class TrialSaleStatusService extends AbstractStatusStrategy { @Autowired private ProductConverter productConverter; @Autowired private ProductStatusRepository productStatusRepository; @Value("${trial.date-num}") private Integer dateNum; //准备上架状态 流转到试销上架 @Transactional(rollbackFor = BaseBizException.class) @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.验证商品和前台类目绑定关系,绑定关系有效 checkFrontCategoryList(productStatusRequest.getItemId()); //2.验证商品数据是否完整 ProductDetailDO productDetailDO = checkProductInfo(productStatusRequest.getItemId()); //3.填入补充试销商品的扩展字段(试销期截止时间) buildAttributeContent(productDetailDO); //4.变更商品信息 updateProduct(productDetailDO, productStatusRequest); //5.发送商品消息变更通知对象(已做MySQL + Canal监听) } //检查商品是否和前台类目存在绑定关系 private void checkFrontCategoryList(String itemId) { //获取类目绑定关系 List<FrontCategoryRelationDO> frontCategoryRelationList = productStatusRepository.queryFrontCategoryList(itemId); //过滤状态,只保留有效的 List<FrontCategoryRelationDO> categoryRelationDOList = frontCategoryRelationList.stream().filter(frontCategoryRelationDO -> { if (frontCategoryRelationDO.getDelFlag().equals(DelFlagEnum.EFFECTIVE.getCode())) { return true; } return false; }).collect(Collectors.toList()); //商品不存在有效的绑定关系,提示错误 if (CollectionUtils.isEmpty(categoryRelationDOList)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_CATEGORY_RELATION_NULL); } } //检查商品的数据完整性 private ProductDetailDO checkProductInfo(String itemId) { //查询商品的详情信息 ProductDetailDO productDetailDO = productStatusRepository.queryProductDetail(itemId); //验证商品完整性 productStatusRepository.checkProductComplete(productDetailDO); return productDetailDO; } ... } @Repository public class ProductStatusRepository { ... //查询商品的详情信息 public ProductDetailDO queryProductDetail(String itemId) { List<ProductDetailDO> productDetailDOList = itemInfoMapper.queryProductInfoList(itemId); if (CollectionUtils.isEmpty(productDetailDOList)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } return productDetailDOList.get(0); } //检查商品得完整性 public void checkProductComplete(ProductDetailDO productDetailDO) { //1.验证商品的存储信息是否完整 if (Objects.isNull(productDetailDO.getStoreConditionType()) || Objects.isNull(StoreLabelEnum.getByCode(productDetailDO.getStoreConditionType()))) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } //2.是否有推广信息 if (StringUtils.isBlank(productDetailDO.getRecommend())) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } //3.是否有销售信息 if (Objects.isNull(productDetailDO.getBasePrice()) || Objects.isNull(productDetailDO.getVipPrice())) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } //4.验证商品是否品控信息完整 List<QualityControlDO> qualityControlList = getQualityControlList(productDetailDO.getItemId()); if (CollectionUtils.isEmpty(qualityControlList)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } //5.是否有图文信息(必须包含1-主图,2-轮播图、3-详情图) List<ItemVideoImgDO> itemVideoImgList = getItemVideoImgList(productDetailDO.getItemId()); //先过滤掉不需要的图文信息 List<ItemVideoImgDO> videoImgDOList = itemVideoImgList.stream().filter(itemVideoImgDO -> { if (ProductConstants.PRODUCT_IMG_TYPE_LIST.contains(itemVideoImgDO.getContentType())) { return true; } return false; }).collect(Collectors.toList()); //空验证 if (CollectionUtils.isEmpty(itemVideoImgList)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } //获取图文集合类型 Set<Integer> contentTypeList = videoImgDOList.stream().map(ItemVideoImgDO::getContentType).collect(Collectors.toSet()); //过滤掉不符合的图片类型后,去重后,剩余的图片类型要和存储的一样多 if (contentTypeList.size() != ProductConstants.PRODUCT_IMG_TYPE_LIST.size()) { throw new BaseBizException(ProductExceptionCode.PRODUCT_DETAIL_NULL); } } ... }
二.商品试销上架时补充扩展属性和变更商品状态
//试销上架状态变更 @Service("trialSaleStatus") public class TrialSaleStatusService extends AbstractStatusStrategy { ... //填充 试销商品的扩展字段(试销期截止时间) private void buildAttributeContent(ProductDetailDO productDetailDO) { //1.只有普通商品或者组套商品才处理扩展信息的变化 if (checkItemType(productDetailDO)) { //获取试销期截止时间 String trialSaleTime = DateFormatUtil.getDate(dateNum); //先获取到试销商品的原有扩展字段 AttributeExtendDO attributeExtend = productStatusRepository.getAttributeExtend(productDetailDO.getItemId()); AttributeExtendBO attributeExtendBO = null; //没有已经存在的扩展信息 if (!Objects.isNull(attributeExtend) || StringUtils.isBlank(attributeExtend.getAttributeContent())) { attributeExtendBO = new AttributeExtendBO(); } else { attributeExtendBO = JsonUtil.json2Object(attributeExtend.getAttributeContent(), AttributeExtendBO.class); } attributeExtendBO.setTrialSaleTime(trialSaleTime); //字段存储起来 productDetailDO.setAttributeContent(JSONObject.toJSONString(attributeExtendBO)); } } //变更商品的状态 private void updateProduct(ProductDetailDO productDetailDO, ProductStatusRequest productStatusRequest) { ItemInfoDO itemInfoDO = productConverter.converterDO(productDetailDO); //更新商品状态 productStatusRepository.updateProductStatus(itemInfoDO, productStatusRequest.getItemStatus()); if (checkItemType(productDetailDO)) { //更新商品扩展信息 productStatusRepository.updateAttributeExtend(productDetailDO); } } ... } @Repository public class ProductStatusRepository { ... //更新商品状态 public void updateProductStatus(ItemInfoDO itemInfoDO, Integer updateItemStatus) { LambdaUpdateWrapper<ItemInfoDO> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(ItemInfoDO::getItemId, itemInfoDO.getItemId()); updateWrapper.eq(ItemInfoDO::getItemStatus, itemInfoDO.getItemStatus()); updateWrapper.set(ItemInfoDO::getItemStatus, updateItemStatus); int count = itemInfoMapper.update(updateWrapper.getEntity(), updateWrapper); if (count <= 0) { throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL); } } //更新商品的扩展信息 public void updateAttributeExtend(ProductDetailDO productDetailDO) { LambdaUpdateWrapper<AttributeExtendDO> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(AttributeExtendDO::getParticipateId, productDetailDO.getItemId()); updateWrapper.eq(AttributeExtendDO::getParticipateType, ProductTypeEnum.ITEM.getCode()); updateWrapper.set(AttributeExtendDO::getAttributeContent, productDetailDO.getAttributeContent()); int count = attributeExtendMapper.update(updateWrapper.getEntity(), updateWrapper); if (count <= 0) { throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL); } } ... }
(2)商品上架与预下架状态流转实现
//预下架状态变更 @Service("advanceShelvesStatus") public class AdvanceShelvesStatusService extends AbstractStatusStrategy { @Autowired private ProductStatusRepository productStatusRepository; @Autowired private ProductConverter productConverter; //上架状态变更到预下架 @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.验证商品和前台类目绑定关系,绑定关系有效 checkFrontCategoryList(productStatusRequest.getItemId()); //2.验证商品数据是否完整 ProductDetailDO productDetailDO = checkProductInfo(productStatusRequest.getItemId()); //3.变更商品信息 updateProduct(productDetailDO, productStatusRequest); } //查询商品是否和前台类目存在绑定关系 private void checkFrontCategoryList(String itemId) { //获取类目绑定关系 List<FrontCategoryRelationDO> frontCategoryRelationList = productStatusRepository.queryFrontCategoryList(itemId); //过滤状态,只保留有效的 List<FrontCategoryRelationDO> categoryRelationDOList = frontCategoryRelationList.stream().filter(frontCategoryRelationDO -> { if (frontCategoryRelationDO.getDelFlag().equals(DelFlagEnum.EFFECTIVE.getCode())) { return true; } return false; }).collect(Collectors.toList()); //商品不存在有效的绑定关系,提示错误 if (CollectionUtils.isEmpty(categoryRelationDOList)) { throw new BaseBizException(ProductExceptionCode.PRODUCT_CATEGORY_RELATION_NULL); } } //验证商品的数据完整性 private ProductDetailDO checkProductInfo(String itemId) { //查询商品的详情信息 ProductDetailDO productDetailDO = productStatusRepository.queryProductDetail(itemId); //验证商品完整性 productStatusRepository.checkProductComplete(productDetailDO); return productDetailDO; } //变更商品的状态 private void updateProduct(ProductDetailDO productDetailDO, ProductStatusRequest productStatusRequest) { ItemInfoDO itemInfoDO = productConverter.converterDO(productDetailDO); productStatusRepository.updateProductStatus(itemInfoDO, productStatusRequest.getItemStatus()); } ... }
(3)商品下架和停售状态流转实现
//下架状态变更 @Service("offShelvesSaleStatus") public class OffShelvesStatusService extends AbstractStatusStrategy { @Autowired private ProductStatusRepository productStatusRepository; @Autowired private ProductInfoRepository productInfoRepository; //上架状态 变更到下架状态 //预上架状态 变更到下架状态 @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.查询商品信息 ItemInfoDO itemInfoDO = productInfoRepository.getItemByItemId(productStatusRequest.getItemId()); //2.商品状态变更 productStatusRepository.updateProductStatus(itemInfoDO, productStatusRequest.getItemStatus()); } } //停售状态 @Service("stopStatus") public class StopStatusService extends AbstractStatusStrategy { @Autowired private ProductStatusRepository productStatusRepository; @Autowired private ProductInfoRepository productInfoRepository; //下架状态 变更到停售 @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.查询商品信息 ItemInfoDO itemInfoDO = productInfoRepository.getItemByItemId(productStatusRequest.getItemId()); //2.商品状态变更 productStatusRepository.updateProductStatus(itemInfoDO, productStatusRequest.getItemStatus()); } }
(4)流转到上架的状态变更
//上架状态变更 @Service("shelvesSaleStatus") public class ShelvesStatusService extends AbstractStatusStrategy { @Autowired private ProductConverter productConverter; @Autowired private ProductStatusRepository productStatusRepository; //准备上架状态 流转到上架 //试销上架状态 流转到上架 @Override public void updateProductStatus(ProductStatusRequest productStatusRequest) { //1.验证商品和前台类目绑定关系,绑定关系有效 checkFrontCategoryList(productStatusRequest.getItemId()); //2.验证商品数据是否完整 ProductDetailDO productDetailDO = checkProductInfo(productStatusRequest.getItemId()); //3.变更商品信息 updateProduct(productDetailDO, productStatusRequest); } ... }
详细介绍后端技术栈的基础内容,包括但不限于:MySQL原理和优化、Redis原理和应用、JVM和G1原理和优化、RocketMQ原理应用及源码、Kafka原理应用及源码、ElasticSearch原理应用及源码、JUC源码、Netty源码、zk源码、Dubbo源码、Spring源码、Spring Boot源码、SCA源码、分布式锁源码、分布式事务、分库分表和TiDB、大型商品系统、大型订单系统等