瑞吉外卖后端代码

一、功能架构

二、数据表 

在这里插入图片描述

 

 三、代码开发

1、maven(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.example</groupId>
    <artifactId>reggie</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.16</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>2.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.6</version>
            </plugin>
        </plugins>
    </build>

</project>

2、application.yml

#配置服务器
server:
  port: 8080

#配置spring框架
spring:
  application:
    name: ReggieTakeOut #应用名称(瑞吉外卖)
  datasource: #数据源
    druid: #druid数据源
      driver-class-name: com.mysql.cj.jdbc.Driver #驱动程序
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root  #用户名
      password:   #密码
  #配置连接redis 连得是linux ip的 需要在linux上启动redis usr/local/bin  命令: ./redis-server redis.conf
  redis:
    host:
    port: 6379
    password:
    database: 0

mybatis-plus:
  configuration:
    #    address_book---->AddressBook
    #    user_name---->userName
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    #日志实现类 把SQL的查询过程输出到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config: #全局配置
    db-config: #数据库配置
      id-type: ASSIGN_ID #id-type: auto #数据ID自增

cache:
  redis:
    time-to-live: 1800000 # 设置缓存数据过期时间

reggie:
  path: E:\reggie\图片资源\

3、创建SpringApplication入口

package org.example;


import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Slf4j
@SpringBootApplication
@ServletComponentScan //过滤器
@EnableTransactionManagement //事务
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功...");
    }
}

4、实体类

①用户user

package org.example.entity;

import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
/**
 * 用户信息
 */
@Data
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //姓名
    private String name;


    //手机号
    private String phone;


    //性别 0 女 1 男
    private String sex;


    //身份证号
    private String idNumber;


    //头像
    private String avatar;


    //状态 0:禁用,1:正常
    private Integer status;
}

②购物车shoppingcart

package org.example.entity;

import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 购物车
 */
@Data
public class ShoppingCart implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    //名称
    private String name;

    //用户id
    private Long userId;

    //菜品id
    private Long dishId;

    //套餐id
    private Long setmealId;

    //口味
    private String dishFlavor;

    //数量
    private Integer number;

    //金额
    private BigDecimal amount;

    //图片
    private String image;

    private LocalDateTime createTime;
}

③套餐setmeal

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 套餐
 */
@Data
public class Setmeal implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //分类id
    private Long categoryId;


    //套餐名称
    private String name;


    //套餐价格
    private BigDecimal price;


    //状态 0:停用 1:启用
    private Integer status;


    //编码
    private String code;


    //描述信息
    private String description;


    //图片
    private String image;


    @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;


    //是否删除
    private Integer isDeleted;
}

④套餐菜品关系

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 套餐菜品关系
 */
@Data
public class SetmealDish implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //套餐id
    private Long setmealId;


    //菜品id
    private Long dishId;


    //菜品名称 (冗余字段)
    private String name;

    //菜品原价
    private BigDecimal price;

    //份数
    private Integer copies;


    //排序
    private Integer sort;


    @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;


    //是否删除
    private Integer isDeleted;
}

⑤菜品Dish

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 菜品
 */
@Data
public class Dish implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //菜品名称
    private String name;


    //菜品分类id
    private Long categoryId;


    //菜品价格
    private BigDecimal price;


    //商品码
    private String code;


    //图片
    private String image;


    //描述信息
    private String description;


    //0 停售 1 起售
    private Integer status;


    //顺序
    private Integer sort;


    @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;


    //是否删除
    private Integer isDeleted;

}

⑥菜品口味DishFlavor

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
菜品口味
 */
@Data
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //菜品id
    private Long dishId;


    //口味名称
    private String name;


    //口味数据list
    private String value;


    @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;


    //是否删除
    private Integer isDeleted;

}

⑦员工employee

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/*
 * 功能:员工实体表
 */

@Data //Lombok注解,注在类上,提供类的get、set、equals、hashCode、CanEqual、toString方法
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L; //序列化的类中建议添加以提供版本兼容性

    private Long id;
    private String username;
    private String name;
    private String password;
    private String phone;
    private String sex;
    private String idNumber;
    private Integer status;

    @TableField(fill = FieldFill.INSERT) // 插入时自动填充字段
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充字段
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT) //mybatis-plus注解,填充策略
    private Long createUser; //对应字段 create_user

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser; //对应字段 update_user



}

⑧订单orders

package org.example.entity;

import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单
 */
@Data
public class Orders implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    //订单号
    private String number;

    //订单状态 1待付款,2待派送,3已派送,4已完成,5已取消
    private Integer status;


    //下单用户id
    private Long userId;

    //地址id
    private Long addressBookId;


    //下单时间
    private LocalDateTime orderTime;


    //结账时间
    private LocalDateTime checkoutTime;


    //支付方式 1微信,2支付宝
    private Integer payMethod;


    //实收金额
    private BigDecimal amount;

    //备注
    private String remark;

    //用户名
    private String userName;

    //手机号
    private String phone;

    //地址
    private String address;

    //收货人
    private String consignee;
}

⑨订单明细orderDetail

package org.example.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;

/**
 * 订单明细
 */
@Data
public class OrderDetail implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    //名称
    private String name;

    //订单id
    private Long orderId;


    //菜品id
    private Long dishId;


    //套餐id
    private Long setmealId;


    //口味
    private String dishFlavor;


    //数量
    private Integer number;

    //金额
    private BigDecimal amount;

    //图片
    private String image;
}

⑩地址address

package org.example.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;

/**
 * 订单明细
 */
@Data
public class OrderDetail implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    //名称
    private String name;

    //订单id
    private Long orderId;


    //菜品id
    private Long dishId;


    //套餐id
    private Long setmealId;


    //口味
    private String dishFlavor;


    //数量
    private Integer number;

    //金额
    private BigDecimal amount;

    //图片
    private String image;
}

⑾分类category

package org.example.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 分类
 */
@Data
public class Category implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;


    //类型 1 菜品分类 2 套餐分类
    private Integer type;


    //分类名称
    private String name;


    //顺序
    private Integer sort;


    //创建时间
    @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;


    //是否删除
//    private Integer isDeleted;

}

5、dto

①DishDto

package org.example.entity.dto;

import lombok.Data;
import org.example.entity.Dish;
import org.example.entity.DishFlavor;

import java.util.ArrayList;
import java.util.List;

@Data
public class DishDto extends Dish { //Dto Data Transfer object 数据传输对象 用于层与服务层之间的数据传输

    // 新增菜品会对应两张表 1、dish 2、dishflavor
    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

②OrderDto

package org.example.entity.dto;


import lombok.Data;
import org.example.entity.OrderDetail;
import org.example.entity.Orders;

import java.util.List;

@Data
public class OrdersDto extends Orders {

    private String userName;

    private String phone;

    private String address;

    private String consignee;

    private List<OrderDetail> orderDetails;

    private int sumNum;
	
}

③SetmealDto

package org.example.entity.dto;


import lombok.Data;
import org.example.entity.Setmeal;
import org.example.entity.SetmealDish;

import java.util.List;

@Data
public class SetmealDto extends Setmeal {

    private List<SetmealDish> setmealDishes;

    private String categoryName;
}

6、过滤器filter检查登录

LoginCheckFilter

package org.example.filter;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.common.BaseContext;
import org.example.common.R;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 检查用户是否已经完成登录
 */

@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {

    //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // 1、获取本次请求的url
        String requestURI = request.getRequestURI();

        log.info("拦截到请求:{}", request.getRequestURI());


        // 2、定义不需要请求的url
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/user/sendMsg",
                "/user/login"
        };

        // 3、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

        // 4、如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求不需要处理:{}", request.getRequestURI());
            filterChain.doFilter(request, response); // 放行
            return; //方法结束
        }


        // 在5.1和5.2前再加个检查 判断是客户端还是服务端

            // 5.1、check为false 检查登录状态
            if (request.getSession().getAttribute("employee") != null) { // 已登录
                log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("employee"));
                Long empId = (Long) request.getSession().getAttribute("employee");
                // 将该线程的当前用户id保存至threadlocal
                BaseContext.setCurrentId(empId);

                filterChain.doFilter(request, response); // 放行
                return;
            }


        // 5.2 判断移动端登录
        if (request.getSession().getAttribute("user") != null) {
            log.info("用户已登录, 用户id为:{}", request.getSession().getAttribute("user"));

            Long userId = (Long) request.getSession().getAttribute("user");
            // 将该线程的当前用户id保存至threadlocal
            BaseContext.setCurrentId(userId);
            filterChain.doFilter(request, response);
            return;

        }

        // 6、未登录根据前端返回未登录的结果
        log.info("用户未登录");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;


    }

    //check方法 遍历urls看是否匹配requestURI
    public boolean check(String[] urls, String requestURI) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
                return true;
            }
        }
        return false;
    }
}

7、配置类config

WebMvcConfig

package org.example.config;


import lombok.extern.slf4j.Slf4j;
import org.example.common.JacksonObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;


@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport{

    /*
    * 设置资源映射 默认是映射static
    * "/backend/**" 表示浏览器访问请求 "classpath:/backend/" 表示映射到本地
    *  ** 表示多层通配符
    * */

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry){
        log.info("开启静态资源映射");

        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");

    }


    /**
     * 扩展mvc框架的消息转换器
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters){
         log.info("扩展消息转换器...");
        // 创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();

        //设置对象转换器 底层使用jackson将java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());

        // 将消息转换器对象追加到mvc框架的转换器集合中
        // 设置自定义的消息转换器为第一优先级
        converters.add(0, messageConverter);


    }
}

RedisConfig

package org.example.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){

        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setKeySerializer(new StringRedisSerializer());

        redisTemplate.setConnectionFactory(connectionFactory);

        return redisTemplate;

    }

}

MybatisPlusConfig 配置mybatis-plus的分页拦截器

package org.example.config;


import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置mybatis-plus提供的分页插件拦截器
 */

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); // 拦截器集合
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 向Mybatis拦截器链中添加分页拦截器
        return mybatisPlusInterceptor;
    }
}

8、通用

BaseContext(ThreadLocal)

package org.example.common;

/**
 * 基于ThreadLocal封装工具类,用于保存和获取当前登录用户id ThreadLocal为每个线程提供单独一份储存空间
 * 客户端每次http请求 服务端都会分配新的线程
 * LoginCheckFilter的doFilter
 * EmployeeController的update
 * MyMetaObjectHandler的updateFill方法为同一个线程
 *
 * 使用:1、编写BaseContext工具类,基于ThreaLocal封装的工具类
 *      2、在LoginCheckFilter的doFilter方法中 设置当前登录用户id
 *      3、在MyMetaObjectHnadler的方法中 获取当前登录用户id
 */
public class BaseContext {

    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id){ // 设置id
        threadLocal.set(id);
    }

    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

CustomException(自定义业务异常)

package org.example.common;


/**
 * 自定义业务异常类
 */
public class CustomException extends RuntimeException{
    public CustomException(String message){
        super(message);
    }
}

GlobalExceptionHandler(全局异常处理)

package org.example.common;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常类
 */

@Slf4j
@ResponseBody
@ControllerAdvice(annotations = {RestController.class, Controller.class}) // 表示拦截哪些类型的controller注解
public class GlobalExceptionHandler {

    // 处理SQLIntegrityConstraintViolationException异常
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandle(SQLIntegrityConstraintViolationException exception){
        log.error(exception.getMessage()); // 报错打日志
        if (exception.getMessage().contains("Duplicate entry")){
            // 获取已经存在的用户名,从报错的异常信息中获取
            String[] split = exception.getMessage().split(" ");
            String msg = split[2] + "用户名已存在";
            return R.error(msg);

        } return R.error("未知错误");
    }


    // 处理自定义异常类
    @ExceptionHandler(CustomException.class)
    public R<String> exceptionHandle(CustomException exception){
        log.error(exception.getMessage()); // 报错需要打进日志

        // 传到前端显示
        return R.error(exception.getMessage());
    }
}

JacksonObjectMapper(消息转换器)

package org.example.common;



import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 定义消息转换器
 * mybatis-plus对id使用了雪花算法,存入数据库中的id是19为长度,但前端的js只能保证数据的前16位的数据的精度 定义消息转换器 long->string
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; //date
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; // date_time
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; // time

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        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(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .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)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

MyMetaObjecthandler(公共字段填充 元数据对象处理器)

package org.example.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;


/**
 * mybatis-plus 公共字段自动填充
 * 1、在实体类的属性上加入@TabelField 注解,指定自动填充策略
 * 2、编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口
 */
@Slf4j
@Component // 交给spring容器管理
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) { // 插入时自动填充
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());

        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());

    }

    @Override
    public void updateFill(MetaObject metaObject) { // 更新时自动填充
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());

    }
}

R(通用返回结果)

package org.example.common;


import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * 通用返回结果类 服务器的数据最终会封装成此对象
 */


@Data //Lombok注解,注在类上,提供类的get、set、equals、hashCode、CanEqual、toString方法
public class R<T> {

    private Integer code; // 编码: 1成功 0和其他数字为失败

    private String msg; //错误信息
    private T data; // 数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object){
        // <T> 表明为泛型方法 因为是静态方法需要表明
        // R<T> 表明返回类型

        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;

    }

    public static <T> R<T> error(String msg){
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }


    public R<T> add(String key, Object value){
        this.map.put(key, value);
        return this;

    }


}

9、Mapper

UserMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.User;

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

CategoryMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.Category;

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}

AddressBookMapper

package org.example.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.AddressBook;

@Mapper
public interface AddressBookMapper extends BaseMapper<AddressBook> {
}

DishMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.Dish;

@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}

DishFlavorMapper

package org.example.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.DishFlavor;

@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}

EmployeeMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.Employee;

/**
 * 功能:EmployeeMapper接口(和数据库交互),采用mybatis-plus插件,不用创建对应的映射器配置文件
 */

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}

OrderMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.Orders;

@Mapper
public interface OrderMapper extends BaseMapper<Orders> {
}

OrderDetailMapper

package org.example.mapper;



import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.OrderDetail;

@Mapper
public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
}

SetmealMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.Setmeal;

@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
}

SetmealDishMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.SetmealDish;

@Mapper
public interface SetmealDishMapper extends BaseMapper<SetmealDish> {
}

ShoppingCartMapper

package org.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.entity.ShoppingCart;


@Mapper
public interface ShoppingCartMapper extends BaseMapper<ShoppingCart> {
}

10、Service

AddressBookService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.AddressBook;

public interface AddressBookService extends IService<AddressBook> {
}

CategoryService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.Category;

public interface CategoryService extends IService<Category> {

    // mybatis-plus 没有提供的方法 需要自己重写

    void remove(Long id);
}

DishService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.Dish;
import org.example.entity.dto.DishDto;

public interface DishService extends IService<Dish> {
    void saveWithFlavor(DishDto dishDto); // 新增菜品 同时插入菜品对应的口味数据 两张表dish dish_flavor

    DishDto getByIdWithFlavor(Long id);

    void updateWithFlavor(DishDto dishDto);
}

DishFlavorsService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.DishFlavor;

public interface DishFlavorService extends IService<DishFlavor> {
}

EmployeeService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.Employee;

/**
 * 采用了mybatis-plus插件,只需继承IService<Employee>接口
 */

public interface EmployeeService extends IService<Employee> {
}

OrdersService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.Orders;

public interface OrderService extends IService<Orders> {
    void submit(Orders orders);
}

OrderDetailService

package org.example.service;


import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.OrderDetail;

public interface OrderDetailService extends IService<OrderDetail> {
}

SetmealService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.Setmeal;
import org.example.entity.dto.SetmealDto;

public interface SetmealService extends IService<Setmeal> {
    void saveWithDish(SetmealDto setmealDto);

    SetmealDto getByIdWithDish(Long id);

    void updateWithDish(SetmealDto setmealDto);
}

SetmealDishService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.SetmealDish;
import org.example.entity.dto.SetmealDto;

public interface SetmealDishService extends IService<SetmealDish> {

}

ShoppingCartService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.ShoppingCart;

public interface ShoppingCartService extends IService<ShoppingCart> {
}

UserService

package org.example.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.example.entity.User;

public interface UserService extends IService<User> {
}

11、ServiceImp

AddressBookServiceImp

package org.example.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.entity.AddressBook;
import org.example.mapper.AddressBookMapper;
import org.example.service.AddressBookService;
import org.springframework.stereotype.Service;

@Service
public class AddressBookServiceImp extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService {
}

CategoryServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.common.CustomException;
import org.example.entity.Category;
import org.example.entity.Dish;
import org.example.entity.Setmeal;
import org.example.mapper.CategoryMapper;
import org.example.service.CategoryService;
import org.example.service.DishService;
import org.example.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CategoryServiceImp extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    @Autowired
    private DishService dishService;

    @Autowired
    private SetmealService setmealService;

    @Override
    public void remove(Long id) {
        // 根据id删除分类 删除之前需要进行判断是否有关联数据

        //1、菜品
        LambdaQueryWrapper<Dish> dishQueryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件
        dishQueryWrapper.eq(Dish::getCategoryId, id);
        //count 根据条件计数
        int dishCount = dishService.count(dishQueryWrapper);
        if(dishCount > 0){ // 有关联的菜品
            throw new CustomException("已经关联菜品,不能删除");

        }

        //2、套餐
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);
        //count 根据条件计数
        int setmealCount = setmealService.count(setmealLambdaQueryWrapper);
        if (setmealCount > 0){
            throw new CustomException("已经关联套餐, 不能删除");
        }

        //正常删除分类使用mybatis-plus
        super.removeById(id);



    }
}

DishServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.Dish;
import org.example.entity.dto.DishDto;
import org.example.entity.DishFlavor;
import org.example.mapper.DishMapper;
import org.example.service.DishFlavorService;
import org.example.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;


@Slf4j
@Service
public class DishServiceImp extends ServiceImpl<DishMapper, Dish> implements DishService {



    @Autowired
    private DishFlavorService dishFlavorService;

    @Override
    @Transactional //两张表 事务
    public void saveWithFlavor(DishDto dishDto) {

        this.save(dishDto); //保存菜品的基本信息到菜品表dish中
        Long dishId = dishDto.getId();

        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) ->{ // 遍历flavors 设置DishId
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        // 保存菜品口味到菜品数据表
        dishFlavorService.saveBatch(flavors); //saveBatch批量保存

    }

    @Override
    public DishDto getByIdWithFlavor(Long id) {

        // 查询菜品基本信息
        Dish dish = this.getById(id);

        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish, dishDto);

        // 查询菜品口味信息
        LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DishFlavor::getDishId, dish.getId());
        List<DishFlavor> list = dishFlavorService.list(lambdaQueryWrapper);

        dishDto.setFlavors(list);

        return dishDto;

    }

    @Override
    public void updateWithFlavor(DishDto dishDto) {
        this.updateById(dishDto); // 更新菜品表

        // 操作口味表
        // 1、先删除表中原来的数据
        LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DishFlavor::getDishId, dishDto.getId()); // 根据菜品id删
        dishFlavorService.remove(lambdaQueryWrapper);

        // 2、给口味集合设置菜品id
        List<DishFlavor> flavors = dishDto.getFlavors();

        flavors = flavors.stream().map((item) ->{
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);

    }



}

DishFlavorsServiceImp

package org.example.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.entity.DishFlavor;
import org.example.mapper.DishFlavorMapper;
import org.example.service.DishFlavorService;
import org.springframework.stereotype.Service;

@Service
public class DishFlavorServiceImp extends ServiceImpl<DishFlavorMapper, DishFlavor> implements DishFlavorService {
}

EmployeeServiceImp

package org.example.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.entity.Employee;
import org.example.mapper.EmployeeMapper;
import org.example.service.EmployeeService;
import org.springframework.stereotype.Service;

/**
 * 雇员服务接口实现类
 */
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {

}

OrdersServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.common.BaseContext;
import org.example.common.CustomException;
import org.example.entity.AddressBook;
import org.example.entity.Orders;
import org.example.entity.ShoppingCart;
import org.example.entity.User;
import org.example.entity.OrderDetail;
import org.example.mapper.OrderMapper;
import org.example.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class OrderServiceImp extends ServiceImpl<OrderMapper, Orders> implements OrderService {


    @Autowired
    private ShoppingCartService shoppingCartService;

    @Autowired
    private UserService userService;

    @Autowired
    private AddressBookService addressBookService;

    @Autowired
    private OrderDetailService orderDetailService;


    @Override
    @Transactional
    public void submit(Orders orders) {

        Long userId = BaseContext.getCurrentId();

        LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getUserId, userId);

        List<ShoppingCart> shoppingCarts = shoppingCartService.list(shoppingCartLambdaQueryWrapper);
        if (shoppingCarts == null || shoppingCarts.size() == 0){
            throw new CustomException("购物车为空,不能下单");
        }



        // 查询地址
        Long addressBookId = orders.getAddressBookId();
        AddressBook addressBook = addressBookService.getById(addressBookId);

        if (addressBook == null){
            throw new CustomException("地址信息有误,不能下单");
        }

        // 生成订单号
        long orderId = IdWorker.getId();
        orders.setId(orderId);

        // 计算金额
        AtomicInteger amount = new AtomicInteger(0); // 使用原子类保存计算结果

        List<OrderDetail> orderDetails = shoppingCarts.stream().map((item) ->{

            // 遍历购物车中的每件商品
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setNumber(item.getNumber());
            orderDetail.setDishFlavor(item.getDishFlavor());
            orderDetail.setDishId(item.getDishId());
            orderDetail.setSetmealId(item.getSetmealId());
            orderDetail.setName(item.getName());
            orderDetail.setImage(item.getImage());
            orderDetail.setAmount(item.getAmount());//单份的金额

            // 累加
            amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());

            return orderDetail;


        }).collect(Collectors.toList());

        // 批量添加订单明细表
        orderDetailService.saveBatch(orderDetails);



        // 给order添加信息

        User user = userService.getById(userId);

        orders.setNumber(String.valueOf(orderId)); // String.valueOf(orderId) 建id转为string类型
        orders.setId(orderId);
        orders.setOrderTime(LocalDateTime.now());
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setStatus(2);
        orders.setAmount(new BigDecimal(amount.get())); // 计算总金额
        orders.setUserId(userId);
        orders.setUserName(user.getName());
        orders.setConsignee(addressBook.getConsignee());
        orders.setPhone(addressBook.getPhone());
        orders.setAddress((addressBook.getProvinceName()==null?"":addressBook.getProvinceName())
                +(addressBook.getCityName()==null?"":addressBook.getCityName())
                +(addressBook.getDistrictName()==null?"":addressBook.getDistrictName())
                +(addressBook.getDetail()==null?"":addressBook.getDetail()));

        // 订单表
        this.save(orders);


        // 下单后清空购物车数据
        shoppingCartService.remove(shoppingCartLambdaQueryWrapper);


    }
}

OrderDetailServiceImp

package org.example.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.entity.OrderDetail;
import org.example.mapper.OrderDetailMapper;
import org.example.service.OrderDetailService;
import org.springframework.stereotype.Service;

@Service
public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService {


}

SetmealServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.Setmeal;
import org.example.entity.SetmealDish;
import org.example.entity.dto.SetmealDto;
import org.example.mapper.SetmealMapper;
import org.example.service.SetmealDishService;
import org.example.service.SetmealService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SetmealServiceImp extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {


    @Autowired
    private SetmealDishService setmealDishService;

    @Override
    @Transactional
    public void saveWithDish(SetmealDto setmealDto) {

        // log.info(setmealDto.toString()); // 可以先查看传入的基本信息
        this.save(setmealDto); // 保存套餐基本信息

        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();

        setmealDishes.stream().map((item) ->{
            item.setSetmealId(setmealDto.getId());
            return item;
        }).collect(Collectors.toList());

        setmealDishService.saveBatch(setmealDishes); // setmeal_dish表

    }

    @Override
    public SetmealDto getByIdWithDish(Long id) {
        // 查询套餐基本信息
        Setmeal setmeal = this.getById(id);

        SetmealDto setmealDto = new SetmealDto();

        BeanUtils.copyProperties(setmeal, setmealDto);

        //查询套餐菜品信息
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId, setmeal.getId());
        List<SetmealDish> list = setmealDishService.list(lambdaQueryWrapper);

        setmealDto.setSetmealDishes(list);

        return setmealDto;

    }

    @Override
    public void updateWithDish(SetmealDto setmealDto) {

        // 更新基本信息
        this.updateById(setmealDto);

        // 更新口味信息
        // 1、先删除原来的口味
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId, setmealDto.getId());
        setmealDishService.remove(lambdaQueryWrapper);

        //2、 写入新的口味
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();

        setmealDishes = setmealDishes.stream().map((item) ->{
            item.setSetmealId(setmealDto.getId());
            return item;
        }).collect(Collectors.toList());

        setmealDishService.saveBatch(setmealDishes);


    }


}

SetmealDishServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.SetmealDish;
import org.example.entity.dto.SetmealDto;
import org.example.mapper.SetmealDishMapper;
import org.example.service.SetmealDishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
public class SetmealDishServiceImp extends ServiceImpl<SetmealDishMapper, SetmealDish> implements SetmealDishService {

}

ShoppingCartServiceImp

package org.example.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.entity.ShoppingCart;
import org.example.mapper.ShoppingCartMapper;
import org.example.service.ShoppingCartService;
import org.springframework.stereotype.Service;


@Service
public class ShoppingCartServiceImp extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService {
}

UserServiceImp

package org.example.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.User;
import org.example.mapper.UserMapper;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements UserService {


}

12、Controller

AddressBookController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.example.common.BaseContext;
import org.example.common.R;
import org.example.entity.AddressBook;
import org.example.service.AddressBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {

    @Autowired
    private AddressBookService addressBookService;


    @PostMapping()
    public R<AddressBook> save(@RequestBody AddressBook addressBook){ // 新增地址

        addressBook.setUserId(BaseContext.getCurrentId()); // 当前用户id

        log.info("addressBook:{}", addressBook);

        addressBookService.save(addressBook);

        return R.success(addressBook);
    }

    @PutMapping("/default")
    public R<AddressBook> setDefault(@RequestBody AddressBook addressBook){ // 设置默认地址

        log.info("addressBook:{}", addressBook);

        // 使用的是update
        LambdaUpdateWrapper<AddressBook> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();

        lambdaUpdateWrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());

        lambdaUpdateWrapper.set(AddressBook::getIsDefault, 0); // 直接更新 把所有都设为非默认

        addressBookService.update(lambdaUpdateWrapper);
        // 把传入的地址设为默认
        addressBook.setIsDefault(1);
        addressBookService.updateById(addressBook);
        return R.success(addressBook);


    }

    @GetMapping("/{id}")
    public R get(@PathVariable Long id){ // 根据id查询地址
        AddressBook addressBook = addressBookService.getById(id);

        if (addressBook != null){
            return R.success(addressBook);

        } else {
            return R.error("没有找到该对象");
        }
    }

    @GetMapping("/default")
    public R<AddressBook> getDefault(){

        LambdaQueryWrapper<AddressBook> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId()); // 先查该用户
        lambdaQueryWrapper.eq(AddressBook::getIsDefault, 1);

        AddressBook addressBook = addressBookService.getOne(lambdaQueryWrapper);

        if (addressBook == null){
            return R.error("没有找到该对象");
        } else {
            return R.success(addressBook);
        }


    }


    @GetMapping("/list")
    public R<List<AddressBook>> list(AddressBook addressBook){ // 展示该用户全部地址
        addressBook.setUserId(BaseContext.getCurrentId());
        log.info("addressBook:{}", addressBook);

        LambdaQueryWrapper<AddressBook> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.eq(addressBook.getUserId() != null, AddressBook::getUserId, addressBook.getUserId());

        lambdaQueryWrapper.orderByDesc(AddressBook::getUpdateTime);

        List<AddressBook> list = addressBookService.list(lambdaQueryWrapper);

        return R.success(list);

    }

}

CategoryController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.common.R;
import org.example.entity.Category;
import org.example.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 菜品分类管理层
 */
@Slf4j
@RestController //交给spring容器管理
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    private CategoryService categoryService;


    @PostMapping() // 新增套餐分类
    public R<String> save(@RequestBody Category category) {
        log.info("category:{}", category);
        categoryService.save(category);
        return R.success("新增分类成功");
    }


    @GetMapping("/page") // 分页
    public R<Page> page(int page, int pageSize) {

        // 创造分页构造器
        Page<Category> categoryPage = new Page<>(page, pageSize);

        // 创造条件构造器用来排序 注意使用泛型
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper();
        // 添加排序条件 sort字段排序
        queryWrapper.orderByAsc(Category::getSort);

        categoryService.page(categoryPage, queryWrapper);

        return R.success(categoryPage);

    }

    @DeleteMapping()
    public R<String> delete(@RequestParam("ids") Long ids) { // 根据id删除分类 前端传的是ids

        categoryService.remove(ids);
        return R.success("分类信息删除成功");
    }

    @PutMapping()
    public R<String> update(@RequestBody Category category){ // 根据id修改分类
        categoryService.updateById(category);
        return R.success("修改分类信息成功");


    }

    @GetMapping("/list") // 菜品分类下拉框
    public R<List<Category>> list(Category category){ // 根据条件查询分类数据

        // 条件构造器
        LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 添加条件
        lambdaQueryWrapper.eq(category.getType() != null, Category::getType, category.getType());
        // 添加排序条件
        lambdaQueryWrapper.orderByAsc(Category::getSort).orderByAsc(Category::getUpdateTime);
        // 调用查询
        List<Category> list = categoryService.list(lambdaQueryWrapper);

        return R.success(list);

    }



}

CommonController

package org.example.controller;


import lombok.extern.slf4j.Slf4j;
import org.example.common.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
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.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {

//    @Value("${reggie.path}")
    @Value("${reggie.path}")
    private String basePath;

    @PostMapping("/upload")
    public R<String> upload(MultipartFile file) { // 文件上传
        // file为临时文件,需要转存到客户端指定位置

        // 拿到文件原名
        String originalFilename = file.getOriginalFilename();
        // 文件后缀
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        // 使用uuid作为文件名的一部分,防止同名覆盖
        String fileName = UUID.randomUUID().toString() + suffix;

        // 创建file对象,该目录若不存在就创建
        File dir = new File(basePath);

        if (!dir.exists()) { // 该目录不存在
            dir.mkdirs(); // 创建目录
        }
        try {
            file.transferTo(new File(basePath + fileName)); // 前端传的文件进行转存
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }

    @GetMapping("/download")
    public void download(String name, HttpServletResponse response) { // 文件下载
        // name 为需要下载的文件名
        try {
            // 下载:先读(输入)
            FileInputStream fileInputStream = new FileInputStream(new File(basePath + name)); // 从储存图片的地方获取用户需要的图片对象

            //再输出(写入响应体)
            ServletOutputStream outputStream = response.getOutputStream();

            // 设置写回的文件类型
            response.setContentType("image/jpeg");

            // 读写文件
            int len = 0;
            byte[] buf = new byte[1024];

            while ((len = fileInputStream.read(buf)) != -1) {
                outputStream.write(buf, 0, len); // 边读边写
                outputStream.flush();
            }

            // 关闭流
            outputStream.close();
            fileInputStream.close();


        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

DishController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.common.R;
import org.example.entity.Category;
import org.example.entity.Dish;
import org.example.entity.DishFlavor;
import org.example.entity.dto.DishDto;
import org.example.service.CategoryService;
import org.example.service.DishFlavorService;
import org.example.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
    @Autowired
    private DishService dishService;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private RedisTemplate redisTemplate;


    @PostMapping()
    public R<String> save(@RequestBody DishDto dishDto) {
        dishService.saveWithFlavor(dishDto);
        // 保持数据库和缓存内容一致
        String key = "dish_" + dishDto.getCategoryId() + "_" + dishDto.getStatus();
        redisTemplate.delete(key); // 删除缓存


        return R.success("新增菜品成功");
    }

    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name) { // 分页

        //分页构造器对象
        Page<Dish> dishPage = new Page<>(page, pageSize); // 菜品表
        Page<DishDto> dishDtoPage = new Page<>(page, pageSize); // 口味表
        //条件
        LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加模糊匹配条件
        lambdaQueryWrapper.like(name != null, Dish::getName, name);
        //添加排序条件
        lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
        //mybatis-plus处理
        dishService.page(dishPage, lambdaQueryWrapper);

        List<Dish> records = dishPage.getRecords(); // 得到dish的所有数据records属性是分页插件中表示分页中所有的数据的一个集合

        List<DishDto> list = records.stream().map((item) -> { // 对每个菜品进行categoryName赋值

            DishDto dishDto = new DishDto();

            BeanUtils.copyProperties(item, dishDto); // item相当于Dish 对dishDto进行除categoryName拷贝

            Long categoryId = item.getCategoryId(); // 获取分类id

            Category category = categoryService.getById(categoryId); // 通过分类id获取分类对象

            if (category != null) {
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName); // 设置categoryName
            }
            return dishDto;

        }).collect(Collectors.toList());

        BeanUtils.copyProperties(dishPage, dishDtoPage, "records"); // records 不拷贝
        dishDtoPage.setRecords(list); // 设置有categoryName的records


        return R.success(dishDtoPage);
    }

    @GetMapping("/{id}") // 修改菜品页面的回显
    public R<DishDto> getById(@PathVariable Long id) {
        DishDto dishDto = dishService.getByIdWithFlavor(id);

        return R.success(dishDto);

    }

    @PutMapping()
    public R<String> update(@RequestBody DishDto dishDto) { //修改菜品
        dishService.updateWithFlavor(dishDto);
        // 保持数据库和缓存内容一致
        String key = "dish_" + dishDto.getCategoryId() + "_" + dishDto.getStatus();
        redisTemplate.delete(key); // 删除缓存
        return R.success("修改菜品成功");
    }

    @PostMapping("/status/{status}") // 起售停售
    public R<String> sale(@PathVariable int status, String[] ids){

        for (String id:ids){
            Dish dish = dishService.getById(id);
            dish.setStatus(status);

            dishService.updateById(dish);
        }

        //String key = "dish_" + dishDto.getCategoryId() + "_" + dishDto.getStatus();
        //redisTemplate.delete(key); // 不用删除缓存 因为key变了
        return R.success("修改成功");
    }

    // 删除菜品
    @DeleteMapping()
    public R<String> delete(String[] ids){
        for (String id: ids){
            dishService.removeById(id);
        }
        return R.success("删除成功");
    }

    @GetMapping("/list")
    /**
     * 移动端展示菜品也是这个请求 但是需要用DishDto来增加口味显示
    public R<List<Dish>> list(Dish dish){ // 展示菜品

        //条件构造器
        LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加条件1、(起售的菜品)
        lambdaQueryWrapper.eq(Dish::getStatus, 1);
        //添加条件2、菜品分类
        lambdaQueryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        //排序(先sort排序再更新时间排序)
        lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

        List<Dish> list = dishService.list(lambdaQueryWrapper);

        return R.success(list);


    }*/
    public R<List<DishDto>> list(Dish dish){

        // 先从redis中看是否有缓存
        List<DishDto> dishDtoList = null;
        String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus();
        dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
        if (dishDtoList != null){
            return R.success(dishDtoList);
        }

        // 如果redis中没有 调用数据库
        //条件构造器
        LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加条件1、(起售的菜品)
        lambdaQueryWrapper.eq(Dish::getStatus, 1);
        //添加条件2、菜品分类
        lambdaQueryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        //排序(先sort排序再更新时间排序)
        lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

        List<Dish> list = dishService.list(lambdaQueryWrapper);

        // 集合泛型转化
        dishDtoList = list.stream().map((item) ->{
            DishDto dishDto = new DishDto();

            BeanUtils.copyProperties(item, dishDto);

            // 添加分类名
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            if (category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }


            // 添加口味
            Long dishId = item.getId();
            LambdaQueryWrapper<DishFlavor> flavorQueryWrapper = new LambdaQueryWrapper<>();
            flavorQueryWrapper.eq(DishFlavor::getDishId, dishId);
            List<DishFlavor> dishFlavorList = dishFlavorService.list(flavorQueryWrapper);
            dishDto.setFlavors(dishFlavorList);


            return dishDto;
        }).collect(Collectors.toList());

        // 添加至redis 有效期60分钟
        redisTemplate.opsForValue().set(key, dishDtoList, 60, TimeUnit.MINUTES);


        return R.success(dishDtoList);

    }


}

EmployeeController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.example.common.R;
import org.example.entity.Employee;
import org.example.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;


/**
 * 员工管理层
 */
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;


    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){

        // 1、根据页面提交的employee获取用户名查询数据库
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername, employee.getUsername());
        // Employee::getUsername 相当于创建Employee对象调用getUsername方法 与 employee.getUsername()页面提交的用户名匹配
        Employee emp = employeeService.getOne(queryWrapper); //返回查询到的employee对象

        // 2、 找不到用户返回错误信息
        if(emp == null){
            return R.error("登录失败[用户名错误]");

        }

        // 3、将页面提交的employee获取password后做md5处理
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        // 4、密码比对
        if(!emp.getPassword().equals(password)){
            return R.error("登录失败[密码错误]");
        }

        // 5、查看员工转态是否禁用
        if(emp.getStatus() == 0){
            return R.error("账号禁用");

        }

        // 6、登录成功,将员工id存入到session域并返回登录结果 或形参为Httpsession

        request.getSession().setAttribute("employee", emp.getId());

        return R.success(emp);
    }


    // 退出登录
    @PostMapping("/logout")
    public R<String> logout(HttpSession session){
        // 清理session中保存的当前员工id
        session.removeAttribute("employee");
        return R.success("退出成功");
    }

    // 注册新员工
    @PostMapping() // 注册的请求就是/employee 在类中已经写了
    public R<String> save(HttpServletRequest request, @RequestBody Employee employee){

        // 初始化密码为12345
        employee.setPassword(DigestUtils.md5DigestAsHex("12345".getBytes()));
        // 创建时间
        employee.setCreateTime(LocalDateTime.now());
        // 更新时间
        employee.setUpdateTime(LocalDateTime.now());

        // 获得当前登录用户的id
        Long empId = (Long) request.getSession().getAttribute("employee");

        // 创建人
        employee.setCreateUser(empId);
        // 更新人
        employee.setUpdateUser(empId);

        // 调用service
        employeeService.save(employee);

        return R.success("新增员工成功");


    }

    @GetMapping("/page") // 员工信息分页
    // ①用户登录成功时,分页查询一次 ②用户使用条件查询的时候分页一次 ③跳转页面的时候分页查询一次
    public R<Page> page(int page, int pageSize, String name){
        //返回page对象(mybatis-plus的page对象),因为前端需要这些分页的数据(比如当前页,总页数)
        // page 当前页 pageSize 一页最多几条数据 name 查询员工的信息

        Page pageInfo = new Page(page, pageSize); // 构造分页构造器即page对象

        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper(); // 构造条件构造器

        // StringUtils.isNotEmpty(name) 条件不为空 模糊匹配
        queryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);

        queryWrapper.orderByDesc(Employee::getUpdateTime); // 添加排序条件

        employeeService.page(pageInfo, queryWrapper); // 执行查询

        return R.success(pageInfo);
    }


    @PutMapping() //put方式提交 update 禁用和编辑员工信息都是这个方法
    public R<String> update(HttpServletRequest request, @RequestBody Employee employee){

        log.info(employee.toString());

        Long empId = (Long) request.getSession().getAttribute("employee"); // 获取操作者id
        employee.setUpdateTime(LocalDateTime.now()); // 更新时间
        employee.setUpdateUser(empId); // 更新操作者

        employeeService.updateById(employee); //动态sql 通用更新

        return R.success("员工信息修改成功");

        }


    @GetMapping("/{id}")
    public R<Employee> getById(@PathVariable Long id){
        Employee employee = employeeService.getById(id); // 调用service的通过id查询方法

        if (employee != null){
            return  R.success(employee);

        }
        return R.error("没有查询到该员工的信息");
    }





}

OrdersController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.example.common.R;
import org.example.entity.OrderDetail;
import org.example.entity.Orders;
import org.example.entity.dto.OrdersDto;
import org.example.service.OrderDetailService;
import org.example.service.OrderService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/order")
public class orderController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderDetailService orderDetailService;

    @PostMapping("/submit")
    public R<String> submit(@RequestBody Orders orders){
        orderService.submit(orders);
        return R.success("下单成功");

    }


    @Transactional
    @GetMapping("/userPage")
    public R<Page> userPage(int page, int pageSize){ // 查看历史订单

        // 构造分页构造器
        Page<Orders> pageInfo = new Page<>(page, pageSize);

        Page<OrdersDto> ordersDtoPage = new Page<>();

        // 构造条件构造器
        LambdaQueryWrapper<Orders> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.orderByDesc(Orders::getOrderTime);

        orderService.page(pageInfo, lambdaQueryWrapper);



        List<Orders> records = pageInfo.getRecords();

        List<OrdersDto> list = records.stream().map((item) ->{
            OrdersDto ordersDto = new OrdersDto();
            BeanUtils.copyProperties(item, ordersDto);

            Long id = item.getId();
            Orders orders = orderService.getById(id);
            String number = orders.getNumber();
            LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();

            orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId, number);

            List<OrderDetail> orderDetailList = orderDetailService.list(orderDetailLambdaQueryWrapper);

            int num = 0;

            for (OrderDetail l: orderDetailList){
                num += l.getNumber().intValue();

            }

            ordersDto.setSumNum(num);

            return ordersDto;


        }).collect(Collectors.toList());

        // 拷贝
        BeanUtils.copyProperties(pageInfo, ordersDtoPage, "records");

        ordersDtoPage.setRecords(list);

        return R.success(ordersDtoPage);

    }

    @Transactional
    @PostMapping("/again")
    public R<String> again(@RequestBody Orders orders){ // 再来一单

        Long id = orders.getId();

        Orders ordersServiceById = orderService.getById(id);

        // 设置订单号码
        long ordersId = IdWorker.getId();

        ordersServiceById.setId(ordersId);
        // 设置订单号码
        String number = String.valueOf(IdWorker.getId());
        ordersServiceById.setNumber(number);

        // 设置下单时间
        ordersServiceById.setOrderTime(LocalDateTime.now());
        ordersServiceById.setCheckoutTime(LocalDateTime.now());
        ordersServiceById.setStatus(2);

        // 写入订单表
        orderService.save(ordersServiceById);

        // 修改订单明细表
        LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();

        orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId, id);
        List<OrderDetail> list = orderDetailService.list(orderDetailLambdaQueryWrapper);

        list.stream().map((item) ->{

            // 设置id
            long detailId = IdWorker.getId();
            item.setOrderId(ordersId);
            item.setId(detailId);

            return item;
        }).collect(Collectors.toList());

        orderDetailService.saveBatch(list);

        return R.success("再来一单");

    }

    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String number, String beginTime, String endTime){ // 后端查看订单

        Page<Orders> pageInfo = new Page<>(page, pageSize);

        Page<OrdersDto> ordersDtoPage = new Page<>();

        LambdaQueryWrapper<Orders> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.like(!StringUtils.isEmpty(number), Orders::getNumber, number);

        if (beginTime != null && endTime != null){
            lambdaQueryWrapper.ge(Orders::getOrderTime, beginTime);
            lambdaQueryWrapper.le(Orders::getOrderTime, endTime);
        }

        lambdaQueryWrapper.orderByDesc(Orders::getOrderTime);

        orderService.page(pageInfo, lambdaQueryWrapper);

        BeanUtils.copyProperties(pageInfo, ordersDtoPage, "records");

        List<Orders> records = pageInfo.getRecords();

        List<OrdersDto> list = records.stream().map((item) ->{
            OrdersDto ordersDto = new OrdersDto();
            BeanUtils.copyProperties(item, ordersDto);

            String name = "用户" + item.getUserId();
            ordersDto.setUserName(name);
            return ordersDto;

        }).collect(Collectors.toList());

        ordersDtoPage.setRecords(list);

        return R.success(ordersDtoPage);


    }

    @PutMapping
    public R<String> send(@RequestBody Orders orders){ // 订单配送按钮

        Long id = orders.getId();
        Integer status = orders.getStatus();

        LambdaQueryWrapper<Orders> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Orders::getId, id);

        Orders order = orderService.getOne(lambdaQueryWrapper);

        order.setStatus(status);
        orderService.updateById(order);
        return R.success("派送成功");
    }


}

SetmealController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.example.common.R;
import org.example.entity.Category;
import org.example.entity.Setmeal;
import org.example.entity.dto.SetmealDto;
import org.example.service.CategoryService;
import org.example.service.SetmealDishService;
import org.example.service.SetmealService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/setmeal")
public class SetmealController {

    @Autowired
    private SetmealService setmealService;

    @Autowired
    private SetmealDishService setmealDishService;

    @Autowired
    private CategoryService categoryService;

    @PostMapping()
    @CacheEvict(value = "setmealCache", allEntries = true) // 标注在需要清除缓存元素的方法或类上
    public R<String> save(@RequestBody SetmealDto setmealDto) {
        // 可以先查看传入内容
        //log.info("setmeal:{}", setmealDto);

        setmealService.saveWithDish(setmealDto);

        return R.success("新增套餐成功");
    }

    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name) { // 分页

        //构造分页构造器
        Page<Setmeal> pageInfo = new Page<>(page, pageSize);
        Page<SetmealDto> pageDtoInfo = new Page<>();

        // 条件构造器
        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 根据name模糊查询
        lambdaQueryWrapper.like(!StringUtils.isEmpty(name), Setmeal::getName, name);
        // 添加排序条件
        lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);
        // 调用mybatis-plus
        setmealService.page(pageInfo, lambdaQueryWrapper);

        // 拷贝
        BeanUtils.copyProperties(pageInfo, pageDtoInfo, "records");
        //修改records
        List<Setmeal> records = pageInfo.getRecords();
        List<SetmealDto> list = records.stream().map((item) -> {
            SetmealDto setmealDto = new SetmealDto();
            BeanUtils.copyProperties(item, setmealDto);
            Long categoryId = item.getCategoryId(); // 获得分类id
            Category category = categoryService.getById(categoryId);
            if (category != null) {
                String categoryName = category.getName();
                setmealDto.setCategoryName(categoryName); // 设置分类名

            }
            return setmealDto;
        }).collect(Collectors.toList());

        pageDtoInfo.setRecords(list);

        return R.success(pageDtoInfo);
    }



    @DeleteMapping()
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> delete(String[] ids) {
        int index = 0;
        for (String id : ids) {
            Setmeal setmeal = setmealService.getById(id);

            if (setmeal.getStatus() != 1) {
                setmealService.removeById(id);

            } else {
                index++;
            }

        }
        if (index > 0 && index == ids.length) {
            return R.error("选中的套餐均为启售状态,不能删除");

        } else {
            return R.success("删除成功");
        }
    }

    @PostMapping("/status/{status}")
    // @CacheEvict(value = "setmealCache", allEntries = true) key变了不用删除
    public R<String> sale(@PathVariable int status, String[] ids){
        for (String id:ids){
            Setmeal setmeal = setmealService.getById(id);
            setmeal.setStatus(status);
            setmealService.updateById(setmeal);

        }
        return R.success("修改成功");
    }


    @GetMapping("/{id}")
    public R<SetmealDto> getById(@PathVariable Long id){
        SetmealDto setmealDto = setmealService.getByIdWithDish(id);

        return R.success(setmealDto);
    }

    @PutMapping()
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> update(@RequestBody SetmealDto setmealDto){
        setmealService.updateWithDish(setmealDto);
        return R.success("修改成功");
    }

    @GetMapping("/list")
    @Cacheable(value = "setmealCache", key = "#setmeal.categoryId + '_' + #setmeal.status") // cache redis注解 查找添加缓存
    public R<List<Setmeal>> list(Setmeal setmeal){ // 移动端套餐展示

        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.eq(setmeal.getCategoryId() != null, Setmeal::getCategoryId, setmeal.getCategoryId());

        lambdaQueryWrapper.eq(setmeal.getStatus() != null, Setmeal::getStatus, setmeal.getStatus());

        lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);

        List<Setmeal> list = setmealService.list(lambdaQueryWrapper);

        return R.success(list);
    }
}

ShoppingCartController

package org.example.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.example.common.BaseContext;
import org.example.common.R;
import org.example.entity.ShoppingCart;
import org.example.service.ShoppingCartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RestController
@RequestMapping("/shoppingCart")
public class ShoppingCartController {

    @Autowired
    private ShoppingCartService shoppingCartService;


    @PostMapping("/add")
    public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){

        Long currentId = BaseContext.getCurrentId();
        shoppingCart.setUserId(currentId); //给购物车设置用户id

        Long dishId = shoppingCart.getDishId();
        LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();

        shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getUserId, currentId); // 该用户的购物车

        if (dishId != null){ // 添加的是菜品
            shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getDishId, dishId);
        } else { // 添加的是套餐 也可能菜品和套餐都没有 即新添加的
            shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        }

        // 查询当前菜品是否在购物车中
        ShoppingCart cartServiceOne = shoppingCartService.getOne(shoppingCartLambdaQueryWrapper);

        if (cartServiceOne != null){ // 已存在就数量+1
            Integer number = cartServiceOne.getNumber();
            cartServiceOne.setNumber(number + 1);
            shoppingCartService.updateById(cartServiceOne);

        } else { // 不存在就添加进购物车
            shoppingCart.setNumber(1);
            shoppingCartService.save(shoppingCart);
            cartServiceOne = shoppingCart;

        }
        return R.success(cartServiceOne);

    }

    @GetMapping("/list") // 购物车展示
    public R<List<ShoppingCart>> list(){
        LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());
        shoppingCartLambdaQueryWrapper.orderByAsc(ShoppingCart::getCreateTime);
        List<ShoppingCart> list = shoppingCartService.list(shoppingCartLambdaQueryWrapper);

        return R.success(list);

    }

    @DeleteMapping("/clean")
    public R<String> clean(){
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());
        shoppingCartService.remove(lambdaQueryWrapper);

        return R.success("清空购物车成功");
    }

    @PostMapping("/sub")
    public R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart){

        Long setmealId = shoppingCart.getSetmealId();
        Long dishId = shoppingCart.getDishId();

        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());

        if (setmealId != null){ // 删除的是套餐
            lambdaQueryWrapper.eq(ShoppingCart::getSetmealId, setmealId);

        } else { // 删除的是菜品
            lambdaQueryWrapper.eq(ShoppingCart::getDishId, dishId);

        }
        ShoppingCart one = shoppingCartService.getOne(lambdaQueryWrapper);
        Integer number = one.getNumber();

        if (number == 1){
            shoppingCartService.remove(lambdaQueryWrapper);
        } else {
            one.setNumber(number-1);
            shoppingCartService.updateById(one);
        }

        return R.success(one);


    }


}

UserController

package org.example.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.example.common.R;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.utils.SMSUtils;
import org.example.utils.ValidateCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession httpSession) {

        String phone = user.getPhone();
        if (StringUtils.isNotEmpty(phone)) {

            Integer integerCode = ValidateCodeUtils.generateValidateCode(4); // 随机生成4位数
            String code = integerCode.toString();

            log.info("phone={}", phone);
            log.info("code={}", code);

            // 调用阿里云提供的短信服务
            //SMSUtils.sendMessage("瑞吉外卖短信服务", "SMS_270275056", phone, code);

            // 生成的验证码保存到session
            //httpSession.setAttribute(phone, code);
            // 改为将生成的验证码存入redis
            redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES); // 有效期5分钟

            return R.success("手机验证码短信发送成功");

        }
        return R.error("手机短信发送失败");
    }


    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session) {
        log.info("map:{}", map.toString());
        //获取手机号
        String phone = map.get("phone").toString();
        //获取验证码
        String code = map.get("code").toString();
        //从Session中获取保存的验证码
        //Object codeInSession = session.getAttribute(phone);
        //改为从redis获取保存的验证码
        Object codeInSession = redisTemplate.opsForValue().get(phone);
        //进行验证码比对(页面提交的验证码和Session中保存的验证码比对)
        if (codeInSession != null && codeInSession.equals(code)) {
            //如果能够比对成功,说明登录成功
            redisTemplate.delete(phone); // 登录成功删除保存的验证码
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
            User user = userService.getOne(queryWrapper);
            if (user == null) {
                //判断当前手机号是否为新用户,如果是新用户则自动完成注册
                user = new User();
                user.setPhone(phone);
                user.setStatus(1);
                userService.save(user);
            }
            session.setAttribute("user", user.getId());
            return R.success(user);
        }
        return R.error("登陆失败");
    }

    @PostMapping("/loginout")
    public R<String> loginout(HttpServletRequest request) {
        // 删除session保存的当前用户
        request.getSession().removeAttribute("user");
        return R.success("退出成功");
    }

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值