springboot+mybatisPlus代码层级梳理

代码层级梳理

代码层级

springboot+mybatisPlus代码层级梳理

annotation层

springboot+mybatisPlus代码层级梳理

ExcelVoAttribut

主要用于写@ExcelVoAttribute注解,可以更灵活的配置Excel数据表导出的一些基本配置

LogDirection

主要用于写@LogDirection注解,注解里包含一个字段(接口描述),通过接口添加这个注解,填写上接口的功能,当用户在访问接口时候,可以利用aop技术,把用户的操作记到日志里

aspect层

springboot+mybatisPlus代码层级梳理

LogDrectionAspect

主要定义了aop切点的位置和增强的方法

/**
 * @Author Johnny
 * @Date 2020/4/16
 * @Version 1.0
 */
@Aspect
@Component
@Slf4j
public class LogDirectionAspect {

    @Pointcut("@annotation(logDirection)")
    public void doLogDirection(LogDirection logDirection){

    }

    @Around(value = "doLogDirection(logDirection)",argNames = "pjp,logDirection")
    public Object doBefore(ProceedingJoinPoint pjp,LogDirection logDirection) throws  Throwable{
        if (!"".equals(logDirection.direction())) {
            log.info(logDirection.direction()+"接口被调用");
            return  pjp.proceed();
        }
        return "接口信息不规范";
    }
}

@Pointcut定义切点,此处定义切点为使用@LogDirection的方法

@Around定义增强的方法,通过around的参数ProceedingJoinPoint可以很好的控制程序能不能继续执行下一步,在权限判定发挥着比较重要的作用

拓展:

利用下面的这种方法,需要在xml文件中配置bean实例,当然也可以在方法中通过手动扫包的方法获得bean实例进行操作

/**
 * @Author Johnny
 * @Date 2020/4/16
 * @Version 1.0
 */
@Aspect
public class MyAdvice {
    /**
     * 配置切点
     */
    @Pointcut("execution(* com.soft1851.spring.web.dao..*.insert*(..))")
    public void pointCut() {
    }

    /**
     * 配置前置增强
     */
    @Before("MyAdvice.pointCut()")
    public void beforeMethod() {
        System.out.println("等待数据插入");
    }

    /**
     * 配置后置增强
     */
    @AfterReturning("execution(* com.soft1851.spring.web.dao..*.*(..))")
    public void afterMethod(){
        System.out.println("关闭数据库");
    }
}

xml配置实例

<bean name="forumDao" class="com.soft1851.spring.web.dao.impl.ForumDaoImpl"/>
    <bean name="myAdvice" class="com.soft1851.spring.web.interceptor.MyAdvice"/>
    <aop:aspectj-autoproxy/>

common层

springboot+mybatisPlus代码层级梳理

ResponseResult和ResultCode

这两个类可以将响应体统一进行封装,将接口的响应体规范化,使前端获取数据也更加友好

贴下代码,防止以后找不到

ResponseResult类

package com.soft1851.music.admin.common;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Johnny
 * @Date: 2020/4/21 19:52
 * @Description:
 */
@Data
public class ResponseResult implements Serializable {
    private static final long serialVersionUID = -3948389268046368059L;

    private Integer code;

    private String msg;

    private Object data;

    private ResponseResult() {
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static ResponseResult success() {
        ResponseResult result = new ResponseResult();
        result.setResultCode(ResultCode.SUCCESS);
        return result;
    }

    public static ResponseResult success(Object data) {
        ResponseResult result = new ResponseResult();
        result.setResultCode(ResultCode.SUCCESS);
        result.setData(data);
        return result;
    }

    public static ResponseResult failure(ResultCode resultCode) {
        ResponseResult result = new ResponseResult();
        result.setResultCode(resultCode);
        return result;
    }

    public static ResponseResult failure(ResultCode resultCode, Object data) {
        ResponseResult result = new ResponseResult();
        result.setResultCode(resultCode);
        result.setData(data);
        return result;
    }

    public void setResultCode(ResultCode code) {
        this.code = code.code();
        this.msg = code.message();
    }

}

ResultCode类(枚举)

package com.soft1851.music.admin.common;

/**
 * @author Johnny
 * @Date: 2020/4/21 19:52
 * @Description:
 */
public enum ResultCode {
    /* 成功状态码 */
    SUCCESS(1, "成功"),

    /* 通用错误:10001-19999 */
    PARAM_IS_INVALID(10001, "参数无效"),
    PARAM_IS_BLANK(10002, "参数为空"),
    PARAM_TYPE_BIND_ERROR(10003, "参数类型错误"),
    PARAM_NOT_COMPLETE(10004, "参数缺失"),
    HTTP_METHOD_ERROR(10005, "请求方法错误"),
    HTTP_METHOD_NOT_ALLOWED(10006, "请求方法不允许"),
    HTTP_NOT_FOUND(10007, "请求地址错误"),
    BOUND_STATEMENT_NOT_FOUND(10008, "Mybatis未绑定"),
    CONNECTION_ERROR(10009, "网络连接错误"),
    ARITHMETIC_ERROR(100010, "计算错误"),


    /* 用户错误:20001-29999*/
    USER_NOT_SIGN_IN(20001, "请先登录"),
    USER_PASSWORD_ERROR(20002, "密码错误"),
    USER_ACCOUNT_ERROR(20003, "账号错误"),
    USER_VERIFY_CODE_ERROR(20004, "验证码错误"),
    USER_CODE_TIMEOUT(20005, "验证码失效"),
    USER_ACCOUNT_FORBIDDEN(20006, "账号已被禁用"),
    USER_SIGN_UP_FAIL(20007, "用户注册失败"),
    USER_SIGN_IN_FAIL(20008, "用户登录失败"),
    USER_NOT_FOUND(20009, "用户不存在"),
    USER_NO_AUTH(20019, "用户权限不足"),
    USER_TOKEN_EXPIRES(200010, "Token已过期"),


    /* 业务错误:30001-39999 */
    SMS_ERROR(30001, "短信业务出现问题"),
    UPLOAD_ERROR(30002, "上传文件业务出现问题"),
    CAPTCHA_ERROR(30003, "验证码业务出现问题"),
    /* 数据错误:40001-49999 */
    RESULT_CODE_DATA_NONE(50001, "数据未找到"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_ALREADY_EXISTED(50003, "数据已存在"),
    DATABASE_ERROR(50004, "数据库操作异常"),


    /* 服务器或系统错误:50001-599999 */
    SERVER_ERROR(50000, "服务器错误,请稍后重试"),
    SYSTEM_ERROR(40001, "系统错误,请稍后重试"),

    /* 接口错误:60001-69999 */
    INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
    INTERFACE_OUTER_INVOKE_ERROR(60002, "外部系统接口调用异常"),
    INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
    INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
    INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
    INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),

    /* 权限错误:70001-79999 */
    PERMISSION_NO_ACCESS(70001,"无访问权限");

    private Integer code;

    private String message;

    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer code() {
        return this.code;
    }

    public String message() {
        return this.message;
    }

    public static String getMessage(String name) {
        for (ResultCode item : ResultCode.values()) {
            if (item.name().equals(name)) {
                return item.message;
            }
        }
        return name;
    }

    public static Integer getCode(String name) {
        for (ResultCode item : ResultCode.values()) {
            if (item.name().equals(name)) {
                return item.code;
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return this.name();
    }

}

config层

springboot+mybatisPlus代码层级梳理

CaptchaConfig

主要是为kaptcha谷歌验证码的一些基本信息进行配置,比如字体,验证码位数等等

附上一些kaptha的基本配置信息

Constant 描述 默认值
kaptcha.border 图片边框,合法值:yes , no yes
kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness 边框厚度,合法值:>0 1
kaptcha.image.width 图片宽 200
kaptcha.image.height 图片高 50
kaptcha.producer.impl 图片实现类 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl 文本实现类 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string 文本集合,验证码值从此集合中获取 abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 验证码长度 5
kaptcha.textproducer.font.names 字体 Arial, Courier
kaptcha.textproducer.font.size 字体大小 40px.
kaptcha.textproducer.font.color 字体颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.textproducer.char.space 文字间隔 2
kaptcha.noise.impl 干扰实现类 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干扰 颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景实现类 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景颜色渐变,开始颜色 light grey
kaptcha.background.clear.to 背景颜色渐变, 结束颜色 white
kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE

CorsConfig

主要是为跨域做一些基本的配置,学习阶段后端必备

代码展示,防止以后忘记

/**
 * @Author Johnny
 * @Date 2020/4/16
 * @Version 1.0
 */
@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        //放行所有跨域的客户端domain
        config.addAllowedOrigin("*");
        //允许的请求方法列表
        String[] requestMethods = {"GET", "POST", "PUT", "DELETE", "OPTIONS"};
        List<String> allowedRequestMethods = Arrays.asList(requestMethods);
        config.setAllowedMethods(allowedRequestMethods);
        //允许的客户端请求头列表
        String[] requestHeaders = {"x-requested-with", "Content-Type", "Authorization"};
        List<String> allowedHeaders = Arrays.asList(requestHeaders);
        config.setAllowedHeaders(allowedHeaders);
        //允许的响应头列表
        String[] responseHeaders = {"Access-Control-Expose-Headers", "Authorization"};
        List<String> allowedExposedHeaders = Arrays.asList(responseHeaders);
        config.setExposedHeaders(allowedExposedHeaders);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        // 这个顺序很重要,设置在最前
        bean.setOrder(0);
        return bean;
    }
}

MybatisPlusConfig

主要是为mybatisplus做一些基本配置(如果要有分页查询数据,则这个配置文件必须要有)

/**
 * @Author Johnny
 * @Date 2020/4/16
 * @Version 1.0
 */
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        //设置请求的页面大于最大页操作,true调回到首页,false继续请求,默认为false
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

MySqlGenerator

代码生成器,功能强大无比,根据数据表,一键生成各种层级,各种实体

/**
 * @ClassName MySqlGenerator
 * @Description 代码生成器
 * @Author Johnny
 * @Date 2020/4/16
 * @Version 1.0
 */
@Slf4j
public class MySqlGenerator {
    public static void main(String[] args) {
        //全局策略配置
        GlobalConfig config = new GlobalConfig();
        //获得当前项目根路径d:/dev/SpringBoot/spring-boot-learning
        String projectPath = System.getProperty("user.dir");
        config.setActiveRecord(true)
                //作者注释
                .setAuthor("Johnny")
                //代码生成输出路径
                .setOutputDir(projectPath + "/src/main/java")
                //覆盖已有文件,默认false
                .setFileOverride(true)
                //是否打开输出目录窗口。默认true
                .setOpen(false)
                //开启swagger2模式
                //.setSwagger2(true)
                //开启ActiveRecord模式
                .setActiveRecord(true)
                //mapper添加restMap
                .setBaseResultMap(true)
                //mapper添加Base_Column_List
                .setBaseColumnList(true)
                //时间类型对应策略,默认time_pack
                //.setDateType(DateType.TIME_PACK)
                //相关包中的接口和类名后缀
                .setMapperName("%sMapper")
                .setServiceName("%sService")
                .setServiceImplName("%sServiceImpl");

        //数据库表配置,通过该配置,可指定需要生成哪些表或者排除哪些表
        StrategyConfig strategyConfig = new StrategyConfig();
        //是否大写命名
        strategyConfig.setCapitalMode(true)
                //是否跳过视图
                .setSkipView(true)
                //数据库表映射到实体的命名策略为驼峰式
                .setNaming(NamingStrategy.underline_to_camel)
                //生成表,可以写多个,如果不加参数,默认为所有表
                .setInclude()
                .setEntityBuilderModel(true)
                .setEntityLombokModel(true)
                .setRestControllerStyle(true)
                .setEntityTableFieldAnnotationEnable(true);

        //包名配置
        PackageConfig packageConfig = new PackageConfig();
        //父包名
        packageConfig.setParent("com.soft1851.music.admin")
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setXml("xml")
                .setEntity("entity");

        //数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL)
                .setDriverName("com.mysql.jdbc.Driver")
                .setUrl("jdbc:mysql://rm-m5ee476bu350735gjeo.mysql.rds.aliyuncs.com:3306/cloud_music?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true")
                .setUsername("root")
                .setPassword("XuNiit_#");

        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setGlobalConfig(config)
                .setStrategy(strategyConfig)
                .setDataSource(dataSourceConfig)
                .setTemplateEngine(new FreemarkerTemplateEngine())
                .setPackageInfo(packageConfig);

        autoGenerator.execute();
        log.info("=============代码生成成功================");
    }
}

RedisConfig

主要对RedisConfig缓存数据库进行一些基本配置

/**
 * @author Johnny
 * @Date: 2020/4/21 20:45
 * @Description:
 */
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        //初始化RedisCacheWrite
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        //设置默认过期时间为1天
        defaultCacheConfig.entryTtl(Duration.ofDays(1));
        //初始化RedisCacheManger
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //使用stringRedisSerializer来序列化和反序列化redis的value值
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(factory);
        return stringRedisTemplate;
    }
}

WebConfig

这个类中主要配置了拦截器,可以对部分页面进行统一拦截,当然这个只是统一的拦截,具体的拦截所要进行的操作,还在下文

/**
 * @author Johnny
 * @Date: 2020/4/21 20:58
 * @Description:
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Resource
    private LoginInterceptor loginInterceptor;

    @Resource
    private JwtInterceptor jwtInterceptor;
    /**
     * 添加拦截器配置
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截路径可自行配置多个 可用 ,分隔开
        registry.addInterceptor(loginInterceptor).addPathPatterns("/sysAdmin/login").excludePathPatterns("/**").excludePathPatterns("/static/**");
//        registry.addInterceptor(jwtInterceptor).addPathPatterns("/**").excludePathPatterns("/sysAdmin/login", "/captcha","/songList/page").excludePathPatterns("/static/**");
    }
}

controller层

controller层暂时带过,后面有时间的话把controller层常用注解汇总整理下

dto层

目前还没有将entity、vo、dto统一放到domain里,dto里主要是用户请求接口时需要传的参数,vo主要时后端返回前端展示的参数,而entity就是我们和数据库表所吻合的类。举例:我们数据库中的用户表的字段肯定不只时账号和密码,会有一些其他字段,比如加密盐、用户基本信息等等,但是用户请求登录接口时,只需要传账号和密码就可以了,此时此刻,为了防止资源的浪费,我们就定义一个dto来接收前端传来的参数。当用户进入主界面时,查看个人信息,后端并不需要将用户id、加密盐等私密信息展示到前端,此时,同样为了节省资源,我们定义vo视图对象,将前端需要的参数放进去,再传入前端。

entity层

entity层主要是将数据库中的表一对一的写好,此处是直接用代码生成器生成

exception

CustomException

定义全局异常处理,使用时直接throws CustomException就好

/**
 * @author Johnny
 * @Date: 2020/4/22 10:48
 * @Description:
 */
public class CustomException extends  RuntimeException {
    protected ResultCode resultCode;

    public CustomException(String msg, ResultCode resultCode) {
        super(msg);
        this.resultCode = resultCode;
    }

    public ResultCode getResultCode() {
        return resultCode;
    }

}

GlobalException

也是全局异常处理,但和上面略有区别,非手动throw,spring在遇到异常时,会自动来这个类里查找,如果已经有配置过的,就会按照配置要求输出内容

/**
 * @author Johnny
 * @Date: 2020/4/30 18:45
 * @Description:
 */
@ControllerAdvice
public class GlobalException {
    @ExceptionHandler(ConstraintViolationException.class)
    ResponseResult handleConstraintViolationException(ConstraintViolationException e) {
        return ResponseResult.failure(ResultCode.DATA_IS_WRONG);
    }

}

JwtException

同CustomException类格式一样,但这个侧重于处理Jwt模块的异常

filter

springboot+mybatisPlus代码层级梳理

ChannelFilter

/**
 * @author Johnny
 * @Date: 2020/4/21 20:19
 * @Description:
 */
@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
public class ChannelFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (servletRequest instanceof HttpServletRequest) {
            requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        if (requestWrapper == null) {
            filterChain.doFilter(servletRequest,servletResponse);
        }else{
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }

    @Override
    public void destroy() {
    }
}

LoginFilter

这个类主要针对用户登录进行过滤

/**
 * @author Johnny
 * @Date: 2020/4/26 10:46
 * @Description:
 */
@Slf4j
@WebFilter(urlPatterns = "/login",filterName = "LoginFilter")
public class LoginFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (servletRequest instanceof HttpServletRequest) {
            String url = ((HttpServletRequest) servletRequest).getRequestURI();
            //判断接口是否为导入接口
            if ("/resource/guide".equals(url)) {
                Part file = ((HttpServletRequest) servletRequest).getPart("file");
                log.info("文件名:" + file);
            }
            requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        if (requestWrapper == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        }else{
            filterChain.doFilter(requestWrapper, servletResponse);
        }

    }

    @Override
    public void destroy() {

    }
}

handle层

springboot+mybatisPlus代码层级梳理

GlobalExceptionHandle

定义全局异常

/**
 * @author Johnny
 * @Date: 2020/4/22 10:40
 * @Description:
 */

@RestControllerAdvice(annotations = {RestController.class, Controller.class})
@Slf4j
public class GlobalExceptionHandle {
    //自定义异常

    /**
     * JwtException
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {JwtException.class})
    @ResponseBody
    public ResponseResult sendError(JwtException exception) {
        log.error(exception.getMessage());
        return ResponseResult.failure(exception.getResultCode());
    }

    /**
     * CustomException
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {CustomException.class})
    @ResponseBody
    public ResponseResult sendError(CustomException exception) {
        log.error(exception.getMessage());
        return ResponseResult.failure(exception.getResultCode());
    }


    //系统级异常

    /**
     * InvalidClassException
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {InvalidClassException.class})
    @ResponseBody
    public ResponseResult sendError(InvalidClaimException exception) {
        log.error(exception.getMessage());
        //返回token已经过期
        return ResponseResult.failure(ResultCode.USER_TOKEN_EXPIRES);
    }

    /**
     * NullPointerException
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {NullPointerException.class})
    @ResponseBody
    public ResponseResult sendError(NullPointerException exception) {
        log.error(exception.getMessage());
        //空指针提示数据未找到
        return ResponseResult.failure(ResultCode.RESULT_CODE_DATA_NONE);
    }

    /**
     * IOException
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {IOException.class})
    @ResponseBody
    public ResponseResult sendError(IOException exception) {
        log.error(exception.getMessage());
        //io异常提示验证码出现问题
        return ResponseResult.failure(ResultCode.CAPTCHA_ERROR);
    }

GlobalResponseHandle

处理全局的响应头(当接口返回类型非ResponseResult类型时,可以自动转换)

/**
 * @author Johnny
 * @Date: 2020/4/22 10:55
 * @Description:
 */
@ControllerAdvice
public class GlobalResponseHandle implements ResponseBodyAdvice {

    /**
     * 处理响应的具体方法
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }


    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ResponseResult) {
            return body;
        }else{
            return ResponseResult.success(body);
        }
    }
}

interceptor

JwtInterceptor

/**
 * @author Johnny
 * @Date: 2020/4/23 19:41
 * @Description:
 */
@Slf4j
@Component
public class JwtInterceptor  implements HandlerInterceptor {
    @Resource
    SysRoleService roleService;

    @Resource
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        log.info("token———————————"+token);
        //认证
        if (token == null) {
            log.info("用户未登录");
            return false;
        }else{
            //登录之后
            //从请求头当中取出用户id
            String adminId = request.getHeader("id");
            log.info("用户id" + adminId);
            //在redis中检查是否存在adminId为key的数据
            if (!redisService.existsKey(adminId)) {
                log.info("redis中不存在此键");
            }else{
                String secrect = redisService.getValue(adminId, String.class);
                //从token中解析出roles字符串
                String roles = JwtTokenUtil.getRoles(token,secrect);
                //反序列化成List
                List<SysRole> sysRoles = JSONArray.parseArray(roles, SysRole.class);
                //从request中取得客户端传输的roleId
                String roleId = request.getParameter("roleId");
                log.info("roleId:"+roleId);
                boolean flag = roleService.checkRole(sysRoles,Integer.parseInt(roleId));
                if(flag){
                    return true;
                }else{
                  log.info("用户不具备此角色");
                }
            }
            return false;
        }
    }
}

LoginInterceptor

/**
 * @author Johnny
 * @Date: 2020/4/21 21:00
 * @Description:
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Resource
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle) {
        //将request包装成HttpServletWrapper类型
        RequestWrapper requestWrapper = new RequestWrapper(request);
        //取得请求的json对象
        String body = requestWrapper.getBody();
        log.info(body);
        //从redis中取得指定用户名的验证码
        JSONObject jsonObject = JSONObject.parseObject(body);
        String name = jsonObject.getString("name");
        System.out.println(name);
        String password = jsonObject.getString("password");
        System.out.println(password);
        String verifyCode = jsonObject.getString("verifyCode");
        System.out.println(verifyCode);
        LoginDto loginDto = LoginDto.builder().name(name).password(password).verifyCode(verifyCode).build();
        return true;
//        if (redisService.existsKey(loginDto.getName())) {
//            //获取redis中的验证码
//            String correctCode = redisService.getValue(name, String.class);
//            //忽略大小写,成功则放行到controller调用登录接口
//            if (correctCode.equalsIgnoreCase(verifyCode)) {
//                return true;
//            }else{
//                log.info("验证码错误");
//                return false;
//            }
//        }else{
//            log.info("验证码失效");
//            return false;
//        }
    }
}

mapper层

这个目前不做过多赘述,我会尽快整理下mybatisplus的常用语法发出来

util层

springboot+mybatisPlus代码层级梳理

ExcelConsumer

/**
 * @author Johnny
 * @Date: 2020/4/28 21:53
 * @Description:
 */
@Slf4j
public class ExcelConsumer<T>  implements Runnable {
    /**
     * 一张工作表可以容纳的最大行数
     */
    private static Integer SHEET_SIZE = 100000;

    /**
     * 导出的Vo数据类型
     */
    private Class<T> clazz;

    /**
     * 工作簿
     */
    private SXSSFWorkbook wb;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 数据缓冲区对象
     */
    private ExportDataAdapter<T> exportDataAdapter;
    /**
     * 线程同步
     */
    private CountDownLatch latch;

    /**
     * 构造方法
     *
     * @param clazz
     */
    public ExcelConsumer(Class<T> clazz, ExportDataAdapter<T> exportDataAdapter, SXSSFWorkbook wb, CountDownLatch latch, String sheetName) {
        if (clazz == null || wb == null || exportDataAdapter == null || latch == null) {
            log.error("ExcelConsumer::初始化对象参数不能为空");
            return;
        }
        this.clazz = clazz;
        this.exportDataAdapter = exportDataAdapter;
        this.wb = wb;
        this.latch = latch;
        this.sheetName = sheetName == null ? "UnNamedSheet" : sheetName;
    }

    @Override
    public void run() {
        //初始化excel导出工具类
        ExcelUtil<T> excelUtil = new ExcelUtil<>(this.clazz);
        Sheet sheet = null;
        int sheetNo = 0;
        int rowNum = 1;
        T vo;
        //生产者还在生产数据
        while (latch.getCount() > 1) {
            //生成sheetName
            if (rowNum == 1) {
                sheetNo++;
                sheet = excelUtil.createSheet(wb, sheetName.concat(Integer.toString(sheetNo)));
                excelUtil.setColumnTitle(sheet);
            }
            //获取数据
            vo = exportDataAdapter.getData();
            //往excel添加一行数据
            excelUtil.addRowData(vo, wb, sheet, rowNum);
            rowNum++;
            //准备生成下一个sheetName
            if (rowNum == SHEET_SIZE + 1) {
                rowNum = 1;
            }
        }
        //生产者不再生产数据,取剩余数据,并将数据写入excel
        Integer reminderDataSize = exportDataAdapter.getDataSize();
        T reminderData;
        if (reminderDataSize > 0) {
            for (int i = 0; i < reminderDataSize; i++) {
                reminderData = exportDataAdapter.getData();
                if (rowNum == 1) {
                    sheetNo++;
                    sheet = excelUtil.createSheet(wb, sheetName.concat(Integer.toString(sheetNo)));
                    excelUtil.setColumnTitle(sheet);
                }
                excelUtil.addRowData(reminderData, wb, sheet, rowNum);
                rowNum++;
                if (rowNum == SHEET_SIZE + 1) {
                    rowNum = 1;
                }
            }
        }
        log.info("数据导出完成");
        latch.countDown();
    }
}

ExcelUtil

/**
 * @author Johnny
 * @Date: 2020/4/28 21:47
 * @Description:
 */
public class ExcelUtil<T> {
    Class<T> clazz;

    /**
     * 表头字段列表
     */
    private List<Field> fields;

    /**
     * 数字单元格对象
     */
    private CellStyle decimalCellStyle = null;

    /**
     * 日期时间单元格对象
     */
    private CellStyle dateTimeCellStyle = null;

    /**
     * 构造方法
     *
     * @param clazz
     */
    public ExcelUtil(Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * 添加一条数据
     *
     * @param vo:需要导出的vo对象
     * @param wb:工作簿
     * @param sheet:工作表
     * @param rowNum:当前行号
     */
    public void addRowData(T vo, SXSSFWorkbook wb, Sheet sheet, int rowNum) {
        //创建一行
        Row row = sheet.createRow(rowNum);
        Field field;
        Cell cell;
        ExcelVoAttribute attr;
        int fieldSize = fields.size();
        // 遍历入参对象的所有属性
        for (int j = 0; j < fieldSize; j++) {
            // 通过反射获得需要导出的入参对象的所有属性
            field = fields.get(j);
            // 设置实体类私有属性可访问
            field.setAccessible(true);
            // 获取所有添加了注解的属性
            attr = field.getAnnotation(ExcelVoAttribute.class);
            // 给每个属性创建一个单元格
            cell = row.createCell(j);
            try {
                this.setCellValue(attr, field.get(vo), wb, cell);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 根据注解类判断字段类型设置excel单元格数据格式方法
     *
     * @param attr
     * @param valueObject
     * @param workbook
     * @param cell
     */
    private void setCellValue(ExcelVoAttribute attr, Object valueObject, SXSSFWorkbook workbook, Cell cell) {
        String returnValue;
        if (attr.isNumber()) {
            cell.setCellStyle(getCellStyle(attr, workbook));
            returnValue = valueObject == null || "".equals(valueObject) ? "0" : valueObject.toString();
            BigDecimal num = new BigDecimal(returnValue);
            cell.setCellValue(num.doubleValue());
        } else if (attr.isDateTime()) {
            cell.setCellStyle(getCellStyle(attr, workbook));
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            returnValue = df.format((TemporalAccessor) valueObject);
            cell.setCellValue(returnValue);
        } else {
            returnValue = valueObject == null ? "" : valueObject.toString();
            cell.setCellValue(returnValue);
        }
    }

    /**
     * 根据注解类判断字段类型,返回excel单元格数据格式方法
     *
     * @param attr
     * @param workbook
     * @return CellStyle
     */
    private CellStyle getCellStyle(ExcelVoAttribute attr, SXSSFWorkbook workbook) {
        if (attr.isNumber()) {
            if (decimalCellStyle == null) {
                decimalCellStyle = workbook.createCellStyle();
                //此处设置数字单元格格式
                DataFormat df = workbook.createDataFormat();
                //千分位,保留1位小数
                decimalCellStyle.setDataFormat(df.getFormat("#,##0.0"));
            }
            return decimalCellStyle;
        }
        if (attr.isDateTime()) {
            if (dateTimeCellStyle == null) {
                dateTimeCellStyle = workbook.createCellStyle();
                //此处设置日期时间单元格格式
                DataFormat df = workbook.createDataFormat();
                dateTimeCellStyle.setDataFormat(df.getFormat("yyyy-MM-dd HH:mm:ss"));
            }
            return dateTimeCellStyle;
        }
        return null;
    }

    /**
     * 创建工作页Sheet
     *
     * @param wb
     * @param sheetName
     * @return Sheet
     */
    public Sheet createSheet(SXSSFWorkbook wb, String sheetName) {

        return wb.createSheet(sheetName);
    }

    /**
     * 设置excel列头及格式
     *
     * @param sheet
     */
    public void setColumnTitle(Sheet sheet) {
        if (fields == null) {
            this.fields = this.getSortFields();
        }
        Row row;
        Cell cell;
        ExcelVoAttribute attr;
        Field field;
        int fieldSize = fields.size();
        row = sheet.createRow(0);
        for (int i = 0; i < fieldSize; i++) {
            field = fields.get(i);
            attr = field.getAnnotation(ExcelVoAttribute.class);
            cell = CellUtil.createCell(row, i, attr.name());
            // 设置列宽,根据相应的字段名的长度等比
            sheet.setColumnWidth(i, attr.name().getBytes().length * 400);
        }
    }

    /**
     * 获取输出对象字段列表,并根据注解进行字段排序
     *
     * @return
     */
    private List<Field> getSortFields() {
        List<Field> fields = Arrays.stream(clazz.getDeclaredFields()).filter(x -> x.isAnnotationPresent(ExcelVoAttribute.class)).collect(Collectors.toList());
        List<Field> sortList = new ArrayList<>(fields);
        //排序
        for (Field field : fields) {
            ExcelVoAttribute excelVoAttribute = field.getAnnotation(ExcelVoAttribute.class);
            int sortNo = excelVoAttribute.column();
            sortList.set(sortNo, field);
        }
        return sortList;
    }
}

ExcelUtils

/**
 * @author Johnny
 * @Date: 2020/4/25 22:56
 * @Description:
 */
@Slf4j
public class ExcelUtils {
    /**
     * 导出歌曲
     * @param response
     * @param list
     * @param map
     * @param title
     */
    public static void exportExcel(HttpServletResponse response, List list, Map<String, String> map, String title) {
        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义标题别名
        Set<Map.Entry<String, String>> entries = map.entrySet();
        //迭代器遍历数据
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            //自定义表头
            writer.addHeaderAlias(next.getKey(), next.getValue());
        }
        //合并单元格后的标题行,使用默认标题样式
        writer.merge(map.size() - 1, title);
        //一次性写出内容,使用默认样式,强制输出标题
        writer.write(list, true);
        //out为outputStream,需要写出到的目标流
        try {
            writer.flush(response.getOutputStream(), true);
        } catch (IOException e) {
            log.info("歌单导出异常");
            e.printStackTrace();
        }
        writer.close();
    }

    /**
     * 导入歌曲
     * @param file
     * @return
     */
    public static List<Song> importExcel(File file) {
        List<Song> songs = new ArrayList<>();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //通过getReader方法确定读取那个sheet中的数据
        ExcelReader reader = ExcelUtil.getReader(inputStream, "sheet1");
        //回去总行数
        List<List<Object>> read = reader.read(1, reader.getRowCount());
        for (List<Object> objects : read) {
            //对每行数据取出构建一个song对象
            Song song = Song.builder()
                    .songName(objects.get(0).toString())
                    .songId(UUID.randomUUID().toString().replace("-", ""))
                    .sortId("0")
                    .singer(objects.get(1).toString())
                    .duration(objects.get(2).toString())
                    .thumbnail(objects.get(3).toString())
                    .url(objects.get(4).toString())
                    .lyric(objects.get(5).toString())
                    .commentCount(0)
                    .playCount(0)
                    .deleteFlag("0")
                    .updateTime(LocalDateTime.now())
                    .createTime(LocalDateTime.now())
                    .build();
            songs.add(song);
        }
        return songs;

    }

}

ExportDataAdapter

/**
 * @author Johnny
 * @Date: 2020/4/28 21:53
 * @Description:
 */
public class ExportDataAdapter<T>{
    /**
     * 默认队列大小
     */
    private static Integer DEFAULT_SIZE = 1000;

    private BlockingQueue<T> resourceQueue = null;

    public ExportDataAdapter() {
        this.resourceQueue = new LinkedBlockingQueue<T>(DEFAULT_SIZE);
    }


    /**
     * 添加数据
     *
     * @param data
     */
    public void addData(T data) {
        try {
            resourceQueue.put(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取剩余数据数量
     *
     * @return
     */
    public Integer getDataSize() {
        return resourceQueue.size();
    }

    /**
     * 从队列中获取数据
     *
     * @return
     */
    public T getData() {
        try {
            return resourceQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

FileUtil

/**
 * @author Johnny
 * @Date: 2020/4/26 10:41
 * @Description:
 */
public class FileUtil {
    public static File fileConversion(MultipartFile file) {
        int n;
        File file1 = new File(file.getOriginalFilename());
        try {
            InputStream in = file.getInputStream();
            OutputStream os = new FileOutputStream(file1);
            byte[] bytes = new byte[4096];
            while ((n=in.read(bytes,0,4096))!=-1){
                os.write(bytes,0,n);
                File file2 = new File(file1.toURI());
                file2.delete();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file1;
    }
}

JwtTokenUtil

/**
 * @Author Johnny
 * @Date 2020/4/15
 * @Version 1.0
 */
@Slf4j
public class JwtTokenUtil {
    /**
     * 加密
     *
     * @param userId
     * @param expiresAt
     * @return String
     */
    public static String getToken(final String userId, String userRole,final String secrect,Date expiresAt) {
        String token = null;
        try {
            token = JWT.create()
                    .withIssuer("auth0")
                    .withClaim("userId", userId)
                    .withClaim("userRole", userRole)
                    .withExpiresAt(expiresAt)
                    // 使用了HMAC256加密算法, mySecret是用来加密数字签名的密钥
                    .sign(Algorithm.HMAC256(secrect));
        } catch (UnsupportedEncodingException e) {
            log.error("不支持的编码格式");
        }
        return token;
    }

    /**
     * 解密
     * @param token
     * @return DecodedJWT
     */
    public static DecodedJWT deToken(final String token,final String secrect) {
        DecodedJWT jwt;
        JWTVerifier verifier = null;
        try {
            verifier = JWT.require(Algorithm.HMAC256(secrect))
                    .withIssuer("auth0")
                    .build();
        } catch (UnsupportedEncodingException e) {
            log.error("不支持的编码格式");
        }
        assert verifier != null;
        jwt = verifier.verify(token);
        return jwt;
    }

    /**
     * 获取userId
     *
     * @param token
     * @return String
     */
    public static String getUserId(String token,String secrect) {
        return deToken(token,secrect).getClaim("userId").asString();
    }

    /**
     * 获取role
     *
     * @param token
     * @return String
     */
    public static String getRoles(String token,String secrect) {
        return deToken(token,secrect).getClaim("userRole").asString();
    }

    /**
     * 验证是否过期
     *
     * @param token
     * @return boolean
     */
    public static boolean isExpiration(String token,String secrect) {
        return deToken(token,secrect).getExpiresAt().before(new Date());
    }

    public static void main(String[] args) {
//        String token = getToken("2000100193", new Date(System.currentTimeMillis() + 10L * 1000L));
//        System.out.println(token);
//        while (true) {
//            boolean flag = isExpiration(token);
//            System.out.println(flag);
//            if (flag) {
//                System.out.println("token已失效");
//                break;
//            }
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
//        String token = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCIsImV4cCI6MTU4NzY3MTQ3OCwidXNlcklkIjoiREUzNUQ3Q0MwNUFGOTZBMjFEN0FERkM4NjUxRTY2MTQifQ.uT2W3QcE3744WNN3inEKT8lUVJs6xAC7TodDCaWkcyM";
//        System.out.println(deToken(token).getClaim("userId").asString());
    }

}

Md5Util

/**
 * @author Johnny
 */
public class Md5Util {
    /**
     * @param pwd     需要加密的字符串
     * @param isUpper 字母大小写(false为默认小写,true为大写)
     * @param bit     加密的位数(16,32,64)
     * @return String
     */
    public static String getMd5(String pwd, boolean isUpper, Integer bit) {
        String md5 = "";
        try {
            // 创建加密对象
            MessageDigest md = MessageDigest.getInstance("md5");
            if (bit == 64) {
                Base64.Encoder encoder = Base64.getEncoder();
                md5 = encoder.encodeToString(md.digest(pwd.getBytes(StandardCharsets.UTF_8)));
            } else {
                // 计算MD5函数
                md.update(pwd.getBytes());
                byte b[] = md.digest();
                int i;
                StringBuilder sb = new StringBuilder();
                for (byte value : b) {
                    i = value;
                    if (i < 0) {
                        i += 256;
                    }
                    if (i < 16) {
                        sb.append("0");
                    }
                    sb.append(Integer.toHexString(i));
                }
                md5 = sb.toString();
                if (bit == 16) {
                    //截取32位md5为16位
                    md5 = md5.substring(8, 24).toString();
                    if (isUpper) {
                        md5 = md5.toUpperCase();
                    }
                    return md5;
                }
            }
            //转换成大写
            if (isUpper) {
                md5 = md5.toUpperCase();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("md5加密抛出异常!");
        }

        return md5;
    }

    public static void main(String[] args) {
        String a = "123456";
        String md5a = getMd5(a, true, 32);
        System.out.println(md5a);
        System.out.println(md5a.length());
    }

ThreadPool

/**
 * @author Johnny
 * @Date: 2020/4/28 21:55
 * @Description:
 */
public class ThreadPool {
    /**
     * 异步线程
     */
    private final static ThreadPoolExecutor executor =
            new ThreadPoolExecutor(20, 100, 10, TimeUnit.MINUTES,
                    new ArrayBlockingQueue<>(2000),
                    r -> new Thread(r, "excelExportThread"),
                    new ThreadPoolExecutor.AbortPolicy());

    public static ThreadPoolExecutor getExecutor() {
        return executor;
    }
}

TreeBuilder

/**
 * @author Johnny
 * @Date: 2020/4/23 18:46
 * @Description:
 */
public class TreeBuilder {

    /**
     * 两层循环实现建树
     *
     * @param treeNodes 传入的树节点列表
     * @return
     */
    public static List<TreeNode> buildTreeByLoop(List<TreeNode> treeNodes) {

        List<TreeNode> trees = new ArrayList<>();
        for (TreeNode treeNode : treeNodes) {
            if (treeNode.getParentId() == 0) {
                trees.add(treeNode);
            }
            for (TreeNode it : treeNodes) {
                if (it.getParentId().equals(treeNode.getId())) {
                    if (treeNode.getSubMenus() == null) {
                        treeNode.setSubMenus(new ArrayList<>());
                    }
                    treeNode.getSubMenus().add(it);
                }
            }
        }
        return trees;
    }

    /**
     * 使用递归方法建树
     *
     * @param treeNodes
     * @return
     */
    public static List<TreeNode> buildTreeByRecursive(List<TreeNode> treeNodes) {
        List<TreeNode> trees = new ArrayList<>();
        for (TreeNode treeNode : treeNodes) {
            if (treeNode.getParentId() == 0) {
                trees.add(findChildren(treeNode, treeNodes));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    public static TreeNode findChildren(TreeNode treeNode, List<TreeNode> treeNodes) {
        for (TreeNode it : treeNodes) {
            if (treeNode.getId().equals(it.getParentId())) {
                if (treeNode.getSubMenus() == null) {
                    treeNode.setSubMenus(new ArrayList<>());
                }
                treeNode.getSubMenus().add(findChildren(it, treeNodes));
            }
        }
        return treeNode;
    }
}

TreeNode

/**
 * @author Johnny
 * @Date: 2020/4/23 18:46
 * @Description:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TreeNode {
    private Integer id;

    private Integer parentId;

    private Integer type;

    private String title;

    private String icon;

    private String path;

    private Integer sort;

    private List<TreeNode> subMenus;

    public TreeNode(Integer id, Integer parentId, Integer type,String title, String icon, String path, Integer sort) {
        this.id = id;
        this.parentId = parentId;
        this.type = type;
        this.title = title;
        this.icon = icon;
        this.path = path;
        this.sort = sort;
    }
}

更多内容请关注 Johnny博客

原文 

https://segmentfault.com/a/1190000022524793

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » springboot+mybatisPlus代码层级梳理

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址