一.项目业务模块
1.后台管理系统(服务端)
1.分类管理
与菜品的关系是1:n的关系,与套餐的关系是1:n的关系
2.员工管理
3.套餐管理
与菜品的关系是n:n的关系
4.菜品管理
5.订单管理
6.数据统计
7.工作台
2. 微信小程序端(用户端)
1.微信登录
需要知道微信小程序登录的开发流程,详细请看:
微信小程序开发官方文档
小程序登录流程
2 菜品套餐浏览
3.购物车
4.下单
5.支付
6.订单管理
7.收餐地址
3.数据库表结构
1 菜品表
2.菜品口味表
3.员工表
4.套餐表
5.分类表
6.user用户表
7.购物车表
8.套餐菜品关系表
9.订单表
10.订单明细表
4.配置文件
二.技术与方案
1.maven
maven使用
2.git
git的使用
3.springboot
springboot详细介绍
4.springmvc
4.1 请求方式
@postMapping(新增)、@putMapping(修改) @DeleteMapping(删除) @GetMapping(查找)、
4.2请求参数
@parm参数接收:
1.直接赋值:只要形参数名和类型与传递参数相同,即可自动接收!
2.@RequestParam注解: @RequestParam
使用场景:
- 指定绑定的请求参数名
- 要求请求参数必须传递
- 为请求参数提供默认值
3 特殊场景接值
- 一名多值,用集合接收
- 实体类接收
@路径参数接收
- @PathVariable注解接收参数
@json参数接收
- @RequestBody注解接收参数
4.3拦截器
- 使用场景:jwt令牌校验的拦截器
- 使用方法:
- First:定义拦截器,实现HandlerInterceptor接口,并重写其所有法。
- Second:注册拦截器,加入到spring的ioc环境
-
解决问题:
-
拦截流程;
在登录成功以后,服务端会生成令牌,返回给前端,以后每次请求时,会携带过来进行校验
4.4全局异常处理器
未做处理时:
由于一直向上抛出异常,假如我们不在controller层做处理,则会继续向上抛给框架,然后显示给用户,显然这是不允许的
处理方法如下:
- 在controller层做try-catch处理,但是代码太繁杂和冗余
- 全局异常处理器
全局异常处理器流程:
使用需要加入两个注解
@RestControllerAdvice//注解一
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler//注解二
public Result baseExceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
5.springframwork
5.1 IOC
IOC(Inversion of Control,控制反转)是 Spring 框架的核心思想之一。它是一种设计模式,通过将对象的创建和依赖关系的管理交给 Spring 容器来实现,从而实现对象之间的解耦和灵活性。
5.2 AOP
AOP(Aspect Oriented Programming,面向切面编程)是 Spring 框架提供的一种重要机制,它可以将应用程序中的横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,并集中管理,提高代码的复用性
AOP思想在本项目中使用的场景:在分类管理模块和员工管理模块当中,新增方法和更新方法在公共属性字段存在问题:代码冗余,不利于维护管理
实现思路:
- 自定义注解
package com.sky.annotation;
import com.sky.mapper.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 标识哪些方法的属性需要填充
* */
@Retention(RetentionPolicy.RUNTIME)//表示该注解运行时生效
@Target(ElementType.METHOD)//表示该注解是标注在方法上面的
public @interface AutoFill {
//标识当前到底是新增还是修改操作
OperationType value();//使用枚举常量标识,不是新增就是更新操作
}
2.自定义切面类
@Slf4j
@Aspect
@Component
public class AutoFillAspect {
/*
* 为公共字段进行属性赋值
*
* */
@Before("execution(* com.sky.mapper.*.*(..))&&@annotation(com.sky.annotation.AutoFill)")
//第一个*:表示返回值;* com.sky.mapper.*.*(..)的意思是mapper包下的任意接口的任意方法,不考虑返回值;
public void autoFill(JoinPoint joinPoint) throws Exception {
log.info("进入aop程序,为公共属性开始赋值");
//1.获取原始方法运行时传入的参数
Object[] args = joinPoint.getArgs();
//2.通过反射获取到对象对应的方法(4个set)
if (ObjectUtils.isEmpty(args)) {
return;
}
//用反射原因:
//不确定object类型
Object obj = args[0];
log.info("开始赋值:{}", obj);
Method setCreateTime = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//3.获取注解对应的value属性
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class);//获取到原始方法上加的AutoFill注解
OperationType operationType = autoFill.value();
//4.如果时insert,为4个属性赋值
if (operationType.equals(OperationType.INSERT)) {
setCreateTime.invoke(obj, LocalDateTime.now());
setCreateUser.invoke(obj, BaseContext.getCurrentId());
}
//5.如果是update,为2个属性赋值
setUpdateTime.invoke(obj, LocalDateTime.now());
setUpdateUser.invoke(obj, BaseContext.getCurrentId());
log.info("结束:{}", obj);
}
}
3.在mapper的方法上加上@AutoFill注解
DI:DI (Dependency Injection),即依赖注入,其基本原则是应用组件不负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IOC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
6.springtask
用于订单超时和派送超时
7.mybatis
7.1 注解开发
@Select("select * from address_book where id = #{id}")
AddressBook getById(Long id);
7.2 xml文件开发
本项目用到的关于动态语句的标签:if-where标签、set标签、foreach标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.AddressBookMapper">
<select id="find" resultType="com.sky.entity.AddressBook">
select * from address_book
<where>
<if test="userId != null">
and user_id = #{userId}
</if>
<if test="phone != null">
and phone = #{phone}
</if>
<if test="isDefault != null">
and is_default = #{isDefault}
</if>
</where>
</select>
8.redis
8.1 五种数据类型
- String、Hash、Set、List、SortedSet
8.2 通用指令
8.3 持久化
- RDB、AOF
8.4 本项目中业务场景
- 菜品/套餐的缓存(redisTemplate)
- 引入redis的启动依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.在application.yml里面配置redis的地址信息等
3.创建redisTemplate的配置类,指定键值序列化方式
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory Factory){
//自定义RedisTemplate
RedisTemplate<Object,Object> redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(Factory);
//指定序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
- 营业状态的设置
- 登录密码错误次数超过五次,锁定账户
9.jwt
9.1介绍jwt
JWT及JSON Web Token,是一种在两方之间以紧凑、可验证的形式传输信息的方式,简单来说就是验证用户信息,本项目就是用来验证用户登录信息
9.2 使用场景:
*授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的路由、服务和资源。
*信息交换:JSON Web 令牌是在各方之间安全传输信息的好方法。因为可以对 JWT 进行签名(例如,使用公钥/私钥对),所以您可以确定发件人就是他们所说的那个人。此外,由于使用标头和有效负载计算签名,您还可以验证内容没有被篡改。
10.websocket
双向通信
11.springcache
11.1 缓存菜品
-
@Cacheable:在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有调用方法获取数据返回,并缓存起来。
-
@CacheEvict:将一条或多条数据从缓存中删除。
-
@CachePut:将方法的返回值放到缓存中
-
@EnableCaching:开启缓存注解功能
12.阿里云oss服务器
图片与文件上传
13.swagger
自动生成接口文档
14.pageHelper分页插件
14.1 使用与参数说明:
- 导入pagehelper的依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper}</version>
</dependency>
@Override
public PageResult page(DishPageQueryDTO d) {
//1.设置分页参数(当前时第几页,当前页数展示的条数)
PageHelper.startPage(d.getPage(), d.getPageSize());
//2.查询
List<DishVO> list = dishMapper.page(d);
//3.封装结果并返回
Page page = (Page) list;
return new PageResult(page.getTotal(), page.getResult());
}
14.2 注意事项:
基于xml格式编写 SQL语句不能有分号
select * from order;
因为pageHelper分页的时候,会在后面加limit 页数如果你语句有分号,比如上面的语句,就会变成select * from order; limit 3 ,此时由于sql语句断开了,导致分页不会生效