Spring Boot(十) 优雅的结果集

140 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

大家好! 我是慕歌,一只想教你学习 Spring Boot的野生coder! 欢迎来到慕歌的 Sping boot系列教程,希望通过这个教程带大家搭建基础的 Spring Boot项目,该教程所有知识点均来源于本人的真实开发!

前言

在前一节的学习中,慕歌向大家介绍了在idea 开发工具中集成 Git版本控制器,进行版本控制处理。希望大家能在自己的开发过程中,也学习使用git 版本控制,进行团队开发,或者对自己的项目进行版本控制。我们在进行开发的过程中,通过查看不同的代码版本,进行代码对比,不断优化我们的开发。并且可以通过gitee 获取优质的开源项目,学习优秀的开源项目,并进行自己的二次开发。那么在这一节中,慕歌将向大家带来优雅的结果集返回。

引入:

这里的结果集返回,将会广泛引用到我们的全局结果返回,用于数据的返回,前端通过我们返回的数据在进行渲染,所有一个统一的结果集返回就显得格外的重要。

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

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

开发:

使用该结果集作为统一结果集,他需要满足我们的多种数据格式的放入,并且作为与前端交互的接口,我们需要与前端进行一定的协调,确定如何确定返回数据的格式,以及状态码。
这里我认为有几个重要的规范:

  • 状态码:尽可能准确的状态码,以及尽可能少的状态码,每个状态码有具体的描述,准确的使用场景,不能出现过多的状态码,以及状态码的状态进行混用。
  • 状态码描述:我们可以将状态码信息放入这个字段中,一同返回前端,前端能够清晰的了解该状态码是何种作用,在某些特殊的接口中还可以将后端的提示信息放入其中。
  • 数据:将数据包放在该字段下,这样前端通过该字段统一获取数据。
{3 items
	"msg":"success"
	"code":200
	"data":{11 items
		"id":12
		"type":1
		"uid":1001
		"createTime":"2022-07-20 17:22:50"
		"updateTime":"2022-07-27 17:08:52"
	}
}

那么如何在后端生成这样的数据返回,我们需要使用自己的工具类,以及状态码定义:

import channel.cret.exception.StatusCode;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
* 全局请求返回接口
* @param <T>
*/
public class R<T> extends HashMap<String, Object> implements Serializable {
	//序列化
	private static final long serialVersionUID = 1L;

	// 状态码
	private Integer code;
	// 返回信息
	private String msg;
	// 返回数据,因为返回数据是不确定类型,所以只能考虑Object或者泛型
	private T data;

	//默认成功code
	private static final int OK_CODE = 200;
	//默认成功msg
	private static final String OK_MSG = "success";

	public R() {
		put("code", OK_CODE);
		put("msg", OK_MSG);
	}

	//无参返回成功
	public static R success(){
		R r = new R();
		return r;
	}

	//返回信息
	public static R success(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}

	//无参返回成功
	public static <T> R success(T data){
		R r = new R();
		r.put("data",data);
		return r;
	}

	//返回信息
	public static <T> R success(String msg,T data) {
		R r = new R();
		r.put("msg", msg);
		r.put("data",data);
		return r;
	}

	//返回信息
	public static <T> R success(ResultCode resultCode,T data) {
		R r = new R();
		r.put("msg", resultCode.getMsg());
		r.put("data",data);
		return r;
	}

	//返回多参数
	public static R success(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}

	//默认错误
	public static R error(){
		R r = new R();
		r.put("code",105);
		r.put("msg","未知错误,请联系管理员");
		return r;
	}

	//自定义 错误信息
	public static R error(String msg) {
		return error(101,msg);
	}

	//自定义 错误码 错误信息
	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	//定义错误枚举
	public static R error(StatusCode statusCode){
		R r = new R();
		r.put("code",statusCode.getCode());
		r.put("msg",statusCode.getMsg());
		return r;
	}

	//追加信息
	public R<T> put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

状态码的使用需要我们与前端进行协调:
public interface StatusCode  {
    //状态码
    public int getCode();
    //状态信息描述
    public String getMsg();
}

这里的状态码是一部分,大家可根据自己的需求进行拓展:

import channel.cret.exception.StatusCode;
import lombok.Getter;

/**
 * 枚举状态码
 * 状态名,状态码,状态信息
 */

@Getter
public enum ResultCode implements StatusCode {
    SUCCESS(200, "请求成功"),
    FAILED(101, "请求失败"),
    NEED_USER_NAME(101,"用户名不能为空"),
    NEED_USER_PWD(101,"密码不能为空"),
    NEED_USER_PWDS(101,"重复密码不能为空"),
    NEED_MOBILE(101,"手机号码不能为空"),
    ERROR_PASSWORD(101,"账号或密码错误"),
    ERROR_USERNAME(101,"用户名不可用"),
    ERROR_FROZE(101,"用户已被注销或冻结"),
    ERROR_UNFROZE(101,"用户已被注销或未被冻结"),
    REPEAT_PASSWORD(101,"密码不能与之前密码相同"),
    NEED_SMSCODE(101,"短信验证码不能为空"),
    NEED_CODE(101,"验证码不能为空"),
    VALIDATE_ERROR(101,"表单信息不能为空"),
    USER_LOGIN_IS_FAILURE(102,"登录状态已过期"),
    RESPONSE_PACK_ERROR(105, "服务器状态异常");

    private int code;
    private String msg;

    ResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

这里的使用可参考,以下登录示例:

    /**
     * 登录
     * @param loginVo 登录表单
     * @return
     */
    @RequestMapping("/login")
    public R login(@RequestBody @Validated AdminLoginVo loginVo){
        //登录
        boolean login = adminUserService.login(loginVo);
        if(login){
            // 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
            if(StpUtil.isDisable(Constant.USER_ID)){
                return R.error("账号被封禁");
            }
            log.info("{}登录成功",loginVo.getUsername());
            //日志
            adminLogService.log("登录","登录成功"+loginVo.getUsername());
            // 获取 Token  相关参数
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            //返回token信息
            return R.success(tokenInfo);
        }
        //登录失败
        return R.error(ResultCode.ERROR_PASSWORD);
    }

结语

这一章的分享到这里就结束了,下一节中还将带来异常控制的分享!
如果您觉得本文不错,欢迎点赞支持,您的关注是我坚持的动力!