【JAVA后端开发】Part3--瑞吉外卖项目(分类管理)
文章目录
- 1,自动填充字段
- 1.1 需求分析和数据模型
- 1.2 代码开发
- 1.3 bug修复
- 2,新增分类
- 2.1 需求分析:
- 2.2 代码开发
- 3,分类信息分页查询
- 3.1 需求分析
- 3.2代码开发
- 4,删除分类
- 4.1 需求分析
- 4.2 代码开发
- 4.3 功能完善
- 5,修改分类
- 5.1 需求分析
- 5.2 代码开发
1,自动填充字段
1.1 需求分析和数据模型
我们每次创建和更新数据时,都会修改数据库中的特定字段。如果我们每次手动修改设定数据,就会增加大量的重复工作,因此我们可以使用MybatisPlus的强大作用,让其为我们自动填充相关字段。
1.2 代码开发
MP公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以同时对这些字段进行处理,避免了重复代码。
具体步骤:
1,在实体类的属性上加入@TableFiled注解,指定自动填充的策略。如:
@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)private Long updateUser;
2,按框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口:
package com.wang.reggie.common;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;/*** 自定义元数据对象处理器*/
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {/*** 插入操作,自动填充* @param metaObject*/@Overridepublic void insertFill(MetaObject metaObject) {log.info("公共字段自动填充[insert]...");log.info(metaObject.toString());metaObject.setValue("createTime", LocalDateTime.now());metaObject.setValue("updateTime",LocalDateTime.now());metaObject.setValue("createUser",new value(1));metaObject.setValue("updateUser",new value(1));}/*** 更新操作,自动填充* @param metaObject*/@Overridepublic void updateFill(MetaObject metaObject) {log.info("公共字段自动填充[update]...");log.info(metaObject.toString());long id = Thread.currentThread().getId();log.info("线程id为:{}",id);metaObject.setValue("updateTime",LocalDateTime.now());metaObject.setValue("updateUser",new value(1));}
}
1.3 bug修复
前面我们已经完成了公共字段自动填充功能的代码开发,但是我们在自动填充createUser
和updateUser
时设置的是固定值,现在我们需要使其能够动态获取当前登录用户的id。这里可以使用ThreadLocal来解决此问题:
实现步骤:
1,编写BaseContext工具类,基于ThreadLocal封装的工具类。
package com.wang.reggie.common;/*** 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id*/
public class BaseContext {private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();/*** 设置值* @param id*/public static void setCurrentId(Long id){threadLocal.set(id);}/*** 获取值* @return*/public static Long getCurrentId(){return threadLocal.get();}
}
2,在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//前面的代码略过Long empId = (Long) request.getSession().getAttribute("employee");BaseContext.setCurrentId(empId);filterChain.doFilter(request,response);return;}log.info("用户未登录");//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));return;}
3,在MyMetaObjectHandler的方法中调用BaseContext来获取登录用户的id
//修复前
metaObject.setValue("updateUser",new value(1));
//修复后
metaObject.setValue("updateUser",BaseContext.getCurrentId());
2,新增分类
2.1 需求分析:
后台系统可以管理分类信息,分类包括两种类型,分别是菜品分类和套餐分类。当我们在后台系统中添加菜品时就需要选择一个菜品分类和套餐分类,因此我们需要先完成菜品分类和套餐功能。页面如下:
ps:菜品分类和套餐分类用的同一张数据表category,通过type来区分(1:为菜品,2:为套餐)。
2.2 代码开发
代码开发步骤:
1,在开发业务功能前,先将需要用到的类和接口基本结构创建好,详细代码不再赘述,具体编写可以参考用户employee的结构:
- Category实体类(可以从资料中导入)
- Mapper接口CategoryMapper
- 业务层接口CategoryService
- 业务层实现类CategoryServiceImpl
- 控制层CategoryController
2,理清整个程序的执行过程。
页面(backend/page/category/list.html)发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端
服务端controller接收页面提交的数据并调用Service将数据保存
Service调用Mapper操作数据库,保存数据
3,查看页面url请求,可以看到:
注:可以看到新增菜品和套餐分类请求的服务器地址和提交的json数据结构相同,所以服务端只需要提供一个方法统一处理就行。具体代码如下:
package com.wang.reggie.controller;import com.wang.reggie.common.R;
import com.wang.reggie.entity.Category;
import com.wang.reggie.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;@PostMappingpublic R<String> save(@RequestBody Category category){log.info("category:{}",category );categoryService.save(category);return R.success("新增分类成功");}
}
需要明白的两点是:
- 这里的菜品和套餐名称等同于employee的用户名,都是唯一约束的。因此,如果两次输入同样的名称,就会进入到我们之前写的全局异常处理器
GlobalExceptionHandler
类中,进行异常抛出。 - 我们新增菜品和套餐时,不需要自行传入创建时间、更新时间、创建人以及更新人,是因为我们在第一部分完成了公共字段自动填充的功能。后续的新增功能也是如此,就不会再详细说明!
3,分类信息分页查询
3.1 需求分析
系统中的分类很多的时候,一个页面中全部展示出来会显得比较乱,不宜与查看,所以一般的系统中都会以分页的方式来展示列表数据。页面需要的信息同员工分页查询相同:
3.2代码开发
程序执行流程:
1,页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端。
2,服务端Controller接收页面提交的数据并调用service层查询数据
3,Service调用Mapper操作数据库,查询分页数据
4,Controller将查询到的分页数据响应给页面
5,页面接收到分页数据并通过ElementUI的Table组件展示到页面上
整体代码实现,和员工信息分页查询一模一样:
/*** 分页查询* @param page* @param pageSize* @return*/@GetMapping("/page")public R<Page> page(int page,int pageSize){//分页构造器Page<Category> pageInfo = new Page<>(page,pageSize);//条件构造器LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();//添加排序条件,根据sort进行排序queryWrapper.orderByAsc(Category::getSort);//分页查询categoryService.page(pageInfo,queryWrapper);return R.success(pageInfo);}
4,删除分类
4.1 需求分析
在分类管理列表页面,可以对某个分类进行删除操作。但是需要主义的时当分类关联了菜品或者套餐时,此分类不允许删除。
4.2 代码开发
1,当我们点击删除按钮时,页面发送ajax请求,将参数(id)提交到服务端。
2,服务端Controller接收页面提交的数据并调用Service删除数据。
3,Service调用Mapper操作数据库。
具体URL请求如下:
/*** 根据id删除分类* @param id* @return*/@DeleteMappingpublic R<String> delete(Long id){log.info("删除分类,id为:{}",id);categoryService.removeById(id);return R.success("分类信息删除成功");}
4.3 功能完善
在4.2中,我们完成的是根据id删除分类的功能,但是在删除前没有检查删除的分类是否关联了菜品或者套餐,因此我们需要进行功能完善。
因为我们需要检查关联的菜品和套餐,因此我们需要导入Dish和Setmeal两个基础的类和接口:
1,实体类Dish和Setmeal、Mapper接口、Service接口和Service接口实现类。
2,从Mapper层开始分析,首先我们需要从Dish和Setmeal类中查找出是否有CategoryId=我们要删除分类的id的菜品和套餐,但是由于我们用了MP,因此我们不需要编写特定的SQL语句。
3,Service层需要调用Mapper层获取到查找结果,因此我们重写remove方法。在remove方法中,我们添加查询条件根据分类和套餐id进行查询:
如果结果大于0,说明该分类关联了菜品,则需要进行特定处理。
这里的特定处理,我们可以抛出一个我们自定义的业务异常。
CustomException类:
package com.wang.reggie.common;/** * 自定义业务异常类*/ public class CustomException extends RuntimeException {public CustomException(String message){super(message);} }
然后再通过全局异常处理器进行处理,在页面上显示出我们手动抛出的错误信息。
GlobalExceptionHandler类下新建的方法:
/*** 异常处理方法* @return*/@ExceptionHandler(CustomException.class)public R<String> exceptionHandler(CustomException ex){log.error(ex.getMessage());return R.error(ex.getMessage());}
如果结果等于0,说明该分类未关联菜品,则直接调用父类的删除方法。
service接口:
package com.wang.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.wang.reggie.entity.Category;public interface CategoryService extends IService<Category> {public void remove(Long ids);
}
Service接口实现类:
package com.wang.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wang.reggie.common.CustomException;
import com.wang.reggie.entity.Category;
import com.wang.reggie.entity.Dish;
import com.wang.reggie.entity.Employee;
import com.wang.reggie.entity.Setmeal;
import com.wang.reggie.mapper.CategoryMapper;
import com.wang.reggie.mapper.EmployeeMapper;
import com.wang.reggie.service.CategoryService;
import com.wang.reggie.service.DishService;
import com.wang.reggie.service.EmployeeService;
import com.wang.reggie.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {@Autowiredprivate DishService dishService;@Autowiredprivate SetmealService setmealService;@Overridepublic void remove(Long id) {LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件,根据分类id进行查询dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);int count1 = dishService.count(dishLambdaQueryWrapper);//查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常if(count1 > 0){//已经关联菜品,抛出一个业务异常throw new CustomException("当前分类下关联了菜品,不能删除");}//查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件,根据分类id进行查询setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);int count2 = setmealService.count(setmealLambdaQueryWrapper);if(count2 > 0){//已经关联套餐,抛出一个业务异常throw new CustomException("当前分类下关联了套餐,不能删除");}//正常删除分类super.removeById(id);}
}
以上是项目第三部分中的难点,我们在学习的时候一定要理清除执行逻辑,再搭建项目。
5,修改分类
5.1 需求分析
我们点击修改按钮时,跳转到编辑页面,在编辑页面中前端回显分类信息并进行修改,最后点击确定按钮完成修改操作。
5.2 代码开发
1,点击编辑按钮时,页面跳转到修改页面并对数据进行回显,我们对数据进行修改。(这一步未与服务器交互)
2,当我们修改完成,点击确定按钮时,页面发送Controller请求,并将修改后的数据以json格式传回。
3,服务端controller接收页面提交的数据并调用Service将数据保存
4,Service调用Mapper操作数据库,保存数据。
具体代码如下:
/*** 根据id修改分类信息* @param category* @return*/@PutMappingpublic R<String> update(@RequestBody Category category){log.info("修改分类信息:{}",category);categoryService.updateById(category);return R.success("修改分类信息成功");}
扩展:ThreadLocal
【JAVA后端开发】Part3--瑞吉外卖项目(分类管理)相关推荐
- 瑞吉外卖项目(一)软件开发流程设计及环境搭建
第一章 软件开发整体介绍 软件开发流程 软件开发流程 需求分析:产品原型.需求规格说明书 设计:产品文档,ui界面设计,概要设计,详细设计,数据库设计 编码:项目代码,单元测试 测试:测试用例,测试报 ...
- 黑马瑞吉外卖项目开发笔记
目录 软件开发整体介绍 开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型展示 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 Maven环境搭建 1.直接创建maven项目( ...
- 瑞吉外卖项目 基于spring Boot+mybatis-plus开发 超详细笔记,有源码链接
本项目是基于自学b站中 黑马程序员 的瑞吉外卖项目:视频链接: 黑马程序员Java项目实战<瑞吉外卖>,轻松掌握springboot + mybatis plus开发核心技术的真java实 ...
- 【SpringBoot项目实战+思维导图】瑞吉外卖①(项目介绍、开发环境搭建、后台登陆/退出功能开发)
文章目录 软件开发整体介绍 软件开发流程 角色分工 软件环境 瑞吉外卖项目介绍 项目介绍 产品原型 技术选型 功能架构 角色 开发环境搭建 数据库环境搭建 创建数据库 数据库表导入 数据库表介绍 Ma ...
- 瑞吉外卖项目剩余功能补充
目录 菜品启售和停售 菜品批量启售和批量停售 菜品的批量删除 菜品删除逻辑优化 套餐管理的启售,停售 套餐管理的修改 后台按条件查看和展示客户订单 手机端减少购物车中的菜品或者套餐数量(前端展示有一点 ...
- 瑞吉外卖项目——瑞吉外卖
软件开发整体介绍 软件开发流程 需求分析:产品原型.需求规格说明书 设计:产品文档.UI界面设计.概要设计.详细设计.数据库设计 编码:项目代码.单元测试 测试:测试用例.测试报告 上线运维:软件环境 ...
- 瑞吉外卖项目笔记+踩坑1——基础功能
导航: [黑马Java笔记+踩坑汇总]JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud/SpringCloudAlibaba+黑马旅游+谷粒商城 目录 1 ...
- 瑞吉外卖项目详细分析笔记及所有功能补充代码
目录 项目刨析简介 技术栈 项目介绍 项目源码 一.架构搭建 1.初始化项目结构 2.数据库表结构设计 3.项目基本配置信息添加 公共字段的自动填充 全局异常处理类 返回结果封装的实体类 二.管理端业 ...
- 瑞吉外卖项目重难点及易错点知识点总结
本文是对b站黑马程序员瑞吉外卖项目的总结,实现流程以及简单部分不做详解,重点归纳难点以及易错点.(前面是对项目的介绍,可以直接略过 看第四点总结) 视频链接:https://www.bilibili. ...
最新文章
- 清理C盘无用的垃圾的文件,给c盘瘦身
- 简述神经网络的训练过程?
- 苹果「热修复门」事件复盘、分析和展望
- [NPUCTF2020]你好sao啊
- python 并发执行命令_python: 多线程实现的两种方式及让多条命令并发执行
- java scanner_Java Scanner radix()方法与示例
- sqlserver 递归查询
- Goland使用技巧
- ZendFramework-2.4 源代码 - 整体架构(类图)
- CTF 湖湘杯 2018 WriteUp (部分)
- 高等数学在计算机中的应用论文1500字,大学高等数学论文范文
- kingbase 修改数据库密码
- IDEA 2021首个大版本发布,羊哥反手就是一个更新!附新亮点演示!
- sql查询每科成绩最高的人和分数
- 电信物联卡网络怎么样_中国电信物联网专用卡(中国电信物联网卡怎么样)
- Fcitx 小企鹅输入法3.0.0
- 音质好的蓝牙耳机有哪些?盘点四款好音质蓝牙耳机
- 2012 ACM 亚洲区总结帖
- Windows下新建多级文件夹
- 从“挖土豆”到全场景营销,纷享销客CRM如何助力噢易云可持续增长?