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();
}
}
配置完成!后面开始用户接口、权限校验实现!