SpringBoot项目:抽奖精灵

抽奖精灵

为企业活动提供在线抽奖功能,可以自由设置奖品、抽奖人员及抽奖方式。

  • 用户注册(企业)
  • 用户登录、会话管理
  • 抽奖设置:奖品管理,抽奖人员管理
  • 人员抽奖(前端控制中奖人员)

1 数据库表

在这里插入图片描述
(1) 数据库:

drop database if exists lucky_draw;
create database lucky_draw character set utf8mb4;

use lucky_draw;

(2)创建用户表

drop table if exists user;
create table user(
    id int primary key auto_increment,
    username varchar(20) not null unique comment '用户账号',
    password varchar(20) not null comment '密码',
    nickname varchar(20) comment '用户昵称',
    email varchar(50) comment '邮箱',
    age int comment '年龄',
    head varchar(255) comment '头像url',
    create_time timestamp default NOW() comment '创建时间'
) comment '用户表';

(3)抽奖设置

drop table if exists setting;
create table setting(
    id int primary key auto_increment,
    user_id int not null comment '用户id',
    batch_number int not null comment '每次抽奖人数',
    create_time timestamp default NOW() comment '创建时间',
    foreign key (user_id) references user(id)
) comment '抽奖设置';

(4)奖项

drop table if exists award;
create table award(
    id int primary key auto_increment,
    name varchar(20) not null comment '奖项名称',
    count int not null comment '奖项人数',
    award varchar(20) not null comment '奖品',
    setting_id int not null comment '抽奖设置id',
    create_time timestamp default NOW() comment '创建时间',
    foreign key (setting_id) references setting(id)
) comment '奖项';

(5)抽奖人员表

drop table if exists member;
create table member(
    id int primary key auto_increment,
    name varchar(20) not null comment '姓名',
    no varchar(20) not null comment '工号',
    user_id int not null comment '用户id',-- 最好调整为setting_id,===>以便于扩展需要
    create_time timestamp default NOW() comment '创建时间',
    foreign key (user_id) references user(id)
) comment '抽奖人员';

(6)抽奖记录表

drop table if exists record;
create table record(
    id int primary key auto_increment,
    member_id int not null,
    award_id int not null,
    create_time timestamp default NOW() comment '创建时间',
    foreign key (member_id) references member(id),
    foreign key (award_id) references award(id)
) comment '抽奖记录表';

(7)插入测试数据

-- 插入企业用户
insert into user(id, username, password, nickname, email, age, head) values (1, '123', '123', 'root', '[email protected]', 18, 'img/test-head.jpg');

-- 插入用户的初始设置
insert into setting(id, user_id, batch_number) values (1, 1, 8);

-- 插入奖项
insert into award(name, count, award, setting_id) values ('一等奖', 1, '太空游', 1);
insert into award(name, count, award, setting_id) values ('二等奖', 5, '红旗 H5', 1);
insert into award(name, count, award, setting_id) values ('三等奖', 20, '国内任意游', 1);


-- 插入抽奖人员
insert into member(name, no, user_id) values ('闵觅珍', 'no2', 1);
insert into member(name, no, user_id) values ('慈新之', 'no3', 1);
insert into member(name, no, user_id) values ('户柔绚', 'no4', 1);
insert into member(name, no, user_id) values ('柯雅容', 'no5', 1);
insert into member(name, no, user_id) values ('邰虹彩', 'no6', 1);
insert into member(name, no, user_id) values ('延易蓉', 'no7', 1);
insert into member(name, no, user_id) values ('吉娇然', 'no8', 1);
insert into member(name, no, user_id) values ('百里惜蕊', 'no9', 1);
insert into member(name, no, user_id) values ('云寻双', 'no10', 1);
insert into member(name, no, user_id) values ('衅嘉颖', 'no11', 1);
insert into member(name, no, user_id) values ('银以晴', 'no12', 1);
insert into member(name, no, user_id) values ('保颐和', 'no13', 1);
insert into member(name, no, user_id) values ('饶燕婉', 'no14', 1);
insert into member(name, no, user_id) values ('单阳平', 'no15', 1);
insert into member(name, no, user_id) values ('墨碧春', 'no16', 1);

2 基本功能

在这里插入图片描述

(1) 用户登录

请求: POST api/user/login

 Content-Type: application/json
{
    
    username: "bit", password: "123"}

响应

{
    
    
  "success" : true
}

(2) 用户注册

请求: POST api/user/register

Content-Type: multipart/form-data; boundary=----WebKitFormBoundarypOUwkGIMUyL0aOZT

username: haha
password: 111
nickname: 牛牛牛
email: 666@163.com
age: 66
headFile: (binary)

注意:
以上请求数据是解析过的,http原生发送的数据还包含其他很多内容,比较多,可以动手抓包看看。其中boundary后边的是随机生成的,请求数据中会使用该信息。

响应

{
    
    
  "success" : true
}

在这里插入图片描述
在这里插入图片描述

(3)查询抽奖设置

请求: GET api/setting/query

响应

{
    
    
  "success" : true,
  "data" : {
    
    
    "id" : 1,
    "userId" : 1,
    "batchNumber" : 8,
    "createTime" : "2020-08-14 08:16:31",
    "user" : {
    
    
      "id" : 1,
      "username" : "123",
      "password" : "123",
      "nickname" : "蜡笔小新",
      "email" : "[email protected]",
      "age" : 18,
      "head" : "img/test-head.jpg",
      "createTime" : "2020-08-14 08:16:31",
      "settingId" : 1
    },
    "awards" : [ {
    
    
      "id" : 1,
      "name" : "一等奖",
      "count" : 1,
      "award" : "火箭",
      "settingId" : 1,
      "createTime" : "2020-08-14 08:16:31",
      "luckyMemberIds" : [ 5 ]
    }, {
    
    
      "id" : 2,
      "name" : "二等奖",
      "count" : 5,
      "award" : "红旗 H5",
      "settingId" : 1,
      "createTime" : "2020-08-14 08:16:31",
      "luckyMemberIds" : [ 56, 40, 32, 65, 81 ]
    }, {
    
    
      "id" : 3,
      "name" : "三等奖",
      "count" : 20,
      "award" : "国内任意游",
      "settingId" : 1,
      "createTime" : "2020-08-14 08:16:31",
      "luckyMemberIds" : [ 48, 68, 43, 73, 13, 83, 63, 25 ]
    } ],
    "members" : [ {
    
    
      "id" : 1,
      "name" : "李寻欢",
      "no" : "水果刀",
      "userId" : 1,
      "createTime" : "2020-08-14 08:16:31"
    }, {
    
    
      "id" : 2,
      "name" : "郭靖",
      "no" : "降猪十八掌",
      "userId" : 1,
      "createTime" : "2020-08-14 08:16:31"
    }, {
    
    
      "id" : 3,
      "name" : "韦小宝",
      "no" : "龙爪手",
      "userId" : 1,
      "createTime" : "2020-08-14 08:16:31"
    } ]
  }
}

(4) 修改抽奖人数

请求 GET api/setting/update?batchNumber=5

响应

{
    
    
  "success" : true
}

(5) 新增奖项

请求 POST api/award/add

Content-Type: application/json

{
    
    name: "牛哄哄", count: 3, award: "华为手机"}

响应

{
    
    
  "success" : true
}

(6) 修改奖项

请求 POST api/award/update

Content-Type: application/json

{
    
    name: "牛哄哄", count: 3, award: "小米手机", id: 4}

响应

{
    
    
  "success" : true
}

(7) 删除奖项

请求 GET api/award/delete/4

响应

{
    
    
  "success" : true
}

(8) 新增抽奖人员

请求 POST api/member/add

Content-Type: application/json

{
    
    name: "羞羞的粉拳", no: "007"}

响应

{
    
    
  "success" : true
}

(9) 修改抽奖人员

请求 POST api/member/update

Content-Type: application/json

{
    
    name: "泰山", no: "000", id: 96}

响应

{
    
    
  "success" : true
}

(10) 删除抽奖人员

请求 GET api/member/delete/97

响应

{
    
    
  "success" : true
}

在这里插入图片描述

(11) 抽奖

请求 POST api/record/add/3

Content-Type: application/json

[92, 22, 43, 76]

以上路径中最后的数字3代表奖项id,请求数据为获奖人员id组成的数组

响应

{
    
    
  "success" : true
}

(12) 删除当前奖项某个获奖人员

请求 GET api/record/delete/member?id=22

根据人员id删除对应的获奖记录

响应

{
    
    
  "success" : true
}

(13) 删除当前奖项已获奖人员

请求 GET api/record/delete/award?id=3

根据奖项id删除对应所有获奖人员记录

响应

{
    
    
  "success" : true
}

3 配置项目pom.xml

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

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
    </parent>

    <groupId>frank</groupId>
    <artifactId>lucky-draw</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <!-- spring-boot-starter-web: 基于SpringBoot开发的依赖包,
                                 会再次依赖spring-framework中基本依赖包,aop相关依赖包,web相关依赖包,
                                 还会引入其他如json,tomcat,validation等依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mybatis-spring-boot-starter: Mybatis框架在SpringBoot中集成的依赖包,
                                Mybatis是一种数据库对象关系映射Object-Relationl Mapping(ORM)框架,
                                其他还有如Hibernate等 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <!-- Mybatis代码生成工具 -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!-- mysql-connector-java: mysql数据库驱动包
                                在编译时没有直接使用,但是运行时需要,所以使用
                                scope runtime -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>

        <!-- druid-spring-boot-starter: 阿里Druid数据库连接池,同样的运行时需要 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>

        <!-- spring-boot-devtools: SpringBoot的热部署依赖包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <!-- 不能被其它模块继承,如果多个子模块可以去掉 -->
            <optional>true</optional>
        </dependency>

        <!-- lombok: 简化bean代码的框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- spring-boot-starter-test: SpringBoot测试框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

    <build>
        <finalName>lucky-draw</finalName>
        <plugins>
            <!-- SpringBoot的maven打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4 Springboot配置文件

debug=true
# 设置打印日志的级别,及打印sql语句
#日志级别:trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
#扫描的包:druid.sql.Statement类和glp包
logging.level.druid.sql.Statement=ERROR
logging.level.glp=DEBUG

# 美化JSON数据格式
spring.jackson.serialization.indent-output=true
# 设置JSON数据的日期格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
# JSON数据属性为null时不返回
spring.jackson.default-property-inclusion=non_null

# 找不到资源404时抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 禁用静态资源的自动映射,如不禁用,不存在的url将被映射到/**,servlet不有机会抛出异常
#spring.resources.add-mappings=false
# get请求参数及表单提交数据的日期格式
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss


# 应用/项目的部署路径,默认为/
server.servlet.context-path=/lucky-draw
#server.port=8081
# SpringMVC中,DispatcherServlet的映射路径,默认为/**
#spring.mvc.servlet.path=/**

# 自定义属性:用户头像本地保存根路径,指定这个为静态路径,浏览器请求资源时,可以直接获取到
user.head.local-path=G:/glp
user.head.remote-path=http://localhost:8080${server.servlet.context-path}
user.head.filename=head.jpg

# 静态资源映射:将路径映射为/,即/static/xxx,映射为/xxx,支持多个字符串,逗号间隔
# 默认为/META-INF/resources/, /resources/, /static/, /public/
# 指定外部web资源文件夹:访问的路径为/
spring.resources.static-locations=classpath:/static/,classpath:/public/,file:${user.head.local-path}

#druid数据库连接池配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/lucky_draw?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true

#Mybatis配置
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
#mybatis.type-aliases-package=glp.mapper
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.config-location=classpath:mybatis/mybatis-config.xml

#mapper
#mappers 多个接口时逗号隔开
#mapper.mappers=tk.mybatis.mapper.common.Mapper,tk.mybatis.mapper.common.MySqlMapper,tk.mybatis.mapper.common.IdsMapper
#mapper.notEmpty=true
#mapper.identity=MYSQL

#pagehelper
#数据库方言:oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
pagehelper.helperDialect=mysql

#默认值为 false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
#pagehelper.offset-as-page-num=falses
#默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。
pagehelper.row-bounds-with-count=true

#默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
#pagehelper.page-size-zero=false
#分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
pagehelper.reasonable=true

#为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
pagehelper.params=pageNum=pageNumber;pageSize=pageSize;count=countSql;reasonable=reasonable;

#支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。
#pagehelper.supportMethodsArguments=true
#用于控制默认不带 count 查询的方法中,是否执行 count 查询,默认 true 会执行 count 查询,这是一个全局生效的参数,多数据源时也是统一的行为。
pagehelper.default-count=false

5 准备SpringBoot启动类

@SpringBootApplication
@MapperScan("glp.mapper")
public class Application {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(Application.class, args);
    }
}

6 整合Mybatis

  • 实体类
  • 接口
  • xml

这里使用了继承,复用接口. 使用Mybatis的接口方法,所有接口方法都是类似,只是传入参数和返回值不同,可以考虑设计统一的基类,以泛型的方式定义出不同的参数类型、返回类型

public interface BaseMapper<T extends BaseEntity>{
    
    

    T selectByPrimaryKey(Integer id);

    int insert(T record);

    int insertSelective(T record);

    int updateByPrimaryKeySelective(T record);

    int updateByPrimaryKey(T record);

    int deleteByPrimaryKey(Integer id);

    T selectOne(T record);

    List<T> selectAll();

    List<T> selectByCondition(T record);

    int deleteByIds(List<Integer> ids);
}

7 设计统一响应类

主要为返回数据的统一字段设计

@Getter
@Setter
public class ResponseResult {
    
    

    private boolean success;
    private String code;
    private String message;
    private Object data;

    private ResponseResult(){
    
    }

    public static ResponseResult ok(Object data){
    
    
        ResponseResult result = new ResponseResult();
        result.setSuccess(true);
        result.setData(data);
        return result;
    }

    public static ResponseResult error(){
    
    
        return error("ERR000", "未知错误");
    }

    public static ResponseResult error(String code, String message){
    
    
        ResponseResult result = new ResponseResult();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

8 设计自定义异常类型

主要针对不同的场景,需要抛异常来处理时,能定位业务含义

  • 客户端请求错误时的异常:需要给定错误码,方便前端提示用户,如用户名存在不允许注册

  • 业务发生错误时的异常:需要给定错误码,方便后端定位问题,一般如程序上的业务错误都可以抛(BUG)

  • 系统发生错误时的异常:需要给定错误码,方便后端定位问题,程序出错,如数据库连接获取失败都可以抛(一般是系统发生错误,如网络断了,数据库挂了等等)

  • 自定义异常前端需要显示错误码和错误消息(一般是自己抛的中文异常描述),用户可以根据提示信息判断原因。

  • 非自定义异常,异常信息一般是框架或JDK抛出的英文,是给开发人员描述错误的,无法给用户提示,所以错误信息提示为未知异常。

先定义异常的基类:抛异常时,有时候是自己抛,有时候是捕获到异常,再往外抛,所以提供两个构造方法

@Getter
@Setter
public class BaseException extends RuntimeException {
    
    

    protected String code;

    public BaseException(String code, String message) {
    
    
        this(code, message, null);
    }

    public BaseException(String code, String message, Throwable cause) {
    
    
        super(message, cause);
        this.code = code;
    }
}

完成异常的子类:提供不同的错误码前缀,以便于前端提示时,知道是哪的问题

public class BusinessException extends BaseException {
    
    

    public BusinessException(String code) {
    
    
        this(code, null);
    }

    public BusinessException(String code, String message) {
    
    
        this(code, message, null);
    }

    public BusinessException(String code, String message, Throwable cause) {
    
    
        super("BUS" + code, message, cause);
    }
}
public class ClientException extends BaseException {
    
    

    public ClientException(String code) {
    
    
        this(code, null);
    }

    public ClientException(String code, String message) {
    
    
        this(code, message, null);
    }

    public ClientException(String code, String message, Throwable cause) {
    
    
        super("CLI" + code, message, cause);
    }
}
public class SystemException extends BaseException {
    
    

    public SystemException(String code) {
    
    
        this(code, null);
    }

    public SystemException(String code, String message) {
    
    
        this(code, message, null);
    }

    public SystemException(String code, String message, Throwable cause) {
    
    
        super("SYS" + code, message, cause);
    }
}

9 设计Controller中抛异常时的拦截器

这里不光是Controller抛异常,只要Controller代码执行,调用其他方法产生的异常,都会退出Controller方法,即HTTP请求方法结束,由拦截器统一处理。

@Slf4j
@ControllerAdvice
public class ExceptionAdvisor {
    
    

    /**
     * 请求数据错误:包括类型转换错误,校验失败
     * @param e
     */
    @ExceptionHandler({
    
    
            BindException.class//使用@Valid 验证路径中请求实体校验失败后抛出的异常
            , ConstraintViolationException.class//处理请求参数格式错误 @RequestParam上validate失败后抛出的异常
            , MethodArgumentNotValidException.class//处理请求参数格式错误 @RequestBody上validate失败后抛出的异常
            , MethodArgumentTypeMismatchException.class//请求参数类型转换错误
    })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public void handleMethodArgumentTypeMismatchException(Throwable e){
    
    
        log.debug("================================");
        log.debug("Controller方法参数类型转换错误", e);
    }

    @ExceptionHandler({
    
    
            MethodNotAllowedException.class
            , HttpRequestMethodNotSupportedException.class
    })
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public void handleMethodNotAllowedException(Throwable e){
    
    
        log.debug("================================");
        log.debug("Controller提供的http方法不支持", e);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public void handleNoHandlerFoundException(Throwable e){
    
    
        log.debug("================================");
        log.debug("找不到http请求处理器", e);
    }

    @ExceptionHandler(BaseException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Object handleBaseException(BaseException e){
    
    
        log.debug("================================");
        log.debug("自定义异常", e);
        return ResponseResult.error(e.getCode(), e.getMessage(), e);
    }

    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Object handleException(Throwable e){
    
    
        log.error("================================");
        log.error("未知异常:"+e.getClass().getName()+",请联系管理员", e);
        return ResponseResult.error(e);
    }
}

10 设计会话管理的拦截器及统一数据响应配置

public class LoginInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        HttpSession session = request.getSession(false);
        //登录校验
        if(session != null && session.getAttribute("user") != null){
    
    
            return true;
        }
        response.setStatus(401);
        return false;
    }
}

设计Web统一处理的配置

  • 会话管理的拦截器要引入配置:只拦截后端服务路径,并排除登录、注册、注销接口
  • 后端服务器路径都有/api的前缀,可以加上统一的路径映射
  • 统一的响应数据格式封装:这里不使用@ControllerAdviceResponseBodyAdvice进行拦截,原因是,返回值为null,会出现无法统一包装,响应体为空
@Configuration
public class SysConfig implements WebMvcConfigurer, InitializingBean{
    
    

    @Resource
    private RequestMappingHandlerAdapter adapter;

    @Override
    public void afterPropertiesSet() {
    
    
        List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers);
        for(int i=0; i<handlers.size(); i++){
    
    
            HandlerMethodReturnValueHandler handler = handlers.get(i);
            if(handler instanceof RequestResponseBodyMethodProcessor){
    
    
                handlers.set(i, new RequestResponseBodyMethodProcessorWrapper(handler));
            }
        }
        adapter.setReturnValueHandlers(handlers);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/user/login")
                .excludePathPatterns("/api/user/register")
                .excludePathPatterns("/api/user/logout")
        ;
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
    
    
        configurer.addPathPrefix("api", c -> true);
    }
}

提供统一响应体封装处理类:

public class RequestResponseBodyMethodProcessorWrapper implements HandlerMethodReturnValueHandler {
    
    

    private final HandlerMethodReturnValueHandler delegate;

    public RequestResponseBodyMethodProcessorWrapper(HandlerMethodReturnValueHandler delegate) {
    
    
        this.delegate = delegate;
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
    
    
        return delegate.supportsReturnType(returnType);
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    
    
        if(!(returnValue instanceof ResponseResult)){
    
    
            returnValue = ResponseResult.ok(returnValue);
        }
        delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
}

源码链接:抽奖精灵

猜你喜欢

转载自blog.csdn.net/glpghz/article/details/108607047