乐优商城(05)–商品管理

一、导入图片资源

现在商品表中虽然有数据,但是所有的图片信息都是无法访问的,因此需要把图片导入到服务器中:

将images.zip文件上传至/leyou/static目录:在leyou下创建static目录

使用命令解压:

unzip images.zip

修改Nginx配置,使nginx反向代理这些图片地址:

vim /opt/nginx/config/nginx.conf

修改成如下配置:

server {listen       80;server_name  image.leyou.com;# 监听域名中带有group的,交给FastDFS模块处理location ~/group([0-9])/ {ngx_fastdfs_module;}# 将其它图片代理指向本地的/leyou/static目录location / {root   /leyou/static/;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

不要忘记重新加载nginx配置

nginx -s reload

二、商品查询

2.1、前端页面

先看整体页面结构(Goods.vue):

并且在Vue实例挂载后就会发起查询(mounted调用getDataFromServer方法初始化数据):

data中数据的修改:

2.2、后端实现

2.2.1、实体类

Spu

@Table(name = "tb_spu")
public class Spu {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;                //主键idprivate String title;           //标题private String subTitle;        //小标题private Long cid1;              //1级类目private Long cid2;              //2级类目private Long cid3;              //3级类目private Long brandId;           //品牌idprivate Boolean saleable;       //是否上架private Boolean valid;          //是否有效  逻辑删除private Date createTime;        //创建时间private Date lastUpdateTime;    //最后修改时间//get和set
}

SpuDetail

@Table(name="tb_spu_detail")
public class SpuDetail {@Idprivate Long spuId;// 对应的SPU的idprivate String description;// 商品描述private String specialSpec;// 商品特殊规格的名称及可选值模板private String genericSpec;// 商品的全局规格属性private String packingList;// 包装清单private String afterService;// 售后服务// get和set
}

2.2.2、mapper

public interface SpuMapper extends Mapper<Spu> {}

2.2.3、controller

  • 请求方式:GET

  • 请求路径:/spu/page

  • 请求参数:

    • page:当前页
    • rows:每页大小
    • key:过滤条件
    • saleable:上架或下架
    • sortBy:排序字段
  • 返回结果:商品SPU的分页信息。

    • 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,新建一个类,继承SPU,并且拓展cname和bname属性,写到leyou-item-interface
    public class SpuBo extends Spu {private String cname; // 商品分类名称private String bname; // 品牌名称private SpuDetail spuDetail;  //商品详情private List<Sku> skus;       //sku列表//get和set
    }
    

    这里提前贴上Sku的代码

    @Table(name = "tb_sku")
    public class Sku {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;                //sku主键idprivate Long spuId;             //spu的idprivate String title;           //商品标题private String images;          //商品图片,会有多张,以,分隔private Long price;             //销售价格,以分为单位private String indexes;         //特有属性在spu中对应的下标private String ownSpec;         //商品特殊规格的键值对private Boolean enable;         //是否有效,逻辑删除用private Date createTime;        //创建时间private Date lastUpdateTime;    //最后修改时间@Transientprivate Integer stock;          //库存//get和set
    }
    

编写controller代码:

这里把与商品相关的一切业务接口都放到一起,起名为GoodsController,业务层也是这样

@RestController
public class GoodsController {@Autowiredprivate GoodsService goodsService;/*** 根据查询条件分页并排序查询商品信息* @param key* @param saleable* @param page* @param rows* @return*/@GetMapping("/spu/page")public ResponseEntity<PageResult<SpuBo>> querySpuBoByPage(@RequestParam(value = "key",required = false) String key,@RequestParam(value = "saleable",required = false) Boolean saleable,@RequestParam(value = "page",defaultValue = "1") Integer page,@RequestParam(value = "rows",defaultValue = "5") Integer rows,@RequestParam(value = "sortBy",required = false) String sortBy,@RequestParam(value = "desc",required = false) Boolean desc){PageResult<SpuBo> pageResult = this.goodsService.querySpuBoByPage(key,saleable,page,rows,sortBy,desc);if (CollectionUtils.isEmpty(pageResult.getItems())){return ResponseEntity.notFound().build();}return ResponseEntity.ok(pageResult);}}

2.2.4、service

public interface GoodsService {/*** 根据查询条件分页并排序查询商品信息* @param key* @param saleable* @param page* @param rows* @return*/PageResult<SpuBo> querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows,String sortBy,Boolean desc);}

实现类:

@Service
public class GoodsServiceImpl implements GoodsService {@Autowiredprivate SpuMapper spuMapper;@Autowiredprivate CategoryService categoryService;@Autowiredprivate BrandMapper brandMapper;/*** 根据查询条件分页并排序查询商品信息** @param key* @param saleable* @param page* @param rows* @return*/@Overridepublic PageResult<SpuBo> querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows,String sortBy,Boolean desc) {//添加搜素条件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);// 添加排序条件if (StringUtils.isNotBlank(sortBy)){example.setOrderByClause(sortBy + " " + (desc ? "desc" : "asc"));}//查询数据List<Spu> spus = this.spuMapper.selectByExample(example);PageInfo<Spu> spuPageInfo = new PageInfo<>(spus);//将Spu->SpuBoArrayList<SpuBo> spuBos = new ArrayList<>();spus.forEach(spu -> {SpuBo spuBo = new SpuBo();//将spu的属性copy到spuBoBeanUtils.copyProperties(spu,spuBo);//查询分类名称List<String> names =  this.categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(),spu.getCid2(),spu.getCid3()));//spuBo设置分类名称spuBo.setCname(StringUtils.join(names,"/"));//spuBo设置品牌名称spuBo.setBname(this.brandMapper.selectByPrimaryKey(spu.getBrandId()).getName());spuBos.add(spuBo);});//返回分页结果信息return new PageResult<>(spuPageInfo.getTotal(),spuBos);}
}

2.2.5、Category中拓展查询名称的功能

页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,

在CategoryService中添加功能:

/*** 通过分类id查询分类名称** @param ids* @return*/
@Override
public List<String> queryNamesByIds(List<Long> ids) {List<Category> categories = this.categoryMapper.selectByIdList(ids);return categories.stream().map(Category::getName).collect(Collectors.toList());
}

mapper的selectByIdList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口:

public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> { }

三、商品新增

当点击新增商品按钮,会出现一个弹窗:

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

  • 基本信息:主要是一些简单的文本数据,包含了SPU和SpuDetail的部分数据,如

    • 商品分类:是SPU中的cid1cid2cid3属性
    • 品牌:是spu中的brandId属性
    • 标题:是spu中的title属性
    • 子标题:是spu中的subTitle属性
    • 售后服务:是SpuDetail中的afterService属性
    • 包装列表:是SpuDetail中的packingList属性
  • 商品描述:是SpuDetail中的description属性,数据较多,所以单独放一个页面
  • 规格参数:商品规格信息,对应SpuDetail中的genericSpec属性
  • SKU属性:spu下的所有Sku信息

对应到GoodsForm.vue页面中的四个v-stepper-content

该弹窗是一个独立组件为GoodsForm.vue,在页面Goods.vue中import导入使用并渲染,当按钮被点击时,会改变data中的show属性,使弹窗可见。

3.1、第一部分:基本信息

3.1.1、前端页面

点击商品分类,可以发现有分类信息显示,这里商品分类信息的接口前面已经实现了

点击所属品牌,查看控制台,请求已经发出:

前端页面是通过watch函数监控商品分类的变化实现的,每当商品分类值有变化,就会发起请求,查询品牌列表:

3.1.2、后端实现

controller

在BrandController中添加:

请求方式:GET

请求路径:/item/brand/cid/{cid}

请求参数:cid

响应数据:品牌集合

/*** 根据分类id查询品牌集合* @param cid* @return*/
@GetMapping("/cid/{cid}")
public ResponseEntity<List<Brand>> queryBrandsByCid(@PathVariable("cid") Long cid){List<Brand> brands = this.brandService.queryBrandsByCid(cid);if (CollectionUtils.isEmpty(brands)){return ResponseEntity.notFound().build();}return ResponseEntity.ok(brands);
}

service

/*** 根据分类id查询品牌集合* @param cid* @return*/
List<Brand> queryBrandsByCid(Long cid);

实现类:

/*** 根据分类id查询品牌集合** @param cid* @return*/
@Override
public List<Brand> queryBrandsByCid(Long cid) {return this.brandMapper.queryBrandsByCid(cid);
}

mapper

/*** 根据分类id查询品牌集合* @param cid* @return*/
@Select("SELECT b.* from tb_brand b INNER JOIN tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}")
List<Brand> queryBrandsByCid(@Param("cid") Long cid);

3.1.3、其它文本框

剩余的几个属性:标题、子标题等都是普通文本框,直接填写即可。

3.2、第二部分:商品描述

商品描述信息比较复杂,而且图文并茂,甚至包括视频。这样的内容,一般都会使用富文本编辑器。

这里使用的是:Vue-Quill-Editor

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

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

3.2.1、使用指南

使用非常简单:已经在项目中集成。以下步骤不需操作,仅供参考

第一步:安装,使用npm命令:

npm install vue-quill-editor --save

第二步:加载,在js中引入:

全局引入:

import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'const options = {}; /* { default global options } */Vue.use(VueQuillEditor, options); // options可选

局部引入:

import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'import {quillEditor} from 'vue-quill-editor'var vm = new Vue({components:{quillEditor}
})

本项目这里采用局部引用:

第三步:页面使用:

<quill-editor v-model="goods.spuDetail.description" :options="editorOption"/>

3.2.2、自定义的富文本编辑器

过这个组件有个小问题,就是图片上传的无法直接上传到后台,因此这里对其进行了封装,支持了图片的上传。

在目录src/components/form/Editor.vue

使用也非常简单:

<v-stepper-content step="2"><v-editor v-model="goods.spuDetail.description" upload-url="/upload/image"/>
</v-stepper-content>
  • upload-url:是图片上传的路径
  • v-model:双向绑定,将富文本编辑器的内容绑定到goods.spuDetail.description

3.3、第三部分:规格参数

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

3.3.1、改造规格参数查询接口

在SpecificationController中修改queryParamsByGid

/*** 根据 多参数查询具体 SpecParam* @param gid* @param cid* @param generic* @param searching* @return*/
@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);
}

service

/*** 根据 多参数查询具体 SpecParam* @param gid* @param cid* @param generic* @param searching* @return*/
List<SpecParam> queryParams(Long gid,Long cid,Boolean generic,Boolean searching);

实现类:

/*** 根据 多参数查询具体 SpecParam* @param gid* @param cid* @param generic* @param searching* @return*/
@Override
public List<SpecParam> queryParams(Long gid,Long cid,Boolean generic,Boolean searching) {//param中有属性为null,则不会把属性作为查询条件,因此该方法具备通用性SpecParam specParam = new SpecParam();specParam.setGroupId(gid);specParam.setCid(cid);specParam.setGeneric(generic);specParam.setSearching(searching);return this.specParamMapper.select(specParam);
}

注意,由于之前deleteSpecGroup方法中依赖了queryParamsByGid,所以同样要修改:

List<SpecParam> specParams = queryParams(gid,null,null,null);

3.4、第四部分:SKU信息

3.4.1、前端页面

Sku属性是SPU下的每个商品的不同特征,当我们填写一些属性后,会在页面下方生成一个sku表格,比如:

  • 颜色共2种:迷夜黑,勃艮第红,绚丽蓝
  • 内存共2种:4GB,6GB
  • 机身存储1种:64GB,128GB

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

在sku列表的下方,有一个提交按钮,绑定了一个点击事件submit,点击后会将数据组织并发送给后台

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

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

3.4.2、后端实现

实体类

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

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

GoodsController

  • 请求方式:POST

  • 请求路径:/goods

  • 请求参数:Spu的json格式的对象,spu中包含spuDetail和Sku集合。

    • 这里该怎么接收?之前定义了一个SpuBo对象,作为业务对象。这里也可以用它,不过需要再扩展spuDetail和skus字段,之前贴的SpuBo代码是完整的,这里省略
  • 返回类型:无

/*** 添加新商品* @param spuBo* @return*/
@PostMapping("/goods")
public ResponseEntity<Void> addGoods(@RequestBody SpuBo spuBo){this.goodsService.addGoods(spuBo);return ResponseEntity.status(HttpStatus.CREATED).build();
}

service

/*** 添加新商品* @param spuBo* @return*/
void addGoods(SpuBo spuBo);

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

/*** 添加新商品* @param spuBo* @return*/
@Override
@Transactional
public void addGoods(SpuBo spuBo) {//先新增spuspuBo.setId(null);spuBo.setSaleable(true);spuBo.setValid(true);spuBo.setCreateTime(new Date());spuBo.setLastUpdateTime(spuBo.getCreateTime());this.spuMapper.insertSelective(spuBo);//再增spuDetailSpuDetail spuDetail = spuBo.getSpuDetail();spuDetail.setSpuId(spuBo.getId());this.spuDetailMapper.insertSelective(spuDetail);saveSkuAndStock(spuBo);}/*** 添加 sku 和 stock* @param spuBo*/
private void saveSkuAndStock(SpuBo spuBo){spuBo.getSkus().forEach(sku -> {//增skusku.setId(null);sku.setSpuId(spuBo.getId());sku.setCreateTime(new Date());sku.setLastUpdateTime(sku.getCreateTime());this.skuMapper.insertSelective(sku);//最后增stockStock stock = new Stock();stock.setSkuId(sku.getId());stock.setStock(sku.getStock());this.stockMapper.insertSelective(stock);});
}

mapper

/*** SpuDetail 的通用mapper*/
public interface SpuDetailMapper extends Mapper<SpuDetail> {}
/*** Sku 通用mapper*/
public interface SkuMapper extends Mapper<Sku> {}
/*** Stock 的通用mapper*/
public interface StockMapper extends Mapper<Stock> {}

四、商品修改

4.1、前端页面

点击修改按钮,会先发送一个请求获取数据

页面代码:

可以看到这里发起了两个请求,在查询商品详情和sku信息。

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

4.2、后端实现

4.2.1、查询SpuDetail接口

controller

  • 请求方式:GET
  • 请求路径:/item/spu/detail/{id}
  • 请求参数:id,应该是spu的id
  • 返回结果:SpuDetail对象
/*** 通过 spuId 查询 spuDetail* @param spuId* @return*/
@GetMapping("/spu/detail/{spuId}")
public ResponseEntity<SpuDetail> querySpuDetailBySpuId(@PathVariable("spuId") Long spuId){SpuDetail spuDetail = this.goodsService.querySpuDetailBySpuId(spuId);if (spuDetail == null){return ResponseEntity.notFound().build();}return ResponseEntity.ok(spuDetail);
}

service

/*** 通过 spuId 查询 spuDetail* @param spuId* @return*/
SpuDetail querySpuDetailBySpuId(Long spuId);

实现类:

/*** 通过 spuId 查询 spuDetail** @param spuId* @return*/
@Override
public SpuDetail querySpuDetailBySpuId(Long spuId) {return this.spuDetailMapper.selectByPrimaryKey(spuId);
}

4.2.2、查询sku

controller

  • 请求方式:Get
  • 请求路径:/sku/list
  • 请求参数:id,应该是spu的id
  • 返回结果:sku的集合
/*** 通过 spuId 查询 Sku 集合* @param spuId* @return*/
@GetMapping("/sku/list")
public ResponseEntity<List<Sku>> querySkusBySpuId(@RequestParam("id") Long spuId){List<Sku> skus = this.goodsService.querySkusBySpuId(spuId);if (CollectionUtils.isEmpty(skus)){return ResponseEntity.notFound().build();}return ResponseEntity.ok(skus);
}

service

/*** 通过 spuId 查询 Sku 集合* @param spuId* @return*/
List<Sku> querySkusBySpuId(Long spuId);

实现类:

/*** 通过 spuId 查询 Sku 集合** @param spuId* @return*/
@Override
public List<Sku> querySkusBySpuId(Long spuId) {Sku sku = new Sku();sku.setSpuId(spuId);List<Sku> skus = this.skuMapper.select(sku);skus.forEach(s -> {//设置每个 sku 的库存量Stock stock = this.stockMapper.selectByPrimaryKey(s.getId());s.setStock(stock.getStock());});return skus;
}

到这里,点击编辑按钮,可以发现数据回显了,修改一点数据,进行保存提交时,发现又发送一个请求

4.2.3、更新商品

controller

  • 请求方式:PUT
  • 请求路径:/goods
  • 请求参数:Spu对象
  • 返回结果:无
/*** 更新商品信息* @param spuBo*/
@PutMapping("/goods")
public ResponseEntity<Void> updateGoods(@RequestBody SpuBo spuBo){this.goodsService.updateGoods(spuBo);return ResponseEntity.noContent().build();
}

service

/*** 更新商品信息* @param spuBo*/
void updateGoods(SpuBo spuBo);

实现类:

/*** 更新商品信息** @param spuBo*/
@Override
@Transactional
public void updateGoods(SpuBo spuBo) {//删除再添加,完成更新//先查询以前的数据List<Sku> skus = this.querySkusBySpuId(spuBo.getId());if (!CollectionUtils.isEmpty(skus)){List<Long> ids = skus.stream().map(Sku::getId).collect(Collectors.toList());//先删除stockExample example = new Example(Stock.class);example.createCriteria().andIn("skuId",ids);this.stockMapper.deleteByExample(example);//再删除skuSku sku = new Sku();sku.setSpuId(spuBo.getId());this.skuMapper.delete(sku);}// 新增sku和stocksaveSkuAndStock(spuBo);//更新spu//数据库某些字段只能有服务器更改//也防止用户恶意修改spuBo.setLastUpdateTime(new Date());spuBo.setCreateTime(null);spuBo.setValid(null);spuBo.setSaleable(null);this.spuMapper.updateByPrimaryKeySelective(spuBo);//更新spuDetailthis.spuDetailMapper.updateByPrimaryKeySelective(spuBo.getSpuDetail());
}

五、商品删除及上下架

5.1、前端页面

点击删除按钮确定后,可以发现已经发生请求

页面代码:

5.2、后端实现

5.2.1、商品删除

controller

  • 请求方式:DELETE
  • 请求路径:/goods
  • 请求参数:SpuId
  • 返回结果:无
/*** 通过spuId删除商品* @param spuId* @return*/
@DeleteMapping("/goods")
public ResponseEntity<Void> deleteGoodsBySpuId(@RequestParam("id") Long spuId){this.goodsService.deleteGoodsBySpuId(spuId);return ResponseEntity.ok().build();
}

service

/*** 通过spuId删除商品* @param spuId* @return*/
void deleteGoodsBySpuId(Long spuId);

实现类:

/*** 通过spuId删除商品** @param spuId* @return*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteGoodsBySpuId(Long spuId) {Sku sku = new Sku();sku.setSpuId(spuId);//删除sku和stockdeleteSkuAndStock(sku);//删除spuDetailthis.spuDetailMapper.deleteByPrimaryKey(spuId);//删除sputhis.spuMapper.deleteByPrimaryKey(spuId);
}/*** 删除sku和stock的方法* @param sku*/
private void deleteSkuAndStock(Sku sku){List<Sku> skuList = this.skuMapper.select(sku);if (!CollectionUtils.isEmpty(skuList)){//删除skuthis.skuMapper.delete(sku);//删除stockList<Long> ids = skuList.stream().map(Sku::getId).collect(Collectors.toList());this.stockMapper.deleteByIdList(ids);}
}

mapper

/*** Stock 的通用mapper*/
public interface StockMapper extends Mapper<Stock>, IdListMapper<Stock,Long> {}

5.2.2、上下架

controller

  • 请求方式:PUT
  • 请求路径:/goods/spu/out/{id}
  • 请求参数:SpuId
  • 返回结果:无
/*** 通过 spuId 修改商品的上下架状态* @param spuId* @return*/
@PutMapping("/goods/spu/out/{id}")
public ResponseEntity<Void> soldOutPut(@PathVariable("id") Long spuId){this.goodsService.updateSaleableBySpuId(spuId);return ResponseEntity.ok().build();
}

service

/*** 通过 spuId 修改商品的上下架状态* @param spuId* @return*/
void updateSaleableBySpuId(Long spuId);

实现类:

/*** 通过 spuId 修改商品的上下架状态** @param spuId* @return*/
@Override
@Transactional
public void updateSaleableBySpuId(Long spuId) {Spu spu = this.spuMapper.selectByPrimaryKey(spuId);Spu spu1 = new Spu();spu1.setId(spu.getId());spu1.setSaleable(!spu.getSaleable());this.spuMapper.updateByPrimaryKeySelective(spu1);
}

乐优商城(05)--商品管理相关推荐

  1. 乐优商城day13(商品详情页,rabbitMQ安装)

    所有代码发布在 [https://github.com/hades0525/leyou] Day13(rabbitmq) 2019年2月13日 14:45 使用thymeleaf thymeleaf基 ...

  2. 乐优商城(四)商品规格管理

    文章目录 1. 商品规格 1.1 SPU 和 SKU 1.2 分析商品规格的关系 1.3 数据库设计 1.3.1 商品规格组表 1.3.2 商品规格参数表 2. 商品规格组 2.1 商品规格组前端 2 ...

  3. 【javaWeb微服务架构项目——乐优商城day05】——商品规格参数管理(增、删、改,查已完成),SPU和SKU数据结构,商品查询

    乐优商城day05 0.学习目标 1.商品规格数据结构 1.1.SPU和SKU 1.2.数据库设计分析 1.2.1.思考并发现问题 1.2.2.分析规格参数 1.2.3.SKU的特有属性 1.2.4. ...

  4. 乐优商城学习笔记五-商品规格管理

    0.学习目标 了解商品规格数据结构设计思路 实现商品规格查询 了解SPU和SKU数据结构设计思路 实现商品查询 了解商品新增的页面实现 独立编写商品新增后台功能 1.商品规格数据结构 乐优商城是一个全 ...

  5. 【javaWeb微服务架构项目——乐优商城day03】——(搭建后台管理前端,Vuetify框架,使用域名访问本地项目,实现商品分类查询,cors解决跨域,品牌的查询)

    乐优商城day03 0.学习目标 1.搭建后台管理前端 1.1.导入已有资源 1.2.安装依赖 1.3.运行一下看看 1.4.目录结构 1.5.调用关系 2.Vuetify框架 2.1.为什么要学习U ...

  6. 乐优商城笔记六:商品详情页

    使用模板引擎 Thymeleaf + nginx 完成商品详情页静态化 完成乐优商城商品详情页 搭建商品详情页微服务 创建子工程 GroupId:com.leyou.service ArtifactI ...

  7. 乐优商城 Day 09(thymeleaf,Rabbitmq,商品详情页,非教程)

    乐优商城学习Day09: 注意:此次代码都是在第八天的基础上 第八天的链接如下: https://blog.csdn.net/zcylxzyh/article/details/100859210 此次 ...

  8. 乐优商城之项目搭建(四)

    文章目录 (一)项目分类 (二)电商行业 (三)专业术语 (四)项目介绍 (五)技术选型 (六)开发环境 (七)搭建后台环境:父工程 (八)搭建后台环境:eureka (九)搭建后台环境:zuul ( ...

  9. 乐优商城(10)--数据同步

    乐优商城(10)–数据同步 一.RabbitMQ 1.1.问题分析 目前已经完成了商品详情和搜索系统的开发.思考一下,是否存在问题? 商品的原始数据保存在数据库中,增删改查都在数据库中完成. 搜索服务 ...

最新文章

  1. 谷歌如何评估产品经理?
  2. Windows Phone 7 不温不火学习之《项目模板》
  3. 英语与计算机的整合,浅谈计算机应用与英语教学的整合
  4. mysql pmm进程_mysql性能监控软件pmm
  5. Android数据存储之文件存储(瞬时数据的存储与读取)项目已上传GitHub
  6. 160家优秀国外技术公司博客
  7. C语言不使用结构体实现链表,不用指针链表和结构体数组怎么编学生成绩管理系统啊...
  8. python po设计模式_Python Selenium设计模式 - PO设计模式
  9. Go语言的三元表达式
  10. vs2015软件系统开源_特别版:2015年开源新闻
  11. GaussDB分布式Stream执行计划详解
  12. CNN进行新闻文本分类代码实战,包含分类文本
  13. 30 道 MySQL 面试题全放送!
  14. C# 静态类的构造函数
  15. 58-20210406华为海思Hi3516DV300的linux系统下获取IMX335的视频(eMMC模式)
  16. iPhone手机开启定位权限后,仍然无法定位
  17. win8计算机休眠的区别,win8系统的休眠和睡眠有什么区别?如何用?
  18. 1026 程序运行时间
  19. 图像处理之图像的像素运算
  20. 智能设计|零基础,低成本,轻松实现设计效率翻倍

热门文章

  1. 刀剑斗神传只显示11个服务器,与官方服务器互通 《刀剑斗神传》电脑版即将上线...
  2. 阿ken的HTML、CSS的学习笔记_文本样式属性(笔记三)
  3. HEVC一些工具汇总
  4. PQ4R Smashin Scope
  5. php网页源码财务管理系统mysql数据库web结构html布局
  6. 软考之数据库系统工程师之知识点整理
  7. Vayo-Gerber View安装教程
  8. 强烈推荐iOS开发取色器
  9. 如何让电脑显示SVG图片的缩略图
  10. 跨境卖家该怎么选择海外收款账户