乐优商城项目---day09-规格参数

3.5.java客户端

余庆先生提供了一个Java客户端,但是作为一个C程序员,写的java代码可想而知。而且已经很久不维护了。

这里推荐一个开源的FastDFS客户端,支持最新的SpringBoot2.0。

配置使用极为简单,支持连接池,支持自动生成缩略图,狂拽酷炫吊炸天啊,有木有。

依赖报错:

。这个问题是maven中央仓库没有fastdfs-client-java的jar包导致的,虽然可以在maven的库里查到依赖,但是无法下载到本地(脑阔疼)。 解决办法是:从gitHub上下载fastdfs的工具包,自己打个jar包出来。 1.下载fastdfs-client-java开发工具包

https://github.com/happyfish100/fastdfs-client-java

 

2.把fastdfs-client-java开发工具包打包到本地的Maven仓库,在Windows窗口输入如下命令(注意:本机必须安装Maven,并配置Maven环境变量,并且盘符要切换到fastdfs-client-java解压的目录下)
mvn clean install

 jar包在仓库安装成功

冲突解决成功

FastDFS文件上传

进行Fdsfs测试的时候一定要把Linx的防火墙关闭

测试成功

地址访问成功:

 

文件下载:

缩略图显示成功:

 

修改文件上传逻辑:

     String extend = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
            StorePath storePath = fastFileStorageClient.uploadFile(file.getInputStream(), file.getSize(), extend, null);
            return "http://image.leyou.com/"+storePath.getFullPath();

完成测试:

--------------------------------------------------------------------------------------------------------

 

1.商品规格数据结构

乐优商城是一个全品类的电商网站,因此商品的种类繁多,每一件商品,其属性又有差别。为了更准确描述商品及细分差别,抽象出两个概念:SPU和SKU,了解一下:

1.1.SPU和SKU

SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集

SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品

以图为例来看:

 

  • 本页的 华为Mate10 就是一个商品集(SPU)

  • 因为颜色、内存等不同,而细分出不同的Mate10,如亮黑色128G版。(SKU)

可以看出:

  • SPU是一个抽象的商品集概念,为了方便后台的管理。

  • SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU

1.2.数据库设计分析

1.2.1.思考并发现问题

弄清楚了SPU和SKU的概念区分,接下来我们一起思考一下该如何设计数据库表。

首先来看SPU,大家一起思考下SPU应该有哪些字段来描述?

 

id:主键
title:标题
description:描述
specification:规格
packaging_list:包装
after_service:售后服务
comment:评价
category_id:商品分类
brand_id:品牌

再看下SKU,大家觉得应该有什么字段?

id:主键
spu_id:关联的spu
price:价格
images:图片
stock:库存
颜色?
内存?
硬盘?

碰到难题了,不同的商品分类,可能属性是不一样的,比如手机有内存,衣服有尺码,我们是全品类的电商网站,这些不同的商品的不同属性,如何设计到一张表中?

其实颜色、内存、硬盘属性都是规格参数中的字段。所以,要解决这个问题,首先要能清楚规格参数。

1.2.2.分析规格参数

仔细查看每一种商品的规格你会发现:

虽然商品规格千变万化,但是同一类商品(如手机)的规格是统一的,有图为证:

华为的规格:

三星的规格:

1.2.3.SKU的特有属性

SPU中会有一些特殊属性,用来区分不同的SKU,我们称为SKU特有属性。如华为META10的颜色、内存属性。

不同种类的商品,一个手机,一个衣服,其SKU属性不相同。

同一种类的商品,比如都是衣服,SKU属性基本是一样的,都是颜色、尺码等。

这样说起来,似乎SKU的特有属性也是与分类相关的?事实上,仔细观察你会发现,SKU的特有属性是商品规格参数的一部分

1.3.规格参数表

1.3.1.表结构

我们看下规格参数的格式:

1.3.2.规格组

规格参数分组表:tb_spec_group

CREATE TABLE `tb_spec_group` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `cid` bigint(20) NOT NULL COMMENT '商品分类id,一个分类下有多个规格组',
  `name` varchar(50) NOT NULL COMMENT '规格组的名称',
  PRIMARY KEY (`id`),
  KEY `key_category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='规格参数的分组表,每个商品分类下有多个规格参数组';

规格组有3个字段:

  • id:主键

  • cid:商品分类id,一个分类下有多个模板

  • name:该规格组的名称。

通用属性

用一个布尔类型字段来标记是否为通用:

  • generic来标记是否为通用属性:

    • true:代表通用属性

    • false:代表sku特有属性

搜索过滤

与搜索相关的有两个字段:

  • searching:标记是否用作过滤

    • true:用于过滤搜索

    • false:不用于过滤

  • segments:某些数值类型的参数,在搜索时需要按区间划分,这里提前确定好划分区间

    • 比如电池容量,0~2000mAh,2000mAh~3000mAh,3000mAh~4000mAh

数值类型

某些规格参数可能为数值类型,这样的数据才需要划分区间,我们有两个字段来描述:

  • numberic:是否为数值类型

    • true:数值类型

    • false:不是数值类型

  • unit:参数的单位

2.商品规格参数管理

2.1.页面布局

2.1.1.整体布局

打开规格参数页面,看到如下内容:

 

 

 

这里使用了v-layout来完成页面布局,并且添加了row属性,代表接下来的内容是行布局(左右)。

可以看出页面分成2个部分:

  • <v-flex xs3>:左侧,内部又分上下两部分:商品分类树及标题

    • v-card-title:标题部分,这里是提示信息,告诉用户要先选择分类,才能看到模板

    • v-tree:这里用到的是我们之前讲过的树组件,展示商品分类树,

  • <v-flex xs9 class="px-1">:右侧:内部是规格参数展示

 

 

2.2.3.后端代码

实体类

leyou-item-interface中添加实体类:后端代码其实还是SSM哪一套Controller,Servivce,Mapper

Controller:

@RestController
@RequestMapping("spec")
public class SpecificationController {

    @Autowired
    private SpecificationService specificationService;

    /**
     * 根据分类id查询分组
     * @param cid
     * @return
     */
    @GetMapping("groups/{cid}")
    public ResponseEntity<List<SpecGroup>> queryGroupsByCid(@PathVariable("cid")Long cid){
        List<SpecGroup> groups = this.specificationService.queryGroupsByCid(cid);
        if (CollectionUtils.isEmpty(groups)){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(groups);
    }
}

Service:

@Service
public class SpecificationService {

    @Autowired
    private SpecGroupMapper groupMapper;

    /**
     * 根据分类id查询分组
     * @param cid
     * @return
     */
    public List<SpecGroup> queryGroupsByCid(Long cid) {
        SpecGroup specGroup = new SpecGroup();
        specGroup.setCid(cid);
        return this.groupMapper.select(specGroup);
    }
}

Mapper:

public interface SpecGroupMapper extends Mapper<SpecGroup> {
}

public interface SpecParamMapper extends Mapper<SpecParam> {
}

我们访问:http://api.leyou.com/api/item/spec/groups/76

完成测试:

 关于这个规格参数的增删改查和之前的基本类似

Controller:

   @PostMapping(value = "param")
    public ResponseEntity<Void> EditBrand(@RequestBody SpecParam specParam){
        this.specificationService.addSpecParam(specParam);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

    @PutMapping(value = "param")
    public ResponseEntity<Void> EditSpecParam(@RequestBody SpecParam specParam){
        this.specificationService.editSpecSpecParam(specParam);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
    @DeleteMapping(value = "param/{id}")
    public ResponseEntity<Void> DeleteSpecParam(@PathVariable("id") Long id){
        this.specificationService.deleteSpecParam(id);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

Service:

  public void addSpecParam(SpecParam specParam) {
        this.specParamMapper.insertSelective(specParam);
    }

    public void editSpecSpecParam(SpecParam specParam) {
        this.specParamMapper.updateByPrimaryKeySelective(specParam);
    }

    public void deleteSpecParam(Long id) {
        SpecParam record=new SpecParam();
        record.setId(id);
        this.specParamMapper.deleteByPrimaryKey(record);
    }

3.SPU和SKU数据结构

规格确定以后,就可以添加商品了,先看下数据库表

3.1.SPU表

这张表中的数据都比较大,为了不影响主表的查询效率我们拆分出这张表。

需要注意的是这两个字段:generic_spec和special_spec。

前面讲过规格参数与商品分类绑定,一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数信息,因此我们计划是这样:

  • SPUDetail中保存通用的规格参数信息。

  • SKU中保存特有规格参数。

来看下我们的表如何存储这些信息。

3.1.1.generic_spec字段

首先是generic_spec,其中保存通用规格参数信息的值,这里为了方便查询,使用了json格式:

 

 

 

 

3.2.SKU表

问题:为什么要将库存独立一张表?

因为库存字段写频率较高,而SKU的其它字段以读为主,因此我们将两张表分离,读写不会干扰。

 

特别需要注意的是sku表中的indexes字段和own_spec字段。sku中应该保存特有规格参数的值,就在这两个字段中。

 

4.商品查询

4.1.效果预览

接下来,我们实现商品管理的页面,先看下我们要实现的效果:

 

4.3.后台提供接口

页面已经准备好,接下来在后台提供分页查询SPU的功能。

Controller:

先分析:

  • 请求方式:GET

  • 请求路径:/spu/page

  • 请求参数:

    • page:当前页

    • rows:每页大小

    • key:过滤条件

    • saleable:上架或下架

  • 返回结果:商品SPU的分页信息。

    • 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办?

      我们可以新建一个类,继承SPU,并且拓展cname和bname属性,写到leyou-item-interface

@Controller
public class SpuController {

    @Autowired
    private GoodService goodService;
    @RequestMapping("spu/page")
    public ResponseEntity<PageResult<SpuBo>> queryGoods(
            @RequestParam(value = "key",required = false) String key,
            @RequestParam(value = "saleable",required = false) String saleable,
            @RequestParam(value = "page",defaultValue = "1") Integer page,
            @RequestParam(value = "rows",defaultValue = "5") Integer rows
    ){
          PageResult<SpuBo> pageResult=  this.goodService.queryGoodsList(key,saleable,page,rows);
        if (pageResult==null || CollectionUtils.isEmpty(pageResult.getItems())){
            return ResponseEntity.badRequest().build();
        }
        return ResponseEntity.ok(pageResult);
    }
}

Service:

@Service
public class GoodService {

    @Autowired
    private BrandMapper brandMapper;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private SpuMapper spuMapper;

    public PageResult<SpuBo> queryGoodsList(String key, String saleable, Integer page, Integer 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);
        }
        //执行分页
        PageHelper.startPage(page,rows);
        //查询
        List<Spu> spus = this.spuMapper.selectByExample(example);
        //创建分页对象
        PageInfo<Spu> pageInfo = new PageInfo<>(spus);
        List<SpuBo> spuBoList=new ArrayList<>();
        //将Spu转换为SpuBo,并设置Bname和Cname
        spus.forEach(spu -> {
            SpuBo spuBo = new SpuBo();
          //赋值属性
            BeanUtils.copyProperties(spu,spuBo);
            spuBo.setBname(this.brandMapper.selectByPrimaryKey(spu.getBrandId()).getName());
           List<String> names= this.categoryService.queryCategoryByNames(spu.getCid1(),spu.getCid2(),spu.getCid3());
            spuBo.setCname(StringUtils.join(names,"/"));
            spuBoList.add(spuBo);
        });
        return new PageResult<>(pageInfo.getTotal(),spuBoList);
    }
}

Mapper:

public interface SpuMapper extends Mapper<Spu> {
}

完成测试:

 

今日总结 :

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值