微服务实战(三):代码生成和公共模块

MyBatisPlusGenerator

1.创建代码生成工具类

public class MyBatisPlusGenerator {
    public static void main(String[] args) {
        // 1.全局配置
        GlobalConfig config = new GlobalConfig();
        // 作者
        config.setAuthor("lcz")
                // 生成路径,最好使用绝对路径,window路径是不一样的
                .setOutputDir("C:\\Users\\lcz\\Desktop\\java")
                // 文件覆盖
                .setFileOverride(true)
                // 主键策略
                .setIdType(IdType.AUTO)
                .setDateType(DateType.ONLY_DATE)
                // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
                .setServiceName("%sService")
                // 实体类结尾名称
                .setEntityName("%sDO")
                // 生成基本的resultMap
                .setBaseResultMap(true)
                // 不使用AR模式
                .setActiveRecord(false)
                // 生成基本的SQL片段
                .setBaseColumnList(true);

        // 2.数据源配置
        DataSourceConfig dsConfig = new DataSourceConfig();
        // 设置数据库类型
        dsConfig.setDbType(DbType.POSTGRE_SQL)
                .setDriverName("org.postgresql.Driver")
                .setUrl("jdbc:postgresql://localhost:5432/user-center")
                .setUsername("postgres")
                .setPassword("123456");

        // 3.策略配置globalConfiguration中
        StrategyConfig stConfig = new StrategyConfig();
        // 全局大写命名
        stConfig.setCapitalMode(true)
                // 数据库表映射到实体的命名策略
                .setNaming(NamingStrategy.underline_to_camel)
                // 使用lombok
                .setEntityLombokModel(true)
                // 使用rest controller注解
                .setRestControllerStyle(true)
                // 生成的表, 支持多表一起生成,以数组形式填写
                .setInclude("sys_user", "sys_role", "sys_permission", "sys_menu");

        // 4.包名策略配置
        PackageConfig pkConfig = new PackageConfig();
        pkConfig.setParent("net.work")
                .setMapper("mapper")
                .setService("service")
                .setController("controller")
                .setEntity("model")
                .setXml("mapper");

        // 5.整合配置
        AutoGenerator ag = new AutoGenerator();
        ag.setGlobalConfig(config)
                .setDataSource(dsConfig)
                .setStrategy(stConfig)
                .setPackageInfo(pkConfig);

        // 6.执行操作
        ag.execute();
        System.out.println("======= Done 相关代码生成完毕  ========");
    }
}

执行生成代码,如图:

2.拷贝代码,将model和mapper中的文件拷贝到项目中

 看一下生成的DO


@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_user")
public class SysUserDO implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String pwd;

    /**
     * 头像
     */
    private String headImg;

    private Date createTime;

    /**
     * 盐,用于个人敏感信息处理
     */
    private String secret;

}

公共模块

1.完成common-center

2.在util包中创建统一的数据返回结构


@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonData {
    /**
     * 状态码 0 表示成功,1表示处理中,-1表示失败
     */
    private Integer code;
    private Object data;
    private String msg;

    /**
     * 序列化和反序列化对象
     *
     * @param typeReference
     * @param <T>
     * @return
     */
    public <T> T getData(TypeReference<T> typeReference) {
        return JSONObject.parseObject(JSONObject.toJSONString(data), typeReference);
    }

    /**
     * 成功,传入数据
     */
    public static JsonData buildSuccess() {
        return new JsonData(0, null, null);
    }

    /**
     * 成功,传入数据
     */
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }

    /**
     * 失败,传入描述信息
     */
    public static JsonData buildError(String msg) {
        return new JsonData(-1, null, msg);
    }

    /**
     * 自定义状态码和错误信息
     */
    public static JsonData buildCodeAndMsg(int code, String msg) {
        return new JsonData(code, null, msg);
    }

    /**
     * 传入枚举,返回信息
     */
    public static JsonData buildResult(BizCodeEnum codeEnum) {
        return JsonData.buildCodeAndMsg(codeEnum.getCode(), codeEnum.getMsg());
    }
}

 3.enums包中创建枚举类

package net.work.enums;

/**
 * 状态码定义约束,共6位数
 */
public enum BizCodeEnum {

    /**
     * 验证码
     */
    CODE_CAPTCHA_ERROR(240001, "验证码错误"),
    CODE_CAPTCHA_NOT_EXIST(240002, "验证码不存在"),

    /**
     * 账号
     */
    ACCOUNT_REPEAT(250001, "账号已经存在"),
    ACCOUNT_UNREGISTER(250002, "账号不存在"),
    ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"),
    ACCOUNT_UN_LOGIN(250004, "账号未登录"),
    ACCOUNT_OLD_PWD_ERROR(250005, "账号原密码错误"),
    ACCOUNT_REJECT_DELETE(250006, "系统账号,禁止删除"),
    ACCOUNT_REJECT_UPDATE(250007, "系统账号,禁止修改"),
    ACCOUNT_REJECT_RESET(250008, "系统账号,禁止重置密码"),
    ACCOUNT_PWD_SAME(250009, "新旧密码相同"),
    ACCOUNT_NO_PERMIT(250010, "无操作权限"),
    ACCOUNT_PWD_LESS(250011, "密码不能少于6位"),

    /**
     * 文件相关
     */
    FILE_UPLOAD_USER_IMG_FAIL(600101, "用户头像文件上传失败"),

    /**
     * 邮箱相关
     */
    MAIL_FORMAT_IS_FALSE(700101, "邮箱格式不正确"),

    /**
     * 系统相关
     */
    REQUEST_PARAMS_ERROR(900001, "请求参数异常");

    int code;
    String msg;

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

}

4.exception包中创建全局异常捕捉

/**
 * 全局异常处理
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class BizException extends RuntimeException {
    private Integer code;
    private String msg;

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

    public BizException(BizCodeEnum bizCodeEnum) {
        super(bizCodeEnum.getMsg());
        this.code = bizCodeEnum.getCode();
        this.msg = bizCodeEnum.getMsg();
    }
}

5.handler中创建自定义异常处理器

/**
 * 自定义异常处理器
 */
@ControllerAdvice
@Slf4j
public class ExceptionHandle {

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public JsonData Handle(Exception e) {
        if (e instanceof BizException) {
            BizException bizException = (BizException) e;
            log.error("[业务异常]{}", e);
            return JsonData.buildError(bizException.getMsg());
        } else {
            log.error("[系统异常]{}", e);
            return JsonData.buildError("全局异常,未知错误");
        }
    }
}

 6.constance包中创建全局常量

public class CacheConst {

    public static final String TOKEN_KEY = "access_token:";

    public static final String PERMIT_KEY = "permit:user:";

}

7.util包中创建其他工具类

(1)CommonUtil:一些公共方法


@Slf4j
public class CommonUtil {

    public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static final SimpleDateFormat day = new SimpleDateFormat("yyyyMMdd");
    /**
     * 邮箱正则
     */
    private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
    /**
     * 手机号正则
     */
    private static final Pattern PHONE_PATTERN = Pattern.compile("^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$");

    /**
     * 获取ip
     *
     * @param request
     * @return
     */
    private static String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if ("127.0.0.1".equals(ipAddress)) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    assert inet != null;
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }

    public static String getCaptchaKey(HttpServletRequest request) {
        return "user-center:captcha:" + MD5(getIpAddr(request) + request.getHeader("User-Agent"));
    }

    /**
     * 获取MD5
     *
     * @param data
     * @return
     */
    private static String MD5(String data) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
            }

            return sb.toString().toUpperCase();
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 获取当前时间戳
     *
     * @return
     */
    public static long getCurrentTimeMillis() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static Date getCurrentDate() {
        return new Date();
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static Date getCurrentDate(long time) {
        return new Date(time);
    }

    /**
     * 根据格式转换 仅能转换日期
     *
     * @return
     */
    public static String getData(String format) {
        return LocalDate.now().format(DateTimeFormatter.ofPattern(format));
    }

    /**
     * 根据格式转换 仅能转换时间
     *
     * @return
     */
    public static String convertDataTime(LocalDateTime localDateTime, String format) {
       return localDateTime.format(DateTimeFormatter.ofPattern(format));
    }


    /**
     * 手机号格式校验
     *
     * @return
     */
    public static Boolean validatePhone(String phone) {
        if (null == phone || "".equals(phone)) {
            return false;
        }
        Matcher m = PHONE_PATTERN.matcher(phone);
        return m.matches();
    }

    /**
     * 生成uuid
     *
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

    /**
     * 生成指定长度随机字母和数字
     *
     * @param length
     * @return
     */
    private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    public static String getStringNumRandom(int length) {
        Random random = new Random();
        StringBuilder saltString = new StringBuilder(length);
        for (int i = 1; i <= length; ++i) {
            saltString.append(ALL_CHAR_NUM.charAt(random.nextInt(ALL_CHAR_NUM.length())));
        }
        return saltString.toString();
    }

    /**
     * 响应json数据给前端
     *
     * @param response
     * @param obj
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj) {
        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json; charset=utf-8");

        try (PrintWriter writer = response.getWriter()) {
            writer.print(objectMapper.writeValueAsString(obj));

            response.flushBuffer();
        } catch (IOException e) {
            log.warn("响应json数据给前端异常:{}", e);
        }
    }

    /**
     * 校验是否存在page和size参数
     *
     * @param params
     * @return
     */
    public static boolean validatePageSize(Map<String, Object> params) {
        return !params.keySet().containsAll(Set.of("page", "size"));
    }

    /**
     * 校验邮箱格式是否正确
     *
     * @param email
     * @return
     */
    public static boolean isEmail(String email) {
        if (null == email || "".equals(email)) {
            return false;
        }
        Matcher m = MAIL_PATTERN.matcher(email);
        return m.matches();
    }

}

(2)JWTUtil:JWT工具

public class JWTUtil {
    // 主题
    private static final String SUBJECT = "fis";
    // token 过期时间,正常是7天
    private static final long EXPIRE = 1000L * 60 * 60 * 24 * 7;
    // 加密的密钥
    private static final String SECRET = "fis-666-123";
    // 令牌前缀
    private static final String TOKEN_PREFIX = "cloud-fast-services";

    /**
     * 根据用户信息,生成令牌
     *
     * @param user
     * @return
     */
    public static String geneJsonWebToken(LoginUser user, String pwd) {
        Integer userId = user.getId();
        String token = Jwts.builder().setSubject(SUBJECT)
                .claim("id", userId)
                .claim("username", user.getUsername())
                .claim("nickname", user.getNickname())
                .claim("pwd", pwd)
                .setIssuedAt(CommonUtil.getCurrentDate())
                .setExpiration(CommonUtil.getCurrentDate(CommonUtil.getCurrentTimeMillis() + EXPIRE))
                .signWith(SignatureAlgorithm.HS256, SECRET).compact();
        token = TOKEN_PREFIX + token;

        return token;
    }

    /**
     * 校验token
     *
     * @param token
     * @return
     */
    public static Claims checkJWT(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
        } catch (Exception e) {
            log.error("jwt token 解密失败");
            return null;
        }
    }
}

(3)PageResult:分页

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PageResult<T> {

    private long totalRecord;

    private long totalPage;

    private T currentData;

}

(4)ConvertBean:对象转换

public class ConvertBean {
    public static <T> T getProcess(Object source, Class<T> tClass) {
        T t;
        try {
            t = tClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            return null;
        }
        BeanUtils.copyProperties(source, t);
        return t;
    }
}

8.model包中创建用户对象 LoginUser

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser {
    private Integer id;

    private String username;

    private String nickname;

    private String headImg;
}

9.config包创建

(1)MybatisPlusPageConfig:分页配置

/**
 * MybatisPlus分页插件配置
 */
@Configuration
public class MybatisPlusPageConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
        return interceptor;
    }
}

(2)RedisSerializerConfig:Redis序列化配置


@Configuration
public class RedisSerializerConfig {

    /**
     * 自定义序列化
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        // json序列化器
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置key和value的序列化规则
        RedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        // 设置支持事物
        // redisTemplate.setEnableTransactionSupport(true);

        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

(3)SwaggerConfig:Swagger配置


@Component
@EnableOpenApi
@Data
public class SwaggerConfig implements WebMvcConfigurer {

    @Bean
    public Docket webApiDoc() {
        return new Docket(DocumentationType.OAS_30)
                .groupName("用户端接口文档")
                .pathMapping("/")
                // 定义是否开启swagger,false为关闭,可以通过变量控制,线上关闭
                .enable(true)
                // 配置api文档元信息
                .apiInfo(apiInfo())
                // 选择哪些接口作为swagger的doc发布
                .select()
                .apis(RequestHandlerSelectors.basePackage("net.work"))
                // 正则匹配请求路径,并分配至当前分组
                .paths(PathSelectors.ant("/api/**"))
                // 正则匹配请求路径,并分配至当前分组,当前所有接口
                .paths(PathSelectors.any())
                .build()
                // 新版swagger3.0配置
                .globalRequestParameters(getGlobalRequestParameters())
                .globalResponses(HttpMethod.GET, getGlobalResponseMessage())
                .globalResponses(HttpMethod.POST, getGlobalResponseMessage());
    }

    /**
     * 生成全局通用参数, 支持配置多个响应参数
     */
    private List<RequestParameter> getGlobalRequestParameters() {
        List<RequestParameter> parameters = new ArrayList<>();
        parameters.add(new RequestParameterBuilder()
                .name("token")
                .description("登录令牌")
                .in(ParameterType.HEADER)
                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                .required(false)
                .build());

        return parameters;
    }

    /**
     * 生成通用响应信息
     */
    private List<Response> getGlobalResponseMessage() {
        List<Response> responseList = new ArrayList<>();
        responseList.add(new ResponseBuilder().code("400").description("请求错误,根据code和msg检查").build());
        return responseList;
    }

    /**
     * 通用拦截器排除swagger设置,所有拦截器都会自动加swagger相关的资源排除信息
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        try {
            Field registrationsField = FieldUtils.getField(InterceptorRegistry.class, "registrations", true);
            List<InterceptorRegistration> registrations = (List<InterceptorRegistration>) ReflectionUtils.getField(registrationsField, registry);
            if (registrations != null) {
                for (InterceptorRegistration interceptorRegistration : registrations) {
                    interceptorRegistration
                            .excludePathPatterns("/swagger**/**")
                            .excludePathPatterns("/webjars/**")
                            .excludePathPatterns("/v3/**")
                            .excludePathPatterns("/doc.html");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("聚合集成平台")
                .description("微服务接口文档")
                .contact(new Contact("lcz", "https://lczwork.net", "1591844498@qq.com"))
                .version("12")
                .build();
    }
}

10.登录拦截器 LoginInterceptor


/**
 * 用户登录拦截器
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();

    private final StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        try {
            String accessToken = request.getHeader("token");
            if (accessToken == null) {
                accessToken = request.getParameter("token");
            }

            if (StringUtils.isNotBlank(accessToken)) {
                Claims claims = JWTUtil.checkJWT(accessToken);
                if (claims == null) {
                    // 告诉前端登录过期或被篡改,重新登录
                    CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UN_LOGIN));
                    return false;
                }

                Integer userId = Integer.valueOf(claims.get("id").toString());
                String username = (String) claims.get("username");
                String nickname = (String) claims.get("nickname");

                // 校验token与服务端存储的是否一致
                String cacheToken = stringRedisTemplate.opsForValue().get(TOKEN_KEY + userId);
                if (StringUtils.isNotBlank(cacheToken) && cacheToken.equals(accessToken)) {
                    // 用户信息传递
                    LoginUser loginUser = LoginUser.builder().id(userId).username(username).nickname(nickname).build();
                    threadLocal.set(loginUser);

                    return true;
                } else {
                    CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UN_LOGIN));
                    return false;
                }
            }
        } catch (Exception e) {
            log.error("拦截器错误:{}", e);
        }

        CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UN_LOGIN));
        return false;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        threadLocal.remove();
    }
}

配置完成!后面开始用户接口、权限校验实现!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值