乐优商城(05)--商品管理
乐优商城(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 }
- 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,新建一个类,继承SPU,并且拓展cname和bname属性,写到
编写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中的
cid1
,cid2
,cid3
属性 - 品牌:是spu中的
brandId
属性 - 标题:是spu中的
title
属性 - 子标题:是spu中的
subTitle
属性 - 售后服务:是SpuDetail中的
afterService
属性 - 包装列表:是SpuDetail中的
packingList
属性
- 商品分类:是SPU中的
- 商品描述:是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)--商品管理相关推荐
- 乐优商城day13(商品详情页,rabbitMQ安装)
所有代码发布在 [https://github.com/hades0525/leyou] Day13(rabbitmq) 2019年2月13日 14:45 使用thymeleaf thymeleaf基 ...
- 乐优商城(四)商品规格管理
文章目录 1. 商品规格 1.1 SPU 和 SKU 1.2 分析商品规格的关系 1.3 数据库设计 1.3.1 商品规格组表 1.3.2 商品规格参数表 2. 商品规格组 2.1 商品规格组前端 2 ...
- 【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. ...
- 乐优商城学习笔记五-商品规格管理
0.学习目标 了解商品规格数据结构设计思路 实现商品规格查询 了解SPU和SKU数据结构设计思路 实现商品查询 了解商品新增的页面实现 独立编写商品新增后台功能 1.商品规格数据结构 乐优商城是一个全 ...
- 【javaWeb微服务架构项目——乐优商城day03】——(搭建后台管理前端,Vuetify框架,使用域名访问本地项目,实现商品分类查询,cors解决跨域,品牌的查询)
乐优商城day03 0.学习目标 1.搭建后台管理前端 1.1.导入已有资源 1.2.安装依赖 1.3.运行一下看看 1.4.目录结构 1.5.调用关系 2.Vuetify框架 2.1.为什么要学习U ...
- 乐优商城笔记六:商品详情页
使用模板引擎 Thymeleaf + nginx 完成商品详情页静态化 完成乐优商城商品详情页 搭建商品详情页微服务 创建子工程 GroupId:com.leyou.service ArtifactI ...
- 乐优商城 Day 09(thymeleaf,Rabbitmq,商品详情页,非教程)
乐优商城学习Day09: 注意:此次代码都是在第八天的基础上 第八天的链接如下: https://blog.csdn.net/zcylxzyh/article/details/100859210 此次 ...
- 乐优商城之项目搭建(四)
文章目录 (一)项目分类 (二)电商行业 (三)专业术语 (四)项目介绍 (五)技术选型 (六)开发环境 (七)搭建后台环境:父工程 (八)搭建后台环境:eureka (九)搭建后台环境:zuul ( ...
- 乐优商城(10)--数据同步
乐优商城(10)–数据同步 一.RabbitMQ 1.1.问题分析 目前已经完成了商品详情和搜索系统的开发.思考一下,是否存在问题? 商品的原始数据保存在数据库中,增删改查都在数据库中完成. 搜索服务 ...
最新文章
- 谷歌如何评估产品经理?
- Windows Phone 7 不温不火学习之《项目模板》
- 英语与计算机的整合,浅谈计算机应用与英语教学的整合
- mysql pmm进程_mysql性能监控软件pmm
- Android数据存储之文件存储(瞬时数据的存储与读取)项目已上传GitHub
- 160家优秀国外技术公司博客
- C语言不使用结构体实现链表,不用指针链表和结构体数组怎么编学生成绩管理系统啊...
- python po设计模式_Python Selenium设计模式 - PO设计模式
- Go语言的三元表达式
- vs2015软件系统开源_特别版:2015年开源新闻
- GaussDB分布式Stream执行计划详解
- CNN进行新闻文本分类代码实战,包含分类文本
- 30 道 MySQL 面试题全放送!
- C# 静态类的构造函数
- 58-20210406华为海思Hi3516DV300的linux系统下获取IMX335的视频(eMMC模式)
- iPhone手机开启定位权限后,仍然无法定位
- win8计算机休眠的区别,win8系统的休眠和睡眠有什么区别?如何用?
- 1026 程序运行时间
- 图像处理之图像的像素运算
- 智能设计|零基础,低成本,轻松实现设计效率翻倍