查漏补缺
暂无
1.Redis入门
1.1 Redis简介
Redis是一个基于内存的 key-value 结构数据库。
基于内存存储,读写性能高适合存储热点数据(热点商品、资讯、新闻)企业应用广泛中文网: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 介绍
HttpClient是Apache的一个子项目,是高效的、功能丰富的支持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;
}
根据接口定义创建UserController的login方法:
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,完善UserServiceImpl的wxLogin方法::
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);
}
}
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);
}
@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注解:
在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入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 微信支付
有需要再看