好好学习,天天向上
本文已收录至我的Github仓库 DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star

  • 畅购商城(一):环境搭建
  • 畅购商城(二):分布式文件系统FastDFS
  • 畅购商城(三):商品管理
  • 畅购商城(四):Lua、OpenResty、Canal实现广告缓存与同步
  • 畅购商城(五):Elasticsearch实现商品搜索
  • 畅购商城(六):商品搜索
  • 畅购商城(七):Thymeleaf实现静态页
  • 畅购商城(八):微服务网关和JWT令牌
  • 畅购商城(九):Spring Security Oauth2
  • 畅购商城(十):购物车
  • 畅购商城(十一):订单
  • 畅购商城(十二):接入微信支付
  • 畅购商城(十三):秒杀系统「上」

代码:github.com/RobodLee/changgou

小练手

这里有三个小练手的任务,内容比较简单,就是对一张表的增删查改,一些简单的CRUD而已。代码我就不贴了,简单记录一下思路以及遇到的问题,步骤和畅购商城(一):环境搭建中的品牌表操作类似,可以去参考,想要代码的小伙伴可以去我的Github获取,或者是去配套资料里面找。

相册管理

相册是用于存储图片的管理单元,我们通常会将商品的图片先上传到相册中,在添加商品时可以直接在相册中选择,获取相册中的图片地址,保存到商品表中。这一小节的任务就是完成对相册表的增删查改操作。相册表的结构如下:

从图中可以看出,相册表有编号,相册名称,相册封面,图片列表四个字段。其中相册封面中是一张图片的信息,图片的内容包括在FastDFS中存储的路径,唯一标识符uuid以及图片的状态。图片列表是一组图片,格式是json数组。

所以要实现相册管理的功能,第一步在changgou-service-goods-api工程中创建com.robod.goods.pojo.Album类,然后在changgou-service-goods工程中分别创建出AlbumController,AlbumService,AlbumServiceImpl,AlbumMapper四个文件,最后在里面写出相应的代码即可。

这里有三个需要注意的是,第一个是AlbumController中用到的Page类是com.github.pagehelper.Page,不是com.robod.entity.Page,不要导错了。第二个是资料里给的代码中AlbumController用了Map传参,这是个不好的习惯,改成数据模型,也就是Album。第三个问题是数据库中的字段和Album类中的字段不匹配,一个是image_items,另一个是imagItems,要是用@Results注解就有点麻烦,刚好后者是前者的驼峰形式,而且有很多都是这种情况,所以简单一点的做法就是在changgou-parent/changgou-service/changgou-service-goods/src/main/resources/application.yml中添加如下配置:

mybatis:configuration:map-underscore-to-camel-case: true #自动将以下画线方式命名的数据库列映射到 Java 对象的驼峰式命名属性中

当代码都写完之后,就来测试一下,

结果就不贴了,测试是成功了,开始下一个功能

规格参数模板

规格参数模板是用于管理规格参数的单元。规格是例如颜色、手机运行内存等信息,参数是例如系统:安卓(Android)后置摄像头像素:2000万及以上 热点:快速充电等信息 。

这一节的任务就是完成对模板表的增删查改。步骤和上一节一样,先是在changgou-service-goods-api工程中创建com.robod.goods.pojo.Template,然后在changgou-service-goods工程中分别创建出TemplateController,TemplateService,TemplateServiceImpl,TemplateMapper四个文件,最后写出对应的代码即可。

在做这个小任务时遇到了一个问题,我之前不是为了锻炼写SQL语句就没用通用Mapper么,但是这几个小任务的代码都是类似的,没必要每个都写一遍,所以我就从资料中直接复制粘贴。因为资料中给的代码是用通用Mapper的,运行的时候就出Bug了。

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error invoking SqlProvider method (tk.mybatis.mapper.provider.base.BaseSelectProvider.dynamicSQL).  Cause: java.lang.InstantiationException: tk.mybatis.mapper.provider.base.BaseSelectProvider

出现这个bug是因为我之前用的@MapperScan注解是org.mybatis.spring.annotation.MapperScan,如果想用通用Mapper的话,就得换成tk.mybatis.spring.annotation.MapperScan,直接换掉就好了。

商品分类

商品分类一共分三级管理,主要作用是在网站首页中显示商品导航,以及在管理后台管理商品时使用(下面的流程分析中会说明这个表的作用)。

步骤还是老样子,先是在changgou-service-goods-api中创建com.robod.goods.pojo.Category,然后在changgou-service-goods工程中分别创建出CategoryController,CategoryService,CategoryServiceImpl,CategoryMapper,最后写出相应的增删查改代码即可。

----------------
|--com.robod
|----controller
|------CategoryController.java
|----Service
|------impl
|--------CategoryServiceImpl.java
|------intf
|--------CategoryService.java
|----Mapper
|------CategoryMapper.java
----------------

SPU和SKU

  • SPU(Standard Product Unit):标准化产品单元

是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。说人话就是同款商品的公共的不可变的属性。比如小米10Pro,不管是哪一部,厂家,品牌,分类这些属性是所有小米10Pro所共有的而且不可变的,这就是SPU。

  • Stock Keeping Unit:最小存货单位

库存保有单位即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。在服装、鞋类商品中使用最多最普遍。用人话说就是每款商品独有的属性。比如说,一款小米10Pro是珍珠白和8+256的,另一款是星空蓝和12+256的,通过这些属性的不同组合,可以唯一标识不同款的产品,这就是SKU。

  • 表结构

商品发布流程分析

1. 三级分类的选择

一个商城的分类实在是太多了,所以我们需要有三级分类来减少页面显示的内容,京东就是三级分类,比如电脑——>电脑配件——>显示器。在前面的小练手中提到过,分类表中有个字段叫parent_id,当我们选择了一级分类的时候,就拿着这个它的id去查询出子分类,然后显示出来。假如现在选择了一个分类电脑,它的id是5,那么查询出电脑整机、电脑配件这几个分类的parent_id是5,就说明这些是电脑的子分类。二级分类和三级分类的关系也是同样的。那么一开始是如何显示一级分类的呢?很简单,一级分类的parent_id是0,查询出parent_id是0就说明是一级分类。

2. SPU信息填写

在这一步中有个商品品牌选项,自然不会去显示所有的品牌,只会显示与所选分类相关的品牌。在数据库中有一张表叫做tb_category_brand,记录了分类与品牌的对应关系。比如现在选择了三级分类是手机,它的id是10,从tb_category_brand中查询出对应的brand_id是134,156,178......,那么就拿着这些brand_id去品牌表中查询出对应的品牌名称,显示在界面上。

3.SKU信息填写

这个界面的规格和参数是怎么筛选出并展示的呢?在前面小练手部分的规格参数模板部分的表结构可以得出,通过分类id去分类表中查出对应的template_id,再通过查询到的template_id去规格表和参数表中查询出对应的数据就OK了。

功能实现

在分析完流程之后,就可以来实现对应的功能了。

1. 根据parent_id查询所有子分类

流程都已经分析过了,直接上代码:

/*** 根据父ID查询* Controller层 CategoryController.java*/
@RequestMapping(value ="/list/{pid}")
public Result<Category> findByParentId(@PathVariable(value = "pid")Integer pid){//根据父节点ID查询List<Category> list = categoryService.findByParentId(pid);return new Result<Category>(true,StatusCode.OK,"查询成功",list);
}
----------------------------------------------------
// Service层 CategorySerivceImpl.java
@Override
public List<Category> findByParentId(Integer pid) {return categoryMapper.findByParentId(pid);
}
------------------------------------------
/**** 根据父节点ID查询* @param pid:父节点ID* Dao层 CategoryMapper.java*/
@Select("SELECT * FROM tb_category WHERE parent_id = #{id}")
public List<Category> findByParentId(Integer pid);

2. 根据分类id查询对应的品牌集合

/*** 根据分类id查询对应的品牌集合* @param categoryId* @return* Controller层 BrandController.java*/
@GetMapping("/category/{id}")
public Result<List<Brand>> findByCategory(@PathVariable("id") int categoryId) {List<Brand> brands = brandService.findByCategory(categoryId);return new Result<>(true,StatusCode.OK,"查询成功",brands);
}
---------------------------------------------------------------------------
//Service层 BrandSerivceImpl.java
@Override
public List<Brand> findByCategory(int categoryId) {return brandMapper.findByCategory(categoryId);
}
---------------------------------------------------------------------------
/*** 根据分类id查询对应的品牌集合* @param categoryId* @return* Dao层 BrandMapper.java*/
@Select("SELECT * FROM tb_brand WHERE id IN " +"(SELECT brand_id FROM tb_category_brand WHERE category_id=#{categoryId})")
public List<Brand> findByCategory(int categoryId);

3.根据商品的分类id查询该分类对应的规格列表

/*** 根据商品分类的ID 查询该分类对应的 规格的列表* Controller层 SpecController.java*/
@GetMapping("/category/{id}")
public Result<List<Spec>> findByCategoryId(@PathVariable Integer id){List<Spec> specList = specService.findByCategoryId(id);return new Result<>(true,StatusCode.OK,"查询规格的列表成功",specList);
}
------------------------------------------------------------------------
//Service层 SpecServiceImpl.java
@Override
public List<Spec> findByCategoryId(Integer id) {//1.先根据商品分类的ID 获取模板的IDint templateId = categoryMapper.findById(id).getTemplateId();//2.再根据模板的ID 获取模板对应的规格的列表return specMapper.findByTemplateId(templateId);
}
-------------------------------------------------------------------------
/*** 根据模板id查询规格集合* @param templateId* @return* Dao层 SpecMapper.java*/
@Select("select * from tb_spec where template_id=#{templateId}")
public List<Spec> findByTemplateId(int templateId);

4. 根据商品的分类id,查询对应的参数列表

/*** 根据商品的分类id,查询对应的参数列表* @param id* @return* Controller层 ParaController.java*/
@GetMapping("/category/{id}")
public Result<List<Para>> findParaByCategoryId(@PathVariable(name = "id") Integer id) {List<Para> paraList = paraService.findParaByCategoryId(id);return new Result<>(true, StatusCode.OK, "参数列表查询成功", paraList);
}
--------------------------------------------------------------------------------------------
//Service层 PataServiceImpl.java
@Override
public List<Para> findParaByCategoryId(Integer id) {//1.根据分类的ID 获取到模板的IDint templateId = categoryMapper.findById(id).getTemplateId();//2.根据模板的ID 获取参数的列表 返回return paraMapper.findByTemplateId(templateId);
}
--------------------------------------------------------------------------------------------
/*** 根据模板的ID,获取参数的列表* @param templateId* @return* Dao层 ParaMapper.java*/
@Select("select * from tb_para where template_id=#{templateId}")
public List<Para> findByTemplateId(Integer templateId);

5. 添加商品功能

一个商品由一个Spu和一组Sku组成,所以在changgou-service-goods-api工程的com.robod.goods.pojo包下创建一个类Goods作为商品类

@Data
public class Goods implements Serializable {//SPUprivate Spu spu;//SKU集合private List<Sku> skuList;}

然后在SpuController,SpuServiceImpl中写出相应代码即可。

/*** 添加商品Goods(SPU+SKU)* Controller层 SpuController.java*/
@PostMapping("/save")
public Result save(@RequestBody Goods goods){spuService.save(goods);return new Result(true,StatusCode.OK,"保存商品成功",null);
}
----------------------------------------------------------------
//Service层 SpuService.java
@Override
public void save(Goods goods) {//新增spuSpu spu = goods.getSpu();spu.setId(idWorker.nextId());spuMapper.insertSelective(spu);Category category = categoryMapper.findById(spu.getCategory3Id());Brand brand = brandMapper.findById(spu.getBrandId());//新增skuList<Sku> skuList = goods.getSkuList();LocalDateTime time = LocalDateTime.now();for (Sku sku : skuList) {StringBuilder name = new StringBuilder(spu.getName());if (sku.getSpec()!=null) {Map<String,String> keyMap = JSONObject.parseObject(sku.getSpec(),Map.class);for (String spec : keyMap.keySet()) {name.append(spec);}}sku.setId(idWorker.nextId());sku.setName(name.toString());sku.setCreateTime(time);sku.setUpdateTime(time);sku.setSpuId(spu.getId());sku.setCategoryId(category.getId());sku.setCategoryName(category.getName());sku.setBrandName(brand.getName());skuMapper.insertSelective(sku);}
}

视频中用的是Date,但是Date现在已经不推荐用了,所以我改成了LocalDateTime。

6. 根据spu_id查询商品信息

思路就是根据spu_id查询出Spu和一组Sku,组合成一个Goods。

/*** 根据点击到的商品的spu_id获取商品* @param id* @return* Controller层 SpuController.java*/
@GetMapping("/goods/{id}")
public Result<Goods> findGoodsById(@PathVariable long id){Goods goods = spuService.findGoodsById(id);return new Result<Goods>(true,StatusCode.OK,"查询goods数据成功",goods);
}
-----------------------------------------------------------------------------
// Service层 SpuServiceImpl.java
@Override
public Goods findGoodsById(long id) {Goods goods = new Goods();Spu spu = spuMapper.findById(id);goods.setSpu(spu);List<Sku> skuList = skuMapper.findBySpuId(spu.getId());goods.setSkuList(skuList);return goods;
}
--------------------------------------------------------------------------------
// Dao层 略

7. 修改商品

因为修改商品是在添加商品的界面操作的,等于是重新添加了一遍。所以方法就是根据spu_id把原有的sku删除,然后重新添加,从而实现修改商品的功能。直接修改SpuServiceImpl中的代码就可以了:

@Override
public void save(Goods goods) {Spu spu = goods.getSpu();if (spu.getId()==null) {//新增spuspu.setId(idWorker.nextId());spuMapper.insertSelective(spu);} else {//修改spu并删除skuspuMapper.updateByPrimaryKeySelective(spu);Sku sku = new Sku();sku.setSpuId(spu.getId());skuMapper.delete(sku);}Category category = categoryMapper.findById(spu.getCategory3Id());Brand brand = brandMapper.findById(spu.getBrandId());//新增skuList<Sku> skuList = goods.getSkuList();LocalDateTime time = LocalDateTime.now();for (Sku sku : skuList) {StringBuilder name = new StringBuilder(spu.getName());if (sku.getSpec()!=null) {Map<String,String> keyMap = JSONObject.parseObject(sku.getSpec(),Map.class);for (String spec : keyMap.keySet()) {name.append(spec);}}sku.setId(idWorker.nextId());sku.setName(name.toString());sku.setCreateTime(time);sku.setUpdateTime(time);sku.setSpuId(spu.getId());sku.setCategoryId(category.getId());sku.setCategoryName(category.getName());sku.setBrandName(brand.getName());skuMapper.insertSelective(sku);}
}

8. 商品审核

商品审核有两点,先判断是否被删除,被删除就不用审核了,然后就将status和is_marketable置为1表示已审核并上架。代码很简单,一条sql就搞定了。

/*** 审核商品 自动上架* @param id  spu的ID* @return* Controller层 SpuController.java*/
@PutMapping("/audit/{id}")
public Result auditSpu(@PathVariable(name="id")long id){spuService.auditSpu(id);return new Result(true,StatusCode.OK,"审核通过");
}
------------------------------------------------------------------
//Service层 SpuServiceImpl.java
@Override
public void auditSpu(long id) {if (spuMapper.audit(id) == 0) {throw new RuntimeException("操作失败");}
}
------------------------------------------------------------------
/*** 审核商品* @param id* @return* Dao层 SpuMapper.java*/
@Update("update tb_spu set status=1,is_marketable=1 where is_delete=0 and id = #{id}")
int audit(long id);

Dao层返回一个数,表示操作的行数,如果等于0则说明没有数据被修改,情况可能有三种,商品已经被审核过了,或者是已经被删除了,再或者是商品压根就不存在。总之就是这个操作失败了,直接抛一个异常”审核失败“。

9. 上架/下架 商品

/*** 上架商品* @param id* @return* Dao层 SpuMapper.java*/
@Update("update tb_spu set is_marketable=1 where id=#{id} and is_delete=0 and is_marketable=0 and status=1")
int putSpu(long id);

和上面一个例子是一样的,要上架一个商品,必须符合未被删除,未被上架,通过审核三个条件,否则上架失败。

下架则要满足未被删除,已经上架,通过审核三个条件。

/*** 商品下架* @param id* @return* Dao层 SpuMapper.java*/
@Update("update tb_spu set is_marketable=0 where id=#{id} and is_delete=0 and is_marketable=1 and status=1")
public int pullSpu(long id);

10. 批量 上架/下架 商品

本以为这只是一个很简单的功能,结果遇到了一堆坑。先把批量上架的代码贴出来吧,批量下架就不说了,都是一样的。

//Service层 SpuServiceImpl.java
@Override
public int putMany(long[] ids) {int num = spuMapper.putMany(ids);if (num == 0) {throw new RuntimeException("上架失败");}return num;
}
---------------------------------------------------------------------
//Dao层
@UpdateProvider(type = SpuMapperProvider.class,method = "putMany")
int putMany(@Param("ids") long[] ids);class SpuMapperProvider {public String putMany(long[] ids) {String s = Arrays.toString(ids);s = s.substring(1, s.length() - 1);return "update tb_spu set is_marketable=1 where id in(" + s+ ") and is_delete=0 and is_marketable=0 and status=1";}
}

有的小伙伴看到这里可能就要问了:你为什么不直接把ids作为占位符,然后从Service层传过去呢?我也是到昨天才知道的,把数组或者集合传到Dao层后拼接成的sql语句是这样的

很明显不是我想要的,那有的小伙伴可能又要问了:那你既然在SpuMapperProvider里面拼接sql,为什么不在Service层直接把ids转换成String然后传到Dao层的占位符里呢?如果是这样的话,那么拼接后的sql又变成了这样

而正确的sql语句应该是这样的

update tb_spu set is_marketable=1 WHERE id in(1281162712482709504, 10000001516600) and is_delete=0 and is_marketable=0 and status=1;

Mybatis知道你传的是String,所以自动帮你加了两个引号。要不是有MyBatis Log Plugin这个强大的插件把拼接好的sql语句打印了出来,我都不知道错在哪儿。其实这都不是最坑的,这好歹还能看到问题在哪儿。我昨天在调试的时候打了几个断点,Dao层也打了,然后调试功能死活用不了,不是断点进不去,就是postman那边一直说连接被拒绝,搞了老半天,还以为是端口被占了,重启一下也不行,最后还是一气之下把所有断点全删了才发现问题所在,Dao打断点调试就用不了,真的是。。。哎!

小结

商品管理功能就写完了,这篇文章先是介绍了几个小练手的任务,然后介绍了商品发布的流程,最后实现了和商品管理有关的一系列功能。如果我的文章对你有些帮助,不要忘了点赞,收藏,转发,关注。要是有什么好的意见欢迎在下方留言。让我们下期再见!

DayDayUP​github.com

商品品牌信息的增删改查操作步骤_畅购商城(三):商品管理相关推荐

  1. 商品品牌信息的增删改查操作步骤_图书信息管理系统

    不仅仅是图书信息管理系统 基于双链表,采用面向对象编程方法制作的图书管理系统 来源微信公众号: 不仅仅是图书信息管理系统​mp.weixin.qq.com 效果演示 框架结构 数据层:双链表管理 核心 ...

  2. 商品品牌信息的增删改查操作步骤_javaweb09-Servlet增删改查

    学习笔记是参考的how2j 本章笔记的目的是介绍如何与JDBC结合,通过servlet对数据库中的数据进行增.删.改.查. 一.前期准备 1.新建一个Dynamic Web Project 步骤为:f ...

  3. JDBC——商品品牌数据的增删改查操作

    JDBC练习--完成商品品牌数据的增删改查操作 一.准备环境 1.数据库表tb_brand 2.实体类Brand /*** 品牌* alt+鼠标左键,整列编辑* 在实体类中,基本数据类型建议使用其对应 ...

  4. JDBC练习-完成商品品牌数据的增删改查操作

    准备环境 数据库表tb_brand -- 删除tb_brand表 drop table if exists tb_brand; -- 创建tb_brand表 create table tb_brand ...

  5. 学生信息管理系统(连接数据库,面向对象的方法实现学生信息的增删改查操作)...

    ---------------------------------------------------------------------------------------------------- ...

  6. SpringMVC-RestfulCRUD || 员工信息表增删改查操作的具体实现

    SpringMVC-RestfulCRUD 利用SpringMVC做一个CRUD(增删改查)符合Rest风格的: C:Create:创建 R:Retrieve:查询 U:Update:更新 D:Del ...

  7. sql语句查询商品的一二三级分类都是一个字段怎么办_畅购商城(三):商品管理...

    好好学习,天天向上 本文已收录至我的Github仓库「DayDayUP」:github.com/RobodLee/DayDayUP,欢迎Star 小练手 这里有三个小练手的任务,内容比较简单,就是对一 ...

  8. 实现对mysql增删改查_Java语言实现对MySql数据库中数据的增删改查操作的代码

    简单说操作的步骤: 1.连接数据库 2.将SQL语句发送到数据库 3.执行SQL语句 这里举个例子: 在一个数据库中有个students表,表中有学号(Id),姓名(Name),性别(Sex),地址( ...

  9. 用 Vue 实现学生信息管理系统的增删改查操作,模拟数据库操作(但并没有连接数据库)

    Ⅰ.项目准备与解题思路: 1.项目准备: 其一.要有写 HTML + CSS + JS 的软件: (如:VSCode 工具): 其二.要提前下载好 Vue 环境,因为在项目中会引用 'vue.js' ...

最新文章

  1. Android逆向--如何调试smali代码?
  2. 实现数据集多( 高 )维可视化(附代码)
  3. python判断集合为空
  4. 企业要SD-WAN组网,请先评估这些问题
  5. web安全101之如何理解XXE?
  6. BCP导出导入大容量数据实践
  7. 字节跳动攻城狮: 我整理了很久的Python面试指南,请查收!
  8. pygtk在windows的安装
  9. codeforces 688 E. The Values You Can Make(01背包+思维)
  10. ListView问题:Your content must have a ListView wh...
  11. JBOSS 5.0.0GA的集群搭建
  12. java 自旋锁_Java并发编程的艺术05-队列自旋锁
  13. 性能测试系列:高可用测试linux常用命令
  14. JSP实现点击链接后下载文件(相当于右键另存)功能
  15. python从入门到实践课后题_Python 从入门到实践 函数篇 8-6-8习题
  16. FastDFS安装手册
  17. 一文告诉你 K8s PR (Pull Request) 怎样才能被 merge?
  18. android 9 qq登录,天天练qq登录版
  19. 边境的悍匪—机器学习实战:第十八章 强化学习
  20. uos命令_UOS与Deepin OS区别详解

热门文章

  1. java基础系列十七(时间日期转换)
  2. 中维带你揭秘倾斜摄影三维实景
  3. 微信废品回收小程序开发上门回收废品小程序开发
  4. aspen压缩因子_Aspen 物性代号及常用的英语单词中英文对照
  5. Python Requests:两个例子说明get和post方法+用谷歌浏览器查看网络请求
  6. 写数据分析报告,建议部分憋到脸红,咋整?
  7. 服装行业施行ERP体系的首要好处是什么?
  8. Chris Hadfield現身《ABS 2020》,各方菁英和THORBOT 雷神量化機器人一同進行深入探討
  9. 直击微软第九频道著名主持Robert Green 对话一站式示例代码库大老板梁梅女士
  10. bad interpreter: No such file or directory解决办法