苍穹外卖(三)

查漏补缺

暂无

1.Redis入门

1.1 Redis简介

Redis是一个基于内存key-value 结构数据库。

基于内存存储,读写性能高
适合存储热点数据(热点商品、资讯、新闻)
企业应用广泛

官网:https://redis.io

中文网:Redis中文网

1.2 Redis服务启动与停止

服务启动命令:redis-server.exe redis.windows.conf

Redis服务默认端口号为 6379通过快捷键Ctrl + C 即可停止Redis服务 

客户端连接命令:redis-cli.exe

通过redis-cli.exe命令默认连接的是本地的redis服务,并且使用默认6379端口。也可以通过指定如下参数连接:

-h ip地址

-p 端口号

-a 密码(如果需要)

2.Redis数据类型

2.1五种常用的数据类型

Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:

字符串 string
哈希 hash
列表 list
集合 set
有序集合 sorted set / zset

2.2 各种数据类型的要点

字符串(string):普通字符串, Redis 中最简单的数据类型
哈希(hash):也叫散列,类似于 Java 中的 HashMap 结构
列表(list):按照插入顺序排序,可以有重复元素,类似于 Java 中的 LinkedList
集合(set):无序集合,没有重复元素,类似于 Java 中的 HashSet
有序集合(sorted set / zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
苍穹外卖(三)_第1张图片

3.Redis常用命令

3.1 字符串操作命令

Redis 字符串类型常用命令:

SET key value  设置指定key的值

GET key  获取指定key的值

SETEX key seconds value  设置指定key的值,并将 key 的过期时间设为 seconds

SETNX key value  只有在 key 不存在时设置 key 的值

3.2 哈希操作命令

Redis hash 是一个string类型的 field value 的映射表,hash特别适合用于存储对象,常用命令:

HSET key field value   将哈希表 key 中的字段 field 的值设为 value

HGET key field   获取存储在哈希表中指定字段的值

HDEL key field  删除存储在哈希表中的指定字段

HKEYS key   获取哈希表中所有字段

HVALS key   获取哈希表中所有值

苍穹外卖(三)_第2张图片

3.3列表操作命令

Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:

LPUSH key value1 [value2]   将一个或多个值插入到列表头部(左边)

LRANGE key start stop   获取列表指定范围内的元素

RPOP key   移除并获取列表最后一个元素(右边)

LLEN key   获取列表长度

苍穹外卖(三)_第3张图片

3.4 集合操作命令

Redis set string类型的无序集合。集合成员是唯一的,集合中不能出现重复的数据,常用命令:

SADD key member1 [member2]   向集合添加一个或多个成员

SMEMBERS key   返回集合中的所有成员

SCARD key   获取集合的成员数

SINTER key1 [key2]   返回给定所有集合的交集

SUNION key1 [key2]   返回所有给定集合的并集

SREM key member1 [member2]   删除集合中一个或多个成员

苍穹外卖(三)_第4张图片

3.5 有序集合操作命令

Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令:

ZADD key score1 member1 [score2 member2]   向有序集合添加一个或多个成员

ZRANGE key start stop [WITHSCORES]   通过索引区间返回有序集合中指定区间内的成员

ZINCRBY key increment member   有序集合中对指定成员的分数加上增量 increment

ZREM key member [member ...]   移除有序集合中的一个或多个成员

苍穹外卖(三)_第5张图片

3.6 通用命令

Redis的通用命令是不分数据类型的,都可以使用的命令:

KEYS pattern   查找所有符合给定模式( pattern)key

EXISTS key   检查给定 key 是否存在

TYPE key   返回 key 所储存的值的类型

DEL key   该命令用于在 key 存在是删除 key

4.在Java中操作Redis

Redis Java 客户端很多,常用的几种:

Jedis

Lettuce

Spring Data Redis

Spring Data Redis 是 Spring 的一部分,对 Redis 底层开发包进行了高度封装。

在 Spring 项目中,可以使用Spring Data Redis来简化操作。

操作步骤:

导入 Spring Data Redis maven 坐标
配置 Redis 数据源
编写配置类,创建 RedisTemplate 对象
通过 RedisTemplate 对象操作 Redis

            org.springframework.boot
            spring-boot-starter-data-redis
        
package com.sky.config;/*
 *
 *  @author pengjx
 *
 * */

import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class RedisConfiguration {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){

        RedisTemplate redisTemplate = new RedisTemplate();

        //设置Redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //设置Redis中key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        return redisTemplate;


    }
}
package com.sky.test;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.config.RedisConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

//@SpringBootTest
public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate(){
        System.out.println(redisTemplate);

        ValueOperations valueOperations = redisTemplate.opsForValue();
        HashOperations hashOperations = redisTemplate.opsForHash();
        ListOperations listOperations = redisTemplate.opsForList();
        SetOperations setOperations = redisTemplate.opsForSet();
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

    }

    @Test
    public void testString(){

        //set get setnx setex
        redisTemplate.opsForValue().set("city","赣州");
        String city = (String) redisTemplate.opsForValue().get("city");
        System.out.println(city);

        redisTemplate.opsForValue().set("code","1234",2L, TimeUnit.MINUTES);

        redisTemplate.opsForValue().setIfAbsent("gender",12);
    }

    @Test
    public void testHash(){
        redisTemplate.opsForHash().put("100","name","tom");
        redisTemplate.opsForHash().put("100","age",12);
        String name = (String) redisTemplate.opsForHash().get("100", "name");
        System.out.println(name);
        Integer age = (Integer) redisTemplate.opsForHash().get("100", "age");
        System.out.println(age);

        Set keys = redisTemplate.opsForHash().keys("100");
        System.out.println(keys);
        List values = redisTemplate.opsForHash().values("100");
        System.out.println(values);

        redisTemplate.opsForHash().delete("100","name");
    }

    @Test
    public void testList(){
        //lpush rpop llen lrange

        ListOperations listOperations = redisTemplate.opsForList();
        listOperations.rightPushAll("mylist", 'a', 'b', 'c', 'd');
        listOperations.leftPush("mylist",'e');

        List mylist = listOperations.range("mylist", 0, -1);
        System.out.println(mylist);
        Long size = listOperations.size("mylist");
        System.out.println(size);

        listOperations.remove("mylist",1,'b');

        List range = listOperations.range("mylist", 0, -1);
        System.out.println(range);


    }

    @Test
    public void testSet(){
        //sadd smembers scard sinter sunion srem
        SetOperations setOperations = redisTemplate.opsForSet();
        setOperations.add("set1",'a','b','c','d');
        setOperations.add("set2",'a','b','x','y');

        Set set1 = setOperations.members("set1");
        System.out.println(set1);

        Long size = setOperations.size("set1");
        System.out.println(size);

        Set intersect = setOperations.intersect("set1", "set2");
        System.out.println(intersect);

        Set union = setOperations.union("set1", "set2");
        System.out.println(union);

        setOperations.remove("set1",'a');


    }

    @Test
    public void testZset(){
        // zadd zrange zcard zrange zincrby
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add("zset1",'a',10);
        zSetOperations.add("zset1",'b',9);
        zSetOperations.add("zset1",'c',7);

        Set zset1 = zSetOperations.range("zset1", 0, -1);
        System.out.println(zset1);

        Long size = zSetOperations.size("zset1");
        System.out.println(size);

        zSetOperations.incrementScore("zset1",'c',3);
        Set range = zSetOperations.range("zset1", 0, -1);
        System.out.println(range);

    }

    @Test
    public void testCommon(){
        //keys exists type del
        Set keys = redisTemplate.keys("*");
        System.out.println(keys);

        Boolean hasKey = redisTemplate.hasKey("100");
        System.out.println(hasKey);

        keys.forEach(key->{
            DataType type = redisTemplate.type(key);
            System.out.println(type);
        });


    }
}

5. 店铺营业状态设置

5.1 需求分析和设计

接口设计:

设置营业状态
管理端查询营业状态
用户端查询营业状态

本项目约定:

管理端 发出的请求,统一使用 /admin 作为前缀
用户端 发出的请求,统一使用 /user 作为前缀

苍穹外卖(三)_第6张图片

苍穹外卖(三)_第7张图片

苍穹外卖(三)_第8张图片

营业状态数据存储方式:基于Redis的字符串来进行存储

苍穹外卖(三)_第9张图片

约定:1表示营业 0表示打烊

5.2 代码开发 

package com.sky.controller.admin;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {


    @Autowired
    private RedisTemplate redisTemplate;

    @PutMapping("/{status}")
    @ApiOperation("设置店铺营业状态")
    public Result setStatus(@PathVariable Integer status){
        log.info("设置店铺营业状态:{}",status==1?"营业中":"打烊");
        redisTemplate.opsForValue().set("SHOP_STATUS",status);
        return Result.success();
    }

    @GetMapping("status")
    @ApiOperation("查询店铺营业状态")
    public Result getStatus(){
        Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
        log.info("当前店铺营业状态:{}",status==1?"营业中":"打烊");

        return Result.success(status);
    }
}
package com.sky.controller.user;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("userShopController")
@RequestMapping("/user/shop")
@Slf4j
@Api(tags = "店铺相关接口")
public class ShopController {


    @Autowired
    private RedisTemplate redisTemplate;



    @GetMapping("status")
    @ApiOperation("查询店铺营业状态")
    public Result getStatus(){
        Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
        log.info("当前店铺营业状态:{}",status==1?"营业中":"打烊");

        return Result.success(status);
    }
}

6.HttpClient

6.1 介绍

HttpClientApache的一个子项目,是高效的、功能丰富的支持HTTP协议的客户端编程工具包。

HttpClient作用:

发送 HTTP 请求
接收响应数据

HttpClient Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。


    org.apache.httpcomponents
    httpclient
    4.5.13

核心API

HttpClient
HttpClients
CloseableHttpClient
HttpGet
HttpPost

发送请求步骤:

创建 HttpClient 对象
创建 Http 请求对象
调用 HttpClient execute 方法发送请求

6.2 入门案例

package com.sky.test;/*
 *
 *  @author pengjx
 *
 * */

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
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.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest
public class HttpClientTest {


    @Test
    public void testGet() throws Exception {
        //http客户端对象,可以发送http请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //构造Get请求
        HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
        //发送请求
        CloseableHttpResponse response = httpClient.execute(httpGet);

        //http响应码
        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println(statusCode);
        //http相应体
        HttpEntity entity = response.getEntity();

        //将响应体转换为String对象
        String body = EntityUtils.toString(entity);

        System.out.println(body);

        response.close();
        httpClient.close();
    }

    @Test
    public void testPost() throws Exception{
        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();

        //创建post请求对象
        HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");

        //JSONObject
        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 = httpClient.execute(httpPost);

        int statusCode = response.getStatusLine().getStatusCode();
        System.out.println(statusCode);

        HttpEntity entity1 = response.getEntity();
        String body = EntityUtils.toString(entity1);
        System.out.println(body);


        response.close();
        httpClient.close();


    }
}

7.微信小程序开发

7.1 入门案例

了解小程序目录结构

小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。

一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:

苍穹外卖(三)_第10张图片

一个小程序页面由四个文件组成:

苍穹外卖(三)_第11张图片




  
    {{msg}}
  


  
    
    昵称:{{nickname}}
    头像:
  

  
    
    {{code}}
  

  
    
  


  



// index.js
Page({

  data:{
    msg:"Hello World",
    nickname:'',
    url:'',
    code:''
  },

  getUserInfo(){
    wx.getUserProfile({
      desc: '获取用户信息',
      success:(res)=>{
        console.log(res.userInfo)
        this.setData({
          nickname:res.userInfo.nickName,
          url:res.userInfo.avatarUrl
        })
      }
    })
  },

  wxLogin(){
    wx.login({
      success: (res) => {
        console.log(res.code)
        this.setData({
          code:res.code
        })
      },
    })
  },

  sendRequest(){
    wx.request({
      url: 'http://localhost:8080/user/shop/status',
      success:(res)=>{
        console.log(res.data)
      }

    })
  }


})

8.微信登录

8.1 微信登录流程

苍穹外卖(三)_第12张图片

8.2 需求分析和设计

业务规则:

基于微信登录实现小程序的登录功能
如果是新用户需要自动完成注册

接口设计

苍穹外卖(三)_第13张图片

数据库设计(user表):

字段名

数据类型

说明

备注

id

bigint

主键

自增

openid

varchar(45)

微信用户的唯一标识

name

varchar(32)

用户姓名

phone

varchar(11)

手机号

sex

varchar(2)

性别

id_number

varchar(18)

身份证号

avatar

varchar(500)

微信用户头像路径

create_time

datetime

注册时间

8.3 代码开发

配置微信登录所需配置项:

  wechat:
    appid: ${sky.wechat.appid}
    secret: ${sky.wechat.secret}

配置为微信用户生成jwt令牌时使用的配置项:

package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}

根据接口定义创建UserControllerlogin方法:

package com.sky.controller.user;/*
 *
 *  @author pengjx
 *
 * */

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.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController("userUserController")
@RequestMapping("/user/user")
@Slf4j
@Api(tags = "微信用户接口")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtProperties jwtProperties;


    /**
     * @description: 微信登录
     * @date: 2023/12/21 8:56
     * @param: userLoginDTO
     * @return: com.sky.result.Result
     **/
    @PostMapping("/login")
    @ApiOperation("用户登录")
    public Result login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("用户登录:{}",userLoginDTO.getCode());
        User user = userService.wxLogin(userLoginDTO);

        Map map=new HashMap<>();
        map.put(JwtClaimsConstant.USER_ID,user.getId());
        String jwt = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), map);

        UserLoginVO build = UserLoginVO.builder()
                .id(user.getId())
                .openid(user.getOpenid())
                .token(jwt)
                .build();
        return Result.success(build);

    }
}

创建UserService接口:

package com.sky.service;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.vo.UserLoginVO;
import org.springframework.stereotype.Service;

@Service
public interface UserService {

    User wxLogin(UserLoginDTO userLoginDTO);
}

创建UserServiceImpl实现类,在UserServiceImpl中创建私有方法getOpenid,完善UserServiceImplwxLogin方法::

package com.sky.service.impl;/*
 *
 *  @author pengjx
 *
 * */

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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    private static final String WX_LOGIN="https://api.weixin.qq.com/sns/jscode2session";

    @Autowired
    private WeChatProperties weChatProperties;

    @Autowired
    private UserMapper userMapper;

    /**
     * @description:微信登录
     * @date: 2023/12/21 8:57
     * @param: userLoginDTO
     * @return: com.sky.entity.User
     **/
    @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.getByOpenid(openid);

        //如果为新用户,则自动完成注册
        if(user==null){
            user=User.builder()
                    .openid(openid)
                    .createTime(LocalDateTime.now())
                    .build();
            userMapper.insert(user);
        }

        return user;


    }

    private String getOpenid(String code){
        Map map=new HashMap<>();
        map.put("appid",weChatProperties.getAppid());
        map.put("secret",weChatProperties.getSecret());
        map.put("js_code",code);
        map.put("grant_type","authorization_code");
        String json = HttpClientUtil.doGet(WX_LOGIN, map);

        JSONObject jsonObject = JSON.parseObject(json);
        String openid = jsonObject.getString("openid");
        return openid;
    }
}

创建UserMapper接口:

package com.sky.mapper;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {


    @Select("select * from user where openid=#{openid}")
    User getByOpenid(String openid);

    void insert(User user);
}

创建UserMapper.xml映射文件:






    
        insert into user
            (openid,name,phone,sex,id_number,avatar,create_time)
        values
            (#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime})

    

编写拦截器JwtTokenUserInterceptor,统一拦截用户端发送的请求并进行jwt校验:

package com.sky.interceptor;

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 {
        //判断当前拦截到的是Controller的方法还是其他资源
        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);
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

WebMvcConfiguration配置类中注册拦截器:

registry.addInterceptor(jwtTokenUserInterceptor)
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");

9.商品浏览功能代码

接口设计:

查询分类
根据分类 id 查询菜品
根据分类 id 查询套餐
根据套餐 id 查询包含的菜品
查询分类 接口

苍穹外卖(三)_第14张图片

package com.sky.controller.user;/*
 *
 *  @author pengjx
 *
 * */

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 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("userCategoryController")
@RequestMapping("/user/category")
@Slf4j
@Api(tags = "微信端分类相关接口")
public class CategoryController {

    @Autowired
    private CategoryService categoryService;


    /**
     * @description:查询分类
     * @date: 2023/12/21 14:27
     * @param: type
     * @return: com.sky.result.Result>
     **/
    @GetMapping("/list")
    @ApiOperation("查询分类")
    public Result> list(Integer type){
        log.info("查询分类:{}",type);
        List list = categoryService.list(type);
        return Result.success(list);
    }


}
根据分类 id 查询菜品 接口

苍穹外卖(三)_第15张图片

package com.sky.controller.user;/*
 *
 *  @author pengjx
 *
 * */

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.data.redis.core.RedisTemplate;
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 = "微信菜品相关接口")
public class DishController {


    @Autowired
    private DishService dishService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @description:根据分类id查询菜品
     * @date: 2023/12/21 14:38
     * @param: categoryId
     * @return: com.sky.result.Result
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result> getByCategoryId(Long categoryId){
        log.info("根据分类id查询菜品:{}",categoryId);
        String key="key_"+categoryId;
        List list = (List) redisTemplate.opsForValue().get(key);

        if(list!=null&&list.size()>0){
            return Result.success(list);
        }

        List dishVOS=dishService.listWithFlavor(categoryId);
        redisTemplate.opsForValue().set(key,dishVOS);
        return Result.success(dishVOS);
    }


}
/**
     * @description:根据分类id查询菜品
     * @date: 2023/12/21 14:43
     * @param: categoryId
     * @return: java.util.List
     **/
    List listWithFlavor(Long categoryId);
 /**
     * @description:根据分类id查询菜品
     * @date: 2023/12/21 14:43
     * @param: categoryId
     * @return: java.util.List
     **/
    @Override
    public List listWithFlavor(Long categoryId) {
        Dish dish = Dish.builder()
                .categoryId(categoryId)
                .status(StatusConstant.ENABLE)
                .build();

        List dishList = dishMapper.list(dish);

        List dishVOList = new ArrayList<>();

        for (Dish d : dishList) {
            DishVO dishVO = new DishVO();
            BeanUtils.copyProperties(d,dishVO);

            //根据菜品id查询对应的口味
            List flavors = dishFlavorMapper.getByDishId(d.getId());

            dishVO.setFlavors(flavors);
            dishVOList.add(dishVO);
        }
根据分类 id 查询套餐 接口

苍穹外卖(三)_第16张图片

@Autowired
    private SetmealService setmealService;

    /**
     * @description:根据分类id查询套餐
     * @date: 2023/12/21 14:59
     * @param: categoryId
     * @return: com.sky.result.Result>
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    public Result> getByCategoryId(Long categoryId){
        log.info("根据分类id查询套餐:{}",categoryId);
        List setmeals=setmealService.getByCategoryId(categoryId);
        return Result.success(setmeals);

    }
/**
     * @description:根据分类id查询套餐
     * @date: 2023/12/21 14:59
     * @param: categoryId
     * @return: java.util.List
     **/
    List getByCategoryId(Long categoryId);
/**
     * @description:根据分类id查询套餐
     * @date: 2023/12/21 14:59
     * @param: categoryId
     * @return: com.sky.result.Result>
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    public Result> getByCategoryId(Long categoryId){
        log.info("根据分类id查询套餐:{}",categoryId);
        List setmeals=setmealService.getByCategoryId(categoryId);
        return Result.success(setmeals);

    }

根据套餐id查询包含的菜品 接口 

苍穹外卖(三)_第17张图片

**
     * @description:根据套餐id查询包含的菜品
     * @date: 2023/12/21 19:19
     * @param: id
     * @return: com.sky.result.Result>
     **/

    @GetMapping("/dish/{id}")
    @ApiOperation("根据套餐id查询包含的菜品")
    public Result> getSetmealDish(@PathVariable Long id ){
        log.info("根据套餐id查询包含的菜品:{}",id);
        List setmealDishes=setmealService.getDishItemById(id);
        return Result.success(setmealDishes);
/**
     * @description:根据套餐id查询包含的菜品
     * @date: 2023/12/21 19:19
     * @param: id
     * @return: java.util.List
     **/
    List getDishItemById(Long id);
/**
     * @description:根据套餐id查询包含的菜品
     * @date: 2023/12/21 19:19
     * @param: id
     * @return: java.util.List
     **/
    @Override
    public List getDishItemById(Long id) {
        List dishItemVOS= setmealMapper.getDishItemBySetmealId(id);
        return dishItemVOS;
    }
 @Select("select sd.copies,d.name,d.image,d.description from setmeal_dish sd left join dish d on sd.dish_id = d.id where sd.setmeal_id=#{setmealId}")
    List getDishItemBySetmealId(Long setmealId);

10.缓存菜品

10.1 问题说明 

用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。

结果:系统响应慢、用户体验差

10.2 实现思路

通过Redis来缓存菜品数据,减少数据库查询操作。

苍穹外卖(三)_第18张图片

缓存逻辑分析:

每个分类下的菜品保存一份缓存数据
数据库中菜品数据有变更时清理缓存数据
苍穹外卖(三)_第19张图片

10.3 代码开发

修改用户端接口 DishController list 方法,加入缓存处理逻辑:

/**
     * @description:根据分类id查询菜品
     * @date: 2023/12/21 14:38
     * @param: categoryId
     * @return: com.sky.result.Result
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result> getByCategoryId(Long categoryId){
        log.info("根据分类id查询菜品:{}",categoryId);
        String key="key_"+categoryId;
        List list = (List) redisTemplate.opsForValue().get(key);

        if(list!=null&&list.size()>0){
            return Result.success(list);
        }

        List dishVOS=dishService.listWithFlavor(categoryId);
        redisTemplate.opsForValue().set(key,dishVOS);
        return Result.success(dishVOS);
    }

修改管理端接口 DishController 的相关方法,加入清理缓存的逻辑,需要改造的方法:

新增菜品
修改菜品
批量删除菜品
起售、停售菜品

抽取清理缓存的方法:

private void clearCache(String patten){
        Set keys = redisTemplate.keys(patten);
        redisTemplate.delete(keys);
    }

调用清理缓存的方法,保证数据一致性:

package com.sky.controller.admin;/*
 *
 *  @author pengjx
 *
 * */


import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
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 io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
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.Set;

@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * @description: 新增菜品
     * @date: 2023/12/16 8:54
     * @param: dishDTO
     * @return: com.sky.result.Result
     **/
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);
        dishService.saveWithFlavor(dishDTO);
        String key="key_"+dishDTO.getCategoryId();
        clearCache(key);

        return Result.success();
    }


    /**
     * @description: 菜品分页查询
     * @date: 2023/12/16 19:17
     * @param: dishPageQueryDTO
     * @return: com.sky.result.Result
     **/
    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result page(DishPageQueryDTO dishPageQueryDTO){
        log.info("菜品分页查询:{}",dishPageQueryDTO);
        PageResult pageResult=dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }


    /**
     * @description: 批量删除菜品
     * @date: 2023/12/16 20:12
     * @param: ids
     * @return: com.sky.result.Result
     **/
    @DeleteMapping
    @ApiOperation("批量删除菜品")
    public Result delete(@RequestParam List ids){
        log.info("批量删除菜品:{}",ids);
        dishService.deleteBatch(ids);

        clearCache("key_*");
        return Result.success();
    }

    /**
     * @description: 根据id查询菜品
     * @date: 2023/12/17 8:48
     * @param: id
     * @return: com.sky.result.Result
     **/
    @GetMapping("/{id}")
    @ApiOperation("根据id查询菜品")
    public Result getById(@PathVariable Long id){
        log.info("根据id查询菜品:{}",id);
        DishVO dishVO=dishService.getByIdWithFlavor(id);
        return Result.success(dishVO);

    }


    /**
     * @description: 修改菜品
     * @date: 2023/12/17 9:05
     * @param: dishDTO
     * @return: com.sky.result.Result
     **/
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO){

        log.info("修改菜品:{}",dishDTO);
        dishService.update(dishDTO);
        clearCache("key_*");

        return Result.success();

    }

    /**
     * @description: 根据分类id查询菜品
     * @date: 2023/12/18 20:16
     * @param: categoryId
     * @return: com.sky.result.Result>
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result> getByCategoryId(Long categoryId){
        log.info("根据分类id查询菜品");
        List dishes=dishService.getByCategoryId(categoryId);
        return Result.success(dishes);
    }

    private void clearCache(String patten){
        Set keys = redisTemplate.keys(patten);
        redisTemplate.delete(keys);
    }




}

11 缓存套餐

11.1 Spring Cache

Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能

Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:

EHCache
Caffeine
Redis

    org.springframework.boot
    spring-boot-starter-cache
    2.7.3

常用注解:

注解

说明

@EnableCaching

开启缓存注解功能,通常加在启动类上

@Cacheable

在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中

@CachePut

将方法的返回值放到缓存中

@CacheEvict

将一条或多条数据从缓存中删除

11.1.1 入门案例
@PostMapping
    @CachePut(cacheNames = "userCache",key = "#user.id")
//    @CachePut(cacheNames = "userCache",key = "#result.id")
//    @CachePut(cacheNames = "userCache",key = "#p0.id")
//    @CachePut(cacheNames = "userCache",key = "#a0.id")
//    @CachePut(cacheNames = "userCache",key = "#root.args[0].id")
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }
@GetMapping
    @Cacheable(cacheNames = "userCache",key = "#id")
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }
@DeleteMapping("/delAll")
    @CacheEvict(cacheNames = "userCache",allEntries = true)
    public void deleteAll(){
        userMapper.deleteAll();
    }

    @GetMapping
    @Cacheable(cacheNames = "userCache",key = "#id")
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }

11.2 实现思路

具体的实现思路如下:

导入 Spring Cache Redis 相关 maven 坐标
在启动类上加入 @ EnableCaching注解,开启缓存注解功能
在用户端接口 SetmealController list 方法 上加入 @Cacheable 注解
管理端接口 SetmealController save delete update startOrStop 等方法上加入CacheEvict注解

在用户端接口SetmealController list 方法上加入@Cacheable注解:

管理端接口SetmealControllersavedeleteupdatestartOrStop等方法上加入CacheEvict注解:

12 添加购物车

12.1 需求分析和设计

接口设计:

请求方式: POST
请求路径: /user/ shoppingCart /add
请求参数:套餐 id 、菜品 id 、口味
返回结果: code data msg

苍穹外卖(三)_第20张图片

数据库设计:

作用:暂时存放所选商品的地方
选的什么商品
每个商品都买了几个
不同用户的购物车需要区分开

数据库设计(shopping_cart表): 

字段名

数据类型

说明

备注

id

bigint

主键

自增

name

varchar(32)

商品名称

冗余字段

image

varchar(255)

商品图片路径

冗余字段

user_id

bigint

用户id

逻辑外键

dish_id

bigint

菜品id

逻辑外键

setmeal_id

bigint

套餐id

逻辑外键

dish_flavor

varchar(50)

菜品口味

number

int

商品数量

amount

decimal(10,2)

商品单价

冗余字段

create_time

datetime

创建时间

12.2 代码开发

根据添加购物车接口创建ShoppingCartController

/**
     * @description:加入购物车
     * @date: 2023/12/22 20:24
     * @param: shoppingCartDTO
     * @return: com.sky.result.Result
     **/
    @PostMapping("/add")
    @ApiOperation("加入购物车")
    public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){
        log.info("加入购物车:{}",shoppingCartDTO);
        shoppingCartService.addShoppingCart(shoppingCartDTO);
        return Result.success();
    }

创建ShoppingCartService接口:

 /**
     * @description:加入购物车
     * @date: 2023/12/22 20:27
     * @param: shoppingCartDTO
     **/
    void addShoppingCart(ShoppingCartDTO shoppingCartDTO);

创建ShoppingCartServiceImpl实现类,并实现add方法:

/**
     * @description:加入购物车
     * @date: 2023/12/22 20:27
     * @param: shoppingCartDTO
     **/
    @Override
    public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
        //查询购物车表中是否有数据
        ShoppingCart shoppingCart=new ShoppingCart();
        BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
        shoppingCart.setUserId(BaseContext.getCurrentId());

        List shoppingCarts=shoppingCartMapper.list(shoppingCart);

        //如果存在,则只更改商品数量
        if(shoppingCarts!=null && shoppingCarts.size()==1){
            shoppingCart = shoppingCarts.get(0);
            shoppingCart.setNumber(shoppingCart.getNumber()+1);
            shoppingCartMapper.updateNumberById(shoppingCart);
            return;
        }
        //如果不存在,则要插入到购物车数据库中
        Long dishId = shoppingCart.getDishId();
        Long setmealId = shoppingCart.getSetmealId();

        if(dishId!=null){
            Dish dish = dishMapper.getById(dishId);
            shoppingCart.setName(dish.getName());
            shoppingCart.setImage(dish.getImage());
            shoppingCart.setAmount(dish.getPrice());


        }else {
            Setmeal setmeal = setmealMapper.getById(setmealId);
            shoppingCart.setName(setmeal.getName());
            shoppingCart.setImage(setmeal.getImage());
            shoppingCart.setAmount(setmeal.getPrice());
        }

        shoppingCart.setNumber(1);
        shoppingCart.setCreateTime(LocalDateTime.now());
        shoppingCartMapper.insert(shoppingCart);


    }

创建ShoppingCartMapper接口:

List list(ShoppingCart shoppingCart);


    @Update("update shopping_cart set number = #{number} where id = #{id}")
    void updateNumberById(ShoppingCart shoppingCart);

    /**
     * 插入购物车数据
     * @param shoppingCart
     */
    @Insert("insert into shopping_cart (name, user_id, dish_id, setmeal_id, dish_flavor, number, amount, image, create_time) " +
            " values (#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number},#{amount},#{image},#{createTime})")
    void insert(ShoppingCart shoppingCart);

创建ShoppingCartMapper.xml






    

13 查看购物车

13.1 需求分析和设计

接口设计:

苍穹外卖(三)_第21张图片

13.2代码开发

ShoppingCartController中创建查看购物车的方法:

/**
     * @description:查看购物车
     * @date: 2023/12/23 8:41
 * @return: com.sky.result.Result
     **/
    @GetMapping("/list")
    @ApiOperation("查看购物车")
    public Result> list(){
        log.info("查看购物车");
        List list=shoppingCartService.showShoppingCart();
        return Result.success(list);

    }

ShoppingCartServiceImpl中实现查看购物车的方法:

/**
     * @description:查看购物车
     * @date: 2023/12/23 8:43
     * @return: java.util.List
     **/
    @Override
    public List showShoppingCart() {
        ShoppingCart shoppingCart = ShoppingCart.builder()
                .userId(BaseContext.getCurrentId())
                .build();

        List list = shoppingCartMapper.list(shoppingCart);
        return list;
    }

14 清空购物车

14.1 需求分析和设计

苍穹外卖(三)_第22张图片

14.2代码开发

ShoppingCartController中创建清空购物车的方法:

/**
     * @description:清除购物车
     * @date: 2023/12/23 8:58
     * @return: com.sky.result.Result
     **/
    @DeleteMapping("/clean")
    @ApiOperation("清除购物车")
    public Result clean(){
        log.info("清除购物车");
        shoppingCartService.cleanShoppingCart();
        return Result.success();
    }

ShoppingCartServiceImpl中实现清空购物车的方法:

/**
     * @description:清除购物车
     * @date: 2023/12/23 8:59
     **/
    @Override
    public void cleanShoppingCart() {
        Long userId = BaseContext.getCurrentId();
        shoppingCartMapper.deleteByUserId(userId);
    }

ShoppingCartMapper接口中创建删除购物车数据的方法:

 @Delete("delete from shopping_cart where user_id=#{userId}")
    void deleteByUserId(Long userId);

15 用户下单

15.1 需求分析和设计

用户下单业务说明:

在电商系统中,用户是通过下单的方式通知商家,用户已经购买了商品,需要商家进行备货和发货。

用户下单后会产生订单相关数据,订单数据需要能够体现如下信息:

苍穹外卖(三)_第23张图片

用户点餐业务流程:

苍穹外卖(三)_第24张图片

提交订单接口设计(分析):

请求方式:POST

请求路径:/user/order/submit

参数:

地址簿 id
配送状态(立即送出、选择送出时间)
打包费
总金额
备注
餐具数量

返回数据:

下单时间
订单总金额
订单号
订单 id

接口设计:

苍穹外卖(三)_第25张图片

15.2 代码开发

创建OrderController并提供用户下单方法:

package com.sky.controller.user;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.dto.OrdersSubmitDTO;
import com.sky.result.Result;
import com.sky.service.OrderService;
import com.sky.vo.OrderSubmitVO;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController("userOrderController")
@RequestMapping("/user/order")
@Slf4j
@Api(tags = "订单相关接口")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * @description:提交订单
     * @date: 2023/12/23 20:44
     * @param: ordersSubmitDTO
     * @return: com.sky.result.Result
     **/
    @PostMapping("/submit")
    @ApiOperation("提交订单")
    public Result submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO){
        log.info("提交订单:{}",ordersSubmitDTO);
        OrderSubmitVO orderSubmitVO=orderService.submitOrder(ordersSubmitDTO);
        return Result.success(orderSubmitVO);
    }

}

创建OrderService接口,并声明用户下单方法:

package com.sky.service;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.dto.OrdersSubmitDTO;
import com.sky.vo.OrderSubmitVO;
import org.springframework.stereotype.Service;

@Service
public interface OrderService {

    /**
     * @description:提交订单
     * @date: 2023/12/23 20:48
     * @param: ordersSubmitDTO
     * @return: com.sky.vo.OrderSubmitVO
     **/
    OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO);
}

创建OrderServiceImpl实现OrderService接口

package com.sky.service.impl;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.entity.AddressBook;
import com.sky.entity.OrderDetail;
import com.sky.entity.Orders;
import com.sky.entity.ShoppingCart;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.ShoppingCartBusinessException;
import com.sky.mapper.AddressBookMapper;
import com.sky.mapper.OrderDetailMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.ShoppingCartMapper;
import com.sky.service.OrderService;
import com.sky.vo.OrderSubmitVO;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;


    /**
     * @description:提交订单
     * @date: 2023/12/23 20:48
     * @param: ordersSubmitDTO
     * @return: com.sky.vo.OrderSubmitVO
     **/
    @Override
    @Transactional
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        //如果地址簿为空,则要抛出异常
        AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if(addressBook==null){
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }

        //如果购物车为空,则要抛出异常
        ShoppingCart shoppingCart = new ShoppingCart();
        shoppingCart.setUserId(BaseContext.getCurrentId());
        List shoppingCartList = shoppingCartMapper.list(shoppingCart);
        if(shoppingCartList==null || shoppingCartList.size()==0){
            throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }

        //构建订单表,并插入数据
        Orders orders=new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO,orders);
        orders.setUserId(BaseContext.getCurrentId());
        orders.setOrderTime(LocalDateTime.now());
        orders.setConsignee(addressBook.getConsignee());
        orders.setNumber(String.valueOf(System.currentTimeMillis()));
        orders.setPhone(addressBook.getPhone());
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setPayStatus(Orders.UN_PAID);
        orders.setAddress(addressBook.getDetail());

        orderMapper.insert(orders);

        //构建订单明细表,并构建数据
        List orderDetailList=new ArrayList<>();
        shoppingCartList.forEach(shoppingCart1->{
            OrderDetail orderDetail = new OrderDetail();
            BeanUtils.copyProperties(shoppingCart1,orderDetail);
            orderDetail.setOrderId(orders.getId());
            orderDetailList.add(orderDetail);
        });

        orderDetailMapper.insertBatch(orderDetailList);

        //清空购物车
        shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());

        //返回VO对象
        OrderSubmitVO orderSubmitVO = new OrderSubmitVO();
        orderSubmitVO.setId(orders.getId());
        orderSubmitVO.setOrderNumber(orders.getNumber());
        orderSubmitVO.setOrderAmount(orders.getAmount());
        orderSubmitVO.setOrderTime(orders.getOrderTime());

        return orderSubmitVO;
    }
}

创建OrderMapper接口和对应的xml映射文件:

package com.sky.mapper;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.entity.Orders;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper {



    void insert(Orders orders);
}





    
        insert into orders
        (number, status, user_id, address_book_id, order_time, checkout_time, pay_method, pay_status,
         amount, remark,phone, address, consignee, estimated_delivery_time, delivery_status,
         pack_amount, tableware_number,tableware_status)
        values
            (#{number}, #{status}, #{userId}, #{addressBookId}, #{orderTime}, #{checkoutTime}, #{payMethod},#{payStatus},
             #{amount}, #{remark}, #{phone},#{address}, #{consignee},#{estimatedDeliveryTime}, #{deliveryStatus},
             #{packAmount}, #{tablewareNumber}, #{tablewareStatus})
    


创建OrderDetailMapper接口和对应的xml映射文件:

package com.sky.mapper;/*
 *
 *  @author pengjx
 *
 * */

import com.sky.entity.OrderDetail;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface OrderDetailMapper {


    void insertBatch(List orderDetailList);
}





    
        insert into order_detail (name, order_id, dish_id, setmeal_id, dish_flavor, number, amount, image)
        values
        
            (#{od.name},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor},#{od.number},#{od.amount},#{od.image})
        

    

16 微信支付

有需要再看

你可能感兴趣的:(苍穹外卖,spring,boot,java,mysql,maven)