苍穹外卖(三)

查漏补缺

暂无

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),根据分数升序排序,没有重复元素

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   获取哈希表中所有值

3.3列表操作命令

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

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

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

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

LLEN key   获取列表长度

3.4 集合操作命令

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

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

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

SCARD key   获取集合的成员数

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

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

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

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 ...]   移除有序集合中的一个或多个成员

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
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
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 作为前缀

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

约定: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<Integer> 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<Integer> 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 协议最新的版本和建议。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

核心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。

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

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

<!--index.wxml-->
<navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar>
<scroll-view class="scrollarea" scroll-y type="list">
  <view class="container">
    {{msg}}
  </view>


  <view>
    <button bind:tap="getUserInfo"  type="primary">获取用户信息</button>
    昵称:{{nickname}}
    头像:<image style="width: 100px; height: 100px;" src="{{url}}" />
  </view>

  <view>
    <button bind:tap="wxLogin" type="warn">微信登录</button>
    {{code}}
  </view>

  <view>
    <button bind:tap="sendRequest" type="default">发送请求</button>
  </view>


  


</scroll-view>
// 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 微信登录流程

8.2 需求分析和设计

业务规则:

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

接口设计

数据库设计(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<com.sky.vo.UserLoginVO>
     **/
    @PostMapping("/login")
    @ApiOperation("用户登录")
    public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("用户登录:{}",userLoginDTO.getCode());
        User user = userService.wxLogin(userLoginDTO);

        Map<String, Object> 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<String, String> 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映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.UserMapper">


    <insert id="insert" parameterType="com.sky.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user
            (openid,name,phone,sex,id_number,avatar,create_time)
        values
            (#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime})

    </insert>
</mapper>

编写拦截器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 查询包含的菜品
查询分类 接口

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<java.util.List<com.sky.entity.Category>>
     **/
    @GetMapping("/list")
    @ApiOperation("查询分类")
    public Result<List<Category>> list(Integer type){
        log.info("查询分类:{}",type);
        List<Category> list = categoryService.list(type);
        return Result.success(list);
    }


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

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<com.sky.vo.DishVO>
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> getByCategoryId(Long categoryId){
        log.info("根据分类id查询菜品:{}",categoryId);
        String key="key_"+categoryId;
        List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);

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

        List<DishVO> 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<com.sky.vo.DishVO>
     **/
    List<DishVO> listWithFlavor(Long categoryId);
 /**
     * @description:根据分类id查询菜品
     * @date: 2023/12/21 14:43
     * @param: categoryId
     * @return: java.util.List<com.sky.vo.DishVO>
     **/
    @Override
    public List<DishVO> listWithFlavor(Long categoryId) {
        Dish dish = Dish.builder()
                .categoryId(categoryId)
                .status(StatusConstant.ENABLE)
                .build();

        List<Dish> dishList = dishMapper.list(dish);

        List<DishVO> dishVOList = new ArrayList<>();

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

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

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

@Autowired
    private SetmealService setmealService;

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

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

    }

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

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

    @GetMapping("/dish/{id}")
    @ApiOperation("根据套餐id查询包含的菜品")
    public Result<List<DishItemVO>> getSetmealDish(@PathVariable Long id ){
        log.info("根据套餐id查询包含的菜品:{}",id);
        List<DishItemVO> setmealDishes=setmealService.getDishItemById(id);
        return Result.success(setmealDishes);
/**
     * @description:根据套餐id查询包含的菜品
     * @date: 2023/12/21 19:19
     * @param: id
     * @return: java.util.List<com.sky.vo.DishItemVO>
     **/
    List<DishItemVO> getDishItemById(Long id);
/**
     * @description:根据套餐id查询包含的菜品
     * @date: 2023/12/21 19:19
     * @param: id
     * @return: java.util.List<com.sky.vo.DishItemVO>
     **/
    @Override
    public List<DishItemVO> getDishItemById(Long id) {
        List<DishItemVO> 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<DishItemVO> getDishItemBySetmealId(Long setmealId);

10.缓存菜品

10.1 问题说明 

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

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

10.2 实现思路

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

缓存逻辑分析:

每个分类下的菜品保存一份缓存数据
数据库中菜品数据有变更时清理缓存数据

10.3 代码开发

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

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

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

        List<DishVO> 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<com.sky.result.PageResult>
     **/
    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> 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<Long> 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<com.sky.vo.DishVO>
     **/
    @GetMapping("/{id}")
    @ApiOperation("根据id查询菜品")
    public Result<DishVO> 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<java.util.List<com.sky.entity.Dish>>
     **/
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<Dish>> getByCategoryId(Long categoryId){
        log.info("根据分类id查询菜品");
        List<Dish> 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
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.7.3</version>
</dependency>

常用注解:

注解

说明

@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

数据库设计:

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

数据库设计(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<ShoppingCart> 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<ShoppingCart> 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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.ShoppingCartMapper">


    <select id="list" resultType="com.sky.entity.ShoppingCart">

        select * from shopping_cart
        <where>
            <if test="userId!=null">
                and user_id=#{userId}
            </if>
            <if test="setmealId!=null">
                and setmeal_id=#{setmealId}
            </if>
            <if test="dishId!=null">
                and dish_id=#{dishId}
            </if>
            <if test="dishFlavor!=null">
                and dish_flavor=#{dishFlavor}
            </if>
        </where>
        order by create_time desc


    </select>
</mapper>

13 查看购物车

13.1 需求分析和设计

接口设计:

13.2代码开发

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

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

    }

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

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

        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
        return list;
    }

14 清空购物车

14.1 需求分析和设计

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 需求分析和设计

用户下单业务说明:

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

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

用户点餐业务流程:

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

请求方式:POST

请求路径:/user/order/submit

参数:

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

返回数据:

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

接口设计:

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<com.sky.vo.OrderSubmitVO>
     **/
    @PostMapping("/submit")
    @ApiOperation("提交订单")
    public Result<OrderSubmitVO> 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<ShoppingCart> 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<OrderDetail> 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);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.OrderMapper">


    <insert id="insert" parameterType="Orders" useGeneratedKeys="true" keyProperty="id">
        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})
    </insert>

</mapper>

创建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<OrderDetail> orderDetailList);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.OrderDetailMapper">


    <insert id="insertBatch">
        insert into order_detail (name, order_id, dish_id, setmeal_id, dish_flavor, number, amount, image)
        values
        <foreach collection="orderDetailList" item="od" separator=",">
            (#{od.name},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor},#{od.number},#{od.amount},#{od.image})
        </foreach>

    </insert>
</mapper>

16 微信支付

有需要再看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值