first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Ss-Common模块
|
||||
|
||||
主要放常用的工具类
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- 此处修改,继承自父模块 -->
|
||||
<parent>
|
||||
<groupId>com.zxdmy.excite</groupId>
|
||||
<artifactId>shensong</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>ss-common</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<!-- 以下两项可选 -->
|
||||
<name>ss-common</name>
|
||||
<description>
|
||||
此模块主要继承公共的工具类。
|
||||
</description>
|
||||
|
||||
<!-- 放置只有此模块用到的依赖项,其他模块用到的是本模块实现的工具类或服务 -->
|
||||
<dependencies>
|
||||
|
||||
<!-- Redis https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>1.28.0</version>
|
||||
</dependency>
|
||||
<!-- 对象池,使用redis时必须引入 -->
|
||||
<!-- 使用对象池,每次创建的对象并不实际销毁,而是缓存在对象池中,下次使用的时候,不用再重新创建,直接从对象池的缓存中取即可-->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 图片验证码 https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
|
||||
<dependency>
|
||||
<groupId>com.github.penggle</groupId>
|
||||
<artifactId>kaptcha</artifactId>
|
||||
<version>2.3.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.zxdmy.excite.common.base;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 通用的控制类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-05 0005 22:58
|
||||
*/
|
||||
public class BaseController {
|
||||
|
||||
@Resource
|
||||
protected HttpServletRequest request;
|
||||
@Resource
|
||||
protected HttpServletResponse response;
|
||||
|
||||
@ModelAttribute
|
||||
public void initReqAndRes(HttpServletRequest request, HttpServletResponse response) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息(JSON code:200;浏览器Status Code:200)
|
||||
* 相关说明:
|
||||
* --- JSON code:指的是返回的JSON数据中的code字段的值
|
||||
* --- Status Code:指的是前端按下F12后,浏览器控制台中请求相关API后,返回给浏览器的状态值。
|
||||
* 如果需要修改Status Code,则需要传入[org.springframework.http.HttpStatus]类中的各个参数,如:HttpStatus.OK.value()。
|
||||
*
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(String message) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(HttpStatus.OK.value(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息(JSON code:自定义;浏览器Status Code:200)
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(int code, String message) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功实体(JSON code:200;浏览器Status Code:200)
|
||||
*
|
||||
* @param data 实体
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(Object data) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(HttpStatus.OK.value(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功实体(JSON code:自定义;浏览器Status Code:200)
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param data 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(int code, Object data) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(code, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息(JSON code:自定义;浏览器Status Code:同返回码)
|
||||
*
|
||||
* @param httpStatus 状态码
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(HttpStatus httpStatus, String message) {
|
||||
response.setStatus(httpStatus.value());
|
||||
return new BaseResult(httpStatus.value(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据(JSON code:200;浏览器Status Code:200)
|
||||
*
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(String message, Object data) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(HttpStatus.OK.value(), message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据(JSON code:自定义;浏览器Status Code:200)
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(int code, String message, Object data) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(code, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据(JSON code:自定义;浏览器Status Code:同返回码)
|
||||
*
|
||||
* @param httpStatus 状态码
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(HttpStatus httpStatus, String message, Object data) {
|
||||
response.setStatus(httpStatus.value());
|
||||
return new BaseResult(httpStatus.value(), message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据+总数,用于分页显示(JSON code:200;浏览器Status Code:200)
|
||||
*
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @param count 记录数
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(String message, Object data, int count) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(HttpStatus.OK.value(), message, data).put("count", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据+总数,用于分页显示(JSON code:自定义;浏览器Status Code:200)
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @param count 记录数
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(int code, String message, Object data, int count) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(code, message, data).put("count", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求成功消息+数据+总数,用于分页显示(JSON code:自定义;浏览器Status Code:同返回码)
|
||||
*
|
||||
* @param httpStatus 状态码
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @param count 记录数
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult success(HttpStatus httpStatus, String message, Object data, int count) {
|
||||
response.setStatus(httpStatus.value());
|
||||
return new BaseResult(httpStatus.value(), message, data).put("count", count);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 请求失败消息(JSON code:400;浏览器Status Code:200)
|
||||
*
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult error(String message) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(HttpStatus.BAD_REQUEST.value(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求失败消息(JSON code:自定义;浏览器Status Code:200)
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult error(int code, String message) {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
return new BaseResult(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求失败消息(JSON code:自定义;浏览器Status Code:同返回码)
|
||||
*
|
||||
* @param httpStatus 状态码
|
||||
* @param message 消息
|
||||
* @return 结果类
|
||||
*/
|
||||
public BaseResult error(HttpStatus httpStatus, String message) {
|
||||
response.setStatus(httpStatus.value());
|
||||
return new BaseResult(httpStatus.value(), message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.zxdmy.excite.common.base;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 通用的返回结果类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-05 0005 23:01
|
||||
*/
|
||||
public class BaseResult extends HashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 几个静态常量
|
||||
*/
|
||||
private static final Integer MAX_SUCCESS_VALUE = 299;
|
||||
private static final String SUCCESS = "success";
|
||||
private static final String CODE = "code";
|
||||
private static final String MSG = "msg";
|
||||
private static final String DATA = "data";
|
||||
private static final String COUNT = "count";
|
||||
private static final String PAGE = "page";
|
||||
private static final String SIZE = "size";
|
||||
|
||||
/**
|
||||
* 初始化 BaseResult 对象,其消息为空。
|
||||
*/
|
||||
public BaseResult() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装结果类,消息。
|
||||
*
|
||||
* @param code 状态代码
|
||||
* @param msg 消息内容
|
||||
*/
|
||||
public BaseResult(int code, String msg) {
|
||||
if (code <= MAX_SUCCESS_VALUE) {
|
||||
super.put(SUCCESS, true);
|
||||
} else {
|
||||
super.put(SUCCESS, false);
|
||||
}
|
||||
super.put(CODE, code);
|
||||
super.put(MSG, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装结果类,实体。
|
||||
*
|
||||
* @param code 状态代码
|
||||
* @param data 消息实体
|
||||
*/
|
||||
public BaseResult(int code, Object data) {
|
||||
if (code <= MAX_SUCCESS_VALUE) {
|
||||
super.put(SUCCESS, true);
|
||||
} else {
|
||||
super.put(SUCCESS, false);
|
||||
}
|
||||
super.put(CODE, code);
|
||||
if (null != data) {
|
||||
super.put(DATA, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 封装结果类,消息+实体。
|
||||
*
|
||||
* @param code 状态类型
|
||||
* @param msg 消息内容
|
||||
* @param data 数据对象
|
||||
*/
|
||||
public BaseResult(int code, String msg, Object data) {
|
||||
if (code <= MAX_SUCCESS_VALUE) {
|
||||
super.put(SUCCESS, true);
|
||||
} else {
|
||||
super.put(SUCCESS, false);
|
||||
}
|
||||
super.put(CODE, code);
|
||||
super.put(MSG, msg);
|
||||
if (null != data) {
|
||||
super.put(DATA, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 以上三类是默认的类型。如果还有其他参数要返回,使用.put(key,value)的链式调用方式追加。
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值/任意数据对象
|
||||
* @return 链式调用
|
||||
*/
|
||||
@Override
|
||||
public BaseResult put(String key, Object value) {
|
||||
super.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zxdmy.excite.common.base;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* 全局请求错误返回结果控制类
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-11 0011 23:27
|
||||
*/
|
||||
@Controller
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/error")
|
||||
public class ErrorController extends BaseController {
|
||||
|
||||
/**
|
||||
* 请求资源不存在返回结果
|
||||
*
|
||||
* @return 错误信息
|
||||
*/
|
||||
@GetMapping(value = "/404")
|
||||
public String error404() {
|
||||
// return error(HttpStatus.NOT_FOUND, "请求的资源不存在");
|
||||
return "error/404";
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器错误返回结果
|
||||
*
|
||||
* @return 错误信息
|
||||
*/
|
||||
@GetMapping(value = "/500")
|
||||
@ResponseBody
|
||||
public BaseResult error500() {
|
||||
return error(HttpStatus.INTERNAL_SERVER_ERROR, "服务器内部错误:可能请求资源不存在");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.ErrorPageRegistrar;
|
||||
import org.springframework.boot.web.server.ErrorPageRegistry;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 全局错误访问返回结果
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-08 0008 21:00
|
||||
*/
|
||||
@Component
|
||||
public class ErrorPageConfig implements ErrorPageRegistrar {
|
||||
@Override
|
||||
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
|
||||
errorPageRegistry.addErrorPages(
|
||||
// 当遇到 NOT_FOUND 错误时,请求 /error/404 路由。
|
||||
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
|
||||
// 当遇到 INTERNAL_SERVER_ERROR 错误时,请求 /error/500 路由。
|
||||
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 描述
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-07 0007 19:04
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "excite")
|
||||
public class ExciteConfig {
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 是否允许Redis缓存:true:允许 | false:不允许
|
||||
* 此Redis缓存用到的地方主要有:用户角色、权限控制;
|
||||
*/
|
||||
private Boolean allowRedis;
|
||||
|
||||
/**
|
||||
* RSA加密算法的公钥
|
||||
*/
|
||||
private String rsaPublicKey;
|
||||
|
||||
/**
|
||||
* RSA加密算法的私钥
|
||||
*/
|
||||
private String rsaPrivateKey;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean getAllowRedis() {
|
||||
return allowRedis;
|
||||
}
|
||||
|
||||
public void setAllowRedis(Boolean allowRedis) {
|
||||
this.allowRedis = allowRedis;
|
||||
}
|
||||
|
||||
public String getRsaPublicKey() {
|
||||
return rsaPublicKey;
|
||||
}
|
||||
|
||||
public void setRsaPublicKey(String rsaPublicKey) {
|
||||
this.rsaPublicKey = rsaPublicKey;
|
||||
}
|
||||
|
||||
public String getRsaPrivateKey() {
|
||||
return rsaPrivateKey;
|
||||
}
|
||||
|
||||
public void setRsaPrivateKey(String rsaPrivateKey) {
|
||||
this.rsaPrivateKey = rsaPrivateKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.code.kaptcha.Constants.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Google验证码配置类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-04 0004 20:12
|
||||
*/
|
||||
@Configuration
|
||||
public class KaptchaConfig {
|
||||
|
||||
/**
|
||||
* 验证码配置默认配置
|
||||
*
|
||||
* @return 配置信息
|
||||
*/
|
||||
@Bean(name = "captchaProducer")
|
||||
public DefaultKaptcha getKaptchaBean() {
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||
properties.setProperty(KAPTCHA_BORDER, "yes");
|
||||
// 验证码文本字符颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
|
||||
// 验证码图片宽度 默认为200
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||
// 验证码图片高度 默认为50
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||
// 验证码文本字符大小 默认为40
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
|
||||
// KAPTCHA_SESSION_KEY
|
||||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
|
||||
// 验证码文本字符长度 默认为5
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
|
||||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
|
||||
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
||||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码数学题类配置(一位数加减乘除)
|
||||
*
|
||||
* @return 配置信息
|
||||
*/
|
||||
@Bean(name = "captchaProducerMathOne")
|
||||
public DefaultKaptcha getKaptchaBeanMathOne() {
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||
properties.setProperty(KAPTCHA_BORDER, "yes");
|
||||
// 边框颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
|
||||
// 验证码文本字符颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
|
||||
// 验证码图片宽度 默认为200
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||
// 验证码图片高度 默认为50
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||
// 验证码文本字符大小 默认为40
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
|
||||
// KAPTCHA_SESSION_KEY
|
||||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
|
||||
// 验证码文本生成器
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.zxdmy.excite.common.utils.KaptchaMathOneTextCreator");
|
||||
// 验证码文本字符间距 默认为2
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
|
||||
// 验证码文本字符长度 默认为 5
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
|
||||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
|
||||
// 验证码噪点颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
|
||||
// 干扰实现类
|
||||
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
|
||||
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
||||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码数学题类配置(两位数的加减乘除)
|
||||
*
|
||||
* @return 配置信息
|
||||
*/
|
||||
@Bean(name = "captchaProducerMathTwo")
|
||||
public DefaultKaptcha getKaptchaBeanMathTwo() {
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||
properties.setProperty(KAPTCHA_BORDER, "yes");
|
||||
// 边框颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
|
||||
// 验证码文本字符颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
|
||||
// 验证码图片宽度 默认为200
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||
// 验证码图片高度 默认为50
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||
// 验证码文本字符大小 默认为40
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
|
||||
// KAPTCHA_SESSION_KEY
|
||||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
|
||||
// 验证码文本生成器
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.zxdmy.excite.common.utils.KaptchaMathTwoTextCreator");
|
||||
// 验证码文本字符间距 默认为2
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
|
||||
// 验证码文本字符长度 默认为5
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
|
||||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
|
||||
// 验证码噪点颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
|
||||
// 干扰实现类
|
||||
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
|
||||
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
||||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mybatis Plus 配置信息
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-01 0001 18:34
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan({"com.zxdmy.excite.system.mapper", ""})
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 分页插件配置
|
||||
*
|
||||
* @return MP拦截器
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Redis 相关配置,参考自:spring-boot-demo
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-30 0030 19:15
|
||||
*/
|
||||
@Component
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
@AutoConfigureAfter(RedisAutoConfiguration.class)
|
||||
@ConfigurationProperties(prefix = "spring.redis")
|
||||
public class RedisConfig {
|
||||
|
||||
/**
|
||||
* 自定义属性:Redis key 的前缀
|
||||
*/
|
||||
private String prefix = "redis:prefix:";
|
||||
|
||||
/**
|
||||
* 自定义属性:是否开启Redis自定义前缀,默认关闭。
|
||||
*/
|
||||
private Boolean allowPrefix = false;
|
||||
|
||||
/**
|
||||
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
|
||||
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
|
||||
// key采用String的序列化方式
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
// hash的key也采用String的序列化方式
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
// value序列化方式采用jackson
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
// hash的value序列化方式采用jackson
|
||||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager(RedisConnectionFactory factory) {
|
||||
// 配置序列化
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
|
||||
RedisCacheConfiguration redisCacheConfiguration = config
|
||||
// 覆盖默认的构造key,否则会多出一个冒号
|
||||
.computePrefixWith(cacheName -> this.allowPrefix ? this.prefix + cacheName + ":" : cacheName)
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
|
||||
// 不缓存空值
|
||||
// .disableCachingNullValues()
|
||||
;
|
||||
|
||||
return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
|
||||
}
|
||||
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public Boolean getAllowPrefix() {
|
||||
return allowPrefix;
|
||||
}
|
||||
|
||||
public void setAllowPrefix(Boolean allowPrefix) {
|
||||
this.allowPrefix = allowPrefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.zxdmy.excite.common.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import org.springdoc.core.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* SpringDoc API配置文档
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-10 0010 21:57
|
||||
*/
|
||||
// 全局只能定义一个,主要配置文档信息和安全配置
|
||||
@OpenAPIDefinition(
|
||||
// 配置接口文档基本信息,用于网页显示
|
||||
info = @Info(
|
||||
title = "ExciteCMS", //标题
|
||||
version = "1.0", //版本号
|
||||
description = "An integrated content management system based on Spring Boot (v2.5.4)" //描述信息
|
||||
)
|
||||
// 当然还可以添加其他的安全配置。
|
||||
)
|
||||
@Configuration // 如果需要在此处配置分组信息,则需要添加此注解。
|
||||
public class SpringDocConfig {
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi guestApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
// 分组名
|
||||
.group("guest")
|
||||
// 扫描的包路径。可选项。
|
||||
.packagesToScan("com.zxdmy.cms.excite.guest")
|
||||
// 匹配的路径。可选项,可以与包路径二选一,也可以都用。
|
||||
.pathsToMatch("/guest/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public GroupedOpenApi thirdApi() {
|
||||
// return GroupedOpenApi.builder()
|
||||
// // 分组名
|
||||
// .group("third")
|
||||
// // 扫描的包路径。可选项。
|
||||
// .packagesToScan("com.zxdmy.cms.excite.third")
|
||||
// // 匹配的路径。可选项,可以与包路径二选一,也可以都用。
|
||||
// .pathsToMatch("/third/**")
|
||||
// .build();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zxdmy.excite.common.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 第三方验证码实体类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-04 0004 20:02
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class CaptchaDomain implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 验证码的Token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 验证码的字符。返回的JSON,禁止返回给前端。
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 验证码的验证字符。比如算式的结果等。
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 验证码缓冲图像
|
||||
*/
|
||||
@JsonIgnore
|
||||
private BufferedImage image;
|
||||
|
||||
/**
|
||||
* 验证码图片的Base64字符串
|
||||
*/
|
||||
private String base64;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.zxdmy.excite.common.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 组件配置信息表
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022-01-03
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class GlobalConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键,自增
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 配置信息的服务:如qiniu,aliyun
|
||||
*/
|
||||
private String confService;
|
||||
|
||||
/**
|
||||
* 配置信息的主键:如qiniu、alipay等等
|
||||
*/
|
||||
private String confKey;
|
||||
|
||||
/**
|
||||
* 配置信息的JSON值
|
||||
*/
|
||||
private String confValue;
|
||||
|
||||
/**
|
||||
* 是否加密:1-是 0-否
|
||||
*/
|
||||
private Integer encrypt;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除时间
|
||||
*/
|
||||
private LocalDateTime deleteTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.zxdmy.excite.common.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PageList<T> {
|
||||
private int page;//当前页数
|
||||
private int total;//总的页数
|
||||
private int count;//总的记录数
|
||||
private ArrayList<T> list;//当前页数据
|
||||
|
||||
/**
|
||||
* @param list 当前页数据
|
||||
* @param page 当前页数
|
||||
* @param size 每页数据量
|
||||
* @param count 总的记录数量
|
||||
*/
|
||||
public PageList(ArrayList<T> list, int page, int size, int count) {
|
||||
this.page = page;
|
||||
this.count = count;
|
||||
this.list = list;
|
||||
this.total = count % size == 0 ? count / size : (count / size) + 1;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public List<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(ArrayList<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zxdmy.excite.common.enums;
|
||||
|
||||
public enum CloudCode {
|
||||
|
||||
FOLDER_IMAGE("https://ltcloudfile.oss-cn-hangzhou.aliyuncs.com/cloud/folder.jpg", "目录图标"),
|
||||
PDF_IMAGE("https://ltcloudfile.oss-cn-hangzhou.aliyuncs.com/cloud/file.png", "PDF"),
|
||||
FOLDER_NAME("文件夹", "目录格式");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String desc;
|
||||
|
||||
CloudCode(String code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return this.desc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zxdmy.excite.common.enums;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 通用的返回结果返回码枚举类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-05 0005 23:59
|
||||
*/
|
||||
public enum ReturnCode {
|
||||
|
||||
// 以下是常用的HTTP状态码
|
||||
/**
|
||||
* 客户端请求成功。状态码:200
|
||||
*/
|
||||
OK(200, "OK"),
|
||||
/**
|
||||
* 客户端请求已创建。状态码:201
|
||||
*/
|
||||
CREATED(201, "Created"),
|
||||
/**
|
||||
* 客户端请求已接受。状态码:202
|
||||
*/
|
||||
ACCEPTED(202, "Accepted"),
|
||||
|
||||
/**
|
||||
* 请求错误。状态码:400
|
||||
*/
|
||||
BAD_REQUEST(400, "Bad Request"),
|
||||
/**
|
||||
* 拒绝访问。状态码:401
|
||||
*/
|
||||
UNAUTHORIZED(401, "Unauthorized"),
|
||||
/**
|
||||
* 禁止访问。状态码:403
|
||||
*/
|
||||
FORBIDDEN(403, "Forbidden"),
|
||||
/**
|
||||
* 请求不存在。状态码:404
|
||||
*/
|
||||
NOT_FOUND(404, "Not Found"),
|
||||
/**
|
||||
* 方法不被允许。状态码:405
|
||||
*/
|
||||
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
|
||||
|
||||
// 当然还可以添加自定义的其他返回码。
|
||||
INVALID_ID(40001, "不合法的ID"),
|
||||
INVALID_FILE_TYPE(40002, "不合法的文件类型"),
|
||||
INVALID_FILE_SIZE(40003, "不合法的文件大小"),
|
||||
INVALID_URL(40004, "不合法的链接");
|
||||
|
||||
private final int code;
|
||||
|
||||
private final String reason;
|
||||
|
||||
ReturnCode(int code, String reason) {
|
||||
this.code = code;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.zxdmy.excite.common.enums;
|
||||
|
||||
public enum SystemCode {
|
||||
|
||||
/**
|
||||
* 系统菜单默认顺序:0
|
||||
*/
|
||||
MENU_DEFAULT_SORT(50, "菜单默认排序"),
|
||||
/**
|
||||
* 默认顺序:50
|
||||
*/
|
||||
SORT_DEFAULT(50, "默认排序"),
|
||||
|
||||
FOLDER_Y(0, "是文件夹"),
|
||||
|
||||
FOLDER_N(1, "是文件"),
|
||||
/**
|
||||
* 记录状态:正常,值:1
|
||||
*/
|
||||
STATUS_Y(1, "记录状态正常"),
|
||||
/**
|
||||
* 记录状态:异常,值:0
|
||||
*/
|
||||
STATUS_N(0, "记录状态封禁"),
|
||||
/**
|
||||
* 记录状态:正常锁定,值:2
|
||||
*/
|
||||
STATUS_Y_BLOCK(2, "状态正常,并且无法编辑状态"),
|
||||
|
||||
/**
|
||||
* 记录可以编辑:1
|
||||
*/
|
||||
EDITABLE_Y(1, "记录可以编辑"),
|
||||
/**
|
||||
* 记录禁止编辑:0
|
||||
*/
|
||||
EDITABLE_N(0, "记录禁止编辑"),
|
||||
|
||||
/**
|
||||
* 记录可以删除:1
|
||||
*/
|
||||
REMOVABLE_Y(1, "记录可以删除"),
|
||||
/**
|
||||
* 记录禁止删除:0
|
||||
*/
|
||||
REMOVABLE_N(0, "记录禁止删除"),
|
||||
/**
|
||||
* 是否删除:是 - 0
|
||||
*/
|
||||
DELETE_Y(1, "已删除"),
|
||||
/**
|
||||
* 是否删除:否 - 0
|
||||
*/
|
||||
DELETE_N(0, "未删除");
|
||||
|
||||
|
||||
private final int code;
|
||||
|
||||
private final String desc;
|
||||
|
||||
SystemCode(int code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return this.desc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.zxdmy.excite.common.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import com.zxdmy.excite.common.base.BaseController;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 全局异常处理
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-12 0012 20:57
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class GlobalException extends BaseController {
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public Object handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("==============全局异常==============");
|
||||
e.printStackTrace();
|
||||
// 不同异常返回不同状态码
|
||||
|
||||
String result = "系统错误,请重试!";
|
||||
// 如果是未登录异常
|
||||
if (e instanceof NotLoginException) {
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
result = "用户未登录";
|
||||
// 前后端同域,所以未登录就直接跳转至登录页面
|
||||
response.sendRedirect(request.getContextPath() + "/system/login");
|
||||
}
|
||||
// 如果是角色异常
|
||||
else if (e instanceof NotRoleException) {
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
result = "无此角色:" + ee.getRole();
|
||||
}
|
||||
// 如果是权限异常
|
||||
else if (e instanceof NotPermissionException) {
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
result = "无此权限:" + ee.getCode();
|
||||
}
|
||||
// 如果是被封禁异常
|
||||
else if (e instanceof DisableLoginException) {
|
||||
DisableLoginException ee = (DisableLoginException) e;
|
||||
result = "账号被封禁:" + ee.getDisableTime() + "秒后解封";
|
||||
}
|
||||
// 不支持的请求
|
||||
else if (e instanceof HttpRequestMethodNotSupportedException) {
|
||||
return "请求不支持";
|
||||
}
|
||||
// 信息校验失败提示
|
||||
else if (e instanceof BindException) {
|
||||
BindingResult bindingResult = ((BindException) e).getBindingResult();
|
||||
StringBuilder errMsg = new StringBuilder();
|
||||
if (bindingResult.hasErrors()) {
|
||||
Integer i = 1;
|
||||
for (FieldError fieldError : bindingResult.getFieldErrors()) {
|
||||
errMsg.append(i).append(". ").append(fieldError.getDefaultMessage()).append("!");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
result = errMsg.toString();
|
||||
}
|
||||
// 其他异常, 输出:500 + 异常信息
|
||||
else {
|
||||
result = e.getMessage();
|
||||
}
|
||||
// 返回给前端
|
||||
return error(500, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.zxdmy.excite.common.exception;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务层异常处理,继承RunTimeException,来保证对异常进行事务回滚
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-23 0023 19:54
|
||||
*/
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String message;
|
||||
|
||||
private String details;
|
||||
|
||||
public ServiceException() {
|
||||
}
|
||||
|
||||
public ServiceException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ServiceException(String message, String details) {
|
||||
this.message = message;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(String details) {
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zxdmy.excite.common.mapper;
|
||||
|
||||
import com.zxdmy.excite.common.entity.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 组件配置信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022-01-03
|
||||
*/
|
||||
public interface GlobalConfigMapper extends BaseMapper<GlobalConfig> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.zxdmy.excite.common.service;
|
||||
|
||||
import cn.hutool.captcha.AbstractCaptcha;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import com.google.code.kaptcha.Producer;
|
||||
|
||||
import com.zxdmy.excite.common.entity.CaptchaDomain;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import sun.misc.BASE64Encoder;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 验证码服务接口的实现
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-15 0015 10:18
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class CaptchaService {
|
||||
|
||||
@Resource(name = "captchaProducer")
|
||||
private Producer captchaProducer;
|
||||
|
||||
@Resource(name = "captchaProducerMathOne")
|
||||
private Producer captchaProducerMathOne;
|
||||
|
||||
@Resource(name = "captchaProducerMathTwo")
|
||||
private Producer captchaProducerMathTwo;
|
||||
|
||||
private static final String TYPE_CHAR = "char";
|
||||
private static final String TYPE_MATH_ONE = "math";
|
||||
private static final String TYPE_MATH_TWO = "math2";
|
||||
|
||||
/**
|
||||
* Kaptcha生成验证码实体
|
||||
*
|
||||
* @param type 类型,char - 字符(缺省) | math - 一位数算式 | math2 - 两位数算式
|
||||
* @return 验证码实体
|
||||
*/
|
||||
public CaptchaDomain createGoogleCaptcha(String type) {
|
||||
// 定义验证码实体
|
||||
CaptchaDomain captchaDomain = new CaptchaDomain();
|
||||
// 一位数加减乘除
|
||||
if (TYPE_MATH_ONE.equals(type)) {
|
||||
// 生成文本
|
||||
String producerText = captchaProducerMathOne.createText();
|
||||
// 设置验证码字符
|
||||
captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
|
||||
// 设置验证码答案码
|
||||
captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
|
||||
// 设置验证码图片
|
||||
captchaDomain.setImage(captchaProducerMathOne.createImage(captchaDomain.getText()));
|
||||
}
|
||||
// 两位数加减乘除
|
||||
else if (TYPE_MATH_TWO.equals(type)) {
|
||||
String producerText = captchaProducerMathTwo.createText();
|
||||
captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
|
||||
captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
|
||||
captchaDomain.setImage(captchaProducerMathTwo.createImage(captchaDomain.getText()));
|
||||
}
|
||||
// 缺省情况:字符
|
||||
else {
|
||||
captchaDomain.setText(captchaProducer.createText());
|
||||
captchaDomain.setCode(captchaDomain.getText());
|
||||
captchaDomain.setImage(captchaProducer.createImage(captchaDomain.getText()));
|
||||
}
|
||||
// 生成base64
|
||||
try {
|
||||
// 定义字节数组输出流
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
// 将图像以 jpg 的形式,写到字节数组输出流中
|
||||
ImageIO.write(captchaDomain.getImage(), "jpg", outputStream);
|
||||
// 对字节数组Base64编码
|
||||
BASE64Encoder encoder = new BASE64Encoder();
|
||||
// 写入base64格式
|
||||
captchaDomain.setBase64("data:image/jpg;base64," + encoder.encode(outputStream.toByteArray()));
|
||||
// 写入唯一Token
|
||||
captchaDomain.setToken(UUID.randomUUID().toString());
|
||||
// 返回结果
|
||||
return captchaDomain;
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public CaptchaDomain createHutoolCaptcha(Integer width, Integer height) {
|
||||
CaptchaDomain captchaDomain = new CaptchaDomain();
|
||||
// 生成 0、1、2 三个整数其一,随机对应下面的三种验证码类型
|
||||
// 注意:这里只是尽可能多的展示每种验证码的使用方法,实际项目中选择一种即可。
|
||||
Random random = new SecureRandom();
|
||||
int type = random.nextInt(3);
|
||||
AbstractCaptcha captcha = null;
|
||||
// 【0】生成 线段干扰验证码
|
||||
if (type == 0) {
|
||||
//定义图形验证码的长和宽
|
||||
captcha = CaptchaUtil.createLineCaptcha(width, height);
|
||||
}
|
||||
// 【1】生成 圆圈干扰验证码
|
||||
else if (type == 1) {
|
||||
//定义图形验证码的长、宽。还可以设置两个参数:验证码字符数、干扰元素个数
|
||||
captcha = CaptchaUtil.createCircleCaptcha(width, height);
|
||||
}
|
||||
// 【2】生成 扭曲干扰验证码
|
||||
else {
|
||||
captcha = CaptchaUtil.createShearCaptcha(width, height);
|
||||
}
|
||||
// 信息配置
|
||||
captchaDomain.setText(captcha.getCode());
|
||||
captchaDomain.setCode(captcha.getCode());
|
||||
captchaDomain.setBase64(captcha.getImageBase64Data());
|
||||
captchaDomain.setImage(captcha.getImage());
|
||||
captchaDomain.setToken(UUID.randomUUID().toString());
|
||||
|
||||
return captchaDomain;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zxdmy.excite.common.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.zxdmy.excite.common.entity.GlobalConfig;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 组件配置信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022-01-03
|
||||
*/
|
||||
public interface IGlobalConfigService extends IService<GlobalConfig> {
|
||||
|
||||
boolean save(String confService, String confKey, Object object, boolean encrypt) throws JsonProcessingException;
|
||||
|
||||
Object get(String confService, String confKey, Object object);
|
||||
|
||||
List<Object> getList(String confService, Object object);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.zxdmy.excite.common.service;
|
||||
|
||||
|
||||
import com.zxdmy.excite.common.config.RedisConfig;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Redis工具类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-09-30 0030 19:15
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class RedisService {
|
||||
|
||||
private RedisTemplate<String, Serializable> redisTemplate;
|
||||
|
||||
private RedisConfig redisConfig;
|
||||
|
||||
/**
|
||||
* 写入普通缓存,key由系统生成
|
||||
*
|
||||
* @param value 值
|
||||
* @return 键
|
||||
*/
|
||||
public String set(Serializable value) {
|
||||
// 随机生成key
|
||||
String key = UUID.randomUUID().toString();
|
||||
// 如果当前生成的key已经存在,重新生成
|
||||
if (hasKey(key)) {
|
||||
return set(value);
|
||||
} else {
|
||||
// 写入缓存
|
||||
redisTemplate.opsForValue().set(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key, value);
|
||||
// 返回key
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入带过期时间的缓存,其中key由系统生成
|
||||
*
|
||||
* @param value 值
|
||||
* @param expireTime 过期时间
|
||||
* @return 键key
|
||||
*/
|
||||
public String set(Serializable value, Long expireTime) {
|
||||
// 随机生成key
|
||||
String key = UUID.randomUUID().toString();
|
||||
// 如果当前随机生成的key已经存在,则重新生成
|
||||
if (hasKey(key)) {
|
||||
return set(value, expireTime);
|
||||
} else {
|
||||
// 写入缓存
|
||||
redisTemplate.opsForValue().set(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key, value, expireTime, TimeUnit.SECONDS);
|
||||
// 返回key
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入普通缓存,指定key
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean set(String key, Serializable value) {
|
||||
boolean result = false;
|
||||
try {
|
||||
redisTemplate.opsForValue().set(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key, value);
|
||||
result = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入带过期时间的缓存,指定key
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param expireTime 过期时间
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean set(String key, Serializable value, Long expireTime) {
|
||||
boolean result = false;
|
||||
try {
|
||||
redisTemplate.opsForValue().set(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key, value, expireTime, TimeUnit.SECONDS);
|
||||
result = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断某个键是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return 存在与否
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
return Boolean.TRUE.equals(redisTemplate.hasKey(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键获取值
|
||||
*
|
||||
* @param key 缓存的键
|
||||
* @return 存在:值 | 不存在:null
|
||||
*/
|
||||
public Serializable get(String key) {
|
||||
if (hasKey(key)) {
|
||||
return redisTemplate.opsForValue().get(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个键值的过期时间
|
||||
*
|
||||
* @param key 键
|
||||
* @return 过期时间(秒),-1表示永久有效,-2表示不存在
|
||||
*/
|
||||
public Long getExpire(String key) {
|
||||
return redisTemplate.getExpire(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键移出某个值
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
public void remove(String key) {
|
||||
if (hasKey(key)) {
|
||||
redisTemplate.delete(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除包含指定前缀的值
|
||||
*
|
||||
* @param prefix 键
|
||||
*/
|
||||
public void removeByPrefix(String prefix) {
|
||||
Set<String> keys = redisTemplate.keys((redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + prefix : prefix) + "*");
|
||||
if (null != keys && !keys.isEmpty()) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过键移出某些值
|
||||
*
|
||||
* @param keys 批量的键,示例:remove("001","002","003")
|
||||
*/
|
||||
public void remove(String... keys) {
|
||||
for (String key : keys) {
|
||||
remove(redisConfig.getAllowPrefix() ? redisConfig.getPrefix() + key : key);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.zxdmy.excite.common.service.impl;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.zxdmy.excite.common.config.ExciteConfig;
|
||||
import com.zxdmy.excite.common.entity.GlobalConfig;
|
||||
import com.zxdmy.excite.common.mapper.GlobalConfigMapper;
|
||||
import com.zxdmy.excite.common.service.IGlobalConfigService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 组件配置信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022-01-03
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class GlobalConfigServiceImpl extends ServiceImpl<GlobalConfigMapper, GlobalConfig> implements IGlobalConfigService {
|
||||
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
ExciteConfig exciteConfig;
|
||||
|
||||
/**
|
||||
* 将配置信息保存/更新至数据库
|
||||
*
|
||||
* @param confService 配置服务名
|
||||
* @param confKey 配置信息主键
|
||||
* @param object 实体类
|
||||
* @param encrypt 是否对value开启加密
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public boolean save(String confService, String confKey, Object object, boolean encrypt) {
|
||||
// 全局配置信息类
|
||||
GlobalConfig globalConfig = new GlobalConfig();
|
||||
globalConfig.setConfService(confService);
|
||||
globalConfig.setConfKey(confKey);
|
||||
// 实体类转JSON格式字符串
|
||||
String confValue;
|
||||
try {
|
||||
confValue = objectMapper.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
// 如果开启加密,则对confValue:使用公钥加密
|
||||
if (encrypt) {
|
||||
RSA rsa = new RSA(null, exciteConfig.getRsaPublicKey());
|
||||
confValue = rsa.encryptBase64(confValue, KeyType.PublicKey);
|
||||
globalConfig.setEncrypt(1);
|
||||
} else {
|
||||
globalConfig.setEncrypt(0);
|
||||
}
|
||||
globalConfig.setConfValue(confValue);
|
||||
globalConfig.setUpdateTime(LocalDateTime.now());
|
||||
QueryWrapper<GlobalConfig> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("conf_service", confService).eq("conf_key", confKey);
|
||||
return this.saveOrUpdate(globalConfig, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库中读取一条配置信息
|
||||
*
|
||||
* @param confService 配置信息服务名
|
||||
* @param confKey 配置信息的key
|
||||
* @param object 配置信息实体
|
||||
* @return 配置信息实体
|
||||
*/
|
||||
@Override
|
||||
public Object get(String confService, String confKey, Object object) {
|
||||
// 根据要求查询指定【模块】和【key】的【value】
|
||||
QueryWrapper<GlobalConfig> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("conf_service", confService).eq("conf_key", confKey);
|
||||
GlobalConfig globalConfig = this.getOne(queryWrapper);
|
||||
String confValue = globalConfig.getConfValue();
|
||||
// 为空:返回null
|
||||
if (null == confValue || "".equals(confValue)) {
|
||||
return null;
|
||||
}
|
||||
// 如果开启了加密:使用私钥进行解密
|
||||
if (1 == globalConfig.getEncrypt()) {
|
||||
RSA rsa = new RSA(exciteConfig.getRsaPrivateKey(), null);
|
||||
confValue = rsa.decryptStr(confValue, KeyType.PrivateKey);
|
||||
}
|
||||
// 尝试转换成指定类型
|
||||
try {
|
||||
object = objectMapper.readValue(confValue, object.getClass());
|
||||
} catch (IOException e) {
|
||||
// 转换出错:报错并返回空
|
||||
System.out.println(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
// 返回转换后的结果
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务的名字,获取其所有的配置信息
|
||||
*
|
||||
* @param confService 服务名称
|
||||
* @param object 类别
|
||||
* @return 结果List<Object>
|
||||
*/
|
||||
@Override
|
||||
public List<Object> getList(String confService, Object object) {
|
||||
// 根据要求查询指定【服务模块】的【value】
|
||||
QueryWrapper<GlobalConfig> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("conf_service", confService);
|
||||
List<GlobalConfig> globalConfigList = this.list(queryWrapper);
|
||||
if (globalConfigList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
List<Object> objectList = new ArrayList<>();
|
||||
for (GlobalConfig globalConfig : globalConfigList) {
|
||||
String confValue = globalConfig.getConfValue();
|
||||
// 为空:继续下一个
|
||||
if (null == confValue || "".equals(confValue)) {
|
||||
continue;
|
||||
}
|
||||
// 如果开启了加密:使用私钥进行解密
|
||||
if (1 == globalConfig.getEncrypt()) {
|
||||
RSA rsa = new RSA(exciteConfig.getRsaPrivateKey(), null);
|
||||
confValue = rsa.decryptStr(confValue, KeyType.PrivateKey);
|
||||
}
|
||||
// 尝试转换成指定类型
|
||||
try {
|
||||
objectList.add(objectMapper.readValue(confValue, object.getClass()));
|
||||
} catch (IOException e) {
|
||||
// 转换出错:报错并返回空
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
return objectList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 精确的浮点数运算工具类
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022/1/22 21:30
|
||||
*/
|
||||
public class ArithUtils {
|
||||
|
||||
/**
|
||||
* 默认除法运算精度
|
||||
*/
|
||||
private static final int DEF_DIV_SCALE = 10;
|
||||
|
||||
/**
|
||||
* 这个类不能实例化
|
||||
*/
|
||||
private ArithUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算。
|
||||
*
|
||||
* @param v1 被加数
|
||||
* @param v2 加数
|
||||
* @return 两个参数的和
|
||||
*/
|
||||
public static double add(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.add(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算。
|
||||
*
|
||||
* @param v1 被减数
|
||||
* @param v2 减数
|
||||
* @return 两个参数的差
|
||||
*/
|
||||
public static double sub(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.subtract(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
*
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static double mul(double v1, double v2) {
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.multiply(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
|
||||
* 小数点以后10位,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2) {
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
|
||||
* 定精度,以后的数字四舍五入。
|
||||
*
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
if (b1.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO.doubleValue();
|
||||
}
|
||||
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的小数位四舍五入处理。
|
||||
*
|
||||
* @param v 需要四舍五入的数字
|
||||
* @param scale 小数点后保留几位
|
||||
* @return 四舍五入后的结果
|
||||
*/
|
||||
public static double round(double v, int scale) {
|
||||
if (scale < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b = new BigDecimal(Double.toString(v));
|
||||
BigDecimal one = BigDecimal.ONE;
|
||||
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2022/1/22 21:31
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
||||
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate() {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static final String getTime() {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow() {
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow(final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static final String dateTime(final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static final Date dateTime(final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static final String datePath() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static final String dateTime() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器启动时间
|
||||
*/
|
||||
public static Date getServerStartDate() {
|
||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
return new Date(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差天数
|
||||
*/
|
||||
public static int differentDaysByMillisecond(Date date1, Date date2) {
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间差
|
||||
*/
|
||||
public static String getDatePoor(Date endDate, Date nowDate) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - nowDate.getTime();
|
||||
// 计算差多少天
|
||||
long day = diff / nd;
|
||||
// 计算差多少小时
|
||||
long hour = diff % nd / nh;
|
||||
// 计算差多少分钟
|
||||
long min = diff % nd % nh / nm;
|
||||
// 计算差多少秒//输出结果
|
||||
// long sec = diff % nd % nh % nm / ns;
|
||||
return day + "天" + hour + "小时" + min + "分钟";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 描述
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021/12/9 16:54
|
||||
*/
|
||||
public class HttpServletRequestUtil {
|
||||
|
||||
/**
|
||||
* 获取request对象
|
||||
*
|
||||
* @return request对象
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的URI(非URL)
|
||||
*
|
||||
* @return 格式如:/system/user/list?size=10&page=2
|
||||
*/
|
||||
public static String getRequestURI() {
|
||||
HttpServletRequest request = getRequest();
|
||||
// URI中的查询字符串
|
||||
String queryString = request.getQueryString();
|
||||
// 请求URL中不含字符串
|
||||
if (!StringUtils.hasText(queryString)) {
|
||||
// 只返回URI
|
||||
return request.getRequestURI();
|
||||
}
|
||||
// 否则返回包含查询的字符串
|
||||
return request.getRequestURI() + "?" + queryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器的UA
|
||||
*
|
||||
* @return UA对象(Hutool工具箱中的)
|
||||
*/
|
||||
public static UserAgent getRequestUserAgent() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return UserAgentUtil.parse(request.getHeader("User-Agent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求用户的IP
|
||||
*
|
||||
* @return IP字符串
|
||||
*/
|
||||
public static String getRemoteIP() {
|
||||
HttpServletRequest request = getRequest();
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
// 多次反向代理后会有多个IP值,第一个为真实IP。
|
||||
int index = ip.indexOf(',');
|
||||
if (index != -1) {
|
||||
ip = ip.substring(0, index);
|
||||
}
|
||||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得请求头信息 name:value
|
||||
*/
|
||||
public static Map getHeaders() {
|
||||
HttpServletRequest request = getRequest();
|
||||
Map<String, String> map = new HashMap<>(32);
|
||||
Enumeration headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String key = (String) headerNames.nextElement();
|
||||
String value = request.getHeader(key);
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体信息
|
||||
*/
|
||||
public static String getBody() {
|
||||
HttpServletRequest request = getRequest();
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = request.getInputStream();
|
||||
return StreamUtils.copyToString(inputStream, Charset.forName("utf-8"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
public static String getAllRequestInfo() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("请求详情为:").append(StrUtil.CRLF);
|
||||
sb.append("RemoteAddress: ").append(getRemoteIP()).append(StrUtil.CRLF);
|
||||
sb.append("Method: ").append(getRequest().getMethod()).append(StrUtil.CRLF);
|
||||
sb.append("URI: ").append(getRequestURI()).append(StrUtil.CRLF);
|
||||
sb.append("Headers: ").append(StrUtil.join(StrUtil.CRLF + " ", mapToList(getHeaders()))).append(StrUtil.CRLF);
|
||||
sb.append("Body: ").append(getBody()).append(StrUtil.CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static List mapToList(Map parameters) {
|
||||
List parametersList = new ArrayList();
|
||||
parameters.forEach((name, value) -> {
|
||||
parametersList.add(name + "=" + value);
|
||||
});
|
||||
return parametersList;
|
||||
}
|
||||
|
||||
public static String getHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
public static String getHostIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 验证码随机文本生成器之:一位数的加减乘除
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-04 0004 20:24
|
||||
*/
|
||||
public class KaptchaMathOneTextCreator extends DefaultTextCreator {
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
// Random random = new SecureRandom();
|
||||
SecureRandom random = new SecureRandom();
|
||||
// 生成两个随机数,随机数范围:[0,10),并返回结果
|
||||
Map<String, String> result = MyCaptchaUtil.mathTextCreator(random.nextInt(10), random.nextInt(10));
|
||||
return result.get("resultString");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 验证码随机文本生成器之:两位数的加减乘除
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-04 0004 20:26
|
||||
*/
|
||||
public class KaptchaMathTwoTextCreator extends DefaultTextCreator {
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
// Random random = new SecureRandom();
|
||||
SecureRandom random = new SecureRandom();
|
||||
// 保存计算结果
|
||||
Map<String, String> result = MyCaptchaUtil.mathTextCreator(random.nextInt(100), random.nextInt(100));
|
||||
// 生成两个随机数,随机数范围:[0,100),并返回结果
|
||||
return result.get("resultString");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.zxdmy.excite.common.utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 自定义的验证码工具类
|
||||
* </p>
|
||||
*
|
||||
* @author 沈松
|
||||
* @since 2021-10-04 0004 20:54
|
||||
*/
|
||||
public class MyCaptchaUtil {
|
||||
|
||||
/**
|
||||
* 生成数学文本算式
|
||||
*
|
||||
* @param a 数字
|
||||
* @param b 数字
|
||||
* @return 字符
|
||||
*/
|
||||
public static Map<String, String> mathTextCreator(int a, int b) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
// Random random = new SecureRandom();
|
||||
// 生成随机操作,操作范围:[0,4),分别表示: + - * /
|
||||
int op = random.nextInt(4);
|
||||
// 定义计算的结果
|
||||
Integer result = 0;
|
||||
// 定义构建的算式字符串
|
||||
StringBuilder resultString = new StringBuilder();
|
||||
// 运算符:-
|
||||
if (1 == op) {
|
||||
if (a >= b) {
|
||||
result = a - b;
|
||||
resultString.append(a).append("-").append(b).append("=?@").append(result);
|
||||
} else {
|
||||
result = b - a;
|
||||
resultString.append(b).append("-").append(a).append("=?@").append(result);
|
||||
}
|
||||
}
|
||||
// *
|
||||
else if (2 == op) {
|
||||
result = a * b;
|
||||
resultString.append(a).append("*").append(b).append("=?@").append(result);
|
||||
}
|
||||
// /
|
||||
else if (3 == op) {
|
||||
if (a != 0 && b % a == 0) {
|
||||
result = b / a;
|
||||
resultString.append(b).append("/").append(a).append("=?@").append(result);
|
||||
} else if (b != 0 && a % b == 0) {
|
||||
result = a / b;
|
||||
resultString.append(a).append("/").append(b).append("=?@").append(result);
|
||||
} else {
|
||||
return mathTextCreator(a, b);
|
||||
}
|
||||
}
|
||||
// +
|
||||
else {
|
||||
result = b + a;
|
||||
resultString.append(a).append("+").append(b).append("=?@").append(result);
|
||||
}
|
||||
Map<String, String> ret = new HashMap<String, String>();
|
||||
ret.put("resultCode", result.toString());
|
||||
ret.put("resultString", resultString.toString());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.zxdmy.excite.common.mapper.GlobalConfigMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.zxdmy.excite.common.entity.GlobalConfig">
|
||||
<id column="id" property="id" />
|
||||
<result column="conf_service" property="confService" />
|
||||
<result column="conf_key" property="confKey" />
|
||||
<result column="conf_value" property="confValue" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="encrypt" property="encrypt" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="delete_time" property="deleteTime" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, conf_service, conf_key, conf_value, encrypt, create_time, update_time, delete_time
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user