一、公共字段自动填充(P31-P34)
(1)问题分析
在上一章节我们已经完成了后台系统的员工管理功能和菜品分类功能的开发,在新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工或者编辑菜品分类时需要设置修改时间、修改人等字段。这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段,如下:
而针对于这些字段,我们的赋值方式为:
1). 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。
2). 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。
如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?
答案是可以的,我们使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。
(2)实现思路
在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
3). 在 Mapper 的方法上加入 AutoFill 注解
若要实现上述步骤,需掌握以下知识(之前课程内容都学过)
技术点:枚举、注解、AOP、反射
(3)代码开发
①步骤一
**自定义注解 AutoFill**
进入到sky-server模块,创建com.sky.annotation包。
其中OperationType已在sky-common模块中定义
②步骤二
自定义切面 AutoFillAspect
在sky-server模块,创建com.sky.aspect包。
完善自定义切面 AutoFillAspect 的 autoFill 方法
③步骤三
在Mapper接口的方法上加入 AutoFill 注解
以CategoryMapper为例,分别在新增和修改方法添加@AutoFill()注解,也需要EmployeeMapper做相同操作
同时,将业务层为公共字段赋值的代码注释掉。
1). 将员工管理的新增和编辑方法中的公共字段赋值的代码注释。
2). 将菜品分类管理的新增和修改方法中的公共字段赋值的代码注释。
(4)功能测试
以新增菜品分类为例,进行测试
启动项目和Nginx
查看控制台
通过观察控制台输出的SQL来确定公共字段填充是否完成
查看表
category表中数据
其中create_time,update_time,create_user,update_user字段都已完成自动填充。
由于使用admin(id=1)用户登录进行菜品添加操作,故create_user,update_user都为1.
(5)代码提交
二、新增菜品(P35-P39)
(1)需求分析与设计
①产品原型
后台系统中可以管理菜品信息,通过 新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片。
新增菜品原型:
当填写完表单信息, 点击"保存"按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据保存至数据库中。
业务规则:
-
菜品名称必须是唯一的
-
菜品必须属于某个分类下,不能单独存在
-
新增菜品时可以根据情况选择菜品的口味
-
每个菜品必须对应一张图片
②接口设计
根据上述原型图先粗粒度设计接口,共包含3个接口。
接口设计:
-
根据类型查询分类(已完成)
-
文件上传
-
新增菜品
接下来细粒度分析每个接口,明确每个接口的请求方式、请求路径、传入参数和返回值。
1. 根据类型查询分类
2. 文件上传
3. 新增菜品
③表设计
通过原型图进行分析:
新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:
数据库设计(dish菜品表和dish_flavor口味表):
(2)代码开发
①文件上传实现
因为在新增菜品时,需要上传菜品对应的图片(文件),包括后绪其它功能也会使用到文件上传,故要实现通用的文件上传接口。
文件上传,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发抖音、发朋友圈都用到了文件上传功能。
实现文件上传服务,需要有存储的支持,那么我们的解决方案将以下几种:
-
直接将图片保存到服务的硬盘(springmvc中的文件上传)
-
优点:开发便捷,成本低
-
缺点:扩容困难
-
-
使用分布式文件系统进行存储
-
优点:容易实现扩容
-
缺点:开发复杂度稍大(有成熟的产品可以使用,比如:FastDFS,MinIO)
-
-
使用第三方的存储服务(例如OSS)
-
优点:开发简单,拥有强大功能,免维护
-
缺点:付费
-
在本项目选用阿里云的OSS服务进行文件存储。(前面课程已学习过阿里云OSS,不再赘述)
实现步骤:
1). 定义OSS相关配置
在sky-server模块
application-dev.yml
application.yml
2). 读取OSS配置
在sky-common模块中,已定义
3). 生成OSS工具类对象
在sky-server模块
其中,AliOssUtil.java已在sky-common模块中定义
4). 定义文件上传接口
在sky-server模块中定义接口
②新增菜品实现
1). 设计DTO类
在sky-pojo模块中
2). Controller层
进入到sky-server模块
3). Service层接口
4). Service层实现类
5). Mapper层
DishMapper.java中添加
在/resources/mapper中创建DishMapper.xml
DishFlavorMapper.java
在/resources/mapper中创建DishFlavorMapper.xml
(3)功能测试
进入到菜品管理--->新建菜品
由于没有实现菜品查询功能,所以保存后,暂且在表中查看添加的数据。
dish表:
dish_flavor表:
测试成功。
(4)代码提交
三、菜品分页查询(P40-P41)
(1)需求分析和设计
①产品原型
系统中的菜品数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
菜品分页原型:
在菜品列表展示时,除了菜品的基本信息(名称、售价、售卖状态、最后操作时间)外,还有两个字段略微特殊,第一个是图片字段 ,我们从数据库查询出来的仅仅是图片的名字,图片要想在表格中回显展示出来,就需要下载这个图片。第二个是菜品分类,这里展示的是分类名称,而不是分类ID,此时我们就需要根据菜品的分类ID,去分类表中查询分类信息,然后在页面展示。
业务规则:
-
根据页码展示菜品信息
-
每页展示10条数据
-
分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
②接口设计
根据上述原型图,设计出相应的接口。
(2)代码开发
①设计DTO类
根据菜品分页查询接口定义设计对应的DTO:
在sky-pojo模块中,已定义
②设计VO类
根据菜品分页查询接口定义设计对应的VO:
在sky-pojo模块中,已定义
③COntroller层
根据接口定义创建DishController的page分页查询方法:
④Service层接口
在 DishService 中扩展分页查询方法:
⑤Service层实现类
在 DishServiceImpl 中实现分页查询方法:
⑥Mapper层
在 DishMapper 接口中声明 pageQuery 方法:
在 DishMapper.xml 中编写SQL:
(3)功能测试
①接口文档测试
启动服务:访问http://localhost:8080/doc.html,进入菜品分页查询接口
注意:使用admin用户登录重新获取token,防止token失效。
点击发送:
②前后端联调测试
启动nginx,访问 http://localhost
点击菜品管理
四、删除菜品(P42-P44)
(1)需求分析和设计
①产品原型
在菜品列表页面,每个菜品后面对应的操作分别为修改、删除、停售,可通过删除功能完成对菜品及相关的数据进行删除。
删除菜品原型:
业务规则:
-
可以一次删除一个菜品,也可以批量删除菜品
-
起售中的菜品不能删除
-
被套餐关联的菜品不能删除
-
删除菜品后,关联的口味数据也需要删除掉
②接口设计
根据上述原型图,设计出相应的接口。
注意:删除一个菜品和批量删除菜品共用一个接口,故ids可包含多个菜品id,之间用逗号分隔。
③表设计
在进行删除菜品操作时,会涉及到以下三张表。
注意事项:
-
在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
-
setmeal_dish表为菜品和套餐关联的中间表。
-
若删除的菜品数据关联着某个套餐,此时,删除失败。
-
若要删除套餐关联的菜品数据,先解除两者关联,再对菜品进行删除。
(2)代码开发
①Controller层
根据删除菜品的接口定义在DishController中创建方法:
②Service层接口
在DishService接口中声明deleteBatch方法:
③Service层实现类
在DishServiceImpl中实现deleteBatch方法:
④Mapper层
在DishMapper中声明getById方法,并配置SQL:
创建SetmealDishMapper,声明getSetmealIdsByDishIds方法,并在xml文件中编写SQL:
在DishMapper.java中声明deleteById方法并配置SQL:
在DishFlavorMapper中声明deleteByDishId方法并配置SQL:
(3)功能测试
既可以通过Swagger接口文档进行测试,也可以通过前后端联调测试,接下来,我们直接使用前后端联调测试。
进入到菜品列表查询页面
对测试菜品进行删除操作
同时,进到dish表和dish_flavor两个表查看测试菜品的相关数据都已被成功删除。
再次,删除状态为启售的菜品
点击批量删除
删除失败,因为起售中的菜品不能删除。
(4)代码提交
五、修改菜品(p45-p48)
(1)需求分析和设计
①产品原型
在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击保存按钮完成修改操作。
修改菜品原型:
②接口设计
通过对上述原型图进行分析,该页面共涉及4个接口。
接口:
-
根据id查询菜品
-
根据类型查询分类(已实现)
-
文件上传(已实现)
-
修改菜品
我们只需要实现根据id查询菜品和修改菜品两个接口,接下来,我们来重点分析这两个接口。
1). 根据id查询菜品
2). 修改菜品 注:因为是修改功能,请求方式可设置为PUT。
(2)代码开发
①根据id查询菜品实现
1). Controller层
根据id查询菜品的接口定义在DishController中创建方法:
2). Service层接口
在DishService接口中声明getByIdWithFlavor方法:
3). Service层实现类
在DishServiceImpl中实现getByIdWithFlavor方法:
4). Mapper层
在DishFlavorMapper中声明getByDishId方法,并配置SQL:
②修改菜品实现
1). Controller层
根据修改菜品的接口定义在DishController中创建方法:
2). Service层接口
在DishService接口中声明updateWithFlavor方法:
3). Service层实现类
在DishServiceImpl中实现updateWithFlavor方法:
4). Mapper层
在DishMapper中,声明update方法:
并在DishMapper.xml文件中编写SQL:
(3)功能测试
本次测试直接通过前后端联调测试 ,可使用Debug方式启动项目,观察运行中步骤。
进入菜品列表查询页面,对第一个菜品的价格进行修改
点击修改,回显成功
菜品价格修改后,点击保存 ,修改成功。
(4)代码提交
六、问题集合及解决方法
(1)新增菜品图片不回显
解决办法:需要在阿里云上建一个自己的对象存储bucket仓库,上传菜品的图片,并修改数据库中的图片地址
步骤如下:在阿里云上建一个自己的对象存储bucket仓库,网址:对象存储 OSS_云存储服务_企业数据管理_存储-阿里云
获取access-key-id和access-key-secret,创建accesskey和查看secret时都要验证信息
替换文件中的信息后,图片回显成功!
(2)菜品管理图片信息没有显示
解决方法: