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

本文主要介绍了乐优商城商品微服务的后半部分,涉及商品规格参数管理,包括规格组和规格参数的查询,以及商品查询、新增和修改的详细步骤。在商品规格管理中,讨论了SpecGroup和SpecParam的数据表和实体类,以及对应的业务实现。商品查询涵盖了SPU和SPUDetail的查询,并讨论了商品新增时的基本数据、商品描述、规格参数和SKU信息的处理。最后,详细阐述了商品修改过程中的SpuDetail和SKU查询以及提交业务的处理逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

: 由于商品微服务内容太多,所以做了优化,拆分成两部分写。地址:商品微服务–前半部分

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());
    }
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值