当前位置: 首页 > news >正文

谷粒商城--SPU和SKU

目录

1.SPU和SKU概念

2.表的关系理解

3.导入前端代码

4.完善后端接口 

5.属性分组详情 

6.规格参数详情

7. 销售属性详情

8.分组与属性关联

9.发布商品

10.仓库服务


1.SPU和SKU概念

SPU:standard product unit(标准化产品单元):是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。如iphone13是SPU,它是一个产品的集合

SKU:stock keeping unit(库存量单位):库存进出计量的基本单元,可以是件/盒/托盘等单位。

SKU是对于大型连锁超市DC配送中心物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品对应有唯一的SKU号。如iphone13ProMax 1T 蓝色 是SKU,

包子店中肉包子是SKU,素包子是SKU,水煎包是SKU…

 规格参数和销售属性

像这里的商品介绍,规格与包装都是属于SPU的属性。它们都属于是规格参数

image-20220806105620408

像版本,颜色等都属是SKU的销售属性

image-20220806105802342 


2.表的关系理解

属性关系-规格参数-销售属性-三级分类 关联关系 

每个三级分类下有各自的属性分组表通过id和catelogid关联,能查出每个分类下的属性分组

属性分组表和属性表通过一个属性&属性关联表进行关联,能查出每个属性分组下的属性

最终这样的关系我们可以查出每个分类的属性分组和每个属性分组对应的属性

image-20220806110205216 

通过思维导图来理解

手机是一级分类,它下面又有属性组,每个属性组又有各自的属性 

image-20220807230244792 

SPU-SKU属性表

商品属性表和属性表通过attridid进行关联,能查出每个spu的属性

sku销售属性表是为了表示spu下不同sku,比如1号spu在此表有两个sku,这两个sku有不同的销售属性,是通过和属性表关联获取

 

image-20220806110506836 

通过思维导图来理解

像网络、像素一般是固定不可选的所以是SPU属性

而内存、容量、颜色等可选的就为SKU销售属性

image-20220807231607457 


3.导入前端代码

重新执行“sys_menus.sql”,完善菜单

正常我们是在系统管理里自定义添加,步骤都是一样的,其实在前端页面添加就是把数据提交到mall_admin表中,这里我们直接把提供的sql语句导入即可!

如下结果:

image-20220806100125647 

实现点击菜单的左边,能够实现在右边展示数据

image-20220806112654338 

这个页面就是三级分类和一个表格显示在一块对吧,属于是父子组件交互

前端不具体写了,我们直接导入代码,效果如下:


4.完善后端接口 

什么是开发接口

开发接口就是开发Controller、service、dao

在线接口文档如下

03、获取分类属性分组 - 谷粒商城谷粒商城 - 03、获取分类属性分组,http GET /product/attrgroup/list/{catelogId},,技术团队的文档管理平台,接口文档工具,支持在线接口调试,一键生成API文档,适合编写接口文档、产品文档、使用手册icon-default.png?t=N176https://easydoc.net/s/78237135/ZUqEdvA4/OXTgKobR
别人告诉你需要什么功能,需要返回什么样的数据,你就通过接口的形式把他们呢实现出来即可!

以后工作了也是这种形式,主要是开发接口为多,前端其实不用写太多,能看懂即可


5.属性分组详情 

显示属性分组image-20220810220705409

controller

    @RequestMapping("/list/{catelogId}")public R list(@RequestParam Map<String, Object> params,@PathVariable("catelogId") Long catelogId){
//        PageUtils page = attrGroupService.queryPage(params);PageUtils page = attrGroupService.queryPage(params, catelogId);return R.ok().put("page", page);}

 service

这里注意,前端有两个查询按钮

查询和查询全部

这两个都要有模糊查询的功能!

PageUtils queryPage(Map<String, Object> params, Long catelogId);@Override
public PageUtils queryPage(Map<String, Object> params, Long catelogId) {//多条件查询String key = (String) params.get("key");QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<>();if (!StringUtils.isEmpty(key)) {wrapper.and((obj) -> {obj.eq("attr_group_id",key).or().like("attr_group_name",key);});}if (catelogId == 0) {//如果是默认的是查全部的一级分类IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),wrapper);return new PageUtils(page);} else {wrapper.eq("catelog_id", catelogId);IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params), wrapper);return new PageUtils(page);}
}

 image-20220811091620116

属性分组回显 

这一部分主要是做属性分组的数据回显的

image-20220810220631503 

controller

/*** 信息*/
@RequestMapping("/info/{attrId}")
public R info(@PathVariable("attrId") Long attrId){AttrEntity attr = attrService.getById(attrId);Long catelogId = attr.getCatelogId();Long[] path = categoryService.findCatelogPath(catelogId);attr.setCatelogPath(path);return R.ok().put("attr", attr);
}

 service

获取分类路径id

通过递归操作完成

过程

给一个分类id,不断的查它的父类id直到查不到为止,最后把查询到的id到放到一个集合里

怎样写好递归?

  1. 确定参数值和返回值
  2. 确定终止条件
  3. 递归逻辑

三者缺一不可!!!

//找到catelogId的完整路径:[父/子/孙]
@Override
public Long[] findCatelogPath(Long catelogId) {ArrayList<Long> list = new ArrayList<>();List<Long> parentPath = findParentPath(catelogId, list);//1.确定递归参数和返回值Collections.reverse(parentPath);return (Long[]) list.toArray(new Long[parentPath.size()]);
}private List<Long> findParentPath(Long catelogId,ArrayList<Long> list){//3.递归逻辑list.add(catelogId);CategoryEntity entity = this.getById(catelogId);if (entity.getParentCid()!=0){//2.递归终止条件findParentPath(entity.getParentCid(),list);}return list;
}

 测试

返回属性的父路径id

image-20220811092606249 


6.规格参数详情

接口如下

image-20220810220725125 

什么是规格参数

image-20220808231624265 

保存规格参数 

/*** 保存*/
@RequestMapping("/save")
public R save(@RequestBody AttrVo vo){attrService.saveAttr(vo);return R.ok();
}

service

这里注意,因为添加规格参数的时候会有选择属性组,因为属性组和属性是通过关联关系表连接的所以要有级联操作。

在往pms_attr表插入数据的时候,pms_attr_group_relation也要插入

小bug:这里有个注意点,当添加规格参数的时候如果没有指定规格参数所属分组,那么就不应该在关联表中保存关联关系!!!

@Override
public void saveAttr(AttrVo attr) {AttrEntity attrEntity = new AttrEntity();//1.将前端接收数据的对象vo赋值给attrEntity对象,从而更新数据库BeanUtils.copyProperties(attr, attrEntity);this.save(attrEntity);if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attr.getAttrGroupId() != null) {//2.保存关联关系//因为属性组和属性是通过关联关系表连接的AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();relationEntity.setAttrGroupId(attr.getAttrGroupId());relationEntity.setAttrId(attrEntity.getAttrId());relationService.save(relationEntity);}}

 显示规格参数

/*** 显示规格参数*/
@GetMapping("/base/list/{catelogId}")
public R baseAttrList(@RequestParam Map<String, Object> params,@PathVariable("catelogId") Integer catelogId) {PageUtils page = attrService.queryBaseAttrPage(params, catelogId);return R.ok().put("page", page);
}

service

//分页查询规格参数
@Override
public PageUtils queryBaseAttrPage(Map<String, Object> params, Integer catelogId) {QueryWrapper<AttrEntity> wrapper = new QueryWrapper<>();if (catelogId != 0) {//如果不是一级分类,那么查询的时候加上where catelog_id = ?wrapper.eq("catelog_id", catelogId);}//多条件模糊查询//搜索框里的key不但可以对catelog_id进行模糊查询,对attr_name也模糊查询String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {wrapper.eq("attr_id", key).or().like("attr_name", key);}//多条件分页查询IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params),wrapper);PageUtils pageUtils = new PageUtils(page);return pageUtils;
}

测试

image-20220810205809739

这些属性的分类和所属分组怎么查呢

规格参数表(pms_attr)中,有所属分类的信息,可以直接调用分类的service进行查询

那分组信息怎么查询呢?规格参数表中没有所属分类相关的信息…

这里我们就要借助第三张表,属性和分组表(pms_attr_attrgroup_relation)进行查询

通过规格参数表(pms_attr)获得attr_id,之后在调用属性和分组表的service获得属性和分组表的实体类,从而获得该属性的分组

下面通过stream流的方式,通过map给list集合中的每一项做映射给新实体类(AttrRespVo)赋值,最后返回AttrRespVo

小bug:这里显示规格参数的时候,会显示规格。参数对应的分组、分类,那么如果它们查出对象分组id或分类id为空那就不设置名字if (attrId != null && attrId.getAttrGroupId() != null) {…}

 

//分页查询规格参数
@Override
public PageUtils queryBaseAttrPage(Map<String, Object> params, String type, Integer catelogId) {QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>().eq("attr_type", "base".equalsIgnoreCase(type) ? ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());if (catelogId != 0) {//如果不是一级分类,那么查询的时候加上where catelog_id = ?//IgnoreCase忽略大小写wrapper.eq("catelog_id", catelogId);}//多条件模糊查询//搜索框里的key不但可以对catelog_id进行模糊查询,对attr_name也模糊查询String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {wrapper.eq("attr_id", key).or().like("attr_name", key);}//多条件分页查询IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params),wrapper);PageUtils pageUtils = new PageUtils(page);List<AttrEntity> list = page.getRecords();//        .map()这个方法是对被筛选过后的流进行映射,一般是对属性进行赋值。List<AttrRespVo> resultList = list.stream().map(item -> {AttrRespVo attrRespvo = new AttrRespVo();BeanUtils.copyProperties(item, attrRespvo);//设置分类和分组的名字if ("base".equalsIgnoreCase(type)) {AttrAttrgroupRelationEntity attrId = relationService.getOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", item.getAttrId()));if (attrId != null && attrId.getAttrGroupId() != null) {//attrgroupRelationEntity.getAttrGroupId()也可以,这里可以直接放进去对象AttrGroupEntity attrGroupEntity = attrGroupService.getById(attrId.getAttrGroupId());attrRespvo.setGroupName(attrGroupEntity.getAttrGroupName());}}CategoryEntity categoryEntity = categoryService.getById(item.getCatelogId());if (categoryEntity != null) {attrRespvo.setCatelogName(categoryEntity.getName());}//返回最后的封装结果return attrRespvo;}).collect(Collectors.toList());//返回的结果是一个集合pageUtils.setList(resultList);//        返回分页后的集合对象return pageUtils;
}

AttrRespVo

@Data
public class AttrRespVo extends AttrVo {private String catelogName;private String  groupName;
}

测试

image-20220811084206902

规格参数回显 

可以看出所属分类和分组都是由这条请求查询的,那么我们改这个接口功能就行

相当于在原来查询基础上返回分类路径信息分组信息

image-20220810221107404 

controller

/*** 信息*/
@RequestMapping("/info/{attrId}")
public R info(@PathVariable("attrId") Long attrId) {AttrRespVo respVo = attrService.getAttrInfo(attrId);return R.ok().put("attr", respVo);
}

 service

@Override
public AttrRespVo getAttrInfo(Long attrId) {AttrRespVo respVo = new AttrRespVo();AttrEntity attrEntity = this.getById(attrId);BeanUtils.copyProperties(attrEntity, respVo);/*** 设置分组信息*/AttrAttrgroupRelationEntity attrgroupRelationEntity = relationService.getOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));if (attrgroupRelationEntity != null){respVo.setAttrGroupId(attrgroupRelationEntity.getAttrGroupId());Long attrGroupId = attrgroupRelationEntity.getAttrGroupId();AttrGroupEntity attrGroupEntity = attrGroupService.getById(attrGroupId);if (attrGroupEntity != null) {respVo.setGroupName(attrGroupEntity.getAttrGroupName());}}/*** 设置分类信息*/Long catelogId = attrEntity.getCatelogId();//有了分类的完整路径,接下来就设置分类名字Long[] catelogPath = categoryService.findCatelogPath(catelogId);respVo.setCatelogPath(catelogPath);//获得分类名字CategoryEntity categoryEntity = categoryService.getById(catelogId);if (categoryEntity != null) {respVo.setCatelogName(categoryEntity.getName());}return respVo;
}

测试

image-20220810232827723

修改Or增加 

提交修改分类和分组是无效的?

更改用的还是默认的update方法,所以我们改update接口!

image-20220810232652674 

controller

/*** 修改*/
@RequestMapping("/update")
public R update(@RequestBody AttrVo attr) {attrService.updateAttr(attr);return R.ok();
}

 service

这里做了优化,对于规格参数中没有所属分组的,如果指定了不在是修改而是添加!

怎么判断规格参数有没有所属分组呢?

拿attr_id去pms_attr_attrgroup_relation表中查询,如果改attr_id存在与该表,那就修改关联关系

如果没有数据,那么就在此表添加数据!

@Transactional
@Override
public void updateAttr(AttrVo attr) {AttrEntity attrEntity = new AttrEntity();BeanUtils.copyProperties(attr, attrEntity);this.updateById(attrEntity);//修改分组关联AttrAttrgroupRelationEntity attrAttrgroupRelationEntity = new AttrAttrgroupRelationEntity();attrAttrgroupRelationEntity.setAttrGroupId(attr.getAttrGroupId());attrAttrgroupRelationEntity.setAttrId(attr.getAttrId());//统计attr_id的关联属性,如果没有初始分组,则进行添加操作;有则进行修改操作Integer count = relation.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attr.getAttrId()));if (count > 0) {relation.update(attrAttrgroupRelationEntity, new UpdateWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attr.getAttrId()));} else {relation.insert(attrAttrgroupRelationEntity);}
}

 spu规格维护

出现400页面,在数据库添加

INSERT INTO sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (76, 37, '规格维护', 'product/attrupdate', '', 2, 'log', 0);更新index.js,哪里更新?找老师的源码

 controller

@PostMapping("/update/{spuId}")
public R updateSpuAttr(@PathVariable("spuId") Long spuId,@RequestBody List<ProductAttrValueEntity> entities){productAttrValueService.updateSpuAttr(spuId,entities);return R.ok();
}

impl

这里的修改其实是先把原来的spu_id下的属性都删除掉

之后在把前端传来的属性集合进行批量保存

@Transactional(rollbackFor = Exception.class)
@Override
public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {//1、删除spuId之前对应的所有属性this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));//2、添加商品规格信息List<ProductAttrValueEntity> collect = entities.stream().map(item -> {item.setSpuId(spuId);return item;}).collect(Collectors.toList());//批量新增this.saveBatch(collect);
}

7. 销售属性详情

显示销售属性

如图http://localhost:88/api/product/attr/sale/list/0?t=1660181297434&page=1&limit=10&key=这个接口有问题!

所以我们就去后端改这个接口即可!

image-20220811092412567 

controller

规格参数和销售参数的区别在于type的值,type为 1是规格参数type为0是销售参数

这里采用一个方法当两个来用!

image-20220811094628873 

@GetMapping("/{attrType}/list/{catelogId}")
public R baseAttrList(@RequestParam Map<String, Object> params,@PathVariable("attrType") String type,@PathVariable("catelogId") Integer catelogId) {PageUtils page = attrService.queryBaseAttrPage(params, type, catelogId);return R.ok().put("page", page);
}

 service

在原来对规格参数的基础上加了限制条件,如果是规格参数那就是WHERE attr_type = 1,否则就是WHERE attr_type = 0;

下面的逻辑和查询规格参数一致,都要模糊查询

这里为了使代码更通用,1和0的值我们写一个常量来控制,如过后期换值了我们直接更改常量的值即可

ProductConstant

package com.xxh.common.constant;public class ProductConstant {public enum AttrEnum{ATTR_TYPE_BASE(1,"基本属性"),ATTR_TYPE_SALE(0,"销售属性");private int code;private String msg;AttrEnum(int code,String msg){this.code = code;this.msg = msg;}public int getCode(){return code;}public String getMsg(){return msg;}}
}

在原来对规格参数的基础上加了限制条件,如果是规格参数那就是WHERE attr_type = 1,否则就是WHERE attr_type = 0;

@Overridepublic PageUtils queryBaseAttrPage(Map<String, Object> params, String type, Integer catelogId) {QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>().eq("attr_type", "base".equalsIgnoreCase(type) ? ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());if (catelogId != 0) {//如果不是一级分类,那么查询的时候加上where catelog_id = ?//IgnoreCase忽略大小写wrapper.eq("catelog_id", catelogId);}//多条件模糊查询//搜索框里的key不但可以对catelog_id进行模糊查询,对attr_name也模糊查询String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {wrapper.eq("attr_id", key).or().like("attr_name", key);}//多条件分页查询IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params),wrapper);PageUtils pageUtils = new PageUtils(page);List<AttrEntity> list = page.getRecords();
//        .map()这个方法是对被筛选过后的流进行映射,一般是对属性进行赋值。List<AttrRespVo> resultList = list.stream().map(item -> {AttrRespVo attrRespvo = new AttrRespVo();BeanUtils.copyProperties(item, attrRespvo);AttrAttrgroupRelationEntity attrgroupRelationEntity = relationService.getOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", item.getAttrId()));if (attrgroupRelationEntity != null) {//attrgroupRelationEntity.getAttrGroupId()也可以,这里可以直接放进去对象AttrGroupEntity attrGroupEntity = attrGroupService.getById(attrgroupRelationEntity);attrRespvo.setGroupName(attrGroupEntity.getAttrGroupName());}CategoryEntity categoryEntity = categoryService.getById(item.getCatelogId());if (categoryEntity != null) {attrRespvo.setCatelogName(categoryEntity.getName());}//返回最后的封装结果return attrRespvo;}).collect(Collectors.toList());//返回的结果是一个集合pageUtils.setList(resultList);//        返回分页后的集合对象return pageUtils;}

 销售属性回显

可以看到,销售属性回显是不需要所属分组的

但是销售属性规格参数用的是同一个回显方法,我们也进行更改,只有是规格参数的时候才进行分组回显

image-20220811104643952 

在原分组回显的逻辑上加上判断,后面逻辑不变 

if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {...
}

 image-20220811104920477

修改销售属性 

销售属性规格参数用的是同一个修改方法,销售属性进行修改时,会对关联表进行一个级联更新,但销售属性不需要

所以也在对关联表级联更新的时候进行判断,只有销售属性修改的时候才进行级联更新!

if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {...
}

 image-20220811105227416

保存销售属性 

销售属性规格参数用的是同一个保存方法,销售属性进行保存时,会对关联表进行一个级联保存,但销售属性不需要

所以也在对关联表级联保存的时候进行判断,只有销售属性保存的时候才进行级联保存!

if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {...
}

 image-20220811105415725


8.分组与属性关联

显示属性 

这里其实就是一个分布查询,流程如下:

  1. 点击分组属性的时候获取到分组id,
  2. 拿分组id去关联表查分组id对应的attr_id
  3. attr_id去pms_attr表中获取属性

image-20220808231237643 

controller

/*** 3.获取属性分组的关联的所有属性*/
@RequestMapping("/{attrgroupId}/attr/relation")
public R attrRelation(@PathVariable("attrgroupId") Long attrgroupId) {List<AttrEntity> entities = attrService.getRelationAttr(attrgroupId);return R.ok().put("data", entities);
}

 service

@Override
public List<AttrEntity> getRelationAttr(Long attrgroupId) {//分布查询,第一步去关联表中查出所有的组和属性idList<AttrAttrgroupRelationEntity> entities = relationService.list(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id",attrgroupId));//第二收集属性idList<Long> attrIds = entities.stream().map((attr) -> {return attr.getAttrId();}).collect(Collectors.toList());List<AttrEntity> list = this.listByIds(attrIds);return list;
}

测试

image-20220811124827549

移除属性 

这里为了方便,我们直接写一个批量删除的接口

controller

  1. /product/attrgroup/attr/relation/delete
  2. post请求会带来json数据,要封装成自定义对象vos需要@RequestBody注解
  3. 意思就是将请求体中的数据封装成vos
/*** 4.移除属性分组和属性的关系*/
@PostMapping("/attr/relation/delete")
public R deleteRelation(@RequestBody AttrGroupRelationVo[] vos) {attrService.deleteRelation(vos);return R.ok();
}

 service

@Override
public void deleteRelation(AttrGroupRelationVo[] vos) {List<AttrAttrgroupRelationEntity> entities = Arrays.asList(vos).stream().map((item) -> {AttrAttrgroupRelationEntity entity = new AttrAttrgroupRelationEntity();BeanUtils.copyProperties(item, entity);return entity;}).collect(Collectors.toList());relation.deleteBatchRelation(entities);
}

mapper

void deleteBatchRelation(@Param("entities") List<AttrAttrgroupRelationEntity> entities);<delete id="deleteBatchRelation">DELETE FROM `pms_attr_attrgroup_relation` where<foreach collection="entities" item="item" separator="OR">(attr_id = #{item.attrId} AND attr_group_id = #{item.attrGroupId})</foreach></delete>

查询分组未关联的属性

image-20220811172114595

controller

/*** 5.获取属性分组没有关联的所有属性* /product/attrgroup/{attrgroupId}/noattr/relation*/
@RequestMapping("/{attrgroupId}/noattr/relation")
public R attrNoRelation(@RequestParam Map<String, Object> params,@PathVariable("attrgroupId") Long attrgroupId) {PageUtils page = attrService.getNoRelationAttr(params,attrgroupId);return R.ok().put("page", page);
}

 service

认真看注释,认真理解,还是很绕的

查询分组未关联的数据三步!

  1. 获得当前分类下的所有分组
  2. 获得这些分组下所有已添加的属性
  3. 添加新属性时移除这些已添加的属性
@Override
public PageUtils getNoRelationAttr(Map<String, Object> params, Long attrgroupId) {/***  1.当前分组只能关联自己所属的分类里面的所有属性*/AttrGroupEntity attrGroupEntity = attrGroupService.getById(attrgroupId);Long catelogId = attrGroupEntity.getCatelogId();/***  2 .当前分组只能引用别的分组没有引用的属性*  2.1 当前分类下的所有分组*  2.2 这些分组关联的属性*  2.3 从当前分类的所有属性中移除这些属性*//*** 2.1 当前分类下的所有分组。收集到他们的组id*/List<AttrGroupEntity> group = attrGroupService.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));List<Long> collectGroupIds = group.stream().map((item) -> {return item.getAttrGroupId();}).collect(Collectors.toList());/***  2.2 收集到分组的所有属性*  (1)拿着上一步收集到的组id到关系表中查找关系表实体类对象,*  (2)通过关系表实体类对象获得所有分组下的所有属性id*/List<AttrAttrgroupRelationEntity> groupId = relationService.list(new QueryWrapper<AttrAttrgroupRelationEntity>().in("attr_group_id", collectGroupIds));List<Long> attrIds = groupId.stream().map((item) -> {return item.getAttrId();}).collect(Collectors.toList());/*** 2.3 从当前分类的所有属性中移除这些属性并筛选出基本属性(where attr_type = 1)*/QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>().eq("catelog_id", catelogId).eq("attr_type",ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());//如果其他分组也没关联属性,那么就不加这个条件if (attrIds != null && attrIds.size() > 0){wrapper.notIn("attr_id", attrIds);}/*** 分页多条件查询* where (`attr_id` = ? or `attr_name` like ?)*/String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {wrapper.and((w) -> {w.eq("attr_id", key).or().like("attr_name", key);});}/*** page方法需要两个参数* 1.IPage对象(通过工具类Query获取并通过.getPage(params)封装页面传来分页参数)* 2.wrapper(自己生成)*/IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), wrapper);PageUtils pageUtils = new PageUtils(page);return pageUtils;
}

 注意非空判断

image-20220811173223505

测试

image-20220811183248352 

给销售属性绑定分组,把9号属性绑定给1号分组 

image-20220811183459861 

查询分组未关联的属性 

image-20220811183601207

image-20220811183612731 

添加属性关联 

常规的调用,注意点是saveBatch传的参数是数据对应的实体类

我们想传其他vo时,需要对这个方法进行一个重写

最后也是通过把vo的值赋给对应实体类,在调用相应批量保存

controller

/*** 6.添加属性与分组关联关系* /product/attrgroup/attr/relation*/
@PostMapping("/attr/relation")
public R addRelation(@RequestBody List<AttrGroupRelationVo> vos) {relationService.saveBatch(vos);return R.ok();
}

 service

@Override
public void saveBatch(List<AttrGroupRelationVo> vos) {List<AttrAttrgroupRelationEntity> collect = vos.stream().map((item) -> {AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();BeanUtils.copyProperties(item, relationEntity);return relationEntity;}).collect(Collectors.toList());this.saveBatch(collect);
}

9.发布商品

调试会员等级接口

启动会员微服务,添加网关,添加前端页面…

添加如下会员:

image-20220812111110133 

获取分类关联的品牌 

/*** 1.获取分类关联的品牌* /product/categorybrandrelation/brands/list*/
@GetMapping("/brands/list")
public R relationBrandList(@RequestParam(value = "catId", required = true) Long catId) {List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);//品牌对象集合在进行筛选,赋予品牌对象id和name,返回封装的vo给前端List<BrandVo> collect = vos.stream().map(item -> {BrandVo brandVo = new BrandVo();brandVo.setBrandId(item.getBrandId());brandVo.setBrandName(item.getName());return brandVo;}).collect(Collectors.toList());return R.ok().put("data",collect);
}

service

@Override
public List<BrandEntity> getBrandsByCatId(Long catId) {//获得CategoryBrandRelationEntity集合对象List<CategoryBrandRelationEntity> catelogId = relationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));//获得所有集合对象中brandid,通过brandService查询所有品牌,封装成品牌对象集合List<BrandEntity> collect = catelogId.stream().map(item -> {Long brandId = item.getBrandId();BrandEntity entity = brandService.getById(brandId);return entity;}).collect(Collectors.toList());//返回品牌对象集合return collect;
}

测试

开发规范

  1. Controller:处理请求,接受和校验数据
  2. Service接受controller传来的数据,进行业务处理
  3. Controller接受service处理完的数据,封装页面指定的vo

 image-20220812115152211

获取分类下所有分组&关联属性 

也就是说当我们选择手机分类时,那就查出手机相关的分组信息,并查出每个分组相应属性信息

image-20220812180531794 

@Data
public class AttrGroupWithAttrsVo {/*** 分组id*/@TableIdprivate Long attrGroupId;/*** 组名*/private String attrGroupName;/*** 排序*/private Integer sort;/*** 描述*/private String descript;/*** 组图标*/private String icon;/*** 所属分类id*/private Long catelogId;private List<AttrEntity> attrs;
}

 controller

/*** 7.获取分类下所有分组&关联属性* /product/attrgroup/{catelogId}/withattr*/
@GetMapping("/{catelogId}/withattr")
public R getAttrGroupWithAttrs(@PathVariable("catelogId") Long catelogId) {List<AttrGroupWithAttrsVo> vos = attrGroupService.getAttrGroupWithAttrsByCatelogId(catelogId);return R.ok().put("data",vos);
}

service

vo的重要性:

vo(value object)当相应数据需要自定义时,用vo是最好的选择,不需要对实体类字段进行修改

 image-20220812181322056

/*** 获取分类下的所有分组及属性* @param catelogId* @return*/@Overridepublic List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {/** 1.获取分类下的所有分组,封装成集合*  分类和组的关系在pms_group表中,所以(where catelog_id = ?)即可查出分类对应的组*  由于这是mp,它会得出所有的这种关系,并把结果封装成集合*/List<AttrGroupEntity> list = this.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));/** 2.获得分组下的属性*  要第三张关联表,直接调用关联表的service即查询分组对应的属性id*  获得属性id在去调用属性表的service即可查询属性名*  以上两步前面已经写好逻辑了直接调用即可attrService.getRelationAttr(groupId)*/List<AttrGroupWithAttrsVo> collect = list.stream().map((item) -> {AttrGroupWithAttrsVo attrGroupWithAttrsVo = new AttrGroupWithAttrsVo();BeanUtils.copyProperties(item, attrGroupWithAttrsVo);List<AttrEntity> attrs = attrService.getRelationAttr(attrGroupWithAttrsVo.getAttrGroupId());if (attrs != null) {attrGroupWithAttrsVo.setAttrs(attrs);}return attrGroupWithAttrsVo;}).filter((attrvo) -> {return attrvo.getAttrs() != null && attrvo.getAttrs().size() > 0;}).collect(Collectors.toList());return collect;}

 测试

image-20220812180358798

商品新增vo抽取 

设置完属性,点击保存之后取消保存,复制控制台输出

image-20220812210242531 

在线JSON字符串转Java实体类(JavaBean、Entity)-BeJSON.com 

直接解析json数据封装成实体类

这里我简单截取一个主要的Vo

此Vo包括每个步骤所携带的数据,有的是单个字段有的是一个集合

逻辑不难,难点是要理清逻辑,注意细节!

@Data
public class SpuSaveVo {@NotEmpty(groups = {AddGroup.class})private String spuName;private String spuDescription;@NotEmpty(groups = {AddGroup.class})private Long catalogId;@NotEmpty(groups = {AddGroup.class})private Long brandId;private double weight;private int publishStatus;private List<String> decript;private List<String> images;private Bounds bounds;@NotEmpty(groups = {AddGroup.class})private List<BaseAttrs> baseAttrs;@NotEmpty(groups = {AddGroup.class})private List<Skus> skus;}

 商品新增业务流程分析

逻辑很简单那,就是把数据保存到多张表

因为这个Vo收集的数据很多,包括每个步骤你所选择的数据

保存spu基本信息 pms_spu_info

因为所有传来的信息都在vo里,所以我们把信息拷贝到对应的实体类中,如果vo没有的那就可以自己赋值

表结构如下:

image-20220813192926303 

这里的infoEntity.setCreateTime(new Date());infoEntity.setUpdateTime(new Date());是因为前端传入的是没有这两个字段的,我们自己赋值即可 

SpuInfoEntity infoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(vo, infoEntity);
infoEntity.setCreateTime(new Date());
infoEntity.setUpdateTime(new Date());
this.saveBaseInfo(infoEntity);

 保存spu的描述图片 pms_spu_info_desc

保存哪个数据到哪个表,就注入那个service

String.join()的作用是把集合中的元素通过","分割形成一个一个的字符串

List<String> decript = vo.getDecript();
SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
descEntity.setSpuId(infoEntity.getId());
descEntity.setDecript(String.join(",", decript));
spuInfoDescService.saveSpuInfoDesc(descEntity);

 保存spu的图片集 pms_spu_images

从vo中获取所有图片集合
调用图片service进行保存,保存只需要两个点
图片id和url地址,传入对象即可

List<String> images = vo.getImages();
imagesService.saveImages(infoEntity.getId(), images);

 保存spu的规格参数 pms_product_attr_value

从vo中获取所有规格参数集合
对规格参数集合进行遍历,设置每项的属性

List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
List<ProductAttrValueEntity> collect = baseAttrs.stream().map((attr) -> {ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();valueEntity.setAttrId(attr.getAttrId());AttrEntity id = attrService.getById(attr.getAttrId());valueEntity.setAttrName(id.getAttrName());valueEntity.setAttrValue(attr.getAttrValues());valueEntity.setQuickShow(attr.getShowDesc());valueEntity.setSpuId(infoEntity.getId());return valueEntity;
}).collect(Collectors.toList());
attrValueService.saveProductAttr(collect);

 保存spu的积分信息 mall_sms -> sms_spu_bounds

Bounds bounds = vo.getBounds();
SpuBoundTo spuBoundTo = new SpuBoundTo();
BeanUtils.copyProperties(bounds, spuBoundTo);
spuBoundTo.setSpuId(infoEntity.getId());
R r0 = couponFeignService.saveSpuBounds(spuBoundTo);
if (r0.getCode() != 0) {log.error("远程保存spu积分信息异常");
}
couponFeignService.saveSpuBounds(spuBoundTo);

保存当前spu对应的所有sku信息

//6.1sku的基本信息;pms_sku_info
List<Skus> skus = vo.getSkus();
if (skus != null && skus.size() > 0) {skus.forEach(item -> {String defalutImg = "";for (Images image : item.getImages()) {if (image.getDefaultImg() == 1) {defalutImg = image.getImgUrl();}}SkuInfoEntity skuInfoEntity = new SkuInfoEntity();BeanUtils.copyProperties(item, skuInfoEntity);//添加vo中没有的信息skuInfoEntity.setBrandId(infoEntity.getBrandId());skuInfoEntity.setCatalogId(infoEntity.getCatalogId());skuInfoEntity.setSaleCount(0L);skuInfoEntity.setSpuId(infoEntity.getId());skuInfoEntity.setSkuDefaultImg(defalutImg);skuInfoService.saveSkuInfo(skuInfoEntity);//6.2sku图片信息;pms_sku_images//没有图片路径的无需保存Long skuId = skuInfoEntity.getSkuId();List<SkuImagesEntity> imageEntities = item.getImages().stream().map(img -> {SkuImagesEntity skuImagesEntity = new SkuImagesEntity();skuImagesEntity.setSkuId(skuId);skuImagesEntity.setImgUrl(img.getImgUrl());skuImagesEntity.setDefaultImg(img.getDefaultImg());return skuImagesEntity;}).filter(entity -> {return !StringUtils.isEmpty(entity.getImgUrl());}).collect(Collectors.toList());skuImagesService.saveBatch(imageEntities);//6.3sku的销售属性;pms_sku_sale_attr_valueList<Attr> attr = item.getAttr();List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {SkuSaleAttrValueEntity attrValueEntity = new SkuSaleAttrValueEntity();BeanUtils.copyProperties(a, attrValueEntity);attrValueEntity.setSkuId(skuId);return attrValueEntity;}).collect(Collectors.toList());skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);//6.4sku的优惠满减信息(跨服务);SkuReductionTo skuReductionTo = new SkuReductionTo();BeanUtils.copyProperties(item, skuReductionTo);skuReductionTo.setSkuId(skuId);if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1) {R r1 = couponFeignService.saveSkuReduction(skuReductionTo);if (r1.getCode() != 0) {log.error("远程保存spu积分信息异常");}}});
}

测试

检索功能 

也就是多条件分页查询,很常见的功能!spu检索

/*** 列表*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){PageUtils page = spuInfoService.queryPageByCondition(params);return R.ok().put("page", page);
}

service

@Override
public PageUtils queryPageByCondition(Map<String, Object> params) {QueryWrapper<SpuInfoEntity> queryWrapper = new QueryWrapper<>();String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {//等价sql: status=1 and (id=1 or spu_name like xxx)queryWrapper.and((w) -> {w.eq("id", key).or().like("spu_name", key);});}String status = (String) params.get("status");if (!StringUtils.isEmpty(status)) {queryWrapper.eq("publish_status", status);}String brandId = (String) params.get("brandId");if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)) {queryWrapper.eq("brand_id", brandId);}String catelogId = (String) params.get("catelogId");if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {queryWrapper.eq("catalog_id", catelogId);}IPage<SpuInfoEntity> page = this.page(new Query<SpuInfoEntity>().getPage(params),queryWrapper);return new PageUtils(page);
}

 sku检索

/*** 列表*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){PageUtils page = skuInfoService.queryPageByParams(params);return R.ok().put("page", page);
}

 service

@Override
public PageUtils queryPageByParams(Map<String, Object> params) {QueryWrapper<SkuInfoEntity> queryWrapper = new QueryWrapper<>();String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {queryWrapper.and((w) -> {w.eq("sku_id", key).or().like("sku_name", key);});}String catelogId = (String) params.get("catelogId");if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {queryWrapper.eq("catalog_id", catelogId);}String brandId = (String) params.get("brandId");if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)) {queryWrapper.eq("brand_id", brandId);}String max = (String) params.get("max");if (!StringUtils.isEmpty(max)) {try {BigDecimal bigDecimal = new BigDecimal(max);if (bigDecimal.compareTo(new BigDecimal("0")) == 1) {queryWrapper.le("price", max);}} catch (Exception e) {}}String min = (String) params.get("min");if (!StringUtils.isEmpty(min)) {queryWrapper.ge("price", min);}IPage<SkuInfoEntity> page = this.page(new Query<SkuInfoEntity>().getPage(params),queryWrapper);return new PageUtils((page));
}

10.仓库服务

整合ware服务&获取仓库列表

  1. 加入微服务注册中心
  2. 加入网关

获取仓库列表就是对仓库表的简单查询,逆向生成代码以帮我们生成好,只要配置好网关就可以直接显示

image-20220906101248116 

我们只要记住,反是单表操作的逆向生成以帮我们生成好了,我们能拿来直接用,就像增加仓库、删除、修改都是可以直接用的 

多条件分页查询 

@Override
public PageUtils queryPage(Map<String, Object> params) {QueryWrapper<WareInfoEntity> queryWrapper = new QueryWrapper<>();String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {queryWrapper.eq("id", key).or().like("name", key).or().like("address", key).or().like("areacode", key);}IPage<WareInfoEntity> page = this.page(new Query<WareInfoEntity>().getPage(params),queryWrapper);return new PageUtils(page);
}

多条件查询都是一样的套路,获得你搜索的key,然后拿这个key去模糊匹配多个字段

比如这里拿你输入的key会在name、address、areacode做模糊查询,条件直接通过or来拼接

查询库存

查询库存也是单表操作,CRUD都帮我们做好了,我们就在分页的基础上加上多条件查询即可 

//多条件分页查询
@Override
public PageUtils queryPage(Map<String, Object> params) {QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();String skuId = (String) params.get("skuId");if (!StringUtils.isEmpty(skuId)) {queryWrapper.eq("sku_id", skuId);}String wareId = (String) params.get("wareId");if (!StringUtils.isEmpty(wareId)) {queryWrapper.eq("ware_id", wareId);}IPage<WareSkuEntity> page = this.page(new Query<WareSkuEntity>().getPage(params),queryWrapper);return new PageUtils(page);
}

 创建采购需求

同上都是单表操作,我们只需要做采购需求的多条件分页查询

@Override
public PageUtils queryPage(Map<String, Object> params) {QueryWrapper<PurchaseDetailEntity> queryWrapper = new QueryWrapper<PurchaseDetailEntity>();String key = (String)params.get("key");if(!StringUtils.isEmpty(key)){queryWrapper.and(w->{w.eq("purchase_id",key).or().eq("sku_id",key);});}String status = (String)params.get("status");if(!StringUtils.isEmpty(status)) {queryWrapper.eq("status",status);}String wareId = (String)params.get("wareId");if(!StringUtils.isEmpty(wareId)) {queryWrapper.eq("ware_id",wareId);}IPage<PurchaseDetailEntity> page = this.page(new Query<PurchaseDetailEntity>().getPage(params),queryWrapper);return new PageUtils(page);
}

 合并采购需求

image-20220906105205032

创建采购单

image-20220906105356726 

合并请求接口

这里有两种情况如下:

  • 如果没有选中采购单,那么会自动创建采购单进行合并
  • 有的话,就用采购单id

 controller

/*** 合并采购单*/
@PostMapping("/merge")
public R merge(@RequestBody MergeVo mergeVo) {boolean flag = purchaseService.mergePurchase(mergeVo);if(flag){return R.ok();}else {return R.error().put("msg","请选择新建或已分配的采购需求");}
}VO如下:@Data
public class MergeVo {private Long purchaseId;private List<Long> items;
}

impl

实际上就是创建完采购需求对象和采购单对象后,点击合并,这两个对象信息会发生变化,整体就是做这些操作

具体的看注释,这里还用到了一些枚举类的写法,通过枚举类获得状态信息,了解即可,这里就不写了,可以去看老师的源码

@Transactional
@Override
public boolean mergePurchase(MergeVo mergeVo) {//一、获取Vo中的信息//如果指定了采购单,那就获取采购单的idLong purchaseId = mergeVo.getPurchaseId();//获得采购需求的idList<Long> items = mergeVo.getItems();//二、过滤采购需求//对采购需求id进行过滤,如果采购需求处于新建或者已分配的收集成新的集合//这样做的目的是为了进行筛选,如果你选中正在采购的是不会被合并的List<Long> collect = items.stream().filter(i -> {//通过采购需求的id获取采购需求实体类PurchaseDetailEntity purchaseDetailEntity = purchaseDetailService.getById(i);if (purchaseDetailEntity.getStatus() == WareConstant.PurchaseDetailStatusEnum.CREATED.getCode()|| purchaseDetailEntity.getStatus() == WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode()) {return true;} else {return false;}}).collect(Collectors.toList());//三、没有指定采购单逻辑和指定了的逻辑if (collect != null && collect.size() > 0) {//3.1如果没有指定采购单,那就自动创建一个if (purchaseId == null) {PurchaseEntity purchaseEntity = new PurchaseEntity();//如果是新创建的采购单,创建时间更新时间,状态都是没有默认值的所以这默认值我们自己来赋值purchaseEntity.setCreateTime(new Date());purchaseEntity.setUpdateTime(new Date());//这里设置采购单的状态采用的是枚举类的形式获取purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATED.getCode());this.save(purchaseEntity);//获得自动创建的采购单idpurchaseId = purchaseEntity.getId();}/** 3.2指定采购单了,逻辑如下* 1.采购单id为Vo中获取的指定id* 2.设置所有的采购需求对象并收集成对象*/Long finalPurchaseId = purchaseId;List<PurchaseDetailEntity> collect1 = collect.stream().map(i -> {//获取所有的采购需求对象//更新采购需求的状态,一共需要该两个点,一个是采购状态,一个是采购单id。设置采购需求的id是为了区分是哪一个进行了更改PurchaseDetailEntity purchaseDetailEntity = purchaseDetailService.getById(i);purchaseDetailEntity.setPurchaseId(finalPurchaseId);purchaseDetailEntity.setId(i);purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());return purchaseDetailEntity;}).collect(Collectors.toList());//批量更改采购需求,这里是MP里的接口,可直接传入对象,MP会自动读取里面的IDpurchaseDetailService.updateBatchById(collect1);//四、优化时间更新,为了显示的时间符合我们的样式PurchaseEntity purchaseEntity = new PurchaseEntity();purchaseEntity.setId(purchaseId);purchaseEntity.setUpdateTime(new Date());//五、更新采购单return this.updateById(purchaseEntity);} else {return false;}
}

 领取采购单

这里我们只用写好接口的功能,这个请求一般是由app来进行发送

controller 

/*** 领取采购单*/
@PostMapping("/received")
public R received(@RequestBody List<Long> ids){purchaseService.received(ids);return R.ok();
}

impl

领取采购单,通过接口测试工具完成请求

领取玩采购单后,更改采购单状态和对应采购需求状态

  1. 采购单状态改为已领取
  2. 采购需求状态改为正在采购
@Overridepublic void received(List<Long> ids) {//1.确认当前采购单状态List<PurchaseEntity> collect = ids.stream().map(item -> {//通过采购单id获取采购单对象PurchaseEntity purchaseEntity = this.getById(item);return purchaseEntity;}).filter(id -> {//对采购单对象进行过滤,如果状态为新建或者已分配的留下if (id.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() ||id.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {return true;} else {return false;}}).map(item -> {//对上面收集好的在进行过滤,改变采购单状态为已领取(RECEIVE)item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());//对上面收集好的在进行过滤,改变采购单更新时间item.setUpdateTime(new Date());return item;}).collect(Collectors.toList());//2.批量修改改变采购单状态this.updateBatchById(collect);//3.改变采购需求中的状态if (collect != null && collect.size() > 0) {collect.forEach(item -> {List<PurchaseDetailEntity> entities = purchaseDetailService.listDetailByPurchaseId(item.getId());List<PurchaseDetailEntity> detailEntities = entities.stream().map(entity -> {PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();purchaseDetailEntity.setId(entity.getId());//将采购需求中的状态改为正在采购purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());return purchaseDetailEntity;}).collect(Collectors.toList());purchaseDetailService.updateBatchById(detailEntities);});}}

 image-20220814004750176

image-20220814004807416 

完成采购 

这里我们只用写好接口的功能,这个请求一般是由app来进行发送

/*** 完成采购单*/
@PostMapping("/done")
public R finished(@RequestBody PurchaseDoneVo doneVo){purchaseService.done(doneVo);return R.ok();
}VO如下:
@Data
public class PurchaseDoneVo {@NonNullprivate Long id;private List<PurchaseItemDoneVo> items;public PurchaseDoneVo(){}
}@Data
public class PurchaseItemDoneVo {private Long itemId;private Integer status;private String reason;
}

 impl

完成采购主要注意有几个地方发生了变化,做好逻辑的判断即可

/*** 采购完成一共三地方会发生变化*  1.采购单状态*  2.库存增加*  3.采购需求状态发生变化* @param doneVo*/@Overridepublic void done(PurchaseDoneVo doneVo) {//获取完成的是哪一个采购单Long id = doneVo.getId();//一、初始化Boolean flag = true;//获取采购单id集合List<PurchaseItemDoneVo> items = doneVo.getItems();//收集结果List<PurchaseDetailEntity> updates = new ArrayList<>();for (PurchaseItemDoneVo item : items) {PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()) {flag = false;purchaseDetailEntity.setStatus(item.getStatus());} else {//二、采购需求状态发生变化purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());//由采购单的id获取采购需求对象,有什么用呢?是用来给增加库存时赋值用的PurchaseDetailEntity entity = purchaseDetailService.getById(item.getItemId());//三、库存增加wareSkuService.addStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());}//采购完成,采购需求中的状态也会发生变化,给实体类对象指明id,从而修改对象的状态purchaseDetailEntity.setId(item.getItemId());//把要修改的采购需求对象放到集合里updates.add(purchaseDetailEntity);}//因为一个采购单里有多个采购需求合并的,所以批量修改采购需求对象purchaseDetailService.updateBatchById(updates);//四.改变采购单状态PurchaseEntity purchaseEntity = new PurchaseEntity();purchaseEntity.setId(id);purchaseEntity.setStatus(flag ? WareConstant.PurchaseStatusEnum.FINISH.getCode() :WareConstant.PurchaseStatusEnum.HASERROR.getCode());purchaseEntity.setUpdateTime(new Date());this.updateById(purchaseEntity);}

 这里id = 6是对6号采购单发起操作,里面的item9和10是采购单对应的采购需求

{"id":16,"items":[{"itemId":17,"status":3,"reason":""},{"itemId":18,"status":4,"reason":"无货"}]
}

 image-20220907215915451

采购单状态如下

有异常是因为我们有一个采购单没有采购完成 

image-20220907215937701 

采购需求如下

没有完成的采购需求会显示采购失败

image-20220907220051000 

库存如下

image-20220907220103041 

显示商品库存中的sku_name 

image-20220907222230675

怎么显示呢?锁定库存就是本表库存表相关的可以直接设置,而sku_name是mall-product微服务里才能查询的到的

那就写Feign接口,这里介绍两种feign接口的写法:

给远程调用的微服务发请求

  1.  @FeignClient("mall-product") 指定微服务
     
  2.  /product/skuinfo/info/{skuId}

给网关发请求

  • @FeignClient(“mall-gateway”)
  • /api/product/skuinfo/info/{skuId}
@FeignClient("mall-gateway")
public interface ProductFeignService {@RequestMapping("/api/product/skuinfo/info/{skuId}")public R info(@PathVariable("skuId") Long skuId);}

 增加库存的时候注入FeignService接口即可实现远程调用

这里采取了try catch的形式来捕获异常,可以防止远程调用失败时,事务回滚

 

@Transactional
@Override
public void addStock(Long skuId, Long wareId, Integer skuNum) {//判断如果没有此库存记录,则为新增操作;如果有则为更改操作List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));if (wareSkuEntities == null || wareSkuEntities.size() == 0) {WareSkuEntity wareSkuEntity = new WareSkuEntity();wareSkuEntity.setSkuId(skuId);wareSkuEntity.setStock(skuNum);wareSkuEntity.setWareId(wareId);wareSkuEntity.setStockLocked(0);//TODO 远程查询sku的名字//如果查询名字查询失败了,事务回滚有点不值得,所以用trycatch来捕捉一下try {R info = productFeignService.info(skuId);Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");if (info.getCode() == 0){wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));}} catch (Exception e) {e.printStackTrace();}wareSkuDao.insert(wareSkuEntity);} else {wareSkuDao.addStock(skuId, wareId, skuNum);}
}

http://www.lryc.cn/news/4192.html

相关文章:

  • 二叉树OJ题(上)
  • 第一章 PDF语法
  • IntelliJ IDEA 创建JavaFX项目运行
  • IC封装常见形式
  • Linux通配符、转义符讲解
  • [OpenMMLab]提交pr时所需的git操作
  • pandas——groupby操作
  • webpack.config.js哪里找?react项目关闭eslint监测
  • OpenCV 图像梯度算子
  • Linux c编程之Wireshark
  • 极客时间_FlinkSQL 实战
  • Pytorch 混合精度训练 (Automatically Mixed Precision, AMP)
  • 使用太极taichi写一个只有一个三角形的有限元
  • 进程,线程
  • 第03章_基本的SELECT语句
  • 干货 | 简单了解运算放大器...
  • C++定位new用法及注意事项
  • 【Android笔记75】Android之翻页标签栏PagerTabStrip组件介绍及其使用
  • 【Kafka】【二】消息队列的流派
  • 现代 cmake (cmake 3.x) 操作大全
  • how https works?https工作原理
  • Docker的资源控制管理
  • MMSeg无法使用单类自定义数据集训练
  • Redis使用方式
  • 无主之地3重型武器节奏评分榜(9.25) 枪械名 红字效果 元素属性 清图评分 Boss战评分 泛用性评分 特殊性评分 最终评级 掉落点 掉率 图片 瘟疫传播
  • 什么是编程什么是算法
  • 【c++】函数
  • [golang gin框架] 1.Gin环境搭建,程序的热加载,路由GET,POST,PUT,DELETE
  • 【开源】祁启云网络验证系统V1.11
  • 震源机制(Focal Mechanisms)之沙滩球(Bench Ball)