前言
本系列博客基于B站谷粒商城,只作为本人学习总结使用。这里我会比较注重业务逻辑的编写和相关配置的流程。有问题可以评论或者联系我互相交流。原视频地址谷粒商城雷丰阳版。本人git仓库地址Draknessssw的谷粒商城
相关概念
PO 数据库表记录,不包含任何数据库操作。
DO 业务实体
TO 不同程序之间传输数据对象
DTO view层和Service层的数据传输对象
DAO 数据库访问对象
VO 业务层数据传输对象,可以和数据库表不一致,由new创建,由GC回收。接收业务请求,然后响应(一般是自定义信息)业务需要的消息。可以用来代替数据校验注解。
BO 结合PO和VO进行业务操作
保存商品的规格参数
这里要对商品属性表进行操作,但是商品属性表没有商品分组属性,而前端请求头确实是有这个信息的
package com.xxxx.gulimall.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 商品属性
*
* @author draknessssw
* @email 2571562818@qq.com
* @date 2022-07-02 17:11:25
*/
@Data
@TableName("pms_attr")
public class AttrEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 属性id
*/
@TableId
private Long attrId;
/**
* 属性名
*/
private String attrName;
/**
* 是否需要检索[0-不需要,1-需要]
*/
private Integer searchType;
/**
* 值类型[0-为单个值,1-可以选择多个值]
*/
private Integer valueType;
/**
* 属性图标
*/
private String icon;
/**
* 可选值列表[用逗号分隔]
*/
private String valueSelect;
/**
* 属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]
*/
private Integer attrType;
/**
* 启用状态[0 - 禁用,1 - 启用]
*/
private Long enable;
/**
* 所属分类
*/
private Long catelogId;
/**
* 快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整
*/
private Integer showDesc;
}
写一个AttrVo
加入商品分组字段,去掉数据库操作特有的注解如@TableId
package com.xxxx.gulimall.product.vo;
import lombok.Data;
@Data
public class AttrVo {
/**
* 属性id
*/
private Long attrId;
/**
* 属性名
*/
private String attrName;
/**
* 是否需要检索[0-不需要,1-需要]
*/
private Integer searchType;
/**
* 属性图标
*/
private String icon;
/**
* 可选值列表[用逗号分隔]
*/
private String valueSelect;
/**
* 属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]
*/
private Integer attrType;
/**
* 启用状态[0 - 禁用,1 - 启用]
*/
private Long enable;
/**
* 所属分类
*/
private Long catelogId;
/**
* 快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整
*/
private Integer showDesc;
private Long attrGroupId;
}
业务逻辑
实现类
通过BeanUtils.copyProperties(attr,attrEntity)方法将AttrVo的数据拷贝到商品属性实体表里面(Vo到Po),此时完成第一步,保存两者对应的基本数据。
AttrEntity attrEntity = new AttrEntity();
BeanUtils.copyProperties(attr,attrEntity);
//1、保存基本数据
this.save(attrEntity);
新建一个公共枚举类作用于销售属性和规格属性的标识取值
package com.xxxx.common.constant;
public class ProductConstant {
public enum AttrEnum {
ATTR_TYPE_BASE(1,"基本属性"),
ATTR_TYPE_SALE(0,"销售属性");
private int code;
private String msg;
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
AttrEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
}
如果这时候传入的表单信息中关联关系是基本属性且不为空,传入属性&属性分组关联实体类(由它来保存属性的关联关系),设置这个实体类的属性分组id和组内排序。即提交的表单中的分组关系id和实体类的商品id。保存到数据库即可。
最终效果如下
@Autowired
AttrAttrgroupRelationDao relationDao;
@Transactional
@Override
public void saveAttr(AttrVo attr) {
AttrEntity attrEntity = new AttrEntity();
BeanUtils.copyProperties(attr,attrEntity);
//1、保存基本数据
this.save(attrEntity);
//2、保存关联关系
//判断类型,如果是基本属性就设置分组id
if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attr.getAttrGroupId() != null) {
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
relationEntity.setAttrGroupId(attr.getAttrGroupId());
relationEntity.setAttrId(attrEntity.getAttrId());
relationDao.insert(relationEntity);
}
}
分页查询规格参数信息
@GetMapping("/{attrType}/list/{catelogId}")
public R baseAttrList(@RequestParam Map<String, Object> params,
@PathVariable("catelogId") Long catelogId,
@PathVariable("attrType")String type){
PageUtils page = attrService.queryBaseAttrPage(params,catelogId,type);
return R.ok().put("page", page);
}
实现类
首先匹配条件先匹配分类id, 。
如果当前商品分类id不等于0,匹配条件添加分类id。
如果输入的字段非空,去模糊匹配商品id和商品名。
然后通过Ipage插件返回分页匹配数据。
QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>().eq("attr_type","base".equalsIgnoreCase(type)?ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode():ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());
if (catelogId != 0) {
queryWrapper.eq("catelog_id",catelogId);
}
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
queryWrapper.and((wrapper) ->{
wrapper.eq("attr_id",key).or().like("attr_name",key);
});
}
IPage<AttrEntity> page = this.page(
new Query<AttrEntity>().getPage(params),
queryWrapper
);
此时虽然已经用分页接口接收数据,但是仍然需要继续封装。
PageUtils pageUtils = new PageUtils(page);
新建商品响应数据
继承AttVo之后,只需要写自己额外的处理字段即可(分类名,组名,分类路径)
package com.xxxx.gulimall.product.vo;
import lombok.Data;
@Data
public class AttrRespVo extends AttrVo {
/**
*
*/
private String catelogName;
private String groupName;
private Long[] catelogPath;
}
这里先用page.getRecords()将分页数据记录下来。
List<AttrEntity> records = page.getRecords();
使用streamAPI进行重新封装,最后收集成list
List<AttrRespVo> respVos = records.stream().map((attrEntity) -> {
……
……
}).collect(Collectors.toList());
接着将商品基本属性拷贝到attrRespVo中
AttrRespVo attrRespVo = new AttrRespVo();
BeanUtils.copyProperties(attrEntity, attrRespVo);
设置需要返回的分组的名字
如果……,通过商品表属性id来匹配这个商品所在的商品&商品属性表中id一样的记录。当商品&商品属性表中商品属性id和商品属性所在组的id均不为空时,通过商品&商品属性表的商品属性id查询这个商品属性id对应的组id,继而去设置分组的名字。
简而言之,这是两个表被中间表对应的过程。
if ("base".equalsIgnoreCase(type)) {
AttrAttrgroupRelationEntity attrId =
relationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attrEntity.getAttrId()));
if (attrId != null && attrId.getAttrGroupId() != null) {
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrId.getAttrGroupId());
attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
而分类名可以直接通过商品属性的分类id对应设置。
CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
if (categoryEntity != null) {
attrRespVo.setCatelogName(categoryEntity.getName());
}
最后返回封装好的数据
pageUtils.setList(respVos);
return pageUtils;
最终如下
@Autowired
AttrAttrgroupRelationDao relationDao;
@Autowired
AttrGroupDao attrGroupDao;
@Autowired
CategoryDao categoryDao;
@Override
public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId, String type) {
QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>().eq("attr_type","base".equalsIgnoreCase(type)?ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode():ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());
if (catelogId != 0) {
queryWrapper.eq("catelog_id",catelogId);
}
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
queryWrapper.and((wrapper) ->{
wrapper.eq("attr_id",key).or().like("attr_name",key);
});
}
IPage<AttrEntity> page = this.page(
new Query<AttrEntity>().getPage(params),
queryWrapper
);
PageUtils pageUtils = new PageUtils(page);
List<AttrEntity> records = page.getRecords();
List<AttrRespVo> respVos = records.stream().map((attrEntity) -> {
AttrRespVo attrRespVo = new AttrRespVo();
BeanUtils.copyProperties(attrEntity, attrRespVo);
//设置分类和分组的名字
if ("base".equalsIgnoreCase(type)) {
AttrAttrgroupRelationEntity attrId =
relationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attrEntity.getAttrId()));
if (attrId != null && attrId.getAttrGroupId() != null) {
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrId.getAttrGroupId());
attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
if (categoryEntity != null) {
attrRespVo.setCatelogName(categoryEntity.getName());
}
return attrRespVo;
}).collect(Collectors.toList());
pageUtils.setList(respVos);
return pageUtils;
}
获取单个具体的规格信息
/**
* 信息
*/
@RequestMapping("/info/{attrId}")
public R info(@PathVariable("attrId") Long attrId){
AttrRespVo respVo = attrService.getAttrInfo(attrId);
return R.ok().put("attr", respVo);
}
实现类
根据传入商品属性id查询其详细信息
AttrEntity attrEntity = this.getById(attrId);
把查询到的规格属性信息拷到respVo中
AttrRespVo respVo = new AttrRespVo();
BeanUtils.copyProperties(attrEntity,respVo);
判断是否是基本数据类型。
通过请求参数查询属性&属性分组表的记录,要是查到了记录,就往这个返回数据里设置分组信息,然后通过查到的记录的组id来查询属性分组实体类的分组名称,若是成功获取到了这个名称,返回给respVo
if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {
//1、设置分组信息
AttrAttrgroupRelationEntity attrgroupRelationEntity = relationDao.selectOne
(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
if (attrgroupRelationEntity != null) {
respVo.setAttrGroupId(attrgroupRelationEntity.getAttrGroupId());
//获取分组名称
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupRelationEntity.getAttrGroupId());
if (attrGroupEntity != null) {
respVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
}
接着设置三级分类路径和分类名称
//2、设置分类信息
Long catelogId = attrEntity.getCatelogId();
Long[] catelogPath = categoryService.findCatelogPath(catelogId);
respVo.setCatelogPath(catelogPath);
CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
if (categoryEntity != null) {
respVo.setCatelogName(categoryEntity.getName());
}
查询路径的方法
/**
* 根据catelogId查询所有父分类ID
*/
@Override
public Long[] findCatelogPath(Long catelogId) {
List<Long> paths = new ArrayList<>();
// 递归查询父类
paths = findParentPath(catelogId, paths);
// 逆序,父在前
Collections.reverse(paths);
return paths.toArray(new Long[paths.size()]);
}
最终效果如下
@Override
public AttrRespVo getAttrInfo(Long attrId) {
//查询详细信息
AttrEntity attrEntity = this.getById(attrId);
//查询分组信息
AttrRespVo respVo = new AttrRespVo();
BeanUtils.copyProperties(attrEntity,respVo);
//判断是否是基本类型
if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {
//1、设置分组信息
AttrAttrgroupRelationEntity attrgroupRelationEntity = relationDao.selectOne
(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
if (attrgroupRelationEntity != null) {
respVo.setAttrGroupId(attrgroupRelationEntity.getAttrGroupId());
//获取分组名称
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupRelationEntity.getAttrGroupId());
if (attrGroupEntity != null) {
respVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
}
//2、设置分类信息
Long catelogId = attrEntity.getCatelogId();
Long[] catelogPath = categoryService.findCatelogPath(catelogId);
respVo.setCatelogPath(catelogPath);
CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
if (categoryEntity != null) {
respVo.setCatelogName(categoryEntity.getName());
}
return respVo;
}
规格修改
实现类
还是差不多的操作……更新商品属性,更新属性&属性关联表的组id和属性id,统计需要更新的记录的数量,大于零就更新,如果没有记录就新增记录。
最终效果如下
@Transactional
@Override
public void updateAttr(AttrVo attr) {
AttrEntity attrEntity = new AttrEntity();
BeanUtils.copyProperties(attr,attrEntity);
this.updateById(attrEntity);
if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {
//1、修改分组关联
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
relationEntity.setAttrGroupId(attr.getAttrGroupId());
relationEntity.setAttrId(attr.getAttrId());
Integer count = relationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>()
.eq("attr_id", attr.getAttrId()));
if (count > 0) {
relationDao.update(relationEntity,
new UpdateWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attr.getAttrId()));
} else {
relationDao.insert(relationEntity);
}
}
}