day01
资料下载
提取码:6633
nginx打包运行前端界面
在双击运行nginx.exe时没有反应,在该文件见下执行cmd命令: nginx -t 发现0.0.0.0:80端口被占用,查询占用端口号进程的pid
netstat -ano|findstr 80
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
根据pid查询该进程发现是system
>netstat -ano|findstr 80
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
网上搜索解决办法说是SQL server reporting service 服务导致的,但是在我关闭该服务后仍然还是被占用,无奈更改nginx的端口。
更改nginx端口需要更改nginx.conf文件,于是先备份一份,以免出错。
打开文件,找到server模块,更改端口号为81。
server {
listen 81; #这里改为端口号81
server_name localhost;
再次执行nginx -t命令,表明已经可以启动。
nginx -t
nginx: the configuration file D:\Deverlop\RealProject\sky\nginx-1.20.2/conf/nginx.conf syntax is ok
nginx: configuration file D:\Deverlop\RealProject\sky\nginx-1.20.2/conf/nginx.conf test is successful
运行nginx.exe,登录http:127.0.0.1:81
成功!
初始提交苍穹外卖项目代码
先commit不要push!
我选择用idea创建远程仓库
自动提交已有的内容
数据库创建
我使用的是MySQL8,可视化工具用的是sqlyog
执行sql脚本,选择文件执行
看到有十一张表
前后端联调
编译以下四个模块
报错,想起来没有改成自己的url ,用户名密码,这里注意mysql驱动,5.0和8.0引入的驱动包不同
再次启动
登录成功。
jwt密钥生成规范
令牌可用作页面操作的用户身份验证,jwt令牌中不建议放入用户私密数据等,因为此规范是开源的。令牌生成后可进行验证,如果密钥篡改后就无法正确验证。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDE3MDYyMDUsInVzZXIiOnsidXNlcmlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9fQ.zEg_hQNJOXYAT9xaxWjB4Ksws0K8LiQq1KyeRVHUmNY
#可分为
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 #头部
.eyJleHAiOjE3MDE3MDYyMDUsInVzZXIiOnsidXNlcmlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9fQ #载荷
.zEg_hQNJOXYAT9xaxWjB4Ksws0K8LiQq1KyeRVHUmNY #头部+载荷根据某种算法生产的字符串
如下为jwt创建和验证测试代码
public class JwtTest {
@Test
public void done(){
Map<String,Object> claims = new HashMap<String, Object>();
claims.put("userid",1);
claims.put("username","张三");
String token = JWT.create()
.withClaim("user", claims) //载荷信息
.withExpiresAt(new Date(System.currentTimeMillis() + 5000))//过期时间当前+5s
.sign(Algorithm.HMAC256("*")); //Algorithm.HMAC256("*")是指定的算法,*是密
//钥,可随意设定,但不可泄漏,否则会导致令牌被解析。
System.out.println(token);
try {
Thread.sleep(3000); #如果这里超时也会解析失败
} catch (InterruptedException e) {
e.printStackTrace();
}
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("*")).build();//创建解析器,
//需要指定算法和密钥
DecodedJWT verify = jwtVerifier.verify(token); //如果这里的token被篡改也会解析失败
System.out.println(verify.getClaims().get("user"));
}
}
Nginx反向代理服务
主要功能:1.发送请求到后端,无需暴露后端服务的接口,提高系统安全性。
2.对于同一个微服务的不同的主机实现对不同主机的访问,实现分布式的负载均衡。
反向代理的配置方式:
将前端带有/api/的请求转发到后端并拼接上动态的请求
例如:localhost/api/admin/lgoin=====>localhost:8080/admin/login
MD5加密
将明文加密后存储到数据库,加密后的字符串无法逆向解析为明文。
完善登录功能,修改数据库中的密码123456: e10adc3949ba59abbe56e057f20f883e
更改项目中EmployeeServiceImpl的代码
//密码比对
// TODO 后期需要进行md5加密,然后再进行比对
password = DigestUtils.md5DigestAsHex(password.getBytes()); //在密码比对前加上
if (!password.equals(employee.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
接口导入
前后端开发流程
yapi地址: YApi Pro-高效、易用、功能强大的可视化接口管理平台
创建两个项目
导入成功
Swagger
介绍:
使用方式:
1.导入knife4j依赖到pom.xml
<!--swagger依赖-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
2.在WebMVCConfig配置类加入knife4j相关配置(给的项目已存在)
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
3.是指静态资源映射,否则文档页面无法访问(给的项目已存在)
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
访问:http://localhost:8080/doc.html
测试:
常用注解:
加上注解后:
day2
新增员工
产品原型
需求分析和接口设计
代码开发
1.controller
@ApiOperation("新增员工")
@PostMapping
public Result<String> save(@RequestBody EmployeeDTO employeeDTO){
log.info("新增员工:{}",employeeDTO);
//调用employeeService
employeeService.save(employeeDTO);
return null;
}
2.service
@Override
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//对象属性拷贝
BeanUtils.copyProperties(employeeDTO,employee);
//设置帐号状态默认正常(使用常量)
//设置初始默认密码123456(使用常量),使用md5加密
//设置当前记录的创建时间和修改时间
//TODO 设置当前记录的创建人id和修改人id
//调用持久层保存数据
employeeMapper.insert(employee);
}
开始编码:
/**
* 创建员工
* @param employeeDTO
*/
@Override
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//对象属性拷贝
BeanUtils.copyProperties(employeeDTO,employee);
//设置帐号状态默认正常(使用常量)
employee.setStatus(StatusConstant.ENABLE);
//设置初始默认密码123456(使用常量),使用md5加密
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//设置当前记录的创建时间和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
//TODO 设置当前记录的创建人id和修改人id
employee.setCreateUser(10L);
employee.setUpdateUser(10L);
//调用持久层保存数据
employeeMapper.insert(employee);
}
3.mapper
@Insert("")
void insert(Employee employee);
开始编码:
@Insert("insert into employee " +
"(name,username,password,phone,sex,id_number,status,create_time,update_time,create_user,update_user)" +
"values" +
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")
void insert(Employee employee);
测试:
发一个登录请求,获取token令牌
设置参数名称,内容以及作为请求头发送。
为什么要用header而不是其他的请求体
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());
jwtProperties.getAdminTokenName()获取令牌配置类中的令牌名称,而令牌名称被作为springconfiguration配置在application.yaml文件中
@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {
/**
* 管理端员工生成jwt令牌相关配置
*/
private String adminSecretKey;
private long adminTtl;
private String adminTokenName;
}
sky:
jwt:
# 设置jwt签名加密时使用的秘钥
admin-secret-key: cpb
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
前后端联调
完善代码
1.当用户名存在时会报SQLIntegrityConstraintViolationException异常,需要增加异常处理。
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'employeetest' for key 'employee.idx_username' //异常信息
找到com.sky.handler.GlobalExceptionHandler.java ,添加异常处理方法
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
String message = ex.getMessage();
//Duplicate entry 'employeetest' for key 'employee.idx_username'
if (message.contains("Duplicate entry")) {
String[] split = message.split(" ");
String username = split[2];
String msg = username + MessageConstant.AlREADY_EXIST;
log.error("异常信息:{}",msg);
return Result.error(msg);
}else {
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
2.设置登录用户的id
了解jwt令牌验证流程
在拦截器中我们已经做了jwt的验证解析,得到了empId的值,但是如何将这个个变量传到service呢?
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
ThreadLocal可以存储线程的变量,且具有线程隔离的作用
在三个类中都打印线程id,发现都属于同一线程id为37的线程
在令牌校验成功时将empId存储到ThreadLocal
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
//将empId存储到ThreadLocal
BaseContext.setCurrentId(empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
补充BaseContext作为一个工具类,创建ThreadLocal实例,封装get(),set(),remove()方法
public class BaseContext {
public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
public static void removeCurrentId() {
threadLocal.remove();
}
}
从线程中获取到刚刚在jwt拦截器中存储的empID
@Override
public void save(EmployeeDTO employeeDTO) {
System.out.println("当前形成的id:" + Thread.currentThread().getId()+ "当前类"+ this.getClass());
Employee employee = new Employee();
//对象属性拷贝
BeanUtils.copyProperties(employeeDTO,employee);
//设置帐号状态默认正常(使用常量)
employee.setStatus(StatusConstant.ENABLE);
//设置初始默认密码123456(使用常量),使用md5加密
employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//设置当前记录的创建时间和修改时间
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
//TODO 设置当前记录的创建人id和修改人id
employee.setCreateUser(BaseContext.getCurrentId());//从线程中获取到刚刚在jwt拦截器中存储的empID
employee.setUpdateUser(BaseContext.getCurrentId());
//调用持久层保存数据
employeeMapper.insert(employee);
}
empId被打印到控制台
员工分页查询
需求分析和设计
产品原型
接口设计
代码开发
根据分页查询接口设计DTO
分页查询的所有结果封装成一个PageResult对象
将查询结果封装至PageResult对象,再将该对象封装至Result<PageResult>对象的data属性
(有点抽象)O(∩_∩)O
controller
@GetMapping("/page")
@ApiOperation("分页")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("分页查询:{}"+employeePageQueryDTO);
//调用分页查询service
//将返回数据封装到Result
return null;
}
@GetMapping("/page")
@ApiOperation("分页")
public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){
log.info("分页查询:{}"+employeePageQueryDTO);
//调用分页查询service
PageResult pageResult = employeeService.page(employeePageQueryDTO);
//将返回数据封装到Result
return Result.success(pageResult);
}
service
//分页查询Service方法
//使用pagehelper进行分页查询
//EmployeeMapper中创建分页查询方法
//将得到的page封装到PageResult返回给controller
/**
* 分页
* @param employeePageQueryDTO
* @return
*/
@Override
public PageResult page(EmployeePageQueryDTO employeePageQueryDTO) {
//使用pagehelper进行分页查询
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());//将页面和页面大小传入pagehelper
//EmployeeMapper中创建分页查询方法
Page<Employee> page = employeeMapper.page(employeePageQueryDTO);
//将得到的page封装到PageResult返回给controller
long total = page.getTotal();
List<Employee> records = page.getResult();
return new PageResult(total,records);
}
dao (dao返回Page给service)
Page<Employee> page(EmployeePageQueryDTO employeePageQueryDTO);
xml文件注意映射类型为employee,mybatis将其注入到page中
<!--写查询sql-->
<select id="page" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name == null and name == ''">
and name like concat('%'+#{name}+'%')
</if>
</where>
order by create_time desc
</select>
功能测试
这里查询列表成功,但是模糊查询失败,看控制台打印的sql:是select count(0) from employee where name = concat('%' + ?+ '%')
拖到sqlyog把?换成”李“也查询不到
再看sql发现写成了concat('%'+#{name}+'%'),concat()函数的字符串是用“,”隔开的(低级错误我还改了好多回sql),如下是我的sql:
<select id="page" resultType="com.sky.entity.Employee">
select * from employee
<if test="name != null and name != ''">
where
name like concat('%',#{name},'%')
</if>
order by create_time desc
</select>
模糊查询成功
代码完善
操作时间展示不友好
解决方法
方式一
创建时间格式化,更改时间没有格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
对比
方式二:
看不懂!/(ㄒoㄒ)/~~,欢迎大佬指导交流
/**
* 扩展springmvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建一个消息转换器
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为一个json数据
converter.setObjectMapper(new JacksonObjectMapper());
//添加消息转换器至容器中
converters.add(0,converter);
}
但是不耽误我用O(∩_∩)O
大概是这里配了几种类型的数据,那么这几种数据就会根据相应的格式进行转换 。
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
启用禁用员工账号
需求分析和设计
代码开发
controller
@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startOrStop(@PathVariable Integer status,Long id){
log.info("启用禁用员工账号:{},{}");
//调用service
//
return null;
}
/**
* 启用禁用员工账号
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("启用禁用员工账号")
public Result startOrStop(@PathVariable Integer status,Long id){
log.info("启用禁用员工账号:{},{}");
//调用service
employeeService.startOrStop(Integer,id);
//
return Result.success();
}
service
/**
* 启用禁用员工账号
* @param status
* @param id
*/
@Override
public void startOrStop(Integer status, Long id) {
//update操作,写动态sql,将参数封装至employee
//调用mapper根据参数动态修改信息
}
/**
* 启用禁用员工账号
* @param status
* @param id
*/
@Override
public void startOrStop(Integer status, Long id) {
//update操作,写动态sql,将参数封装至employee
Employee employee = new Employee();
employee.builder()
.status(status)
.updateTime(LocalDateTime.now())
.updateUser(BaseContext.getCurrentId())
.id(id)
.build();
//调用mapper根据参数动态修改信息
employeeMapper.update(employee);
}
dao&sql
/**
* 根据employee传来的参数动态修改信息
*/
void update(Employee employee);
<!--动态修改employee-->
<update id="update" parameterType="employee">
update employee
<set>
<if test="name != null">name = #{name},</if>
<if test="password != null">password = #{password},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="sex != null">sex = #{sex},</if>
<if test="idNumber != null">id_number = #{idNumber},</if>
<if test="status != null">status = #{status},</if>
<if test="updateTime != null"> update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser}</if>
</set>
where id = #{id}
</update>
功能测试
swagger
前端
编辑员工
需求分析和设计
根据id查员工
代码开发
controller
/**
* 根据id查询员工信息
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询员工信息")
public Result<Employee> getEmployeeById(@PathVariable Long id){
Employee employee = employeeService.getEmployeeById(id);
return Result.success(employee);
}
service
/**
* 根据id查询员工信息
* @param id
* @return
*/
@Override
public Employee getEmployeeById(Long id) {
Employee employee =employeeMapper.getEmployeeById(id);
employee.setPassword("****");
return employee;
}
dao
/**
* 根据id查询员工信息
* @param id
* @return
*/
@Select("select * from employee where id = #{id}")
Employee getEmployeeById(Long id);
功能测试
代码开发
controller
/**
* 修改员工信息
* @param employeeDTO
* @return
*/
@PutMapping
@ApiOperation("修改员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO){
employeeService.update(employeeDTO);
return Result.success();
}
service
/**
* 修改员工信息
* @param employeeDTO
*/
@Override
public void update(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//拷贝属性到employee
BeanUtils.copyProperties(employeeDTO,employee);
//设置更新时间和操作用户
employee.setUpdateUser(BaseContext.getCurrentId());
employee.setUpdateTime(LocalDateTime.now());
//修改
employeeMapper.update(employee);
}
功能测试
分类相关接口
controller
package com.sky.controller.admin;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.CategoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.jaxb.SpringDataJaxb;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-07 16:05
*/
@RestController
@Slf4j
@RequestMapping("/admin")
@Api(tags = "分类相关接口")
public class CategoryController {
@Resource
private CategoryService categoryService;
/**
*分类分页
* @param pageDto
* @return
*/
@GetMapping("/category/page")
@ApiOperation("分类分页")
public Result<PageResult> page(CategoryPageQueryDTO pageDto){
PageResult pageResult = categoryService.page(pageDto);
return Result.success(pageResult);
}
/**
* 创建分类
* @param categoryDTO
* @return
*/
@PostMapping("/category")
@ApiOperation("创建分类")
public Result createCategory (@RequestBody CategoryDTO categoryDTO){
categoryService.create(categoryDTO);
return Result.success();
}
/**
* 启用禁用分类
* @param status
* @param id
* @return
*/
@PostMapping("/category/status/{status}")
@ApiOperation("启用禁用分类")
public Result startOrStopCategory(@PathVariable Integer status,Long id){
categoryService.startOrStop(status,id);
return Result.success();
}
/**
* 分类修改
* @param categoryDTO
* @return
*/
@PutMapping("/category")
@ApiOperation("修改菜品分类")
public Result updateCategory(@RequestBody CategoryDTO categoryDTO){
categoryService.update(categoryDTO);
return Result.success();
}
/**
* 根据id删除分类
* @param id
* @return
*/
@DeleteMapping("/category")
@ApiOperation("根据id删除分类")
public Result deleteCategory(Long id){
categoryService.delete(id);
return Result.success();
}
@GetMapping("/list")
public Result<List<Category>> list(Integer type){
List<Category> categories = categoryService.list(type);
return Result.success(categories);
}
}
service (这里跟之前的不同就是删除操作有一定的依赖关系,当删除当前分类时需要查看菜品表和套餐表有没有菜品属于改套餐,如果有就不能删除改套餐,否则菜品和套餐无法关联分类,则需要进行一个判断。)
首先查看setmeal表和dish表格
setmeal
dish
category
发现dish表和setmeal表都有category_id字段对应category表的id字段。
例如王老吉的category_id是11,对应category表的id那么它就属于分类酒水饮料类,那么该分类酒水饮料就不能被删除,除非该分类下没有其他的菜品。
我们需要确定该分类下没有其他菜品。前端删除操作回传过来一个参数id,就是category的id,
这样我们就可以查询套餐表和菜品表是否有菜品的category_id 等于该id。
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.entity.Category;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.mapper.CategoryMapper;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetMealMapper;
import com.sky.result.PageResult;
import com.sky.service.CategoryService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-07 16:27
*/
@Service
public class CategoryServiceImpl implements CategoryService {
@Resource
private CategoryMapper categoryMapper;
@Resource
private DishMapper dishMapper;
@Resource
private SetMealMapper setMealMapper;
/**
* 分类分页
* @param pageDto
* @return
*/
@Override
public PageResult page(CategoryPageQueryDTO pageDto) {
PageHelper.startPage(pageDto.getPage(),pageDto.getPageSize());
Page<Category> page = categoryMapper.page(pageDto);
long total = page.getTotal();
List<Category> result = page.getResult();
return new PageResult(total,result);
}
/**
* 创建分类
* @param categoryDTO
*/
@Override
public void create(CategoryDTO categoryDTO) {
Category category = Category.builder()
.status(0)
.createTime(LocalDateTime.now())
.createUser(BaseContext.getCurrentId())
.updateTime(LocalDateTime.now())
.updateUser(BaseContext.getCurrentId())
.build();
BeanUtils.copyProperties(categoryDTO,category);
categoryMapper.insert(category);
}
/**
* 修改分类状态
* @param status
*/
@Override
public void startOrStop(Integer status,Long id) {
Category category = Category.builder()
.status(status)
.id(id)
.build();
categoryMapper.update(category);
}
@Override
public void update(CategoryDTO categoryDTO) {
Category category = Category.builder()
.updateTime(LocalDateTime.now())
.updateUser(BaseContext.getCurrentId())
.build();
BeanUtils.copyProperties(categoryDTO,category);
categoryMapper.update(category);
}
/**
* 删除分类
* @param id
*/
@Override
public void delete(Long id) {
//查询当前分类是否关联了菜品,如果关联了就抛出业务异
Integer count = dishMapper.count(id);
if (count > 0) {
//当前分类下有菜品,不能删除
throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);
}
//查询当前分类是否关联了套餐,如果关联了就抛出业务异常
count = setMealMapper.count(id);
if (count > 0) {
//当前分类下有菜品,不能删除
throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);
}
//删除分类
categoryMapper.deleteById(id);
}
@Override
public List<Category> list(Integer type) {
List<Category> categories = categoryMapper.list(type);
return categories;
}
}
此处的两个mapper查询setmeal表和dish表中是否有菜品属于该id的分类,如果count>0说明该分类不能被删除
@Mapper
public interface SetMealMapper {
@Select("select count(id) from setmeal where category_id = #{id}")
Integer count(Long id);
}
@Mapper
public interface DishMapper {
@Select("select count(id) from dish where category_id = #{id}")
Integer count(Long id);
}
dao
<?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.CategoryMapper">
<select id="page" resultType="com.sky.entity.Category">
select * from category
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="type != null">
and `type` like concat('%',#{type},'%')
</if>
</where>
order by create_time desc
</select>
<update id="update" parameterType="category">
update category
<set>
<if test="type != null">type = #{type} ,</if>
<if test="name != null">name = #{name} ,</if>
<if test="sort != null">sort = #{sort} ,</if>
<if test="status != null">status = #{status} ,</if>
<if test="createTime != null">create_time = #{createTime} ,</if>
<if test="createUser != null">create_user = #{createUser} ,</if>
<if test="updateUser != null">update_user = #{updateUser} ,</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
where id = #{id}
</update>
<select id="list" resultType="com.sky.entity.Category">
select *
from category
where status = 1
<if test="type != null">and type = #{type}</if>
order by sort, create_time desc
</select>
</mapper>
day03
公共字段填充
大致思路就是使用aop方法在dao层前置执行操作。
自定义注解类,作用是在执行aop方法时细粒度到某个方法上,并且获取到该方法的操作(通过注解的value属性标识操作类型)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
* @author CPB Email:[email protected]
* @create 12-07 20:23
*/
@Target(ElementType.METHOD)//指定注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型:UPDATE,INSERT
OperationType value();
}
自定义aop切片方法
@pointcut定义切点,指定该自定义切片方法的作用范围
其中”*.com.sky.mapper.*.(..)“指定该切片方法可执行在mapper包下的所有类的所有方法,并且需要有@AutoFill标识的方法才会执行该切片方法。
@before注解表示实用类该注解的方法会在切点前置执行,即在dao操作前执行。
import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MemberSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/**
* 自定义切面,实现公共字段自动填充
*
* @author CPB Email:[email protected]
* @create 12-07 20:28
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {
}
/**
* 前置通知,在通知中进行公共字段赋值
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始进行公共字段的自动填充...");
//获取当前被拦截方法的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
OperationType value = autoFill.value();
//获取被拦截方法的参数
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Object entity = args[0];
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//判断操作方法,通过反射更改参数的公共属性
if (value == OperationType.INSERT) {
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//执行方法
setCreateUser.invoke(entity, currentId);
setUpdateUser.invoke(entity, currentId);
setCreateTime.invoke(entity, now);
setUpdateTime.invoke(entity, now);
} catch (Exception e) {
e.printStackTrace();
}
} else if (value == OperationType.UPDATE) {
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//执行方法
setUpdateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
} catch (Exception e) {
e.printStackTrace();
}
log.info("数据信息:{}", entity);
}
}
}
关键在于使用joinpoint获取方法的注解信息和参数信息,另外在于使用反射获取参数的方法。看弹幕上有同学说为什么不直接将参数向下转型为employee,为何要大费周章的的使用反射?其实如果向下转型也可以,使用判断if(entity instanceof xxx){}也可以但是我们不确定后续栋的开发是否还会有更多的诸如此类的操作,或者后续更新会更改这里面的内容,需要写更多的代码,这就背离了我们当初使用aop来实现这一操作的初衷了。话不多说最后也是执行成功了的。
新增菜品
需求分析
代码开发
文件上传
AliOssUtil类中已经有了文件上传的方法upload,但是我们需要为该类的属性配置数据源,类似配置数据库的datasouce,endpoint就是url,id和我secret是用户名密码,bucket-name相当于指定数据库名称。而这些数据我们是以配置文件的形式写在yaml文件里的。所以我们需要写一个Properties类来指定ymal文件的配置的值,在该形目中已经写好了,其中
@ConfigurationProperties(prefix = "sky.alioss")
制定了获取哪个标签下的内容;
但是这时配置文件中的值并没有赋值给AliOssUtil,我们需要写一个配置类来给其赋值。
import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* @author CPB Email:[email protected]
* @create 12-08 13:53
*/
@Configuration
@Slf4j
public class AliOssConfiguration {
@Value("${sky.alioss.endpoint}")
private String endpoint;
@Value("${sky.alioss.access-key-id}")
private String accessKeyId;
@Value("${sky.alioss.access-key-secret}")
private String accessKeySecret;
@Value("${sky.alioss.bucket-name}")
private String buketName;
@Bean
@ConditionalOnMissingBean
public AliOssUtil aliOssUtil (){
log.info("创建阿里Oss对象存储存储对象");
return new AliOssUtil(endpoint,accessKeyId,accessKeySecret,buketName);
}
}
我这里使用的是spring的@value注解获取的alioss的连接信息
AliOss对象存储服务器申请
搜索oss对象存储
点击立即试用,没有用过的话会有一个三个月的试用期,容量为20G学习的话够用。
创建buket
名字自己随意,但是配置yaml时要一致,选择标准存储、本地冗余,读写全写这里写公共读,这里代表写操作需要权限,读操作不需要,如果私有的话会导致前端读取不到文件。确定创建。
右上角头像点击AccessKey管理
创建AccessKey,然后会给你Id和secret复制下来,这个只能查看一次,需要立刻复制下来。
然后将其配置在自己的配置文件中。
controller
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.UUID;
/**
* @author CPB Email:[email protected]
* @create 12-08 12:55
*/
@Slf4j
@RestController
@Api(tags = "通用接口")
@RequestMapping("/admin/common")
public class CommonController {
@Resource
private AliOssUtil aliOssUtil;
@ApiOperation("文件上传")
@PostMapping("/upload")
public Result<String> upload(MultipartFile file){
log.info("文件上传:{}",file);
try {
//原始文件名
String originalFilename = file.getOriginalFilename();
//截取原始文件名的后缀
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String objectName = UUID.randomUUID().toString() + extension;
String filePath = aliOssUtil.upload(file.getBytes(), objectName);
return Result.success(filePath);
} catch (IOException e) {
log.info("文件上传失败:{}",e);
return Result.error("文件上传失败");
}
}
}
新增菜品
controller
@PostMapping
@ApiOperation("新增菜品")
public Result createDish(@RequestBody DishDTO dishDTO){
log.info("新增菜品:{}",dishDTO);
dishService.saveWithFlavor(dishDTO);
return Result.success();
}
service
这里不仅需要操作dish表,而且号要操作dish_flavor表,两个表的数据要保持一致,所以我们要保证这两个表统一成功或统一失败,保证事务的原子性,因此我们要在service业务方法上添加@transaction注解同时在主启动类上启动开启事务管理器。
@EnableTransactionManagement //开启注解方式的事务管理
import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorsMapper;
import com.sky.mapper.DishMapper;
import com.sky.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-08 23:44
*/
@Service
@Slf4j
public class DishServiceImpl implements DishService {
@Resource
private DishMapper dishMapper;
@Resource
private DishFlavorsMapper dishFlavorsMapper;
/**
* 新增菜品和口味的数据
* @param dishDTO
*/
@Override
@Transactional
public void saveWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//向菜品表插入1条数据
dishMapper.insert(dish);
//获取存入的菜品的id
Long dishId = dish.getId();
List<DishFlavor> flavors = dishDTO.getFlavors();
//遍历flavors设置dishId属性
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishId);
});
//向口味表插入n条数据
dishFlavorsMapper.insert(flavors);
}
}
这里在插入dish_flavor表时dishDTO.getFlavors得到的Flavors数组中的每项flavor并没有dishId的值,因为刚刚添加的菜品的id是数据库自动生成的所以要重新获取该值。这里我们不需要重新查询一次,直接使用useGeneratedKeys=“true” -> KeyProperty=“id”表示获得刚刚操作的菜品的id值,并且将值传给我们刚刚传入的dish对象的id属性
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into dish
(name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
values
(#{name},#{categoryId},#{price},#{image},#{description},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})
</insert>
功能测试
ok
这里也能看出“顶针炒饭”在dish表中已经存在,且dish_flavor表中dish_id为70的口味也只有两个,说明了事务的同时失败。
菜品分页查询
需求分析
代码开发
controller
@GetMapping("/page")
@ApiOperation("菜品分页")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
PageResult pageResult = dishService.page(dishPageQueryDTO);
return Result.success(pageResult);
}
service
这里还是使用pagehelper
/**
* 菜品分页查询
* @param dishPageQueryDTO
* @return
*/
@Override
public PageResult page(DishPageQueryDTO dishPageQueryDTO) {
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
Page<DishVO> page = dishMapper.page(dishPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
dao
这里我们希望将查询到的结果封装到Page里面,并且作为DishVO封装。
/**
* 菜品分页
* @param dishPageQueryDTO
* @return
*/
Page<DishVO> page(DishPageQueryDTO dishPageQueryDTO);
sql
这里用到了多表联查,关联dish表和category表的category_id和id两个字段,查询另外在查询类别名称时需要给category.name改一个别名category_name否则无法识别将其赋值给DishVO的categoryName属性。
<select id="page" resultType="com.sky.vo.DishVO">
SELECT
dish.id, dish.name, dish.category_id, dish.price, dish.image, dish.description, dish.status, dish.update_time,category.name AS category_name
FROM dish JOIN category ON (dish.`category_id` = category.`id`)
<where>
<if test="categoryId != null">
and dish.category_id = #{categoryId}
</if>
<if test="name != null">
and dish.name = #{name}
</if>
<if test="status != null">
and dish.status = #{status}
</if>
</where>
</select>
功能测试
删除菜品
需求分析
代码开发
controller
/**
* 菜品批量删除
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量删除")
public Result delete(@RequestParam List<Long> ids){
dishService.deleteBatch(ids);
return Result.success();
}
service
我这里省去了多次循环查询的方法,都是将dishIds传到mapper里面使用<foreach> 拼接字符来实现查找和删除操作。
/**
* 批量删除菜品
* @param dishIds
*/
@Transactional
@Override
public void deleteBatch(List<Long> dishIds) {
//判断菜品是否能够删除===>该菜品是否在启售中
List<Integer> statuses = dishMapper.selectById(dishIds);
for (Integer status : statuses) {
if (status == 1) {
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断菜品是否能够删除===>当前菜品是否被套餐关联
List<Long> setMealId = setMealDishMapper.selectSetMealWithDish(dishIds);
if (setMealId != null && setMealId.size() > 0) {
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
//删除菜品表的菜品数据
dishMapper.deleteById(dishIds);
//删除菜品关联的口味数据
dishFlavorsMapper.deleteByDishId(dishIds);
}
sql
dish表操作
<select id="selectById" resultType="java.lang.Integer">
select status from dish where id in
<foreach collection="disIds" item="disId" open="(" close=")" separator=",">
#{disId}
</foreach>
</select>
<delete id="deleteById">
delete from dish where id in
<foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
#{dishId}
</foreach>
</delete>
dish_setmeal表操作
<select id="selectSetMealWithDish" resultType="java.lang.Long">
select setmeal_id from setmeal_dish
where dish_id in
<foreach collection="disIds" item="disId" separator="," open="(" close=")">
#{disId}
</foreach>
</select>
dish_flavor表操作
<delete id="deleteByDishId">
delete from dish_flavor where dish_flavor.dish_id in
<foreach collection="dishIds" item="dishId" open="(" close=")" separator=",">
#{dishId}
</foreach>
</delete>
功能测试
修改菜品
需求分析
代码开发
/**
* 根据id查询菜品
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result<DishVO> getDishById(@PathVariable Long id){
DishVO dishVO = dishService.getDishById(id);
return Result.success(dishVO);
}
/**
* 修改菜品
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result updateDish(@RequestBody DishDTO dishDTO){
dishService.updateDishWithFlavor(dishDTO);
return Result.success();
}
service
/**
* 修改菜品回显
* @param id
* @return
*/
@Override
public DishVO getDishById(Long id) {
List<DishFlavor> flavors = dishFlavorsMapper.getByDishId(id);
DishVO dishVO = dishMapper.selectByIdWithCategoryName(id);
for (DishFlavor flavor : flavors) {
dishVO.getFlavors().add(flavor);
}
return dishVO;
}
/**
* 修改菜品和其口味信息
* @param dishDTO
*/
@Transactional
@Override
public void updateDishWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//检查是否在售,在售不允许修改
DishVO dishVO = dishMapper.selectByIdWithCategoryName(dish.getId());
if (dishVO.getStatus() == 1) {
throw new DeletionNotAllowedException(MessageConstant.DISH_IS_ON_SALE);
}
dishMapper.update(dish);
ArrayList<Long> dishIds = new ArrayList<>();
dishIds.add(dishDTO.getId());
dishFlavorsMapper.deleteByDishId(dishIds);
List<DishFlavor> flavors = dishDTO.getFlavors();
for (DishFlavor flavor : flavors) {
flavor.setDishId(dishDTO.getId());
}
dishFlavorsMapper.
sql
<update id="update">
update dish
<set>
<if test="name != null">name = #{name},</if>
<if test="categoryId != null">category_id = #{categoryId},</if>
<if test="price != null">price = #{price},</if>
<if test="image != null">image = #{image},</if>
<if test="description != null">description = #{description},</if>
<if test="status != null">status = #{status},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUser != null">update_user = #{updateUser}</if>
</set>
where id =#{id}
</update>
<insert id="insert">
insert into dish_flavor (dish_id, name, value)
values
<foreach collection="flavors" item="flavor" separator=",">
(#{flavor.dishId},#{flavor.name},#{flavor.value})
</foreach>
</insert>
<delete id="deleteByDishId">
delete from dish_flavor where dish_flavor.dish_id in
<foreach collection="dishIds" item="dishId" open="(" close=")" separator=",">
#{dishId}
</foreach>
</delete>
功能测试
day04
添加菜品
需求分析
发现有个添加菜品给该套餐添加菜品
这里发现好像是点击分类右侧应该出现该分类下的菜品所以分析接口应该是根据套餐id查询菜品。
但是在套餐类接口里没有发现该接口,然后去菜品接口看看
果然在这里发现了根据分类id查询菜品
请求参数是id ,返回是一个Object[] 所以我们需要在result里添加一个含有菜品的数组,
代码开发
controller (此接口写在DIshController)
/**
* 根据分类Id查询菜品
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类Id查询菜品")
public Result<List<Dish>> getDishesByCategoryId(Long categoryId){
List<Dish> dishes = dishService.getDishesByCategoryId(categoryId);
return Result.success(dishes);
}
service
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
@Override
public List<Dish> getDishesByCategoryId(Long categoryId) {
List<Dish> dishes = dishMapper.getDishesByCategory(categoryId);
return dishes;
}
dao
@Select("select * from dish where category_id = #{categoryId}")
List<Dish> getDishesByCategory(Long categoryId);
接下来查看套餐添加的接口
请求体
请求体有些复杂
但是不难看出这就是我们的setmeal和setmeal_dish这两张表的结合体,因此我们需要写一个setmealDTO来封装这个请求体,在setmeal实体类的基础上增加setmealdish属性。(在资料中已经存在该DTO)
代码开发
controller
@PostMapping
@ApiOperation("新增套餐")
public Result createSetMeal(@RequestBody SetmealDTO setmealDTO){
setMealService.createSetMeal(setmealDTO);
return Result.success();
}
service
/**
* 添加套餐
* @param setmealDTO
*/
@Override
@Transactional
public void createSetMeal(SetmealDTO setmealDTO) {
Setmeal setMeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO,setMeal);
//添加套餐
setMealMapper.insert(setMeal);
//添加套餐中的菜品
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
//将刚刚插入的套餐id赋给setmealdishes
for (SetmealDish dish : setmealDishes) {
dish.setSetmealId(setMeal.getId());
}
setMealDishMapper.insert(setmealDishes);
}
sql
setmealmapper
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into setmeal
(category_id, name, price, status, description, image, create_time, update_time, create_user, update_user)
values
(#{categoryId},#{name},#{price},0,#{description},#{image},#{createTime},#{updateTime},#{createUser},#{updateUser})
</insert>
setmealdishmapper
<insert id="insert">
insert into setmeal_dish
(setmeal_id, dish_id, name, price, copies)
VALUES
<foreach collection="setmealDishes" item="setmealDish" separator=",">
(#{setmealDish.setmealId},#{setmealDish.dishId},#{setmealDish.name},#{setmealDish.price},#{setmealDish.copies})
</foreach>
</insert>
剩下的在代码编写时忘了写文章了,套餐模块代码全部奉上
controller
import com.sky.dto.SetmealDTO;
import com.sky.dto.SetmealPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.entity.Setmeal;
import com.sky.mapper.SetMealMapper;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.service.SetMealService;
import com.sky.vo.SetmealVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-10 14:33
*/
@Slf4j
@RestController
@RequestMapping("/admin/setmeal")
@Api("套餐类相关接口")
public class SetMealController {
@Resource
private SetMealService setMealService;
@PostMapping
@ApiOperation("新增套餐")
public Result createSetMeal(@RequestBody SetmealDTO setmealDTO){
setMealService.createSetMeal(setmealDTO);
return Result.success();
}
/**
* 套餐分页查询
* @param setmealPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("套餐分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
PageResult pageResult = setMealService.page(setmealPageQueryDTO);
return Result.success(pageResult);
}
/**
* 修改套餐状态
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
public Result startOrStop(@PathVariable Integer status,Long id){
setMealService.startOrStop(status,id);
return Result.success();
}
/**
* 根据id查询套餐,修改套餐回显
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result<SetmealVO> getSetmealById(@PathVariable Long id){
SetmealVO setmealVO = setMealService.getSetmealById(id);
return Result.success(setmealVO);
}
/**
* 修改套餐
* @param setmealDTO
* @return
*/
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO){
setMealService.update(setmealDTO);
return Result.success();
}
/**
* 批量删除套餐
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List<Long> ids){
setMealService.delete(ids);
return Result.success();
};
}
service
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.dto.SetmealDTO;
import com.sky.dto.SetmealPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.entity.Setmeal;
import com.sky.entity.SetmealDish;
import com.sky.exception.SetmealEnableFailedException;
import com.sky.mapper.SetMealDishMapper;
import com.sky.mapper.SetMealMapper;
import com.sky.result.PageResult;
import com.sky.service.SetMealService;
import com.sky.vo.SetmealVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-10 15:23
*/
@Service
@Slf4j
public class SetMealServiceImpl implements SetMealService {
@Resource
private SetMealMapper setMealMapper;
@Resource
private SetMealDishMapper setMealDishMapper;
/**
* 添加套餐
* @param setmealDTO
*/
@Override
@Transactional
public void createSetMeal(SetmealDTO setmealDTO) {
//将信息封装至setmeal
Setmeal setMeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO,setMeal);
//添加套餐
setMealMapper.insert(setMeal);
//添加套餐中的菜品
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
//将刚刚插入的套餐id赋给setmealdishes
for (SetmealDish dish : setmealDishes) {
dish.setSetmealId(setMeal.getId());
}
setMealDishMapper.insert(setmealDishes);
}
/**
* 套餐分页
* @param setmealPageQueryDTO
* @return
*/
@Override
public PageResult page(SetmealPageQueryDTO setmealPageQueryDTO) {
PageHelper.startPage(setmealPageQueryDTO.getPage(),setmealPageQueryDTO.getPageSize());
Page<Setmeal> page = setMealMapper.page(setmealPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
/**
* 修改套餐状态
* @param status
* @param id
*/
@Override
public void startOrStop(Integer status,Long id) {
//如果套餐中的菜品是停售状态则不准起售套餐
if (status == 1) {
List<Dish> dishes = setMealDishMapper.selectBySetmealId(id);
dishes.forEach(dish -> {
if (dish.getStatus() == 0) {
throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
}
});
}
Setmeal setmeal = new Setmeal();
setmeal.setId(id);
setmeal.setStatus(status);
setMealMapper.update(setmeal);
}
/**
* 修改套餐回显
* @param id
* @return
*/
@Override
public SetmealVO getSetmealById(Long id) {
//获取该套餐套餐
Setmeal setmeal = setMealMapper.getSetmealById(id);
//获取改套餐包含的菜品
List<SetmealDish> dishes = setMealDishMapper.getSetmealDishBySetmealId(id);
//封装至Vo
SetmealVO setmealVO = new SetmealVO();
BeanUtils.copyProperties(setmeal,setmealVO);
setmealVO.setSetmealDishes(dishes);
return setmealVO;
}
/**
* 修改套餐
* @param setmealDTO
*/
@Transactional
@Override
public void update(SetmealDTO setmealDTO) {
//处理DTO
Setmeal setmeal = new Setmeal();
BeanUtils.copyProperties(setmealDTO,setmeal);
//查询套餐状态如果在启售中则不允许修改
Setmeal setmealById = setMealMapper.getSetmealById(setmeal.getId());
if (setmealById.getStatus() == 1) {
throw new SetmealEnableFailedException(MessageConstant.DISH_IS_ON_SALE);
}
//修改套餐
setMealMapper.update(setmeal);
//修改套餐中的菜品,先删后添加
setMealDishMapper.deleteBySetmealId(setmeal.getId());
//给套餐中的菜品setmealdish附上setmealId
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmeal.getId());});
//添加
setMealDishMapper.insert(setmealDishes);
}
/**
* 批量删除套餐
* @param ids
*/
@Override
public void delete(List<Long> ids) {
//批量获取套餐的状态,启售中不允许删除
List<Setmeal> setmeals = setMealMapper.getSetmealByIds(ids);
setmeals.forEach(setmeal -> {
if (setmeal.getStatus() == 1) {
throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ON_SALE);
}
});
//批量删除套餐
setMealMapper.delete(ids);
//批量删除套餐所包含的菜品
setMealDishMapper.deleteBySetmealIds(ids);
}
}
dao
setmealmapper
import com.github.pagehelper.Page;
import com.sky.annotation.AutoFill;
import com.sky.dto.SetmealPageQueryDTO;
import com.sky.entity.Setmeal;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-07 19:23
*/
@Mapper
public interface SetMealMapper {
@Select("select count(id) from setmeal where category_id = #{id}")
Integer count(Long id);
/**
* 添加套餐
* @param setMeal
*/
@AutoFill(OperationType.INSERT)
void insert(Setmeal setMeal);
/**
* 套餐分页
* @param setmealPageQueryDTO
* @return
*/
Page<Setmeal> page(SetmealPageQueryDTO setmealPageQueryDTO);
/**
* 修改套餐(套餐修改以及起售停售)
* @param setmeal
*/
@AutoFill(OperationType.UPDATE)
void update(Setmeal setmeal);
/**
* 根据id获取套餐(修改套餐回显)
* @param id
* @return
*/
@Select("select * from setmeal where id = #{id}")
Setmeal getSetmealById(Long id);
/**
* 批量删除套餐(检查套餐的状态)
* @param ids
* @return
*/
List<Setmeal> getSetmealByIds(List<Long> ids);
/**
* 批量删除套餐(删除套餐)
* @param ids
*/
void delete(List<Long> ids);
}
<?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.SetMealMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into setmeal
(category_id, name, price, status, description, image, create_time, update_time, create_user, update_user)
values
(#{categoryId},#{name},#{price},0,#{description},#{image},#{createTime},#{updateTime},#{createUser},#{updateUser})
</insert>
<select id="page" resultType="com.sky.entity.Setmeal">
select * from setmeal
<where>
<if test="categoryId != null">
and category_id = #{categoryId}
</if>
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
<if test="status != null">
and status = #{status}
</if>
</where>
order by create_time desc
</select>
<update id="update">
update setmeal
set
<if test="categoryId !=null"> category_id = #{categoryId},</if>
<if test="name !=null"> name = #{name},</if>
<if test="price !=null"> price = #{price},</if>
<if test="status !=null"> status = #{status},</if>
<if test="description !=null"> description = #{description},</if>
<if test="image !=null"> image = #{image},</if>
<if test="updateUser !=null"> update_user = #{updateUser},</if>
<if test="updateTime !=null"> update_time = #{updateTime}</if>
where id = #{id}
</update>
<select id="getSetmealByIds" resultType="com.sky.entity.Setmeal">
select * from setmeal
where id in <foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<delete id="delete">
delete from setmeal where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
setmealdishmapper
import com.sky.entity.Dish;
import com.sky.entity.SetmealDish;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-09 21:07
*/
@Mapper
public interface SetMealDishMapper {
List<Long> selectSetMealWithDish(List<Long> disIds);
/**
* 修改套餐中的菜品(先删后添加)
* @param setmealDishes
*/
void insert(List<SetmealDish> setmealDishes);
/**
* 根据id查询菜品(用于判断菜品状态,套餐起售)
* @param id
* @return
*/
@Select("select d.* from setmeal_dish sd join dish d on sd.dish_id = d.id where setmeal_id = #{id}")
List<Dish> selectBySetmealId(Long id);
/**
* 根据套餐id查询套餐包含的菜品(修改套餐回显)
* @param id
* @return
*/
@Select("select * from setmeal_dish where setmeal_id = #{id}")
List<SetmealDish> getSetmealDishBySetmealId(Long id);
/**
* 修改套餐中的菜品(先删后添加)
* @param id
*/
@Delete("delete from setmeal_dish where setmeal_id = #{id}")
void deleteBySetmealId(Long id);
/**
* 批量删除套餐所包含的菜品
* @param ids
*/
void deleteBySetmealIds(List<Long> ids);
}
<?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.SetMealDishMapper">
<select id="selectSetMealWithDish" resultType="java.lang.Long">
select setmeal_id from setmeal_dish
where dish_id in
<foreach collection="disIds" item="disId" separator="," open="(" close=")">
#{disId}
</foreach>
</select>
<insert id="insert">
insert into setmeal_dish
(setmeal_id, dish_id, name, price, copies)
VALUES
<foreach collection="setmealDishes" item="setmealDish" separator=",">
(#{setmealDish.setmealId},#{setmealDish.dishId},#{setmealDish.name},#{setmealDish.price},#{setmealDish.copies})
</foreach>
</insert>
<delete id="deleteBySetmealIds">
delete from setmeal_dish where setmeal_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>
day05
linux环境下安装redis
首先安装c语言环境,以centos7为例:
安装c语言环境
yum install gcc-c++
查看版本
gcc --version
安装wegt
yum install wget
下载redis资源包
wget https://download.redis.io/releases/redis-6.2.0.tar.gz
解压
tar xf redis-6.2.0.tar.gz
cd redis-6.2.0
安装make PREFIX指定安装目录
make install PREFIX=/usr/local/xxx
修改配置文件
找到protected-mode yes 把yes改成no允许远程连接
# bind 127.0.0.1 ::1 注释掉这一行
然后到redis文件夹执行redis-server redist.conf启动服务
下载another redis desktop manager 到Windows系统(这是一个可视化软件)
输入redis所在服务器的地址,端口号没改过的话默认是6379用户名密码默认也是没有的。这里要先保证linux系统的防火墙关闭,保证windows能够ping通linux。
systemctl stop firewalld 关闭linux防火墙。
连接成功。
数据类型
常用命令
使用spring data redis操作redis
店铺营业状态
需求分析
代码开发
admin端
import com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author CPB Email:[email protected]
* @create 12-11 20:04
*/
@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api("店铺相关接口")
public class ShopController {
private static final String SHOP_STATUS ="SHOP_STATUS";
@Resource
private RedisTemplate redisTemplate;
@PutMapping("/{status}")
@ApiOperation("设置营业状态")
public Result setStatus(@PathVariable Integer status){
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set(SHOP_STATUS,status);
return Result.success();
}
@GetMapping("/status")
@ApiOperation("获取店铺营业状态")
public Result<Integer> getStatus(){
ValueOperations valueOperations = redisTemplate.opsForValue();
Integer shop_status = (Integer) valueOperations.get(SHOP_STATUS);
return Result.success(shop_status);
}
}
user端
mport com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author CPB Email:[email protected]
* @create 12-11 20:04
*/
@RestController("userShopController")
@RequestMapping("/user/shop")
@Api("店铺相关接口")
public class ShopController {
private static final String SHOP_STATUS ="SHOP_STATUS";
@Resource
private RedisTemplate redisTemplate;
@GetMapping("/status")
@ApiOperation("获取店铺营业状态")
public Result<Integer> getStatus(){
ValueOperations valueOperations = redisTemplate.opsForValue();
Integer shop_status = (Integer) valueOperations.get(SHOP_STATUS);
return Result.success(shop_status);
}
}
功能测试
day06
HttpClient简介
入门案例
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.swing.text.html.parser.Entity;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author CPB Email:[email protected]
* @create 12-11 20:53
*/
//@SpringBootTest
public class HttpClientTest {
/**
* 通过httpclient发送GET请求
*/
@Test
public void testGET() throws IOException {
//创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//
// 创建HttpGET请求对象
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
//发送请求
CloseableHttpResponse response = httpClient.execute(httpGet);
//获取服务端返回的状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("相应码===========>"+statusCode);
HttpEntity entity = response.getEntity();
String entityStr = EntityUtils.toString(entity);
System.out.println("实体===========>"+entityStr);
// 关闭资源
response.close();
httpClient.close();
}
/**
* 通过httpclient发送POST请求
*/
@Test
public void tetsPOST() throws JSONException, IOException {
//创建httpclient对象
CloseableHttpClient client = HttpClients.createDefault();
//创建请求对象
HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
//创建请求体
JSONObject jsonObject = new JSONObject();
jsonObject.put("username","admin");
jsonObject.put("password","123456");
StringEntity entity = new StringEntity(jsonObject.toString());
//指定请求编码格式
entity.setContentEncoding("utf-8");
//指定数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);
//发送请求
CloseableHttpResponse response = client.execute(httpPost);
//获取响应码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("相应码===========>"+statusCode);
HttpEntity entity1 = response.getEntity();
String s = EntityUtils.toString(entity1);
System.out.println("实体===========>"+s);
}
}
小程序
微信登陆
先获取wx.login的code再交给后端携带appId,appSecret,grand_type发送请求
利用postman发送请求 https://api.weixin.qq.com/sns/jscode2session
appSecret在注册的小程序详情界面
需求分析
接口设计
数据库设计
代码开发
controller
import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.properties.JwtProperties;
import com.sky.result.Result;
import com.sky.service.UserService;
import com.sky.utils.JwtUtil;
import com.sky.vo.UserLoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @author CPB Email:[email protected]
* @create 12-12 14:15
*/
@RestController
@Slf4j
@RequestMapping("/user/user")
@Api(tags = "用户相关接口")
public class UserController {
@Resource
private UserService userService;
@Resource
private JwtProperties jwtProperties;
/**
*
* @param userLoginDTO
* @return
*/
@ApiOperation("微信登录")
@PostMapping("/login")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.info("微信登录:{}",userLoginDTO.getCode());
//微信登陆
User user = userService.wxLogin(userLoginDTO);
//为微信用户生成jwt令牌
Map claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID,user.getId());
String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);
//将user和jwt封装至UserLoginVo
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
}
service
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.exception.LoginFailedException;
import com.sky.mapper.UserMapper;
import com.sky.properties.WeChatProperties;
import com.sky.service.UserService;
import com.sky.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @author CPB Email:[email protected]
* @create 12-12 14:52
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
//微信登录服务接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Resource
private WeChatProperties weChatProperties;
@Resource
private UserMapper userMapper;
/**
* 微信登陆
* @param userLoginDTO
* @return
*/
@Override
public User wxLogin(UserLoginDTO userLoginDTO) {
//获取openid
String openid = getOpenId(userLoginDTO.getCode());
//判断openId是否为空,如果为空则抛出异常登陆失败
if (openid == null) {
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//判断用户是否微信用户,如是新用户则在数据库中插入此用户
User user = userMapper.getuserByOpenId(openid);
if (user == null) {
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.createUser(user);
}
//返沪用户对象
return user;
}
/**
* 获取openid
* @param code
* @return
*/
private String getOpenId(String code){
//调用微信接口,获取微信用户的openId
Map<String, String> map = new HashMap<>();
map.put("appId",weChatProperties.getAppid());
map.put("secret",weChatProperties.getSecret());
map.put("js_code",code);
map.put("grand_type","client_credential");
String json = HttpClientUtil.doGet(WX_LOGIN, map);
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");
return openid;
}
}
dao
import com.sky.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* @author CPB Email:[email protected]
* @create 12-12 15:11
*/
@Mapper
public interface UserMapper {
@Select("select * from user where openid = #{openid}")
User getuserByOpenId(String openid);
void createUser(User user);
}
<?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.UserMapper">
<insert id="createUser" useGeneratedKeys="true" keyProperty="id">
insert into user (openid, name, phone, sex, id_number, avatar, create_time)
values (#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime})
</insert>
</mapper>
这里添加了一过滤器,只有拿到token的用户才能进行操作。
import com.sky.constant.JwtClaimsConstant;
import com.sky.context.BaseContext;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getUserTokenName());
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
log.info("当前员工id:"+ userId);
//将empId存储到ThreadLocal
BaseContext.setCurrentId(userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
商品浏览代码
需求分析
接口设计
代码导入
controller
import com.sky.entity.Category;
import com.sky.result.Result;
import com.sky.service.CategoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("userCategoryController")
@RequestMapping("/user/category")
@Api(tags = "C端-分类接口")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 查询分类
* @param type
* @return
*/
@GetMapping("/list")
@ApiOperation("查询分类")
public Result<List<Category>> list(Integer type) {
List<Category> list = categoryService.list(type);
return Result.success(list);
}
}
import com.sky.constant.StatusConstant;
import com.sky.entity.Dish;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
@Autowired
private DishService dishService;
/**
* 根据分类id查询菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List<DishVO> list = dishService.listWithFlavor(dish);
return Result.success(list);
}
}
import com.sky.constant.StatusConstant;
import com.sky.entity.Setmeal;
import com.sky.result.Result;
import com.sky.service.SetMealService;
import com.sky.vo.DishItemVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("userSetmealController")
@RequestMapping("/user/setmeal")
@Api(tags = "C端-套餐浏览接口")
public class SetmealController {
@Autowired
private SetMealService setmealService;
/**
* 条件查询
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
public Result<List<Setmeal>> list(Long categoryId) {
Setmeal setmeal = new Setmeal();
setmeal.setCategoryId(categoryId);
setmeal.setStatus(StatusConstant.ENABLE);
List<Setmeal> list = setmealService.list(setmeal);
return Result.success(list);
}
/**
* 根据套餐id查询包含的菜品列表
*
* @param id
* @return
*/
@GetMapping("/dish/{id}")
@ApiOperation("根据套餐id查询包含的菜品列表")
public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id) {
List<DishItemVO> list = setmealService.getDishItemById(id);
return Result.success(list);
}
}
service
categoryServie
/**
* 获得分类列表
* @param type
* @return
*/
@Override
public List<Category> list(Integer type) {
List<Category> categories = categoryMapper.list(type);
return categories;
}
dishService
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
@Override
public List<Dish> getDishesByCategoryId(Long categoryId) {
List<Dish> dishes = dishMapper.getDishesByCategory(categoryId);
return dishes;
}
/**
* 获取菜品及其口味
* @param dish
* @return
*/
@Override
public List<DishVO> listWithFlavor(Dish dish) {
List<Dish> dishes = dishMapper.listWithStatus(dish);
ArrayList<DishVO> dishVOList = new ArrayList<>();
for (Dish d : dishes) {
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(d,dishVO);
//根据菜品id查询口味
List<DishFlavor> dishFlavors = dishFlavorsMapper.getByDishId(d.getId());
dishVO.setFlavors(dishFlavors);
dishVOList.add(dishVO);
}
return dishVOList;
}
}
setmealservice
/**
* 根据分类id查询套餐
* @param setmeal
* @return
*/
@Override
public List<Setmeal> list(Setmeal setmeal) {
List<Setmeal> setmealList = setMealMapper.getSetmealByCategoryId(setmeal);
return setmealList;
}
/**
* 获取套餐包含的菜品
* @param id
* @return
*/
@Override
public List<DishItemVO> getDishItemById(Long id) {
List<DishItemVO> dishItemVOList = setMealDishMapper.getDishWithcoopies(id);
return dishItemVOList;
}
功能测试
day07
缓存菜品
代码开发
/**
* 根据分类id查询菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
//构造redis中的key,规则:dish_分类id
String key = "dish_" + categoryId;
//查询redis中是否存在菜品数据。
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null && list.size() > 0) {
//如果存在则直接返回,无需查询数据库
return Result.success(list);
}
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
list = dishService.listWithFlavor(dish);
//如果不存在查询数据库,将查询到的数据放入redis中
redisTemplate.opsForValue().set(key,list);
return Result.success(list);
}
功能实现
不在频繁的查询sql
实现数据库更新后缓存的清除
新增菜品,起售菜品、修改菜品、删除菜品后都要清除缓存数据,否则小程序显示的还是之前缓存的数据,并不是最新的数据.
实现思路就是在这几个操作中删除对应或全部的缓存数据
代码开发
admin.DishController的修改
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Delete;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
/**
* @author CPB Email:[email protected]
* @create 12-08 23:35
*/
@Slf4j
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
public class DishController {
@Resource
private DishService dishService;
@Resource
private RedisTemplate redisTemplate;
@PostMapping
@ApiOperation("新增菜品")
public Result createDish(@RequestBody DishDTO dishDTO){
log.info("新增菜品:{}",dishDTO);
dishService.saveWithFlavor(dishDTO);
//清除缓存
String key = "dish_" + dishDTO.getCategoryId();
cleanCache(key);
return Result.success();
}
@GetMapping("/page")
@ApiOperation("菜品分页")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
PageResult pageResult = dishService.page(dishPageQueryDTO);
return Result.success(pageResult);
}
/**
* 菜品批量删除
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量删除")
public Result delete(@RequestParam List<Long> ids){
dishService.deleteBatch(ids);
cleanCache("dish_*");
return Result.success();
}
/**
* 根据id查询菜品
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result<DishVO> getDishById(@PathVariable Long id){
DishVO dishVO = dishService.getDishById(id);
return Result.success(dishVO);
}
/**
* 修改菜品
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result updateDish(@RequestBody DishDTO dishDTO){
dishService.updateDishWithFlavor(dishDTO);
cleanCache("dish_*");
return Result.success();
}
/**
* 起售停售菜品
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
public Result startOrStop(@PathVariable Integer status,Long id){
dishService.startOrStop(status,id);
cleanCache("dish_*");
return Result.success();
}
/**
* 根据分类Id查询菜品
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类Id查询菜品")
public Result<List<Dish>> getDishesByCategoryId(Long categoryId){
List<Dish> dishes = dishService.getDishesByCategoryId(categoryId);
return Result.success(dishes);
}
private void cleanCache(String pattern){
//获取key
Set keys = redisTemplate.keys(pattern);
//删除对应的key
redisTemplate.delete(keys);
}
}
Spring Cache
缓存套餐
实现思路
代码开发
我这里为查询套餐包含的菜品也添加了缓存,缓存命名为stemealdishCache::setmealId
import com.sky.constant.StatusConstant;
import com.sky.entity.Setmeal;
import com.sky.result.Result;
import com.sky.service.SetMealService;
import com.sky.vo.DishItemVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController("userSetmealController")
@RequestMapping("/user/setmeal")
@Api(tags = "C端-套餐浏览接口")
public class SetmealController {
@Autowired
private SetMealService setmealService;
/**
* 条件查询
*
* @param categoryId
* @return
*/
@Cacheable(cacheNames = "setmealCache",key = "#categoryId") //key:setmealCache::categoryId
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
public Result<List<Setmeal>> list(Long categoryId) {
Setmeal setmeal = new Setmeal();
setmeal.setCategoryId(categoryId);
setmeal.setStatus(StatusConstant.ENABLE);
List<Setmeal> list = setmealService.list(setmeal);
return Result.success(list);
}
/**
* 根据套餐id查询包含的菜品列表
*
* @param id
* @return
*/
@Cacheable(cacheNames = "setmealDishCache",key = "#id")
@GetMapping("/dish/{id}")
@ApiOperation("根据套餐id查询包含的菜品列表")
public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id) {
List<DishItemVO> list = setmealService.getDishItemById(id);
return Result.success(list);
}
}
管理端controller
因为我在查看套餐菜品的接口也使用了缓存所以当套餐菜品发生变更时同时也需要删除相关的套餐菜品信息,因此我需要给修改套餐的接口上再加一个setmealdishCache的key,发现cacheNames是个String数组则
@CacheEvict(cacheNames = {"setmealCache","setmealDishCache"},allEntries = true)
import com.sky.dto.SetmealDTO;
import com.sky.dto.SetmealPageQueryDTO;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.SetMealService;
import com.sky.vo.SetmealVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-10 14:33
*/
@Slf4j
@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐类相关接口")
public class SetMealController {
@Resource
private SetMealService setMealService;
@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
@PostMapping
@ApiOperation("新增套餐")
public Result createSetMeal(@RequestBody SetmealDTO setmealDTO){
setMealService.createSetMeal(setmealDTO);
return Result.success();
}
/**
* 套餐分页查询
* @param setmealPageQueryDTO
* @return
*/
@GetMapping("/page")
@ApiOperation("套餐分页查询")
public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){
PageResult pageResult = setMealService.page(setmealPageQueryDTO);
return Result.success(pageResult);
}
/**
* 修改套餐状态
* @param status
* @param id
* @return
*/
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
@PostMapping("/status/{status}")
@ApiOperation("修改套餐状态")
public Result startOrStop(@PathVariable Integer status,Long id){
setMealService.startOrStop(status,id);
return Result.success();
}
/**
* 根据id查询套餐,修改套餐回显
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation("根据id查询套餐")
public Result<SetmealVO> getSetmealById(@PathVariable Long id){
SetmealVO setmealVO = setMealService.getSetmealById(id);
return Result.success(setmealVO);
}
/**
* 修改套餐
* @param setmealDTO
* @return
*/
@CacheEvict(cacheNames = {"setmealCache","setmealDishCache"},allEntries = true)
@PutMapping
@ApiOperation("修改套餐")
public Result update(@RequestBody SetmealDTO setmealDTO){
setMealService.update(setmealDTO);
return Result.success();
}
/**
* 批量删除套餐
* @param ids
* @return
*/
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
@DeleteMapping
@ApiOperation("批量删除套餐")
public Result delete(@RequestParam List<Long> ids){
setMealService.delete(ids);
return Result.success();
}
}
当删除套餐时会同时删除setmealCachehe、setmealDishCahe的所有key
添加购物车业务
需求分析
代码开发
controller
import com.sky.dto.ShoppingCartDTO;
import com.sky.result.Result;
import com.sky.service.ShoppingCartService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author CPB Email:[email protected]
* @create 12-13 21:41
*/
@RestController
@RequestMapping("/user/shoppingCart")
@Api(tags = "C-端购物车相关接口")
@Slf4j
public class ShoppingCartController {
@Resource
private ShoppingCartService shoppingCartService;
/**
* 添加购物车
* @return
*/
@PostMapping("/add")
@ApiOperation("添加购物车")
public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
log.info("添加购物车,商品信息为:{}",shoppingCartDTO);
shoppingCartService.addShoppingCart(shoppingCartDTO);
return Result.success();
}
}
service
import com.sky.context.BaseContext;
import com.sky.dto.ShoppingCartDTO;
import com.sky.entity.Dish;
import com.sky.entity.Setmeal;
import com.sky.entity.ShoppingCart;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetMealMapper;
import com.sky.mapper.ShoppingCartMapper;
import com.sky.service.ShoppingCartService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-13 21:48
*/
@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {
@Resource
private ShoppingCartMapper shoppingCartMapper;
@Resource
private DishMapper dishMapper;
@Resource
private SetMealMapper setMealMapper;
/**
* 添加购物车
* @param shoppingCartDTO
*/
@Override
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
//查询购物车是否存在该商品,如果存在就数量加一,不存在则重新插入数据
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
//判断查询到的结果是否为空,为空则添加商品,否则给商品数量加一
if (list != null && list.size() > 0) {
//不为空说明购物车存在该商品,则对该商品数量加一
shoppingCart = list.get(0);
shoppingCart.setNumber(shoppingCart.getNumber() + 1);
shoppingCartMapper.updateCount(shoppingCart);
}else {
//为空说明购物车中不存在该商品,进行添加业务
Long dishId = shoppingCartDTO.getDishId();
//判断添加的是菜品还是套餐
if (dishId != null) {
//dishId不为空添加的是菜品
ArrayList<Long> ids = new ArrayList<>();
ids.add(dishId);
List<Dish> dishes = dishMapper.selectById(ids);
Dish dish = dishes.get(0);
shoppingCart.setName(dish.getName());
shoppingCart.setImage(dish.getImage());
shoppingCart.setAmount(dish.getPrice());
}else {
Setmeal setmeal = setMealMapper.getSetmealById(shoppingCartDTO.getSetmealId());
shoppingCart.setName(setmeal.getName());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());
}
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCart.setUserId(BaseContext.getCurrentId());
shoppingCartMapper.insert(shoppingCart);
}
}
}
dao
import com.sky.entity.ShoppingCart;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @author CPB Email:[email protected]
* @create 12-13 22:10
*/
@Mapper
public interface ShoppingCartMapper {
List<ShoppingCart> list(ShoppingCart shoppingCart);
@Update("update shopping_cart set number = #{number} where id = #{id}")
void updateCount(ShoppingCart shoppingCart);
@Insert("insert into shopping_cart " +
"(name, image, user_id, dish_id, setmeal_id, dish_flavor, amount, create_time) " +
"VALUES (#{name},#{image},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{amount},#{createTime})")
void insert(ShoppingCart shoppingCart);
}
<select id="list" resultType="com.sky.entity.ShoppingCart">
select * from shopping_cart
<where>
<if test="dishId != null">
and dish_id = #{dishId}
</if>
<if test="setmealId != null">
and setmeal_id = #{setmealId}
</if>
<if test="dishFlavor != null">
and dish_flavor = #{dishFlavor}
</if>
</where>
</select>