您的位置:首页 > 移动开发

乐优商城--服务(三) : 商品微服务(LyItemApplication)--后半部分

2019-05-27 21:35 771 查看

商品微服务--后半部分:

  • 4.7.1.3.2 service
  • 4.7.1.3.3 mapper
  • 4.7.2 规格参数查询
  • 4.7.2.3.1 service
  • 4.7.2.3.1 mapper
  • 4.8 商品查询
  • 4.8.3.1 service
  • 4.8.3.1 mapper
  • 4.9 商品新增
  • 4.9.2 商品描述
  • 4.9.3 商品规格参数
  • 4.9.4 SKU信息
  • 4.9.5 提交
  • 4.9.5.3.2 service
  • 4.9.5.3.3 mapper
  • 4.10. 商品修改
  • 4.10.1.2 service
  • 4.10.2 查询sku
  • 4.10.2.1 service
  • 4.10.3 提交业务
  • 4.10.3.1 service
  • 注: 由于商品微服务内容太多,所以做了优化,拆分成两部分写。地址:商品微服务–前半部分

    4.6 商品规格数据结构与商品表结构分析

    内容偏多,重新写了一篇商品规格数据结构与商品表结构分析

    4.7 实现商品规格参数管理

    4.7.1 规格组查询

    4.7.1.1 SpecGroup数据表

    由于在商品规格数据结构与商品表结构分析中SpecGroup数据表已给出,这里就不再附一遍了。

    4.7.1.2 SpecGroup实体类

    @Data
    @Table(name = "tb_spec_group")
    public class SpecGroup {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long cid;
    private String name;
    
    @Transient  //数据库表中没有params这一字段,因此要加上@Transient注解
    private List<SpecParam> params;
    }
    • @Transient注解

      serialization
      会忽略掉
      Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。
      为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient

    • 不跟数据库表做映射 就是表中没有这个字段
      @

      Transient
      表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性.

    4.7.1.3 业务

    4.7.1.3.1 web
    4.7.1.3.1.1 页面分析

    • 请求方式:get
    • 请求路径:/spec/groups/cid
    • 请求参数:cid
    • 返回结果:group集合
    4.7.1.3.1.2 实现业务
    @RestController
    @RequestMapping("spec")
    public class SpecificationController {
    
    @Autowired
    private SpecificationService specificationService;
    
    // 根据分类id查询规格组
    @GetMapping("groups/{cid}")
    public ResponseEntity<List<SpecGroup>>  queryGroupByCid(@PathVariable("cid")Long cid){
    return ResponseEntity.ok(specificationService.queryGroupByCid(cid));
    }
    4.7.1.3.2 service
    @Service
    public class SpecificationService {
    
    @Autowired
    private SpecGroupMapper groupMapper;
    
    @Autowired
    private SpecParamMapper paramMapper;
    
    // 根据分类id查询规格组
    public List<SpecGroup> queryGroupByCid(Long cid) {
    
    SpecGroup group = new SpecGroup();
    group.setCid(cid);
    //根据非空字段进行查询
    List<SpecGroup> list = groupMapper.select(group);
    
    if(CollectionUtils.isEmpty(list)){
    throw new LyException(ExceptionEnum.SPEC_GROUP_NOT_FOUND);
    }
    
    return list;
    }
    }
    4.7.1.3.3 mapper
    public interface SpecGroupMapper extends Mapper<SpecGroup> {
    }

    4.7.2 规格参数查询

    当我们点击规格组,切换到规格参数显示

    4.7.2.1 SpecParam数据表

    表结构见商品规格数据结构与商品表结构分析

    4.7.2.2 SpecParam实体类

    @Data
    @Table(name = "tb_spec_param")
    public class SpecParam {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long cid;
    private Long groupId;
    private String name;
    //通用mapper生成sql语句时,不要直接拼接numeric,而要拼接`numeric`,反引号是转义为字符串,numeric是一个关键字
    @Column(name = "`numeric`")
    private Boolean numeric;
    private String unit;
    private Boolean generic;
    private Boolean searching;
    private String segments;
    }

    4.7.2.3 业务

    4.7.2.3.1 web
    4.7.2.3.1.1 页面分析

    • 请求方式:get
    • 请求路径:/spec/params
    • 请求参数:gid,分组id
    • 返回结果:当前组下的所有规格参数
    4.7.2.3.1.2 实现业务
    // 根据组id查询规格参数
    @GetMapping("/params")
    public ResponseEntity<List<SpecParam>> querySpecParams(@RequestParam("gid")Long gid){
    return ResponseEntity.ok(specificationService.querySpecParams(gid));
    }
    4.7.2.3.1 service
    // 根据组id查询规格参数
    public List<SpecParam> querySpecParams(Long gid) {
    SpecParam param = new SpecParam();
    param.setGroupId(gid);
    List<SpecParam> params = paramMapper.select(param);
    
    if(CollectionUtils.isEmpty(params)){
    throw new LyException(ExceptionEnum.SPEC_PARAM_NOT_FOUND);
    }
    
    return params;
    }
    4.7.2.3.1 mapper
    public interface SpecParamMapper extends Mapper<SpecParam> {
    }

    4.8 商品查询

    4.8.1 spu与spuDetail数据表

    表结构见商品规格数据结构与商品表结构分析

    4.8.1 spu与spuDetail实体类

    @Data
    @Table(name = "tb_spu")
    public class Spu {
    @Id
    @KeySql(useGeneratedKeys=true)
    private Long id;
    private Long brandId;
    private Long cid1;// 1级类目
    private Long cid2;// 2级类目
    private Long cid3;// 3级类目
    private String title;// 标题
    private String subTitle;// 子标题
    private Boolean saleable;// 是否上架
    @JsonIgnore     //在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。
    private Boolean valid;// 是否有效,逻辑删除用
    private Date createTime;// 创建时间
    @JsonIgnore
    private Date lastUpdateTime;// 最后修改时间
    
    @Transient
    private String cname;
    @Transient
    private String bname;
    @Transient
    private List<Sku> skus;
    @Transient
    private SpuDetail spuDetail;
    }

    注:

    • po:persisent object,持久对象;它的字段必须和数据库字段完全一致
    • 由于数据库里没有商品分类和品牌的name字段,应该写成专门的vo对象(专门返回页面上的)
    • 查出来的是spu po,应该转成vo。但将来从页面接收到vo转成spu po才能往数据库传,可能会有各种对象转换的错误
    • 为了方便,我们把 商品分类和品牌的name字段 等字段添加到spu里去,由于这些字段数据库中没有,因此都应逐个添加
      @Transient
      注解(javax.persistence包下的)
    • 不想返回到界面的字段 可以加一个
      @JsonIgnore
      注解
    @Data
    @Table(name="tb_spu_detail")
    public class SpuDetail {
    @Id  // id没有加上自增主键,因为这张表id不是自增的,而是和spu表的id关联的
    private Long spuId;// 对应的SPU的id
    private String description;// 商品描述
    private String genericSpec;// 商品特殊规格的名称及可选值模板
    private String specialSpec;// 商品的全局规格属性
    private String packingList;// 包装清单
    private String afterService;// 售后服务
    }

    4.8.1 业务

    4.8.3.1 web

    4.8.3.1.1 页面分析

    效果预览:

    :这的id为spu_id,标题为spu的title字段。
    多个sku有大量的共享数据,因此商品管理的单位应该为spu。

    • 请求方式:GET
    • 请求路径:/spu/page
    • 请求参数: page:当前页
    • rows:每页大小
    • key:过滤条件
    • saleable:上架或下架
  • 返回结果:商品SPU的分页信息。
  • 4.8.3.1.1 实现业务
    @RestController
    public class GoodsController {
    @Autowired
    private GoodsService goodsService;
    // 分页查询spu
    @GetMapping("/spu/page")
    public ResponseEntity<PageResult<Spu>> querySpuByPage(
    @RequestParam(value = "page",defaultValue = "1")Integer page,
    @RequestParam(value = "rows",defaultValue = "5")Integer rows,
    @RequestParam(value = "saleable",required = false)Boolean saleable,
    @RequestParam(value = "key",required = false)String key
    ){
    return ResponseEntity.ok(goodsService.querySpuByPage(page,rows,saleable,key));
    }
    }

    4.8.3.1 service

    @Service
    public class GoodsService {
    @Autowired
    private SpuMapper spuMapper;
    
    @Autowired
    private SpuDetailMapper spuDetailMapper;
    
    // 分页查询spu
    public PageResult<Spu> querySpuByPage(Integer page, Integer rows, Boolean saleable, String key) {
    //分页
    PageHelper.startPage(page,rows);
    //过滤
    Example example = new Example(Spu.class);
    Example.Criteria criteria = example.createCriteria();
    //搜索字段过滤
    if(StringUtils.isNotBlank(key))
    criteria.andLike("title","%"+key+"%");
    //上下架过滤
    if(saleable!=null)
    criteria.andEqualTo("saleable",saleable);
    //默认排序
    example.setOrderByClause("last_update_time DESC");
    //查询
    List<Spu> spus = spuMapper.selectByExample(example);
    if(CollectionUtils.isEmpty(spus))
    throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
    //解析分类和品牌名称
    for (Spu spu : spus) {
    //处理分类名称
    List<String> names = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()))
    .stream().map(Category::getName).collect(Collectors.toList());
    spu.setCname(StringUtils.join(names,"/"));
    //处理品牌名称
    spu.setBname(brandService.queryById(spu.getBrandId()).getName());
    }
    
    //解析分页结果
    PageInfo<Spu> pageInfo = new PageInfo<>(spus);
    return new PageResult<>(pageInfo.getTotal(),spus);
    }
    }

    注:

    • 这里用到了
      categoryService.queryByIds(cids)
      , // 根据商品分类cid列表查询分类集合,因此应在categoryService写出该方法
    • 用户同时输入根据 名称(title)过滤 和 上下架(saleable)过滤,因此采用
      and
      连接
    • 由于spu数据库表中 商品和品牌存的是id,因此需要解析分类和品牌名称
    @Service
    public class CategoryService {
    
    @Autowired
    private CategoryMapper categoryMapper;
    
    // 根据商品分类cid列表查询分类集合
    public List<Category> queryByIds(List<Long> cids){
    
    List<Category> idList = categoryMapper.selectByIdList(cids);
    if(CollectionUtils.isEmpty(idList)){
    throw new LyException(ExceptionEnum.CATEGORY_NOT_FOUND);
    }
    
    return idList;
    }
    }
    public interface CategoryMapper extends Mapper<Category> , IdListMapper<Category,Long>{
    }

    注:

    • 由于根据一堆id查询,因此需要在 CategoryMapper 中添加
      IdListMapper

      IdListMapper中继承了
      SelectByIdListMapper<T, PK>
      DeleteByIdListMapper<T, PK>

    4.8.3.1 mapper

    public interface SpuMapper extends Mapper<Spu>{
    }
    public interface SpuDetailMapper extends BaseMapper<SpuDetail> {
    }

    4.9 商品新增

    4.9.1 基本数据

    当我们点击新增商品按钮时:

    会出现一个弹框:

    里面把商品的数据分为了4部分来填写:

    • 基本信息:主要是一些简单的文本数据,包含了SPU和SpuDetail的部分数据,如 商品分类:是SPU中的cid1,cid2,cid3属性
    • 品牌:是spu中的brandId属性
    • 标题:是spu中的title属性
    • 子标题:是spu中的subTitle属性
    • 售后服务:是SpuDetail中的afterService属性
    • 包装列表:是SpuDetail中的packingList属性
  • 商品描述:是SpuDetail中的description属性,数据较多,所以单独放一个页面
  • 规格参数:商品规格信息,对应SpuDetail中的genericSpec属性
  • SKU属性:spu下的所有Sku信息
  • 4.9.1.2 根据商品分类id查询品牌

    商品分类信息查询我们之前已经做过,品牌也是一个下拉选框,不过其选项是不确定的,只有当用户选择了商品分类,才会把这个分类下的所有品牌展示出来。

    (页面编写了watch函数,监控商品分类的变化,每当商品分类值有变化,就会发起请求,查询品牌列表)

    因此我们只要编写后台接口,根据商品分类id查询对应品牌即可。

    4.9.1.2.1 web
    @RestController
    @RequestMapping("brand")
    public class BrandController {
    // 根据cid查询品牌
    @GetMapping("cid/{cid}")
    public ResponseEntity<List<Brand>> queryBrandByCid(@PathVariable("cid")Long cid){
    return ResponseEntity.ok(brandService.queryBrandByCid(cid));
    }
    }
    4.9.1.2.2 service
    @Service
    public class BrandService {
    // 根据cid查到所有的品牌
    public List<Brand> queryBrandByCid(Long cid) {
    
    List<Brand> brands = brandMapper.queryByCategoryId(cid);
    if(CollectionUtils.isEmpty(brands)){
    throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);
    }
    return brands;
    }
    }
    • cid 和 tb_category,tb_category_brand表都有关系,要自己写sql语句
    4.9.1.2.3 mapper
    public interface BrandMapper extends BaseMapper<Brand> {
    
    @Select("SELECT b.* FROM tb_brand b LEFT JOIN tb_category_brand cb ON b.id = cb.brand_id WHERE cb.category_id = #{cid}")
    List<Brand> queryByCategoryId(@Param("cid")Long cid);
    }

    4.9.2 商品描述

    商品描述信息比较复杂,而且图文并茂,甚至包括视频。

    这样的内容,一般都会使用富文本编辑器。

    4.9.2.1 什么是富文本编辑器

    百度百科:

    通俗来说:富文本,就是比较丰富的文本编辑器。普通的框只能输入文字,而富文本还能给文字加颜色样式等。

    富文本编辑器有很多,例如:KindEditor、Ueditor。但并不原生支持vue

    但我们用的是一款支持Vue的富文本编辑器:vue-quill-editor

    4.9.2.2 Vue-Quill-Editor

    GitHub的主页:https://github.com/surmon-china/vue-quill-editor

    Vue-Quill-Editor是一个基于Quill的富文本编辑器:Quill官网

    4.9.3 商品规格参数

    • 规格参数的查询我们之前也已经编写过接口,因为商品规格参数也是与商品分类绑定,所以需要在商品分类变化后去查询(通过watch监控来实现)

    • 因此我们一样实现根据商品分类id查询规格参数。我们之前写过一个根据gid(分组id)来查询规格参数的接口,若如果再编写一个新的接口(根据分类id查询规格参数)由于和 根据gid来查询规格参数的接口 的路径一模一样,会发生冲突(路径都是/spec/params,参数不一样,但SpringMVC不会因为参数不同就会认定是两次请求)

    • 最终:我们在 根据gid来查询规格参数的接口上 对其进行扩展

    4.9.3.1 web

    @RestController
    @RequestMapping("spec")
    public class SpecificationController {
    // 查询规格参数集合
    @GetMapping("/params")
    public ResponseEntity<List<SpecParam>> querySpecParams(@RequestParam(value = "gid",required = false)Long gid,
    @RequestParam(value = "cid",required = false)Long cid,
    @RequestParam(value = "searching",required = false)Boolean searching){
    return ResponseEntity.ok(specificationService.querySpecParams(gid,cid,searching));
    }
    }
    • 因为两个参数传一个就好,因此应加上
      required = false
    • 现在我们想要的功能实现了,但从长远角度来看,以后可能会有 根据搜索字段来查规格参数 需求(搜索微服务,做索引库新增),因此应再请求参数里再加一个参数
      value = "searching"

    4.9.3.2 service

    @Service
    public class SpecificationService {
    // 查询规格参数集合
    public List<SpecParam> querySpecParams(Long gid, Long cid, Boolean searching) {
    SpecParam param = new SpecParam();
    param.setGroupId(gid);
    param.setCid(cid);
    param.setSearching(searching);
    List<SpecParam> params = paramMapper.select(param);
    
    if(CollectionUtils.isEmpty(params)){
    throw new LyException(ExceptionEnum.SPEC_PARAM_NOT_FOUND);
    }
    return params;
    }
    }

    4.9.4 SKU信息

    Sku属性是SPU下的每个商品的不同特征,如图:

    当我们填写一些属性后,会在页面下方生成一个sku表格(不同属性的Sku)

    当我们选择了上图中的这些选项时:

    • 颜色共2种:夜空黑,绚丽红
    • 内存共2种:4GB,6GB
    • 机身存储1种:64GB

    此时会产生 2 * 2 * 1 = 4种,这其实就是在求笛卡尔积。

    我们会在页面下方生成一个sku的表格:

    4.9.5 提交

    整体是一个json格式数据,包含Spu表所有数据:

    • brandId:品牌id
    • cid1、cid2、cid3:商品分类id
    • subTitle:副标题
    • title:标题
    • spuDetail:是一个json对象,代表商品详情表数据 afterService:售后服务
    • description:商品描述
    • packingList:包装列表
    • specialSpec:sku规格属性模板
    • genericSpec:通用规格参数
  • skus:spu下的所有sku数组,元素是每个sku对象:
      title:标题
    • images:图片
    • price:价格
    • stock:库存
    • ownSpec:特有规格参数
    • indexes:特有规格参数的下标

    4.9.5.1 Sku和Stock表结构

    Spu、SpuDetail和 Sku表结构 见商品规格数据结构与商品表结构分析

    CREATE TABLE `tb_stock` (
    `sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',
    `seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',
    `seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',
    `stock` int(9) NOT NULL COMMENT '库存数量',
    PRIMARY KEY (`sku_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息'

    4.9.5.2 Sku和Stock实体类

    @Data
    @Table(name = "tb_sku")
    public class Sku {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long spuId;
    private String title;
    private String images;
    private Long price;
    private String ownSpec;// 商品特殊规格的键值对
    private String indexes;// 商品特殊规格的下标
    private Boolean enable;// 是否有效,逻辑删除用
    private Date createTime;// 创建时间
    private Date lastUpdateTime;// 最后修改时间
    
    @Transient    //和数据库无关
    private Integer stock;// 库存
    }
    @Data
    @Table(name = "tb_stock")
    public class Stock {
    @Id
    private Long skuId;
    private Integer seckillStock;// 秒杀可用库存
    private Integer seckillTotal;// 已秒杀数量
    private Integer stock;// 正常库存
    }

    4.9.5.3 业务

    4.9.5.3.1 web
    4.9.5.3.1.1 页面分析

    • 请求方式:PUT
    • 请求路径:/
    • 请求参数:Spu对象
    • 返回结果:无
    4.9.5.3.1.1 实现业务
    @RestController
    public class GoodsController {
    //商品新增
    @PostMapping("goods")
    public ResponseEntity<Void> saveGoods(@RequestBody Spu spu){ //json结构,加上@RequestBody注解
    goodsService.saveGoods(spu);
    return ResponseEntity.status(HttpStatus.CREATED).build();//没有返回值
    }
    }
    4.9.5.3.2 service
    @Service
    public class GoodsService {
    //商品新增
    @Transactional
    public void saveGoods(Spu spu) {
    //新增spu
    spu.setId(null);
    spu.setSaleable(true);
    spu.setValid(false);
    spu.setCreateTime(new Date());
    spu.setLastUpdateTime(spu.getCreateTime());
    int count = spuMapper.insert(spu);
    if (count != 1) {
    throw new LyException(ExceptionEnum.GOODS_SAVE_ERROR);
    }
    //新增detail
    SpuDetail spuDetail = spu.getSpuDetail();
    spuDetail.setSpuId(spu.getId());
    spuDetailMapper.insert(spuDetail);
    //新增sku和库存
    saveSkuAndStock(spu);
    
    //发送mq消息(这一步后边用到,可忽略)
    //amqpTemplate.convertAndSend("item.insert",spu.getId());
    }
    }

    在这里,我们把 新增sku和库存 单独抽取成一个方法了

    // 新增sku和库存
    private void saveSkuAndStock(Spu spu) {
    int count;//新增sku
    List<Sku> skus = spu.getSkus();
    List<Stock> stockList = new ArrayList<>();
    for (Sku sku : skus) {
    sku.setCreateTime(new Date());
    sku.setLastUpdateTime(sku.getCreateTime());
    sku.setSpuId(spu.getId());
    
    count = skuMapper.insert(sku);
    if(count!=1)
    throw new LyException(ExceptionEnum.GOODS_SAVE_ERROR);
    
    //新增库存
    Stock stock = new Stock();
    stock.setSkuId(sku.getId());
    stock.setStock(sku.getStock());
    stockList.add(stock);
    }
    //批量新增库存
    count = stockMapper.insertList(stockList);
    if(count!=stockList.size())
    throw new LyException(ExceptionEnum.GOODS_SAVE_ERROR);
    }
    4.9.5.3.3 mapper
    public interface SkuMapper extends BaseMapper<Sku>{
    }
    public interface StockMapper extends BaseMapper<Stock> {
    }

    注:

    • 为了以后Mapper实现的功能更加全面,简单起见,我们自己定义一个
      BaseMapper<>
      ,泛型为要传递的 类
    • BaseMapper继承
      Mapper<T>,IdListMapper<T,Long>,InsertListMapper<T>
      ,可实现 从数据库中批量查询与添加数据
    • 将BaseMapper放在ly-common 工具包中,供其他mapper继承
    • 同时,InsertListMapper要导 tk.mybatis.mapper.additional.insert.InsertListMapper包下的;(另外一个包下的InsertListMapper接口限制实体中必须包含‘Id’属性且必须为自增列)
    @RegisterMapper
    public interface BaseMapper<T> extends Mapper<T>,IdListMapper<T,Long>,InsertListMapper<T> {
    }

    @RegisterMapper
    注解可以避免 mappers 参数配置,通用 Mapper 检测到该接口被继承时,会自动注册。

    即使不增加该接口,如果只用到了通用 Mapper 提供的方法,也可以自动注册,通用 Mapper 会自动向上查找带有该注解的父接口。如果是自己开发的通用方法,建议加上该注解,否则还需要自己配置 mappers 参数

    4.10. 商品修改


    因为在商品列表页面,只有spu的基本信息:id、标题、品牌、商品分类等。比较复杂的商品详情(spuDetail)和sku信息都没有,编辑页面要回显数据,就需要查询这些内容。

    因此,接下来我们就编写后台接口,提供查询服务接口。

    4.10.1 查询SpuDetail接口

    4.10.1.1 web

    4.10.1.1.1 页面分析

    • 请求方式:GET
    • 请求路径:/spu/detail/{id}
    • 请求参数:id,应该是spu的id
    • 返回结果:SpuDetail对象
    4.10.1.1.2 实现业务
    @RestController
    public class GoodsController {
    //根据spu的id查询详情detail
    @GetMapping("/spu/detail/{id}")
    public ResponseEntity<SpuDetail> querySpuDetailById(@PathVariable("id")Long id){
    return ResponseEntity.ok(goodsService.queryDetailById(id));
    }
    }

    4.10.1.2 service

    @Service
    public class GoodsService {
    public SpuDetail queryDetailById(Long spuId) {
    SpuDetail spuDetail = spuDetailMapper.selectByPrimaryKey(spuId);
    if(spuDetail==null)
    throw new LyException(ExceptionEnum.GOODS_SAVE_ERROR);
    return spuDetail;
    }
    }

    4.10.2 查询sku

    4.10.2.1 web

    4.10.2.1.1 页面分析

    • 请求方式:Get
    • 请求路径:/sku/list
    • 请求参数:id,应该是spu的id
    • 返回结果:sku的集合
    4.10.2.1.2 实现业务
    @RestController
    public class GoodsController {
    //根据spu查询下面所有的sku
    @GetMapping("/sku/list")
    public ResponseEntity<List<Sku>> querySkuBySpuId(@RequestParam("id") Long spuId){
    return ResponseEntity.ok(goodsService.querySkuBySpuId(spuId));
    }
    }

    4.10.2.1 service

    @Service
    public class GoodsService {
    //根据spu查询下面所有的sku
    public List<Sku> querySkuBySpuId(Long spuId) {
    //查询sku
    Sku sku = new Sku();
    sku.setSpuId(spuId);
    List<Sku> skuList = skuMapper.select(sku);
    if(CollectionUtils.isEmpty(skuList))
    throw new LyException(ExceptionEnum.GOODS_SKU_NOT_FOUND);
    
    List<Long> ids = skuList.stream().map(Sku::getId).collect(Collectors.toList());
    //将库存放到相应的sku中
    //查询库存
    List<Stock> stockList = stockMapper.selectByIdList(ids);
    if(CollectionUtils.isEmpty(stockList))
    throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
    
    //把stock变成一个map,其key:skuId,值:库存值
    Map<Long, Integer> stockMap = stockList.stream().collect(Collectors.toMap(Stock::getSkuId, Stock::getStock));
    skus.forEach(s ->s.setStock(stockMap.get(s.getId())));
    return skuList;
    }
    }

    4.10.3 提交业务

    4.10.3.1 web

    4.10.3.1.1 页面分析

    • 请求方式:PUT
    • 请求路径:/
    • 请求参数:Spu对象
    • 返回结果:无
    4.10.3.1.1 实现业务
    @RestController
    public class GoodsController {
    //商品修改
    @PutMapping("goods")
    public ResponseEntity<Void> updateGoods(@RequestBody Spu spu){
    goodsService.updateGoods(spu);
    return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
    }
    }

    4.10.3.1 service

    spu数据可以修改,但是SKU数据无法修改,因为有可能之前存在的SKU现在已经不存在了,或者以前的sku属性都不存在了。比如以前内存有4G,现在没了。

    因此这里直接删除以前的SKU,然后新增即可

    @Service
    public class GoodsService {
    @Transactional
    public void updateGoods(Spu spu) {
    if(spu.getId() == null)
    throw new LyException(ExceptionEnum.GOODS_ID_CANNOT_BE_NULL);
    Sku sku = new Sku();
    sku.setSpuId(spu.getId());
    //查询sku
    List<Sku> skuList = skuMapper.select(sku);
    if(!CollectionUtils.isEmpty(skuList)){
    //删除sku
    skuMapper.delete(sku);
    //删除库存
    List<Long> ids = skuList.stream().map(Sku::getId).collect(Collectors.toList());
    stockMapper.deleteByIdList(ids);
    }
    //修改spu
    spu.setValid(null);
    spu.setSaleable(null);
    spu.setCreateTime(null);
    spu.setLastUpdateTime(new Date());
    
    int count = spuMapper.updateByPrimaryKeySelective(spu);
    if(count!=1)
    throw new LyException(ExceptionEnum.GOODS_UPDATE_ERROR);
    //修改detail
    spuDetailMapper.updateByPrimaryKeySelective(spu.getSpuDetail());
    if(count!=1)
    throw new LyException(ExceptionEnum.GOODS_UPDATE_ERROR);
    //新增sku和库存stock(单独提取成一个方法了,上边已经写过了)
    saveSkuAndStock(spu);
    
    //发送mq消息(以后会用到)
    amqpTemplate.convertAndSend("item.update",spu.getId());
    }
    }

    以上,商品微服务可以先告一段落了!

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐