商品新增后台代码

本文详细介绍了商品规格参数的查询接口改造,包括根据商品分类ID查询规格参数的方法,并实现了SKU信息的生成与提交流程,涵盖了从规格参数设置到SKU生成的全过程。

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

商品规格参数

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

可以看到这里是根据商品分类id查询规格参数:SpecParam。我们之前写过一个根据gid(分组id)来查询规格参数的接口,我们接下来完成根据分类id查询规格参数。

改造查询规格参数接口

我们在原来的根据 gid(规格组id)查询规格参数的接口上,添加一个参数:cid,即商品分类id。

等一下, 考虑到以后可能还会根据是否搜索、是否为通用属性等条件过滤,我们多添加几个过滤条件:

    @GetMapping("params")
    public ResponseEntity<List<SpecParam>> queryParams(
            @RequestParam(value = "gid", required = false)Long gid,
            @RequestParam(value = "cid", required = false)Long cid,
            @RequestParam(value = "generic", required = false)Boolean generic,
            @RequestParam(value = "searching", required = false)Boolean searching
    ){

        List<SpecParam> params = this.specificationService.queryParams(gid, cid, generic, searching);

        if (CollectionUtils.isEmpty(params)){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(params);
    }

改造SpecificationService:

    /**
     * 根据gid查询规格参数
     * @param gid
     * @return
     */
    public List<SpecParam> queryParams(Long gid, Long cid, Boolean generic, Boolean searching) {
        SpecParam record = new SpecParam();
        record.setGroupId(gid);
        record.setCid(cid);
        record.setGeneric(generic);
        record.setSearching(searching);
        return this.specParamMapper.select(record);
    }

如果param中有属性为null,则不会把属性作为查询条件,因此该方法具备通用性,即可根据gid查询,也可根据cid查询。

测试:

刷新页面测试:

SKU信息

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

当我们填写一些属性后,会在页面下方生成一个sku表格,大家可以计算下会生成多少个不同属性的Sku呢?

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

  • 颜色共2种:迷夜黑,勃艮第红,绚丽蓝

  • 内存共2种:4GB,6GB

  • 机身存储1种:64GB,128GB

此时会产生多少种SKU呢? 应该是 3 * 2 * 2 = 12种,这其实就是在求笛卡尔积。

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

页面表单提交

在sku列表的下方,有一个提交按钮:

并且绑定了点击事件:

点击后会组织数据并向后台提交:

    submit() {
      // 表单校验。
      if(!this.$refs.basic.validate){
        this.$message.error("请先完成表单内容!");
      }
      // 先处理goods,用结构表达式接收,除了categories外,都接收到goodsParams中
      const {
        categories: [{ id: cid1 }, { id: cid2 }, { id: cid3 }],
        ...goodsParams
      } = this.goods;
      // 处理规格参数
      const specs = {};
      this.specs.forEach(({ id,v }) => {
        specs[id] = v;
      });
      // 处理特有规格参数模板
      const specTemplate = {};
      this.specialSpecs.forEach(({ id, options }) => {
        specTemplate[id] = options;
      });
      // 处理sku
      const skus = this.skus
        .filter(s => s.enable)
        .map(({ price, stock, enable, images, indexes, ...rest }) => {
          // 标题,在spu的title基础上,拼接特有规格属性值
          const title = goodsParams.title + " " + Object.values(rest).map(v => v.v).join(" ");
          const obj = {};
          Object.values(rest).forEach(v => {
            obj[v.id] = v.v;
          });
          return {
            price: this.$format(price), // 价格需要格式化
            stock,
            indexes,
            enable,
            title, // 基本属性
            images: images ? images.join(",") : '', // 图片
            ownSpec: JSON.stringify(obj) // 特有规格参数
          };
        });
      Object.assign(goodsParams, {
        cid1,
        cid2,
        cid3, // 商品分类
        skus // sku列表
      });
      goodsParams.spuDetail.genericSpec = JSON.stringify(specs);
      goodsParams.spuDetail.specialSpec = JSON.stringify(specTemplate);

      // 提交到后台
      this.$http({
        method: this.isEdit ? "put" : "post",
        url: "/item/goods",
        data: goodsParams
      })
        .then(() => {
          // 成功,关闭窗口
          this.$emit("close");
          // 提示成功
          this.$message.success("保存成功了");
        })
        .catch(() => {
          this.$message.error("保存失败!");
        });
    }

点击提交,查看控制台提交的数据格式:

整体是一个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:特有规格参数的下标

后台实现

1.7.1.实体类

SPU和SpuDetail实体类已经添加过,添加Sku和Stock对象:

Sku

@Table(name = "tb_sku")
public class Sku {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    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;// 库存
}

注意:这里保存了一个库存字段,在数据库中是另外一张表保存的,方便查询。

Stock

@Table(name = "tb_stock")
public class Stock {
    @Id
    private Long skuId;
    private Integer seckillStock;// 秒杀可用库存
    private Integer seckillTotal;// 已秒杀数量
    private Integer stock;// 正常库存
}

GoodsController

结合浏览器页面控制台,可以发现:

请求方式:POST

请求路径:/goods

请求参数:Spu的json格式的对象,spu中包含spuDetail和Sku集合。这里我们该怎么接收?我们之前定义了一个SpuBo对象,作为业务对象。这里也可以用它,不过需要再扩展spuDetail和skus字段:

public class SpuBo extends Spu {

    String cname;// 商品分类名称
    String bname;// 品牌名称
    SpuDetail spuDetail;// 商品详情
    List<Sku> skus;// sku列表
}
  • 返回类型:无

在GoodsController中添加新增商品的代码:

@PostMapping("goods")
public ResponseEntity<Void> saveGoods(@RequestBody SpuBo spuBo){
    this.goodsService.saveGoods(spuBo);
    return ResponseEntity.status(HttpStatus.CREATED).build();
}

注意:通过@RequestBody注解来接收Json请求

GoodsService

这里的逻辑比较复杂,我们除了要对SPU新增以外,还要对SpuDetail、Sku、Stock进行保存

/**
     * 新增商品
     * @param spuBo
     */
@Transactional
public void saveGoods(SpuBo spuBo) {
    // 新增spu
    // 设置默认字段
    spuBo.setId(null);
    spuBo.setSaleable(true);
    spuBo.setValid(true);
    spuBo.setCreateTime(new Date());
    spuBo.setLastUpdateTime(spuBo.getCreateTime());
    this.spuMapper.insertSelective(spuBo);

    // 新增spuDetail
    SpuDetail spuDetail = spuBo.getSpuDetail();
    spuDetail.setSpuId(spuBo.getId());
    this.spuDetailMapper.insertSelective(spuDetail);

    saveSkuAndStock(spuBo);
}

private void saveSkuAndStock(SpuBo spuBo) {
    spuBo.getSkus().forEach(sku -> {
        // 新增sku
        sku.setSpuId(spuBo.getId());
        sku.setCreateTime(new Date());
        sku.setLastUpdateTime(sku.getCreateTime());
        this.skuMapper.insertSelective(sku);

        // 新增库存
        Stock stock = new Stock();
        stock.setSkuId(sku.getId());
        stock.setStock(sku.getStock());
        this.stockMapper.insertSelective(stock);
    });
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值