@@ -0,0 +1,40 @@ | |||
HELP.md | |||
target/ | |||
!.mvn/wrapper/maven-wrapper.jar | |||
!**/src/main/**/target/ | |||
!**/src/test/**/target/ | |||
### STS ### | |||
.apt_generated | |||
.classpath | |||
.factorypath | |||
.project | |||
.settings | |||
.springBeans | |||
.sts4-cache | |||
### IntelliJ IDEA ### | |||
.idea | |||
*.iws | |||
*.iml | |||
*.ipr | |||
### NetBeans ### | |||
/nbproject/private/ | |||
/nbbuild/ | |||
/dist/ | |||
/nbdist/ | |||
/.nb-gradle/ | |||
build/ | |||
!**/src/main/**/build/ | |||
!**/src/test/**/build/ | |||
### VS Code ### | |||
.vscode/ | |||
/.idea | |||
/.vscode | |||
/.svn | |||
tuoheng-ui | |||
target/ | |||
HELP.md |
@@ -0,0 +1,84 @@ | |||
<?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>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-parent</artifactId> | |||
<version>2.3.5.RELEASE</version> | |||
<relativePath/> <!-- lookup parent from repository --> | |||
</parent> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng</artifactId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<!--父模块打包类型必须为pom--> | |||
<packaging>pom</packaging> | |||
<name>tuoheng</name> | |||
<description>Demo project for Spring Boot</description> | |||
<!-- 子模块依赖 --> | |||
<modules> | |||
<module>tuoheng-common</module> | |||
<module>tuoheng-generator</module> | |||
<module>tuoheng-system</module> | |||
<module>tuoheng-admin</module> | |||
<module>tuoheng-api</module> | |||
<!-- <module>tuoheng-generator</module>--> | |||
</modules> | |||
<properties> | |||
<java.version>1.8</java.version> | |||
<!-- 表示打包时跳过mvn test --> | |||
<maven.test.skip>true</maven.test.skip> | |||
<!--全局配置项目版本号--> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<spring-kafka.version>2.8.3</spring-kafka.version> | |||
</properties> | |||
<!-- 依赖声明 --> | |||
<dependencyManagement> | |||
<dependencies> | |||
<!-- 子模块依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-generator</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-system</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-admin</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-api</artifactId> | |||
<version>${version}</version> | |||
</dependency> | |||
<!-- 第三方依赖 --> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<version>1.18.10</version> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
<version>1.2.62</version> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||
</project> |
@@ -0,0 +1,247 @@ | |||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<!-- 子模块的parent要使用顶层的父模块--> | |||
<parent> | |||
<artifactId>tuoheng</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-admin</artifactId> | |||
<packaging>jar</packaging> | |||
<name>tuoheng-admin</name> | |||
<description>Demo project for Spring Boot</description> | |||
<!-- 依赖声明 --> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>vod20170321</artifactId> | |||
<version>2.16.8</version> | |||
</dependency> | |||
<!-- 核心模块 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-system</artifactId> | |||
</dependency> | |||
<!-- 代码生成 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-generator</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.amqp</groupId> | |||
<artifactId>spring-rabbit-test</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- 引入阿里数据库连接池 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>druid-spring-boot-starter</artifactId> | |||
<version>1.1.10</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/log4j/log4j --> | |||
<dependency> | |||
<groupId>log4j</groupId> | |||
<artifactId>log4j</artifactId> | |||
<version>1.2.17</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.hibernate</groupId> | |||
<artifactId>hibernate-validator</artifactId> | |||
<version>6.0.15.Final</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.protobuf</groupId> | |||
<artifactId>protobuf-java</artifactId> | |||
<version>3.9.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-websocket</artifactId> | |||
<version>2.0.4.RELEASE</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-amqp</artifactId> | |||
<version>2.5.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.amqp</groupId> | |||
<artifactId>spring-rabbit</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>net.coobird</groupId> | |||
<artifactId>thumbnailator</artifactId> | |||
<version>[0.4, 0.5)</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-scratchpad</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-ooxml</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-ooxml-schemas</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.lowagie</groupId> | |||
<artifactId>itext</artifactId> | |||
<version>2.1.7</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.lowagie</groupId> | |||
<artifactId>itext-rtf</artifactId> | |||
<version>2.1.7</version> | |||
</dependency> | |||
<!--阿里云OSS--> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-core</artifactId> | |||
<version>4.5.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun.oss</groupId> | |||
<artifactId>aliyun-sdk-oss</artifactId> | |||
<version>3.10.2</version> | |||
</dependency> | |||
</dependencies> | |||
<profiles> | |||
<!-- 本地开发环境 --> | |||
<profile> | |||
<id>local</id> | |||
<properties> | |||
<package.environment>local</package.environment> | |||
</properties> | |||
<activation> | |||
<activeByDefault>true</activeByDefault> | |||
</activation> | |||
</profile> | |||
<!-- 开发环境 --> | |||
<profile> | |||
<id>dev</id> | |||
<properties> | |||
<package.environment>dev</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 测试环境 --> | |||
<profile> | |||
<id>test</id> | |||
<properties> | |||
<package.environment>test</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 生产环境 --> | |||
<profile> | |||
<id>prod</id> | |||
<properties> | |||
<package.environment>prod</package.environment> | |||
</properties> | |||
</profile> | |||
</profiles> | |||
<build> | |||
<finalName>tuoheng_qmhh_admin</finalName> | |||
<resources> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
<includes> | |||
<include>**/*.xml</include> | |||
<include>**/*.properties</include> | |||
<include>**/*.yml</include> | |||
</includes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>false</filtering> | |||
<includes> | |||
<include>**/*.docx</include> | |||
</includes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/java</directory> | |||
<includes> | |||
<include>**/*.*</include> | |||
</includes> | |||
<excludes> | |||
<exclude>**/*.java</exclude> | |||
</excludes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
<targetPath>WEB-INF/classes</targetPath> | |||
<includes> | |||
<include>application-${package.environment}.yml</include> | |||
</includes> | |||
</resource> | |||
</resources> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
<version>2.1.11.RELEASE</version> | |||
<configuration> | |||
<finalName>${project.build.finalName}</finalName> | |||
</configuration> | |||
<executions> | |||
<execution> | |||
<goals> | |||
<goal>repackage</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<version>2.7</version> | |||
<configuration> | |||
<skipTests>true</skipTests> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.admin; | |||
import org.mybatis.spring.annotation.MapperScan; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration; | |||
import org.springframework.scheduling.annotation.EnableAsync; | |||
import org.springframework.transaction.annotation.EnableTransactionManagement; | |||
@EnableAsync | |||
//排除原有的Multipart配置 | |||
@SpringBootApplication(scanBasePackages = {"com.tuoheng.*"}, exclude = {MultipartAutoConfiguration.class}) | |||
@MapperScan("com.tuoheng.**.mapper") | |||
@EnableTransactionManagement | |||
public class AdminApplication { | |||
public static void main(String[] args) { | |||
SpringApplication.run(AdminApplication.class, args); | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.tuoheng.admin.controller; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
@RestController | |||
@RequestMapping("/test") | |||
public class TestController { | |||
@GetMapping("/hello") | |||
public String hello() { | |||
return "Hello"; | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
package com.tuoheng.admin.utils; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.tuoheng.common.config.CommonConfig; | |||
import lombok.extern.slf4j.Slf4j; | |||
import java.io.BufferedReader; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.net.HttpURLConnection; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.text.DecimalFormat; | |||
import java.util.Map; | |||
@Slf4j | |||
public class GaodeUtil { | |||
/** | |||
* 逆地理编码 | |||
* | |||
* @param longitude 经度 | |||
* @param latitude 纬度 | |||
* @return | |||
*/ | |||
public static String getGaodeAddress(String longitude, String latitude) { | |||
String address = null; | |||
JSONObject json = new JSONObject(); | |||
try { | |||
json.put("key", CommonConfig.gaodeKey); | |||
json.put("location", longitude + "," + latitude); | |||
log.info("调用高德逆地理编码API请求参数:" + json); | |||
String result = doGet("https://restapi.amap.com/v3/geocode/regeo", json, null); | |||
log.info("调用高德逆地理编码API成功:" + result); | |||
JSONObject resultJson = JSONObject.parseObject(result.trim()); | |||
if("1".equals(resultJson.getString("status"))){ | |||
address = resultJson.getJSONObject("regeocode").getString("formatted_address"); | |||
} | |||
} catch (Exception e) { | |||
log.error("逆地理编码方法异常:", e); | |||
} | |||
return address; | |||
} | |||
/** | |||
* 高德坐标转换 | |||
* | |||
* @param longitude 经度 | |||
* @param latitude 纬度 | |||
* @return | |||
*/ | |||
public static JSONObject getGaodeCoordinate(String longitude, String latitude) { | |||
JSONObject json = new JSONObject(); | |||
JSONObject param = new JSONObject(); | |||
try { | |||
//取六位小数 | |||
DecimalFormat df = new DecimalFormat("0.000000"); | |||
param.put("key", CommonConfig.gaodeKey); | |||
param.put("locations", df.format(Double.valueOf(longitude)) + "," + df.format(Double.valueOf(latitude))); | |||
param.put("coordsys", "gps"); | |||
log.info("调用高德坐标转换API请求参数:" + param); | |||
String result = doGet("https://restapi.amap.com/v3/assistant/coordinate/convert", param, null); | |||
log.info("调用高德坐标转换API成功:" + result); | |||
JSONObject resultJson = JSONObject.parseObject(result.trim()); | |||
if("1".equals(resultJson.getString("status"))){ | |||
String locations = JSONObject.parseObject(result).getString("locations"); | |||
String lonStr = locations.substring(0, locations.indexOf(",")); | |||
String latStr = locations.substring(lonStr.length()+1); | |||
json.put("longitude", lonStr); | |||
json.put("latitude", latStr); | |||
} | |||
} catch (Exception e) { | |||
log.error("高德坐标转换方法异常:", e); | |||
} | |||
return json; | |||
} | |||
public static String doGet(String url, JSONObject data, Map<String, String> properties) { | |||
HttpURLConnection connection = null; | |||
InputStream is = null; | |||
BufferedReader br = null; | |||
String result = null;// 返回结果字符串 | |||
StringBuffer urlNameString = new StringBuffer().append(url).append("?"); | |||
if (data != null) { | |||
for (String key : data.keySet()) { | |||
urlNameString.append(key).append("=").append(data.get(key)).append("&"); | |||
} | |||
url = urlNameString.substring(0, urlNameString.lastIndexOf("&")); | |||
} | |||
try { | |||
// 创建远程url连接对象 | |||
URL httpUrl = new URL(url); | |||
// 通过远程url连接对象打开一个连接,强转成httpURLConnection类 | |||
connection = (HttpURLConnection) httpUrl.openConnection(); | |||
// 设置连接方式:get | |||
connection.setRequestMethod("GET"); | |||
// 设置连接主机服务器的超时时间:15000毫秒 | |||
connection.setConnectTimeout(15000); | |||
// 设置读取远程返回的数据时间:60000毫秒 | |||
connection.setReadTimeout(60000); | |||
if(null != properties && !properties.isEmpty()){ | |||
for (String key:properties.keySet()){ | |||
connection.setRequestProperty(key, properties.get(key)); | |||
} | |||
} | |||
// 发送请求 | |||
connection.connect(); | |||
// 通过connection连接,获取输入流 | |||
if (connection.getResponseCode() == 200) { | |||
is = connection.getInputStream(); | |||
// 封装输入流is,并指定字符集 | |||
br = new BufferedReader(new InputStreamReader(is, "UTF-8")); | |||
// 存放数据 | |||
StringBuffer sbf = new StringBuffer(); | |||
String temp = null; | |||
while ((temp = br.readLine()) != null) { | |||
sbf.append(temp); | |||
sbf.append("\r\n"); | |||
} | |||
result = sbf.toString(); | |||
} | |||
} catch (MalformedURLException e) { | |||
e.printStackTrace(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} finally { | |||
// 关闭资源 | |||
if (null != br) { | |||
try { | |||
br.close(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
if (null != is) { | |||
try { | |||
is.close(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
connection.disconnect();// 关闭远程连接 | |||
} | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,182 @@ | |||
# 端口配置 | |||
server: | |||
port: 9060 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
tomcat: | |||
basedir: /data/java/tuoheng_qmhh/uploads/temp | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: https://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://192.168.11.13:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: idontcare | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 192.168.11.13 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_qmhh/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,188 @@ | |||
# 端口配置 | |||
server: | |||
port: 9060 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
tomcat: | |||
basedir: /data/java/tuoheng_qmhh/uploads/temp | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: http://localhost:9055/api | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: http://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://127.0.0.1:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 127.0.0.1 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: E:\Gitea仓库源码\tuoheng_5gai_new\uploads\ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,188 @@ | |||
# 端口配置 | |||
server: | |||
port: 9060 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
tomcat: | |||
basedir: /data/java/tuoheng_qmhh/uploads/temp | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: https://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://rm-uf6x76i111rb1eo48.mysql.rds.aliyuncs.com:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: TH22#2022 | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: r-uf6r5lm7c7sfdv3ehb.redis.rds.aliyuncs.com | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_hhz/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,187 @@ | |||
# 端口配置 | |||
server: | |||
port: 9060 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
tomcat: | |||
basedir: /data/java/tuoheng_hhz/uploads/temp | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: https://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://192.168.11.242:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: idontcare | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 192.168.11.242 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_hhz/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,25 @@ | |||
spring: | |||
profiles: | |||
active: @package.environment@ | |||
# 服务模块 | |||
devtools: | |||
restart: | |||
# 热部署开关 | |||
enabled: false | |||
# 自定义国际化配置 | |||
messages: | |||
# 国际化资源文件路径 | |||
basename: i18n/messages | |||
encoding: UTF-8 | |||
# MyBatis | |||
mybatis-plus: | |||
mapper-locations: classpath*:mapper/*Mapper.xml | |||
# 实体扫描,多个package用逗号或者分号分隔 | |||
type-aliases-package: com.tuoheng.**.mapper | |||
configuration: | |||
map-underscore-to-camel-case: true | |||
use-generated-keys: true | |||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl | |||
@@ -0,0 +1,4 @@ | |||
log4j.rootLogger=DEBUG, stdout | |||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender | |||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout | |||
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n |
@@ -0,0 +1,67 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --> | |||
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true --> | |||
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。 | |||
当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> | |||
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> | |||
<configuration scan="true" scanPeriod="60 seconds" debug="false"> | |||
<!-- | |||
contextName说明: | |||
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字, | |||
用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。 | |||
--> | |||
<contextName>tuoheng-admin</contextName> | |||
<!--定义日志变量--> | |||
<property name="logging.path" value="/data/java/logs/tuoheng_qmhh"/> | |||
<!--日志格式: [时间] [级别] [线程] [行号] [logger信息] - [日志信息]--> | |||
<property name="logging.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%level][%thread][%L] %logger - %msg%n"/> | |||
<property name="logging.charset" value="UTF-8"/> | |||
<property name="logging.maxHistory" value="15"/> | |||
<property name="logging.totalSizeCap" value="5GB"/> | |||
<property name="logging.maxFileSize" value="40MB"/> | |||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | |||
<encoder> | |||
<pattern>${logging.pattern}</pattern> | |||
<charset>${logging.charset}</charset> | |||
</encoder> | |||
</appender> | |||
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
<File>${logging.path}/admin/tuoheng_hhz.log</File> | |||
<append>true</append> | |||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> | |||
<fileNamePattern>${logging.path}/admin/tuoheng_qmhh-%d-%i.log</fileNamePattern> | |||
<!-- 最大保存天数--> | |||
<maxHistory>${logging.maxHistory}</maxHistory> | |||
<totalSizeCap>${logging.totalSizeCap}</totalSizeCap> | |||
<maxFileSize>${logging.maxFileSize}</maxFileSize> | |||
</rollingPolicy> | |||
<!--编码器--> | |||
<encoder> | |||
<pattern>${logging.pattern}</pattern> | |||
<charset>${logging.charset}</charset> | |||
</encoder> | |||
</appender> | |||
<appender name="file.async" class="ch.qos.logback.classic.AsyncAppender"> | |||
<discardingThreshold>0</discardingThreshold> | |||
<queueSize>512</queueSize> | |||
<includeCallerData>true</includeCallerData> | |||
<appender-ref ref="LOG_FILE" /> | |||
</appender> | |||
<logger name="com.tuoheng" level="DEBUG" additivity="false"> | |||
<appender-ref ref="file.async" /> | |||
</logger> | |||
<!--log4jdbc --> | |||
<logger name="jdbc.sqltiming" level="DEBUG" additivity="false"> | |||
<appender-ref ref="file.async" /> | |||
</logger> | |||
<root level="INFO"> | |||
<appender-ref ref="console" /> | |||
<appender-ref ref="file.async" /> | |||
</root> | |||
</configuration> |
@@ -0,0 +1,47 @@ | |||
#Matser的ip地址 | |||
redis.host=127.0.0.1 | |||
#端口号 | |||
redis.port=6379 | |||
#如果有密码 | |||
redis.password= | |||
#客户端超时时间单位是毫秒 默认是2000 | |||
redis.timeout=10000 | |||
# 连接池最大连接数(使用负值表示没有限制) 默认 8 | |||
redis.lettuce.pool.max-active=8 | |||
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 | |||
redis.lettuce.pool.max-wait=-1 | |||
# 连接池中的最大空闲连接 默认 8 | |||
redis.lettuce.pool.max-idle=8 | |||
# 连接池中的最小空闲连接 默认 0 | |||
redis.lettuce.pool.min-idle=0 | |||
#最大空闲数 | |||
redis.maxIdle=300 | |||
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal | |||
#redis.maxActive=600 | |||
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性 | |||
redis.maxTotal=1000 | |||
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。 | |||
redis.maxWaitMillis=1000 | |||
#连接的最小空闲时间 默认1800000毫秒(30分钟) | |||
redis.minEvictableIdleTimeMillis=300000 | |||
#每次释放连接的最大数目,默认3 | |||
redis.numTestsPerEvictionRun=1024 | |||
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 | |||
redis.timeBetweenEvictionRunsMillis=30000 | |||
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 | |||
redis.testOnBorrow=true | |||
#在空闲时检查有效性, 默认false | |||
redis.testWhileIdle=true | |||
#redis集群配置 | |||
#spring.redis.cluster.nodes=192.168.177.128:7001,192.168.177.128:7002,192.168.177.128:7003,192.168.177.128:7004,192.168.177.128:7005,192.168.177.128:7006 | |||
#spring.redis.cluster.max-redirects=3 | |||
#哨兵模式 | |||
#redis.sentinel.host1=192.168.177.128 | |||
#redis.sentinel.port1=26379 | |||
#redis.sentinel.host2=172.20.1.231 | |||
#redis.sentinel.port2=26379 |
@@ -0,0 +1,239 @@ | |||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<parent> | |||
<artifactId>tuoheng</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-api</artifactId> | |||
<packaging>jar</packaging> | |||
<name>tuoheng-api</name> | |||
<description>小程序服务模块</description> | |||
<!-- 依赖声明 --> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-scratchpad</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-ooxml</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-ooxml-schemas</artifactId> | |||
<version>4.0.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.lowagie</groupId> | |||
<artifactId>itext</artifactId> | |||
<version>2.1.7</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.lowagie</groupId> | |||
<artifactId>itext-rtf</artifactId> | |||
<version>2.1.7</version> | |||
</dependency> | |||
<!-- 引入子模块依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<!-- MySql驱动 --> | |||
<dependency> | |||
<groupId>mysql</groupId> | |||
<artifactId>mysql-connector-java</artifactId> | |||
<scope>runtime</scope> | |||
</dependency> | |||
<!--mybatis-plus 代码自动生成 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-generator</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<!-- 引入阿里数据库连接池 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>druid-spring-boot-starter</artifactId> | |||
<version>1.1.10</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/log4j/log4j --> | |||
<dependency> | |||
<groupId>log4j</groupId> | |||
<artifactId>log4j</artifactId> | |||
<version>1.2.17</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.apache.poi/ooxml-schemas --> | |||
<dependency> | |||
<groupId>com.itextpdf</groupId> | |||
<artifactId>itextpdf</artifactId> | |||
<version>5.4.3</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> | |||
<dependency> | |||
<groupId>org.freemarker</groupId> | |||
<artifactId>freemarker</artifactId> | |||
<version>2.3.30</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.itextpdf</groupId> | |||
<artifactId>itextpdf</artifactId> | |||
<version>5.5.13</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian --> | |||
<dependency> | |||
<groupId>com.itextpdf</groupId> | |||
<artifactId>itext-asian</artifactId> | |||
<version>5.2.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.github.pagehelper</groupId> | |||
<artifactId>pagehelper</artifactId> | |||
<version>5.2.0</version> | |||
</dependency> | |||
<!--阿里云和STS--> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-sts</artifactId> | |||
<version>3.0.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-core</artifactId> | |||
<version>4.4.6</version> | |||
</dependency> | |||
</dependencies> | |||
<profiles> | |||
<!-- 本地开发环境 --> | |||
<profile> | |||
<id>local</id> | |||
<properties> | |||
<package.environment>local</package.environment> | |||
</properties> | |||
<activation> | |||
<activeByDefault>true</activeByDefault> | |||
</activation> | |||
</profile> | |||
<!-- 开发环境 --> | |||
<profile> | |||
<id>dev</id> | |||
<properties> | |||
<package.environment>dev</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 测试环境 --> | |||
<profile> | |||
<id>test</id> | |||
<properties> | |||
<package.environment>test</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 生产环境 --> | |||
<profile> | |||
<id>prod</id> | |||
<properties> | |||
<package.environment>prod</package.environment> | |||
</properties> | |||
</profile> | |||
</profiles> | |||
<build> | |||
<finalName>tuoheng_hhz_api</finalName> | |||
<resources> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
<includes> | |||
<include>**/*.xml</include> | |||
<include>**/*.properties</include> | |||
<include>**/*.yml</include> | |||
</includes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>false</filtering> | |||
<includes> | |||
<include>**/*.docx</include> | |||
</includes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/java</directory> | |||
<includes> | |||
<include>**/*.*</include> | |||
</includes> | |||
<excludes> | |||
<exclude>**/*.java</exclude> | |||
</excludes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
<targetPath>WEB-INF/classes</targetPath> | |||
<includes> | |||
<include>application-${package.environment}.yml</include> | |||
</includes> | |||
</resource> | |||
</resources> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
<version>2.1.11.RELEASE</version> | |||
<configuration> | |||
<finalName>${project.build.finalName}</finalName> | |||
</configuration> | |||
<executions> | |||
<execution> | |||
<goals> | |||
<goal>repackage</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<version>2.7</version> | |||
<configuration> | |||
<skipTests>true</skipTests> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.api; | |||
import org.mybatis.spring.annotation.MapperScan; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.scheduling.annotation.EnableScheduling; | |||
import org.springframework.transaction.annotation.EnableTransactionManagement; | |||
@SpringBootApplication(scanBasePackages = {"com.tuoheng.*"}) | |||
@MapperScan("com.tuoheng.**.mapper") | |||
@EnableTransactionManagement | |||
// 开启定时任务支持 | |||
@EnableScheduling | |||
public class ApiApplication { | |||
public static void main(String[] args) { | |||
SpringApplication.run(ApiApplication.class, args); | |||
System.out.println("(♥◠‿◠)ノ゙ 小程序API启动成功 ლ(´ڡ`ლ)゙"); | |||
} | |||
} |
@@ -0,0 +1,187 @@ | |||
# 端口配置 | |||
server: | |||
port: 9061 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: http://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://192.168.11.13:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: idontcare | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 192.168.11.13 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_hhz/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,188 @@ | |||
# 端口配置 | |||
server: | |||
port: 9061 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: http://localhost:9056/api | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: http://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://192.168.11.242:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: idontcare | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 127.0.0.1 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: E:\拓恒项目2021\tuoheng_5gai\uploads\ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.admin | |||
# 模块名 | |||
moduleName: tuoheng-admin | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: th_ |
@@ -0,0 +1,192 @@ | |||
# 端口配置 | |||
server: | |||
port: 9061 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: https://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://rm-uf6x76i111rb1eo48.mysql.rds.aliyuncs.com:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: TH22#2022 | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: r-uf6r5lm7c7sfdv3ehb.redis.rds.aliyuncs.com | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_hhz/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ | |||
# 日志记录 | |||
logging: | |||
config: classpath:logback.xml |
@@ -0,0 +1,187 @@ | |||
# 端口配置 | |||
server: | |||
port: 9061 | |||
servlet: | |||
# 项目的前缀名 | |||
context-path: /api | |||
# 自定义配置 | |||
tuoheng: | |||
# 图片域名 | |||
image-url: https://image.t-aaron.com/ | |||
# OSS域名 | |||
oss-url: https://ta-tech-image.oss-cn-shanghai.aliyuncs.com | |||
# 视频域名 | |||
video-url: http://vod.play.t-aaron.com/ | |||
# 高德Key | |||
gaodeKey: 8eb26a06684d34501e5a56dcc2f5af15 | |||
#阿里云 | |||
aliyuncsVod: | |||
accessKeyId: LTAI5tE7KWN9fsuGU7DyfYF4 | |||
accessKeySecret: yPPCyfsqWgrTuoz5H4sisY0COclx8E | |||
roleArn: acs:ram::1399733914954856:role/ramosstest | |||
bucketName: ta-tech-image | |||
spring: | |||
# 配置数据源 | |||
datasource: | |||
# 使用阿里的Druid连接池 | |||
type: com.alibaba.druid.pool.DruidDataSource | |||
driver-class-name: com.mysql.cj.jdbc.Driver | |||
# 填写你数据库的url、登录名、密码和数据库名 | |||
url: jdbc:mysql://192.168.11.242:3306/tuoheng_hhz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false | |||
username: root | |||
password: idontcare | |||
druid: | |||
# 连接池的配置信息 | |||
# 初始连接数 | |||
initialSize: 5 | |||
# 最小连接池数量 | |||
minIdle: 5 | |||
# 最大连接池数量 | |||
maxActive: 20 | |||
# 配置获取连接等待超时的时间 | |||
maxWait: 60000 | |||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 | |||
timeBetweenEvictionRunsMillis: 60000 | |||
# 配置一个连接在池中最小生存的时间,单位是毫秒 | |||
minEvictableIdleTimeMillis: 300000 | |||
# 配置一个连接在池中最大生存的时间,单位是毫秒 | |||
maxEvictableIdleTimeMillis: 900000 | |||
# 配置检测连接是否有效 | |||
validationQuery: SELECT 1 FROM DUAL | |||
testWhileIdle: true | |||
testOnBorrow: false | |||
testOnReturn: false | |||
# 打开PSCache,并且指定每个连接上PSCache的大小 | |||
poolPreparedStatements: true | |||
maxPoolPreparedStatementPerConnectionSize: 20 | |||
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 | |||
filters: stat,wall,log4j | |||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录 | |||
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 | |||
# 配置DruidStatFilter | |||
webStatFilter: | |||
enabled: true | |||
url-pattern: "/*" | |||
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" | |||
# 配置DruidStatViewServlet | |||
statViewServlet: | |||
url-pattern: "/druid/*" | |||
# IP白名单(没有配置或者为空,则允许所有访问) | |||
allow: 127.0.0.1,192.168.163.1 | |||
# IP黑名单 (存在共同时,deny优先于allow) | |||
deny: 192.168.1.73 | |||
# 禁用HTML页面上的“Reset All”功能 | |||
reset-enable: false | |||
# 登录名 | |||
login-username: admin | |||
# 登录密码 | |||
login-password: 123456 | |||
# Redis数据源 | |||
redis: | |||
# 缓存库默认索引0 | |||
database: 0 | |||
# Redis服务器地址 | |||
host: 192.168.11.242 | |||
# Redis服务器连接端口 | |||
port: 6379 | |||
# Redis服务器连接密码(默认为空) | |||
password: | |||
# 连接超时时间(毫秒) | |||
timeout: 6000 | |||
# 默认的数据过期时间,主要用于shiro权限管理 | |||
expire: 2592000 | |||
jedis: | |||
pool: | |||
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) | |||
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) | |||
max-idle: 10 # 连接池中的最大空闲连接 | |||
min-idle: 1 # 连接池中的最小空闲连接 | |||
servlet: | |||
multipart: | |||
# 过滤springmvc的文件上传 | |||
enabled: false | |||
# 单个文件最大值 | |||
max-file-size: 50MB | |||
# 上传文件总的最大值 | |||
max-request-size: 100MB | |||
#邮件配置 | |||
mail: | |||
# 设置邮箱主机 | |||
host: smtp.qq.com | |||
# 开启邮箱POP3/SMTP服务,获取客户端授权码(注意并不是邮箱密码,而是授权码) | |||
password: | |||
# 邮箱的用户名 | |||
username: | |||
properties: | |||
mail: | |||
smtp: | |||
# 设置是否需要认证,如果为true,那么用户名和密码就必须的。如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。 | |||
auth: true | |||
starttls: | |||
# STARTTLS[1] 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 | |||
enable: true | |||
require: true | |||
alisms: | |||
accessKeyId: | |||
accessKeySecret: | |||
regionId: cn-hangzhou | |||
signName: 拓恒 | |||
templateCode: | |||
file: | |||
#上传的服务器上的映射文件夹 | |||
accessPath: /uploads/ | |||
#静态资源对外暴露的访问路径 | |||
staticAccessPath: /** | |||
#静态资源实际存储路径 | |||
uploadFolder: /data/java/tuoheng_hhz/uploads/ | |||
# Shiro | |||
shiro: | |||
cipher-key: f/SX5TIve5WWzT4aQlABJA== | |||
cookie-name: shiro-cookie2 | |||
user: | |||
# 登录地址 | |||
loginUrl: /login | |||
# 权限认证失败地址 | |||
unauthorizedUrl: /unauth | |||
# 首页地址 | |||
indexUrl: /index | |||
# 验证码开关 | |||
captchaEnabled: true | |||
# 验证码类型 math 数组计算 char 字符 | |||
captchaType: math | |||
cookie: | |||
# 设置Cookie的域名 默认空,即当前访问的域名 | |||
domain: | |||
# 设置cookie的有效访问路径 | |||
path: / | |||
# 设置HttpOnly属性 | |||
httpOnly: true | |||
# 设置Cookie的过期时间,天为单位 | |||
maxAge: 30 | |||
session: | |||
# Session超时时间(默认30分钟) | |||
expireTime: 300 | |||
# 同步session到数据库的周期(默认1分钟) | |||
dbSyncPeriod: 1 | |||
# 相隔多久检查一次session的有效性,默认就是10分钟 | |||
validationInterval: 10 | |||
# 代码生成 | |||
generate: | |||
# 作者 | |||
author: 拓恒 | |||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool | |||
packageName: com.tuoheng.system | |||
# 模块名 | |||
moduleName: tuoheng-system | |||
# 自动去除表前缀,默认是true | |||
autoRemovePre: true | |||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) | |||
tablePrefix: sys_ |
@@ -0,0 +1,24 @@ | |||
spring: | |||
profiles: | |||
active: @package.environment@ | |||
# 服务模块 | |||
devtools: | |||
restart: | |||
# 热部署开关 | |||
enabled: false | |||
# 自定义国际化配置 | |||
messages: | |||
# 国际化资源文件路径 | |||
basename: i18n/messages | |||
encoding: UTF-8 | |||
# MyBatis | |||
mybatis-plus: | |||
mapper-locations: classpath*:mapper/*Mapper.xml | |||
# 实体扫描,多个package用逗号或者分号分隔 | |||
type-aliases-package: com.tuoheng.**.mapper | |||
configuration: | |||
map-underscore-to-camel-case: true | |||
use-generated-keys: true | |||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl |
@@ -0,0 +1,4 @@ | |||
log4j.rootLogger=DEBUG, stdout | |||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender | |||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout | |||
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n |
@@ -0,0 +1,62 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<configuration scan="true" scanPeriod="60 seconds" debug="false"> | |||
<!-- | |||
contextName说明: | |||
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字, | |||
用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。 | |||
--> | |||
<contextName>tuoheng-api</contextName> | |||
<!--定义日志变量--> | |||
<property name="logging.path" value="/data/java/logs/tuoheng_qmhh"/> | |||
<!--日志格式: [时间] [级别] [线程] [行号] [logger信息] - [日志信息]--> | |||
<property name="logging.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%level][%thread][%L] %logger - %msg%n"/> | |||
<property name="logging.charset" value="UTF-8"/> | |||
<property name="logging.maxHistory" value="15"/> | |||
<property name="logging.totalSizeCap" value="5GB"/> | |||
<property name="logging.maxFileSize" value="40MB"/> | |||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | |||
<encoder> | |||
<pattern>${logging.pattern}</pattern> | |||
<charset>${logging.charset}</charset> | |||
</encoder> | |||
</appender> | |||
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
<File>${logging.path}/api/tuoheng_hhz.log</File> | |||
<append>true</append> | |||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> | |||
<fileNamePattern>${logging.path}/api/tuoheng_qmhh-%d-%i.log</fileNamePattern> | |||
<!-- 最大保存天数--> | |||
<maxHistory>${logging.maxHistory}</maxHistory> | |||
<totalSizeCap>${logging.totalSizeCap}</totalSizeCap> | |||
<maxFileSize>${logging.maxFileSize}</maxFileSize> | |||
</rollingPolicy> | |||
<!--编码器--> | |||
<encoder> | |||
<pattern>${logging.pattern}</pattern> | |||
<charset>${logging.charset}</charset> | |||
</encoder> | |||
</appender> | |||
<appender name="file.async" class="ch.qos.logback.classic.AsyncAppender"> | |||
<discardingThreshold>0</discardingThreshold> | |||
<queueSize>512</queueSize> | |||
<includeCallerData>true</includeCallerData> | |||
<appender-ref ref="LOG_FILE" /> | |||
</appender> | |||
<logger name="com.tuoheng" level="DEBUG" additivity="false"> | |||
<appender-ref ref="file.async" /> | |||
</logger> | |||
<!--log4jdbc --> | |||
<logger name="jdbc.sqltiming" level="DEBUG" additivity="false"> | |||
<appender-ref ref="file.async" /> | |||
</logger> | |||
<root level="INFO"> | |||
<appender-ref ref="console" /> | |||
<appender-ref ref="file.async" /> | |||
</root> | |||
</configuration> |
@@ -0,0 +1,173 @@ | |||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<!-- 子模块的parent要使用顶层的父模块--> | |||
<parent> | |||
<artifactId>tuoheng</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-common</artifactId> | |||
<packaging>jar</packaging> | |||
<name>tuoheng-common</name> | |||
<description>Demo project for Spring Boot</description> | |||
<!-- 依赖声明 --> | |||
<dependencies> | |||
<!--任务调度--> | |||
<dependency> | |||
<groupId>com.xuxueli</groupId> | |||
<artifactId>xxl-job-core</artifactId> | |||
<version>2.3.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.drewnoakes</groupId> | |||
<artifactId>metadata-extractor</artifactId> | |||
<version>2.6.2</version> | |||
</dependency> | |||
<!--mybatis-plus 起始依赖 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-boot-starter</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<!-- WEB 模块依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<!-- Redis 起始依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
<version>1.2.62</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-lang3</artifactId> | |||
<version>3.9</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<version>2.5</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-aop</artifactId> | |||
</dependency> | |||
<!-- UserAgent工具类 --> | |||
<dependency> | |||
<groupId>eu.bitwalker</groupId> | |||
<artifactId>UserAgentUtils</artifactId> | |||
<version>1.20</version> | |||
</dependency> | |||
<!-- Excel依赖 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>easyexcel</artifactId> | |||
<version>1.1.2-beat1</version> | |||
</dependency> | |||
<!-- 文件上传 --> | |||
<dependency> | |||
<groupId>commons-fileupload</groupId> | |||
<artifactId>commons-fileupload</artifactId> | |||
<version>1.3.1</version> | |||
</dependency> | |||
<!-- 阿里短信SDK --> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-core</artifactId> | |||
<version>4.1.0</version> | |||
</dependency> | |||
<!-- 邮件发送起始依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-mail</artifactId> | |||
</dependency> | |||
<!-- thymeleaf 模板引擎依赖(如:模板邮件) --> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-thymeleaf</artifactId> | |||
</dependency> | |||
<!-- 极光推送 --> | |||
<dependency> | |||
<groupId>cn.jpush.api</groupId> | |||
<artifactId>jpush-client</artifactId> | |||
<version>3.3.10</version> | |||
</dependency> | |||
<!-- 文件压缩、解压缩 --> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-compress</artifactId> | |||
<version>1.8.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>net.coobird</groupId> | |||
<artifactId>thumbnailator</artifactId> | |||
<version>0.4.8</version> | |||
</dependency> | |||
<!-- JWT依赖 --> | |||
<dependency> | |||
<groupId>io.jsonwebtoken</groupId> | |||
<artifactId>jjwt</artifactId> | |||
<version>0.9.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.auth0</groupId> | |||
<artifactId>java-jwt</artifactId> | |||
<version>3.4.0</version> | |||
</dependency> | |||
<!-- 二维码生成依赖 --> | |||
<dependency> | |||
<groupId>com.google.zxing</groupId> | |||
<artifactId>core</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.zxing</groupId> | |||
<artifactId>javase</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<!-- https://gitee.com/loolly/hutool --> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-core</artifactId> | |||
<version>5.4.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-log</artifactId> | |||
<version>5.4.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.validation</groupId> | |||
<artifactId>validation-api</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.sun.mail</groupId> | |||
<artifactId>javax.mail</artifactId> | |||
<version>1.6.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-test</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,112 @@ | |||
package com.tuoheng.common.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 自定义导出Excel | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Target(ElementType.FIELD) | |||
public @interface Excel { | |||
/** | |||
* 导出到Excel中的名字. | |||
*/ | |||
String name() default ""; | |||
/** | |||
* 日期格式, 如: yyyy-MM-dd | |||
*/ | |||
String dateFormat() default ""; | |||
/** | |||
* 读取内容转表达式 (如: 0=男,1=女,2=未知) | |||
*/ | |||
String readConverterExp() default ""; | |||
/** | |||
* 导出类型(0数字 1字符串) | |||
*/ | |||
ColumnType cellType() default ColumnType.STRING; | |||
/** | |||
* 导出时在excel中每个列的高度 单位为字符 | |||
*/ | |||
double height() default 14; | |||
/** | |||
* 导出时在excel中每个列的宽 单位为字符 | |||
*/ | |||
double width() default 16; | |||
/** | |||
* 文字后缀,如% 90 变成90% | |||
*/ | |||
String suffix() default ""; | |||
/** | |||
* 当值为空时,字段的默认值 | |||
*/ | |||
String defaultValue() default ""; | |||
/** | |||
* 提示信息 | |||
*/ | |||
String prompt() default ""; | |||
/** | |||
* 设置只能选择不能输入的列内容. | |||
*/ | |||
String[] combo() default {}; | |||
/** | |||
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. | |||
*/ | |||
boolean isExport() default true; | |||
/** | |||
* 另一个类中的属性名称,支持多级获取,以小数点隔开 | |||
*/ | |||
String targetAttr() default ""; | |||
/** | |||
* 字段类型(0:导出导入;1:仅导出;2:仅导入) | |||
*/ | |||
Type type() default Type.ALL; | |||
/** | |||
* 导出类型枚举 | |||
*/ | |||
enum Type { | |||
ALL(0), EXPORT(1), IMPORT(2); | |||
private final int value; | |||
Type(int value) { | |||
this.value = value; | |||
} | |||
public int value() { | |||
return this.value; | |||
} | |||
} | |||
/** | |||
* 列类型枚举 | |||
*/ | |||
enum ColumnType { | |||
NUMERIC(0), STRING(1); | |||
private final int value; | |||
ColumnType(int value) { | |||
this.value = value; | |||
} | |||
public int value() { | |||
return this.value; | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
package com.tuoheng.common.annotation; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 自定义导出Excel注解集 | |||
*/ | |||
@Target(ElementType.FIELD) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface Excels { | |||
// Excel数组 | |||
Excel[] value(); | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.tuoheng.common.annotation; | |||
import com.tuoheng.common.enums.LockType; | |||
import org.springframework.core.annotation.AliasFor; | |||
import java.lang.annotation.*; | |||
import java.util.concurrent.TimeUnit; | |||
@Target({ElementType.METHOD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Inherited | |||
public @interface LockAction { | |||
/** 锁的资源,key。支持spring El表达式*/ | |||
@AliasFor("key") | |||
String value() default "'default'"; | |||
@AliasFor("value") | |||
String key() default "'default'"; | |||
String prefix() default "lock-"; | |||
/** 锁类型*/ | |||
LockType lockType() default LockType.REENTRANT_LOCK; | |||
/** 获取锁等待时间,默认3秒*/ | |||
long waitTime() default 3000L; | |||
/** 锁自动释放时间,默认30秒*/ | |||
long leaseTime() default 30000L; | |||
/** 时间单位(获取锁等待时间和持锁时间都用此单位)*/ | |||
TimeUnit unit() default TimeUnit.MILLISECONDS; | |||
} |
@@ -0,0 +1,36 @@ | |||
package com.tuoheng.common.annotation; | |||
import com.tuoheng.common.enums.LogType; | |||
import com.tuoheng.common.enums.OperType; | |||
import java.lang.annotation.*; | |||
/** | |||
* 自定义操作日志注解 | |||
*/ | |||
@Target({ElementType.PARAMETER, ElementType.METHOD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface Log { | |||
/** | |||
* 模块 | |||
*/ | |||
String title() default ""; | |||
/** | |||
* 日志类型 | |||
*/ | |||
LogType logType() default LogType.OTHER; | |||
/** | |||
* 操作人类别 | |||
*/ | |||
OperType operType() default OperType.MANAGE; | |||
/** | |||
* 是否保存请求的参数 | |||
*/ | |||
boolean isSaveRequestData() default true; | |||
} |
@@ -0,0 +1,4 @@ | |||
package com.tuoheng.common.common; | |||
public class BaseController { | |||
} |
@@ -0,0 +1,57 @@ | |||
package com.tuoheng.common.common; | |||
import com.baomidou.mybatisplus.annotation.IdType; | |||
import com.baomidou.mybatisplus.annotation.TableId; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import lombok.Data; | |||
import org.springframework.format.annotation.DateTimeFormat; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
/** | |||
* 基类实体对象 | |||
* | |||
* @author 拓恒 | |||
* @date 2019/11/28 | |||
*/ | |||
@Data | |||
public class BaseEntity implements Serializable { | |||
/** | |||
* 主键ID | |||
*/ | |||
@TableId(value = "id", type = IdType.AUTO) | |||
private Integer id; | |||
/** | |||
* 添加人 | |||
*/ | |||
private Integer createUser; | |||
/** | |||
* 创建时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date createTime; | |||
/** | |||
* 更新人 | |||
*/ | |||
private Integer updateUser; | |||
/** | |||
* 更新时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date updateTime; | |||
/** | |||
* 有效标识 | |||
*/ | |||
private Integer mark; | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.tuoheng.common.common; | |||
import lombok.Data; | |||
/** | |||
* 查询对象基类 | |||
*/ | |||
@Data | |||
public class BaseQuery { | |||
/** | |||
* 页码 | |||
*/ | |||
private Integer page; | |||
/** | |||
* 每页数 | |||
*/ | |||
private Integer limit; | |||
} |
@@ -0,0 +1,37 @@ | |||
package com.tuoheng.common.common; | |||
import com.tuoheng.common.constant.Constant; | |||
import lombok.Data; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* 结果集 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@Data | |||
@Accessors(chain = true) | |||
public class BaseResponse<T> { | |||
/** | |||
* success:成功,fail:业务返回的失败,error:非业务异常失败 | |||
*/ | |||
private String status = Constant.SUCCESS; | |||
/** | |||
* 状态码 | |||
**/ | |||
private Integer code; | |||
/** | |||
* 结果描述 | |||
**/ | |||
private String message; | |||
/** | |||
* 结果数据 | |||
**/ | |||
private T data; | |||
} | |||
@@ -0,0 +1,188 @@ | |||
package com.tuoheng.common.common; | |||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; | |||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
import com.tuoheng.common.utils.DateUtils; | |||
import com.tuoheng.common.utils.JsonResult; | |||
import org.springframework.util.StringUtils; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements IBaseService<T> { | |||
/** | |||
* 根据查询条件获取数据列表 | |||
* | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult getList(BaseQuery query) { | |||
return null; | |||
} | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult info(Integer id) { | |||
if (id == null && id <= 0) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
Object result = this.getInfo(id); | |||
return JsonResult.success(result); | |||
} | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public Object getInfo(Serializable id) { | |||
T entity = this.getById(id); | |||
return entity; | |||
} | |||
/** | |||
* 传入实体对象添加记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult add(T entity) { | |||
entity.setCreateTime(DateUtils.now()); | |||
entity.setMark(1); | |||
boolean result = this.save(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success(); | |||
} | |||
/** | |||
* 传入实体对象更新记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult update(T entity) { | |||
entity.setUpdateTime(DateUtils.now()); | |||
boolean result = this.updateById(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success(); | |||
} | |||
/** | |||
* 根据实体对象添加、编辑记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult edit(T entity) { | |||
if (entity == null) { | |||
return JsonResult.error("实体对象不存在"); | |||
} | |||
if (entity.getId() != null && entity.getId() > 0) { | |||
// 修改记录 | |||
return this.update(entity); | |||
} else { | |||
// 新增记录 | |||
return this.add(entity); | |||
} | |||
} | |||
/** | |||
* 删除记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult delete(T entity) { | |||
entity.setUpdateTime(DateUtils.now()); | |||
entity.setMark(0); | |||
boolean result = this.updateById(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult deleteById(Integer id) { | |||
if (StringUtils.isEmpty(id)) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
// 设置Mark=0 | |||
UpdateWrapper updateWrapper = new UpdateWrapper(); | |||
updateWrapper.set("mark", 0); | |||
updateWrapper.eq("id", id); | |||
boolean result = update(updateWrapper); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param ids 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult deleteByIds(Integer[] ids) { | |||
if (StringUtils.isEmpty(ids)) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
// String[] item = ids.split(","); | |||
// 设置Mark=0 | |||
UpdateWrapper updateWrapper = new UpdateWrapper(); | |||
updateWrapper.set("mark", 0); | |||
updateWrapper.in("id", ids); | |||
boolean result = update(updateWrapper); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 设置状态 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult setStatus(T entity) { | |||
return this.update(entity); | |||
} | |||
/** | |||
* 导出Excel | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<T> exportExcel() { | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.common; | |||
/** | |||
* 枚举类 封装 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public interface ExceptionInterface { | |||
/** | |||
* 获取错误码 | |||
* | |||
* @return | |||
*/ | |||
int getCode(); | |||
/** | |||
* 获取异常信息 | |||
* | |||
* @return | |||
*/ | |||
String getMessage(); | |||
} |
@@ -0,0 +1,100 @@ | |||
package com.tuoheng.common.common; | |||
import com.baomidou.mybatisplus.extension.service.IService; | |||
import com.tuoheng.common.utils.JsonResult; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
public interface IBaseService<T> extends IService<T> { | |||
/** | |||
* 根据查询条件获取数据列表 | |||
* | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
JsonResult getList(BaseQuery query); | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
JsonResult info(Integer id); | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
Object getInfo(Serializable id); | |||
/** | |||
* 根据实体对象添加记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult add(T entity); | |||
/** | |||
* 根据实体对象更新记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult update(T entity); | |||
/** | |||
* 根据实体对象添加、编辑记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult edit(T entity); | |||
/** | |||
* 删除记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult delete(T entity); | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
JsonResult deleteById(Integer id); | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param ids 记录ID | |||
* @return | |||
*/ | |||
JsonResult deleteByIds(Integer[] ids); | |||
/** | |||
* 设置状态 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult setStatus(T entity); | |||
/** | |||
* 导出Excel | |||
* | |||
* @return | |||
*/ | |||
List<T> exportExcel(); | |||
} |
@@ -0,0 +1,52 @@ | |||
package com.tuoheng.common.common; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* 全局枚举常量类 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@Accessors(chain = true) | |||
@AllArgsConstructor | |||
public enum OperationEnum implements ExceptionInterface { | |||
/** | |||
* 登陆成功 | |||
*/ | |||
LOGIN_SUCCESS(200, "登陆成功!"), | |||
/** | |||
* 新增操作成功 | |||
*/ | |||
SAVE_SUCCESS(201, "新增成功!"), | |||
/** | |||
* 修改操作成功 | |||
*/ | |||
UPDATE_SUCCESS(202, "修改成功!"), | |||
/** | |||
* 删除操作成功 | |||
*/ | |||
DELETE_SUCCESS(203, "删除成功!"), | |||
/** | |||
* 操作异常 | |||
*/ | |||
OPERATION_ERROR(204, "操作失败!"), | |||
/** | |||
* 操作成功 | |||
*/ | |||
OPERATION_SUCCESS(205, "操作成功!"); | |||
/** | |||
* 结果类型CODE | |||
*/ | |||
@Getter | |||
private final int code; | |||
/** | |||
* 结果类型描述 | |||
*/ | |||
@Getter | |||
private final String message; | |||
} |
@@ -0,0 +1,96 @@ | |||
package com.tuoheng.common.common; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
/** | |||
* 系统异常 枚举 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@AllArgsConstructor | |||
public enum SysExceptionEnum implements ExceptionInterface { | |||
/* ===========================User====================================== */ | |||
/** | |||
* 数据校验异常 | |||
*/ | |||
PARAMETER_EMPTY_EXCEPTION(10000, null), | |||
/** | |||
* 登录用户名不能为空 | |||
*/ | |||
ACCOUNT_IS_EMPTY(10001, "登录用户名不能为空!"), | |||
/** | |||
* 用户名不存在 | |||
*/ | |||
PASSWORD_IS_EMPTY(10002, "登录密码不能为空!"), | |||
/** | |||
* 用户信息不存在 | |||
*/ | |||
ACCOUNT_NOT_EXIST(10003, "用户信息不存在!"), | |||
/** | |||
* 用户名或密码错误 | |||
*/ | |||
ACCOUNT_OR_PASSWORD_ERROR(10004, "用户名或密码错误,请重新输入!"), | |||
/** | |||
* token为空,鉴权失败 | |||
*/ | |||
TOKEN_IS_EMPTY(10005, "token不能为空!"), | |||
/** | |||
* token已失效 | |||
*/ | |||
TOKEN_EXPIRED_EXCEPTION(10006,"token 已失效!"), | |||
/** | |||
* token 验证异常 | |||
*/ | |||
JWT_VERIFICATION_EXCEPTION(10007,"token 验证异常!"), | |||
/** | |||
* 无权访问 | |||
*/ | |||
PERMISSION_NOT(10008, "无访问权限"), | |||
/** | |||
* 系统发生异常 | |||
*/ | |||
SYS_EXCEPTION(10009, "系统发生异常!"), | |||
/** | |||
* 系统数据访问异常 | |||
*/ | |||
DATAACCESS_EXCEPTION(100010, "系统数据访问异常!"), | |||
/** | |||
* 创建线程失败异常 | |||
*/ | |||
INCREMENT_LESS_THAN_ZERO(10011, "递增因子小于0!"), | |||
/** | |||
* 文件导出关闭流异常 | |||
*/ | |||
EXPORT_EXCEPTION(10012, "文件导出关闭流异常!"), | |||
/** | |||
* 文件读取异常 | |||
*/ | |||
EXPORT_READ_EXCEPTION(10013, "文件读取异常!"), | |||
/** | |||
* 账号已被禁用 | |||
*/ | |||
ACCOUNT_IS_DISABLE(123,"您的账号已被禁用,请联系管理员"), | |||
; | |||
@Getter | |||
private final int code; | |||
@Getter | |||
private final String message; | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.tuoheng.common.config; | |||
import lombok.Data; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* 阿里短信配置类 | |||
*/ | |||
// 让Spring启动的时候扫描到该类,并添加到Spring容器中 | |||
@Configuration | |||
// 设置前缀 | |||
@ConfigurationProperties(prefix = "spring.alisms") | |||
@Data | |||
public class AliSmsConfig { | |||
/** | |||
* KEY | |||
*/ | |||
private String accessKeyId; | |||
/** | |||
* 密钥 | |||
*/ | |||
private String accessKeySecret; | |||
/** | |||
* 区域ID | |||
*/ | |||
private String regionId; | |||
/** | |||
* 短信签名 | |||
*/ | |||
private String signName; | |||
/** | |||
* 短信模板ID | |||
*/ | |||
private String templateCode; | |||
} |
@@ -0,0 +1,71 @@ | |||
package com.tuoheng.common.config; | |||
import lombok.Data; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
@Configuration | |||
@Data | |||
public class CommonConfig { | |||
/** | |||
* 图片域名 | |||
*/ | |||
public static String imageURL; | |||
/** | |||
* OSS域名 | |||
*/ | |||
public static String ossURL; | |||
/** | |||
* 视频域名 | |||
*/ | |||
public static String videoURL; | |||
/** | |||
* 高德KEY | |||
*/ | |||
public static String gaodeKey; | |||
/** | |||
* 图片域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.image-url}") | |||
public void setImageURL(String url) { | |||
imageURL = url; | |||
} | |||
/** | |||
* 阿里云OSS域名 | |||
* | |||
* @param url 图片地址 | |||
*/ | |||
@Value("${tuoheng.oss-url}") | |||
public void setOssURL(String url) { | |||
ossURL = url; | |||
} | |||
/** | |||
* 视频域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.video-url}") | |||
public void setVideoURL(String url) { | |||
videoURL = url; | |||
} | |||
/** | |||
* 高德KEY赋值 | |||
* | |||
* @param key 高德KEY | |||
*/ | |||
@Value("${tuoheng.gaodeKey}") | |||
public void setGaodeKey(String key) { | |||
gaodeKey = key; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.tuoheng.common.config; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.web.servlet.LocaleResolver; | |||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | |||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; | |||
import org.springframework.web.servlet.i18n.SessionLocaleResolver; | |||
import java.util.Locale; | |||
/** | |||
* 国际化配置文件 | |||
*/ | |||
@Configuration | |||
public class I18nConfig implements WebMvcConfigurer { | |||
@Bean | |||
public LocaleResolver localeResolver() { | |||
SessionLocaleResolver slr = new SessionLocaleResolver(); | |||
// 默认语言 | |||
slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); | |||
return slr; | |||
} | |||
@Bean | |||
public LocaleChangeInterceptor localeChangeInterceptor() { | |||
LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); | |||
// 参数名 | |||
lci.setParamName("lang"); | |||
return lci; | |||
} | |||
@Override | |||
public void addInterceptors(InterceptorRegistry registry) { | |||
registry.addInterceptor(localeChangeInterceptor()); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.tuoheng.common.config; | |||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.transaction.annotation.EnableTransactionManagement; | |||
/** | |||
* 分页插件配置类 | |||
*/ | |||
@EnableTransactionManagement(proxyTargetClass = true) | |||
@Configuration | |||
public class MybatisPlusConfig { | |||
/** | |||
* 分页插件 | |||
* | |||
* @return | |||
*/ | |||
@Bean | |||
public PaginationInterceptor paginationInterceptor() { | |||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); | |||
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); | |||
return paginationInterceptor; | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
package com.tuoheng.common.config; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@Builder | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class QrImage { | |||
/** | |||
* 二维码的内容(非空) | |||
*/ | |||
private String qrCodeContent; | |||
/** | |||
* 二维码的宽度(非空) | |||
*/ | |||
private Integer qrCodeWidth; | |||
/** | |||
* 二维码的高度(非空) | |||
*/ | |||
private Integer qrCodeHeight; | |||
/** | |||
* 二维码内嵌图片的文件路径(为空则表示:二维码中间不嵌套图片) | |||
*/ | |||
private String embeddedImgFilePath; | |||
/** | |||
* 文字的大小(即:正方形文字的长度、宽度)(非空) | |||
*/ | |||
private Integer wordSize; | |||
/** | |||
* 文字的内容(非空) | |||
*/ | |||
private String wordContent; | |||
/** | |||
* 二维码文件的输出路径(非空) | |||
*/ | |||
private String qrCodeFileOutputPath; | |||
} |
@@ -0,0 +1,164 @@ | |||
package com.tuoheng.common.config; | |||
import com.tuoheng.common.utils.RedisUtils; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
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.StringRedisSerializer; | |||
import java.util.Locale; | |||
@Configuration | |||
//@PropertySource("classpath:redis.properties") | |||
public class RedisConfig { | |||
@Value("${spring.redis.host}") | |||
private String hostName; | |||
@Value("${spring.redis.port}") | |||
private Integer port; | |||
@Value("${spring.redis.password}") | |||
private String password; | |||
@Value("${spring.redis.timeout}") | |||
private Integer timeout; | |||
/* | |||
@Value("${redis.maxIdle}") | |||
private Integer maxIdle; | |||
@Value("${redis.maxTotal}") | |||
private Integer maxTotal; | |||
@Value("${redis.maxWaitMillis}") | |||
private Integer maxWaitMillis; | |||
@Value("${redis.minEvictableIdleTimeMillis}") | |||
private Integer minEvictableIdleTimeMillis; | |||
@Value("${redis.numTestsPerEvictionRun}") | |||
private Integer numTestsPerEvictionRun; | |||
@Value("${redis.timeBetweenEvictionRunsMillis}") | |||
private long timeBetweenEvictionRunsMillis; | |||
@Value("${redis.testOnBorrow}") | |||
private boolean testOnBorrow; | |||
@Value("${redis.testWhileIdle}") | |||
private boolean testWhileIdle; | |||
*/ | |||
/** | |||
* JedisPoolConfig 连接池 | |||
* @return | |||
@Bean public JedisPoolConfig jedisPoolConfig() { | |||
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); | |||
// 最大空闲数 | |||
jedisPoolConfig.setMaxIdle(maxIdle); | |||
// 连接池的最大数据库连接数 | |||
jedisPoolConfig.setMaxTotal(maxTotal); | |||
// 最大建立连接等待时间 | |||
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); | |||
// 逐出连接的最小空闲时间 默认1800000毫秒(30分钟) | |||
jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); | |||
// 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 | |||
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun); | |||
// 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 | |||
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); | |||
// 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 | |||
jedisPoolConfig.setTestOnBorrow(testOnBorrow); | |||
// 在空闲时检查有效性, 默认false | |||
jedisPoolConfig.setTestWhileIdle(testWhileIdle); | |||
return jedisPoolConfig; | |||
} | |||
/** | |||
* 单机版配置 | |||
* @Title: JedisConnectionFactory | |||
* @param @param jedisPoolConfig | |||
* @param @return | |||
* @return JedisConnectionFactory | |||
* @autor lpl | |||
* @date 2018年2月24日 | |||
* @throws | |||
@Bean public JedisConnectionFactory jedisConnectionFactory(){ | |||
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); | |||
redisStandaloneConfiguration.setHostName(hostName); | |||
redisStandaloneConfiguration.setPort(port); | |||
redisStandaloneConfiguration.setPassword(password); | |||
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfigurationBuilder = JedisClientConfiguration.builder(); | |||
jedisClientConfigurationBuilder.connectTimeout(Duration.ofMillis(timeout)); | |||
JedisConnectionFactory factory = new JedisConnectionFactory(redisStandaloneConfiguration,jedisClientConfigurationBuilder.build()); | |||
return factory; | |||
} | |||
*/ | |||
/** | |||
* 实例化 RedisTemplate 对象 jredis实现方式,springboot2.x以后使用下面的方法 | |||
* | |||
* @return | |||
@Bean public RedisTemplate<String, Object> functionDomainRedisTemplate(RedisConnectionFactory redisConnectionFactory) { | |||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); | |||
initDomainRedisTemplate(redisTemplate, redisConnectionFactory); | |||
return redisTemplate; | |||
} | |||
*/ | |||
/** | |||
* lettuce实现redis方式 | |||
* | |||
* @param redisConnectionFactory | |||
* @return | |||
*/ | |||
@Bean | |||
public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { | |||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); | |||
initDomainRedisTemplate(redisTemplate, redisConnectionFactory); | |||
return redisTemplate; | |||
} | |||
/** | |||
* 设置数据存入 redis 的序列化方式,并开启事务 | |||
* | |||
* @param redisTemplate | |||
* @param factory | |||
*/ | |||
private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { | |||
//如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String! | |||
redisTemplate.setKeySerializer(new StringRedisSerializer()); | |||
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); | |||
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); | |||
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); | |||
// 开启事务 | |||
redisTemplate.setEnableTransactionSupport(true); | |||
redisTemplate.setConnectionFactory(factory); | |||
} | |||
/** | |||
* 注入封装RedisTemplate | |||
* | |||
* @return RedisUtil | |||
* @throws | |||
* @Title: redisUtil | |||
* @autor lpl | |||
* @date 2017年12月21日 | |||
*/ | |||
@Bean(name = "redisUtils") | |||
public RedisUtils redisUtils(RedisTemplate<String, Object> redisTemplate) { | |||
RedisUtils redisUtil = new RedisUtils(); | |||
redisUtil.setRedisTemplate(redisTemplate); | |||
return redisUtil; | |||
} | |||
// @Bean("redissonClient") | |||
// public Redisson getRedisson(){ | |||
// Config config = new Config(); | |||
// String redisson_url = String.format(Locale.ENGLISH, "redis://%s:%s", hostName, port); | |||
// config.useSingleServer().setAddress(redisson_url).setDatabase(0); | |||
// return (Redisson) Redisson.create(config); | |||
// } | |||
} |
@@ -0,0 +1,58 @@ | |||
package com.tuoheng.common.config; | |||
import com.tuoheng.common.utils.ThreadUtils; | |||
import org.apache.commons.lang3.concurrent.BasicThreadFactory; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import java.util.concurrent.ScheduledThreadPoolExecutor; | |||
import java.util.concurrent.ThreadPoolExecutor; | |||
/** | |||
* 线程池配置 | |||
*/ | |||
@Configuration | |||
public class ThreadPoolConfig { | |||
// 核心线程池大小 | |||
private int corePoolSize = 50; | |||
// 最大可创建的线程数 | |||
private int maxPoolSize = 200; | |||
// 队列最大长度 | |||
private int queueCapacity = 1000; | |||
// 线程池维护线程所允许的空闲时间 | |||
private int keepAliveSeconds = 300; | |||
@Bean(name = "threadPoolTaskExecutor") | |||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() { | |||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | |||
executor.setMaxPoolSize(maxPoolSize); | |||
executor.setCorePoolSize(corePoolSize); | |||
executor.setQueueCapacity(queueCapacity); | |||
executor.setKeepAliveSeconds(keepAliveSeconds); | |||
// 线程池对拒绝任务(无线程可用)的处理策略 | |||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); | |||
return executor; | |||
} | |||
/** | |||
* 执行周期性或定时任务 | |||
*/ | |||
@Bean(name = "scheduledExecutorService") | |||
protected ScheduledExecutorService scheduledExecutorService() { | |||
return new ScheduledThreadPoolExecutor(corePoolSize, | |||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { | |||
@Override | |||
protected void afterExecute(Runnable r, Throwable t) { | |||
super.afterExecute(r, t); | |||
ThreadUtils.printException(r, t); | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.tuoheng.common.config; | |||
import lombok.Data; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* 文件上传配置 | |||
*/ | |||
@Configuration | |||
@Data | |||
public class UploadFileConfig { | |||
/** | |||
* 上传目录 | |||
*/ | |||
public static String uploadFolder; | |||
/** | |||
* 访问路径 | |||
*/ | |||
public static String staticAccessPath; | |||
/** | |||
* 上传服务器的映射文件夹 | |||
*/ | |||
public static String accessPath; | |||
@Value("${file.uploadFolder}") | |||
public void setUploadFolder(String path) { | |||
uploadFolder = path; | |||
} | |||
@Value("${file.staticAccessPath}") | |||
public void setStaticAccessPath(String path) { | |||
staticAccessPath = path; | |||
} | |||
@Value("${file.accessPath}") | |||
public void setAccessPath(String path) { | |||
accessPath = path; | |||
} | |||
} |
@@ -0,0 +1,83 @@ | |||
package com.tuoheng.common.config; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.web.cors.CorsConfiguration; | |||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | |||
import org.springframework.web.filter.CorsFilter; | |||
import org.springframework.web.servlet.config.annotation.CorsRegistry; | |||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; | |||
/** | |||
* 设置虚拟路径,访问绝对路径下资源 | |||
*/ | |||
@Configuration | |||
public class WebMvcConfig extends WebMvcConfigurationSupport { | |||
@Value("${file.staticAccessPath}") | |||
private String staticAccessPath; | |||
@Value("${file.uploadFolder}") | |||
private String uploadFolder; | |||
static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE"}; | |||
/** | |||
* 注册静态文件的自定义映射路径 | |||
* | |||
* @param registry | |||
*/ | |||
@Override | |||
protected void addResourceHandlers(ResourceHandlerRegistry registry) { | |||
super.addResourceHandlers(registry); | |||
// 定义到新文件夹 | |||
registry.addResourceHandler("/static/**") | |||
.addResourceLocations("classpath:/static/"); | |||
// 定义到指定目录 | |||
registry.addResourceHandler(staticAccessPath) | |||
.addResourceLocations("file:" + uploadFolder); | |||
} | |||
/** | |||
* 跨域问题解决 | |||
* | |||
* @param registry | |||
*/ | |||
@Override | |||
protected void addCorsMappings(CorsRegistry registry) { | |||
registry.addMapping("/**") | |||
.allowedOrigins("*") | |||
.allowCredentials(true) | |||
.allowedMethods(ORIGINS) | |||
.maxAge(3600); | |||
} | |||
/** | |||
* 跨域配置 | |||
* | |||
* @return | |||
*/ | |||
private CorsConfiguration corsConfig() { | |||
CorsConfiguration corsConfiguration = new CorsConfiguration(); | |||
corsConfiguration.addAllowedOrigin("*"); | |||
corsConfiguration.addAllowedHeader("*"); | |||
corsConfiguration.addAllowedMethod("*"); | |||
corsConfiguration.setAllowCredentials(true); | |||
corsConfiguration.setMaxAge(3600L); | |||
return corsConfiguration; | |||
} | |||
/** | |||
* 跨域过滤器 | |||
* | |||
* @return | |||
*/ | |||
@Bean | |||
public CorsFilter corsFilter() { | |||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | |||
source.registerCorsConfiguration("/**", corsConfig()); | |||
return new CorsFilter(source); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package com.tuoheng.common.config.http; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpRequest; | |||
import org.springframework.http.client.ClientHttpRequestExecution; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.ClientHttpResponse; | |||
import java.io.IOException; | |||
import java.io.UnsupportedEncodingException; | |||
/** | |||
* 默认拦截器 | |||
*/ | |||
public class DefaultClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClientHttpRequestInterceptor.class); | |||
@Override | |||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { | |||
trackRequest(request,body); | |||
ClientHttpResponse httpResponse = execution.execute(request, body); | |||
trackResponse(httpResponse); | |||
return httpResponse; | |||
} | |||
private void trackResponse(ClientHttpResponse httpResponse)throws IOException { | |||
LOGGER.info("============================response begin=========================================="); | |||
LOGGER.info("Status code : {}", httpResponse.getStatusCode()); | |||
LOGGER.info("Status text : {}", httpResponse.getStatusText()); | |||
LOGGER.info("Headers : {}", httpResponse.getHeaders()); | |||
LOGGER.info("=======================response end================================================="); | |||
} | |||
private void trackRequest(HttpRequest request, byte[] body)throws UnsupportedEncodingException { | |||
LOGGER.info("======= request begin ========"); | |||
LOGGER.info("uri : {}", request.getURI()); | |||
LOGGER.info("method : {}", request.getMethod()); | |||
LOGGER.info("headers : {}", request.getHeaders()); | |||
LOGGER.info("request body : {}", new String(body, "UTF-8")); | |||
LOGGER.info("======= request end ========"); | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.tuoheng.common.config.http; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpHeaders; | |||
import org.springframework.http.HttpRequest; | |||
import org.springframework.http.client.ClientHttpRequestExecution; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.ClientHttpResponse; | |||
import java.io.IOException; | |||
/** | |||
* 默认拦截器 | |||
*/ | |||
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(HeadClientHttpRequestInterceptor.class); | |||
@Override | |||
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { | |||
LOGGER.info("#####head handle########"); | |||
HttpHeaders headers = httpRequest.getHeaders(); | |||
headers.add("Accept", "application/json"); | |||
headers.add("Accept-Encoding", "gzip"); | |||
headers.add("Content-Encoding", "UTF-8"); | |||
headers.add("Content-Type", "application/json; charset=UTF-8"); | |||
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes); | |||
HttpHeaders headersResponse = response.getHeaders(); | |||
headersResponse.add("Accept", "application/json"); | |||
return response; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package com.tuoheng.common.config.http; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
@Configuration | |||
@Getter | |||
@Setter | |||
public class RestProperties { | |||
public final static String HTTP = "http"; | |||
public final static String HTTPS = "https"; | |||
/** | |||
* 连接超时, 默认10秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.connect.timeout: 10000}") | |||
private Integer connectTimeout; | |||
/** | |||
* 响应超时, 默认2分钟 | |||
*/ | |||
@Value("${spring.http.restTemplate.read.timeout: 120000}") | |||
private Integer readTimeout; | |||
/** | |||
* 请求超时时间, 默认10秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.connection.request.timeout: 10000}") | |||
private Integer connectionRequestTimeout; | |||
/** | |||
* 请求失败重试次数, 默认重试3次 | |||
*/ | |||
@Value("${spring.http.restTemplate.retryCount: 3}") | |||
private Integer retryCount; | |||
/** | |||
* 请求失败重试开关,默认开启 | |||
*/ | |||
@Value("${spring.http.restTemplate.requestSentRetryEnabled: true}") | |||
private Boolean requestSentRetryEnabled; | |||
/** | |||
* 线程池最大连接数,默认1000 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.maxTotal: 1000}") | |||
private Integer maxTotal; | |||
/** | |||
* 线程池主机最大并发数,默认100 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.maxPerRoute: 100}") | |||
private Integer maxPerRoute; | |||
/** | |||
* 线程池空闲连接过期时间,默认60秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.validateAfterInactivity: 60000}") | |||
private Integer validateAfterInactivity; | |||
} |
@@ -0,0 +1,176 @@ | |||
package com.tuoheng.common.config.http; | |||
import org.apache.http.HttpEntityEnclosingRequest; | |||
import org.apache.http.HttpRequest; | |||
import org.apache.http.NoHttpResponseException; | |||
import org.apache.http.client.HttpRequestRetryHandler; | |||
import org.apache.http.client.protocol.HttpClientContext; | |||
import org.apache.http.config.Registry; | |||
import org.apache.http.config.RegistryBuilder; | |||
import org.apache.http.conn.ConnectTimeoutException; | |||
import org.apache.http.conn.HttpClientConnectionManager; | |||
import org.apache.http.conn.socket.ConnectionSocketFactory; | |||
import org.apache.http.conn.socket.PlainConnectionSocketFactory; | |||
import org.apache.http.conn.ssl.NoopHostnameVerifier; | |||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||
import org.apache.http.impl.client.*; | |||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | |||
import org.apache.http.protocol.HttpContext; | |||
import org.apache.http.ssl.SSLContextBuilder; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.http.client.ClientHttpRequestFactory; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | |||
import org.springframework.http.converter.HttpMessageConverter; | |||
import org.springframework.http.converter.StringHttpMessageConverter; | |||
import org.springframework.web.client.RestTemplate; | |||
import javax.net.ssl.SSLContext; | |||
import javax.net.ssl.SSLException; | |||
import javax.net.ssl.SSLHandshakeException; | |||
import java.io.IOException; | |||
import java.io.InterruptedIOException; | |||
import java.net.UnknownHostException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.security.GeneralSecurityException; | |||
import java.security.cert.X509Certificate; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@Configuration | |||
public class RestTemplateConfig { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class); | |||
@Bean | |||
public RestTemplate restTemplate(RestProperties restProperties) { | |||
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory(restProperties)); | |||
// 获取restTemplate中的转换器集合 | |||
List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); | |||
// 遍历转换器集合,找到对应的StringHttpMessageConverter转换器 | |||
for (HttpMessageConverter<?> converter : converterList) { | |||
if(converter.getClass() == StringHttpMessageConverter.class){ | |||
converterList.remove(converter); | |||
break; | |||
} | |||
} | |||
// 添加新的StringHttpMessageConverter转换器,并设置字符集为UTF-8 | |||
converterList.add(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); | |||
// 添加拦截器 | |||
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); | |||
// interceptors.add(new DefaultClientHttpRequestInterceptor()); | |||
interceptors.add(new HeadClientHttpRequestInterceptor()); | |||
restTemplate.setInterceptors(interceptors); | |||
return restTemplate; | |||
} | |||
private ClientHttpRequestFactory clientHttpRequestFactory(RestProperties restProperties) { | |||
// httpClientBuilder配置构架器 | |||
HttpClientBuilder httpClientBuilder = HttpClients.custom(); | |||
// 设置重试次数,此处注意,如果使用无参构造,重试次数为3。this(3, false); | |||
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(restProperties.getRetryCount(), restProperties.getRequestSentRetryEnabled())); | |||
// 设置保持长连接 | |||
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); | |||
// 使用连接池 | |||
httpClientBuilder.setConnectionManager(poolingConnectionManager(restProperties)); | |||
// 获取httpClient | |||
CloseableHttpClient httpClient = httpClientBuilder.build(); | |||
// 配置HttpClient的对应工厂HttpComponentsClientHttpRequestFactory | |||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); | |||
factory.setConnectTimeout(restProperties.getConnectTimeout()); | |||
factory.setReadTimeout(restProperties.getReadTimeout()); | |||
factory.setConnectionRequestTimeout(restProperties.getConnectionRequestTimeout()); | |||
return factory; | |||
} | |||
private HttpClientConnectionManager poolingConnectionManager(RestProperties restProperties) { | |||
// 注册http和https请求 | |||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | |||
.register(RestProperties.HTTP, PlainConnectionSocketFactory.getSocketFactory()) | |||
.register(RestProperties.HTTPS, createSSLConn()) | |||
.build(); | |||
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(registry); | |||
// 最大连接数 | |||
poolingConnectionManager.setMaxTotal(restProperties.getMaxTotal()); | |||
// 每个主机的并发 | |||
poolingConnectionManager.setDefaultMaxPerRoute(restProperties.getMaxPerRoute()); | |||
// 空闲连接过期时间 | |||
poolingConnectionManager.setValidateAfterInactivity(restProperties.getValidateAfterInactivity()); | |||
return poolingConnectionManager; | |||
} | |||
private SSLConnectionSocketFactory createSSLConn() { | |||
SSLConnectionSocketFactory sslsf = null; | |||
try | |||
{ | |||
SSLContext sslContext = new SSLContextBuilder() | |||
// 不检查证书 | |||
.loadTrustMaterial(null, (X509Certificate[] chain, String authType) -> true) | |||
.build(); | |||
sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); // 不检查hostname | |||
} catch (GeneralSecurityException e){ | |||
LOGGER.error("restTemplate开启SSL校验失败, error:{}", e); | |||
} | |||
return sslsf; | |||
} | |||
/** | |||
* 自定义的重试策略,需要的时候使用 | |||
*/ | |||
private void customHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled){ | |||
// 请求失败时,进行请求重试 | |||
HttpRequestRetryHandler handler = new HttpRequestRetryHandler() { | |||
@Override | |||
public boolean retryRequest(IOException e, int currentRetryCount, HttpContext httpContext) { | |||
if(!requestSentRetryEnabled){ | |||
return false; | |||
} | |||
if (currentRetryCount > retryCount){ | |||
// 重试超过3次,放弃请求 | |||
LOGGER.error("retry has more than 3 time, give up request"); | |||
return false; | |||
} | |||
if (e instanceof NoHttpResponseException){ | |||
// 服务器没有响应,可能是服务器断开了连接,应该重试 | |||
LOGGER.error("receive no response from server, retry"); | |||
return true; | |||
} | |||
if (e instanceof SSLHandshakeException){ | |||
// SSL握手异常 | |||
LOGGER.error("SSL hand shake exception"); | |||
return false; | |||
} | |||
if (e instanceof InterruptedIOException){ | |||
// 超时 | |||
LOGGER.error("InterruptedIOException"); | |||
return false; | |||
} | |||
if (e instanceof UnknownHostException){ | |||
// 服务器不可达 | |||
LOGGER.error("server host unknown"); | |||
return false; | |||
} | |||
if (e instanceof ConnectTimeoutException){ | |||
// 连接超时 | |||
LOGGER.error("Connection Time out"); | |||
return false; | |||
} | |||
if (e instanceof SSLException){ | |||
LOGGER.error("SSLException"); | |||
return false; | |||
} | |||
HttpClientContext context = HttpClientContext.adapt(httpContext); | |||
HttpRequest request = context.getRequest(); | |||
if (!(request instanceof HttpEntityEnclosingRequest)){ | |||
// 如果请求不是关闭连接的请求 | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,109 @@ | |||
//package com.tuoheng.common.config.redisson; | |||
// | |||
//import com.tuoheng.common.annotation.LockAction; | |||
//import org.aspectj.lang.ProceedingJoinPoint; | |||
//import org.aspectj.lang.annotation.Around; | |||
//import org.aspectj.lang.annotation.Aspect; | |||
//import org.aspectj.lang.annotation.Pointcut; | |||
//import org.aspectj.lang.reflect.MethodSignature; | |||
//import org.redisson.api.RLock; | |||
//import org.redisson.api.RedissonClient; | |||
//import org.slf4j.Logger; | |||
//import org.slf4j.LoggerFactory; | |||
//import org.springframework.beans.factory.annotation.Autowired; | |||
//import org.springframework.beans.factory.annotation.Qualifier; | |||
//import org.springframework.context.annotation.Configuration; | |||
//import org.springframework.core.LocalVariableTableParameterNameDiscoverer; | |||
//import org.springframework.expression.EvaluationContext; | |||
//import org.springframework.expression.ExpressionParser; | |||
//import org.springframework.expression.spel.standard.SpelExpressionParser; | |||
//import org.springframework.expression.spel.support.StandardEvaluationContext; | |||
//import org.springframework.util.StringUtils; | |||
// | |||
//import java.lang.reflect.Method; | |||
//import java.util.Locale; | |||
// | |||
//@Aspect | |||
//@Configuration | |||
//public class RedissonDistributedLockAspectConfiguration { | |||
// | |||
// private final Logger logger = LoggerFactory.getLogger(RedissonDistributedLockAspectConfiguration.class); | |||
// | |||
// @Autowired | |||
// @Qualifier("redissonClient") | |||
// private RedissonClient redissonClient; | |||
// | |||
// private final ExpressionParser parser = new SpelExpressionParser(); | |||
// | |||
// private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); | |||
// | |||
// @Pointcut("@annotation(com.tuoheng.common.annotation.LockAction)") | |||
// private void lockPoint(){ | |||
// | |||
// } | |||
// | |||
// @Around("lockPoint()") | |||
// public Object around(ProceedingJoinPoint pjp) throws Throwable{ | |||
// Method method = ((MethodSignature) pjp.getSignature()).getMethod(); | |||
// LockAction lockAction = method.getAnnotation(LockAction.class); | |||
// String key = lockAction.value(); | |||
// String prefix = lockAction.prefix(); | |||
// Object[] args = pjp.getArgs(); | |||
// key = parse(key, method, args); | |||
// if(!StringUtils.hasLength(key) || !StringUtils.hasLength(prefix)){ | |||
// logger.error("get key or prefix failed. key is null or prefix is null"); | |||
// return null; | |||
// } | |||
// RLock lock = getLock(String.format(Locale.ENGLISH,"%s%s", prefix, key), lockAction); | |||
// if(!lock.tryLock(lockAction.waitTime(), lockAction.leaseTime(), lockAction.unit())) { | |||
// logger.debug("get lock failed [{}]", key); | |||
// return null; | |||
// } | |||
// | |||
// //得到锁,执行方法,释放锁 | |||
// logger.debug("get lock success [{}]", key); | |||
// try { | |||
// return pjp.proceed(); | |||
// } finally { | |||
// lock.unlock(); | |||
// logger.debug("release lock [{}]", key); | |||
// } | |||
// } | |||
// | |||
// /** | |||
// * 解析spring EL表达式 | |||
// * @param key 表达式 | |||
// * @param method 方法 | |||
// * @param args 方法参数 | |||
// * @return string | |||
// */ | |||
// private String parse(String key, Method method, Object[] args) { | |||
// String[] params = discoverer.getParameterNames(method); | |||
// if(params == null || params.length == 0){ | |||
// return null; | |||
// } | |||
// EvaluationContext context = new StandardEvaluationContext(); | |||
// for (int i = 0; i < params.length; i ++) { | |||
// context.setVariable(params[i], args[i]); | |||
// } | |||
// return parser.parseExpression(key).getValue(context, String.class); | |||
// } | |||
// | |||
// private RLock getLock(String key, LockAction lockAction) { | |||
// switch (lockAction.lockType()) { | |||
// case REENTRANT_LOCK: | |||
// return redissonClient.getLock(key); | |||
// | |||
// case FAIR_LOCK: | |||
// return redissonClient.getFairLock(key); | |||
// | |||
// case READ_LOCK: | |||
// return redissonClient.getReadWriteLock(key).readLock(); | |||
// | |||
// case WRITE_LOCK: | |||
// return redissonClient.getReadWriteLock(key).writeLock(); | |||
// default: | |||
// throw new RuntimeException("do not support lock type:" + lockAction.lockType().name()); | |||
// } | |||
// } | |||
//} |
@@ -0,0 +1,86 @@ | |||
package com.tuoheng.common.config.xxl; | |||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* xxl-job config | |||
* | |||
* @author xuxueli 2017-04-28 | |||
*/ | |||
@Configuration | |||
@ConditionalOnProperty(name = XxlJobConfig.XXL_ENABLE, havingValue = XxlJobConfig.TRUE) | |||
public class XxlJobConfig { | |||
public static final String XXL_ENABLE = "xxl.enable"; | |||
public static final String TRUE = "true"; | |||
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); | |||
@Value("${xxl.job.admin.addresses}") | |||
private String adminAddresses; | |||
@Value("${xxl.job.accessToken}") | |||
private String accessToken; | |||
@Value("${xxl.job.executor.appname}") | |||
private String appname; | |||
@Value("${xxl.job.executor.address}") | |||
private String address; | |||
@Value("${xxl.job.executor.ip}") | |||
private String ip; | |||
@Value("${xxl.job.executor.port}") | |||
private int port; | |||
@Value("${xxl.job.executor.logpath}") | |||
private String logPath; | |||
@Value("${xxl.job.executor.logretentiondays}") | |||
private int logRetentionDays; | |||
@Bean | |||
public XxlJobSpringExecutor xxlJobExecutor() throws InterruptedException { | |||
logger.info(">>>>>>>>>>> xxl-job config init."); | |||
Thread.sleep(5000); | |||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); | |||
xxlJobSpringExecutor.setAdminAddresses(adminAddresses); | |||
xxlJobSpringExecutor.setAppname(appname); | |||
xxlJobSpringExecutor.setAddress(address); | |||
xxlJobSpringExecutor.setIp(ip); | |||
xxlJobSpringExecutor.setPort(port); | |||
xxlJobSpringExecutor.setAccessToken(accessToken); | |||
xxlJobSpringExecutor.setLogPath(logPath); | |||
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); | |||
return xxlJobSpringExecutor; | |||
} | |||
/** | |||
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; | |||
* | |||
* 1、引入依赖: | |||
* <dependency> | |||
* <groupId>org.springframework.cloud</groupId> | |||
* <artifactId>spring-cloud-commons</artifactId> | |||
* <version>${version}</version> | |||
* </dependency> | |||
* | |||
* 2、配置文件,或者容器启动变量 | |||
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' | |||
* | |||
* 3、获取IP | |||
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); | |||
*/ | |||
} |
@@ -0,0 +1,108 @@ | |||
package com.tuoheng.common.constant; | |||
/** | |||
* 系统公共常量 | |||
*/ | |||
public class CommonConstants { | |||
/** | |||
* UTF-8 字符集 | |||
*/ | |||
public static final String UTF8 = "UTF-8"; | |||
/** | |||
* GBK 字符集 | |||
*/ | |||
public static final String GBK = "GBK"; | |||
/** | |||
* http请求 | |||
*/ | |||
public static final String HTTP = "http://"; | |||
/** | |||
* https请求 | |||
*/ | |||
public static final String HTTPS = "https://"; | |||
/** | |||
* 成功标记 | |||
*/ | |||
public static final Integer SUCCESS = 200; | |||
/** | |||
* 失败标记 | |||
*/ | |||
public static final Integer FAIL = 500; | |||
/** | |||
* 登录成功 | |||
*/ | |||
public static final String LOGIN_SUCCESS = "Success"; | |||
/** | |||
* 注销 | |||
*/ | |||
public static final String LOGOUT = "Logout"; | |||
/** | |||
* 注册 | |||
*/ | |||
public static final String REGISTER = "Register"; | |||
/** | |||
* 登录失败 | |||
*/ | |||
public static final String LOGIN_FAIL = "Error"; | |||
/** | |||
* 当前记录起始索引 | |||
*/ | |||
public static final String PAGE_NUM = "pageNum"; | |||
/** | |||
* 每页显示记录数 | |||
*/ | |||
public static final String PAGE_SIZE = "pageSize"; | |||
/** | |||
* 排序列 | |||
*/ | |||
public static final String ORDER_BY_COLUMN = "orderByColumn"; | |||
/** | |||
* 排序的方向 "desc" 或者 "asc". | |||
*/ | |||
public static final String IS_ASC = "isAsc"; | |||
/** | |||
* 验证码 redis key | |||
*/ | |||
public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; | |||
/** | |||
* 验证码有效期(分钟) | |||
*/ | |||
public static final Integer CAPTCHA_EXPIRATION = 2; | |||
/** | |||
* 参数管理 cache key | |||
*/ | |||
public static final String SYS_CONFIG_KEY = "sys_config:"; | |||
/** | |||
* 字典管理 cache key | |||
*/ | |||
public static final String SYS_DICT_KEY = "sys_dict:"; | |||
/** | |||
* 资源映射路径 前缀 | |||
*/ | |||
public static final String RESOURCE_PREFIX = "/profile"; | |||
/** | |||
* 类型:1巡检任务 | |||
*/ | |||
public static final int INSPECTION_TASK_TYPE = 1; | |||
} |
@@ -0,0 +1,60 @@ | |||
package com.tuoheng.common.constant; | |||
/** | |||
* 常量类 | |||
* | |||
* @author: zhu_zishuang | |||
* @date: 2020-04-22 14:22 | |||
*/ | |||
public final class Constant { | |||
/** | |||
* 构造器私有化 | |||
*/ | |||
private Constant() { | |||
// 可抛出异常,防止通过反射实例化对象 | |||
} | |||
/** | |||
* 返回结果 success:成功,fail:业务返回的失败,error:非业务异常失败 | |||
*/ | |||
public static final String SUCCESS = "success"; | |||
public static final String FAIL = "fail"; | |||
public static final String ERROR = "error"; | |||
/** | |||
* 常用数值 | |||
*/ | |||
public static final Integer ZERO = 0; | |||
public static final Integer ONE = 1; | |||
public static final Long MAX_PAGE_SIZE = 100_000L; | |||
/** | |||
* 常用字符 | |||
*/ | |||
public static final String SPLIT_CLASS = " "; | |||
public static final String LOG_INFO_PREFIX = "登录异常,异常信息:{}"; | |||
/** | |||
* 是否 | |||
*/ | |||
public static final Byte YES = 1; | |||
public static final Byte NO = 0; | |||
public static final Integer YES_INT = 1; | |||
/** | |||
* 平台管理员 | |||
*/ | |||
public static final String ADMIN = "admin"; | |||
/** | |||
* 业务标识(kafka使用,区分AI分析结果后用做业务判断) | |||
*/ | |||
public static final String BIZ_ID = "hehuzhang"; | |||
/** | |||
* 租户ID | |||
*/ | |||
public static final String TENANT_ID="tenant_id"; | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.tuoheng.common.constant; | |||
/** | |||
* redis常量类 | |||
*/ | |||
public interface RedisConstant { | |||
/** | |||
* TOKEN前缀 | |||
*/ | |||
String REDIS_PREFIX_LOGIN = "login_token_%s"; | |||
/** | |||
* 过期时间2小时 | |||
*/ | |||
Integer REDIS_EXPIRE_TWO = 7200; | |||
/** | |||
* 过期时间15分 | |||
*/ | |||
Integer REDIS_EXPIRE_EMAIL = 900; | |||
/** | |||
* 过期时间5分钟 | |||
*/ | |||
Integer REDIS_EXPIRE_KAPTCHA = 300; | |||
/** | |||
* 暂无过期时间 | |||
*/ | |||
Integer REDIS_EXPIRE_NULL = -1; | |||
} |
@@ -0,0 +1,50 @@ | |||
package com.tuoheng.common.controller; | |||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; | |||
import com.tuoheng.common.config.UploadFileConfig; | |||
import com.tuoheng.common.utils.FileUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
@RestController | |||
@RequestMapping("/common") | |||
public class CommonController { | |||
private static final Logger log = LoggerFactory.getLogger(CommonController.class); | |||
/** | |||
* 下载文件 | |||
* | |||
* @param fileName 文件名 | |||
* @param isDelete 是否下载后删除 | |||
* @param request 网络请求 | |||
* @param response | |||
*/ | |||
@GetMapping("/download") | |||
public void downloadFile(String fileName, boolean isDelete, HttpServletRequest request, HttpServletResponse response) { | |||
try { | |||
if (!FileUtils.isValidFilename(fileName)) { | |||
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); | |||
} | |||
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); | |||
String filePath = UploadFileConfig.uploadFolder + fileName; | |||
response.setCharacterEncoding("utf-8"); | |||
response.setContentType("multipart/form-data"); | |||
response.setHeader("Content-Disposition", "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName)); | |||
FileUtils.writeBytes(filePath, response.getOutputStream()); | |||
if (isDelete) { | |||
// 删除文件 | |||
FileUtils.deleteFile(filePath); | |||
} | |||
} catch (Exception e) { | |||
log.error("下载文件失败", e); | |||
} | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
package com.tuoheng.common.controller; | |||
import com.tuoheng.common.service.IUploadService; | |||
import com.tuoheng.common.utils.JsonResult; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import javax.servlet.http.HttpServletRequest; | |||
/** | |||
* <p> | |||
* 角色菜单关联表 前端控制器 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
@RestController | |||
@RequestMapping("/upload") | |||
public class UploadController { | |||
@Autowired | |||
private IUploadService uploadService; | |||
/** | |||
* 上传图片 | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
@PostMapping("/uploadImage/{name}") | |||
public JsonResult uploadImage(HttpServletRequest request, @PathVariable("name") String name) { | |||
return uploadService.uploadImage(request, name); | |||
} | |||
/** | |||
* 上传文件(非图片) | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
@PostMapping("/uploadFile/{name}") | |||
public JsonResult uploadFile(HttpServletRequest request, @PathVariable("name") String name) { | |||
return uploadService.uploadFile(request, name); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.common.enums; | |||
public enum LockType { | |||
/** 可重入锁*/ | |||
REENTRANT_LOCK, | |||
/** 公平锁*/ | |||
FAIR_LOCK, | |||
/** 读锁*/ | |||
READ_LOCK, | |||
/** 写锁*/ | |||
WRITE_LOCK; | |||
} |
@@ -0,0 +1,18 @@ | |||
package com.tuoheng.common.enums; | |||
/** | |||
* 操作状态 | |||
*/ | |||
public enum LogStatus { | |||
/** | |||
* 成功 | |||
*/ | |||
SUCCESS, | |||
/** | |||
* 失败 | |||
*/ | |||
FAIL, | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.tuoheng.common.enums; | |||
/** | |||
* 日志类型 | |||
*/ | |||
public enum LogType { | |||
/** | |||
* 其它 | |||
*/ | |||
OTHER, | |||
/** | |||
* 新增 | |||
*/ | |||
INSERT, | |||
/** | |||
* 修改 | |||
*/ | |||
UPDATE, | |||
/** | |||
* 删除 | |||
*/ | |||
DELETE, | |||
/** | |||
* 授权 | |||
*/ | |||
GRANT, | |||
/** | |||
* 导出 | |||
*/ | |||
EXPORT, | |||
/** | |||
* 导入 | |||
*/ | |||
IMPORT, | |||
/** | |||
* 强退 | |||
*/ | |||
FORCE, | |||
/** | |||
* 生成代码 | |||
*/ | |||
GENCODE, | |||
/** | |||
* 清空数据 | |||
*/ | |||
CLEAN, | |||
/** | |||
* 状态 | |||
*/ | |||
STATUS, | |||
/** | |||
* 重置密码 | |||
*/ | |||
RESETPWD, | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.enums; | |||
/** | |||
* 操作类型 | |||
*/ | |||
public enum OperType { | |||
/** | |||
* 其它 | |||
*/ | |||
OTHER, | |||
/** | |||
* 后台用户 | |||
*/ | |||
MANAGE, | |||
/** | |||
* 手机端用户 | |||
*/ | |||
MOBILE | |||
} |
@@ -0,0 +1,84 @@ | |||
package com.tuoheng.common.exception; | |||
import com.tuoheng.common.utils.MessageUtils; | |||
import org.springframework.util.StringUtils; | |||
/** | |||
* 异常基类 | |||
*/ | |||
public class BaseException extends RuntimeException { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 所属模块 | |||
*/ | |||
private String module; | |||
/** | |||
* 错误码 | |||
*/ | |||
private String code; | |||
/** | |||
* 错误码对应的参数 | |||
*/ | |||
private Object[] args; | |||
/** | |||
* 错误消息 | |||
*/ | |||
private String defaultMessage; | |||
public BaseException(String module, String code, Object[] args, String defaultMessage) { | |||
this.module = module; | |||
this.code = code; | |||
this.args = args; | |||
this.defaultMessage = defaultMessage; | |||
} | |||
public BaseException(String module, String code, Object[] args) { | |||
this(module, code, args, null); | |||
} | |||
public BaseException(String module, String defaultMessage) { | |||
this(module, null, null, defaultMessage); | |||
} | |||
public BaseException(String code, Object[] args) { | |||
this(null, code, args, null); | |||
} | |||
public BaseException(String defaultMessage) { | |||
this(null, null, null, defaultMessage); | |||
} | |||
@Override | |||
public String getMessage() { | |||
String message = null; | |||
if (!StringUtils.isEmpty(code)) { | |||
message = MessageUtils.message(code, args); | |||
} | |||
if (message == null) { | |||
message = defaultMessage; | |||
} | |||
return message; | |||
} | |||
public String getModule() { | |||
return module; | |||
} | |||
public String getCode() { | |||
return code; | |||
} | |||
public Object[] getArgs() { | |||
return args; | |||
} | |||
public String getDefaultMessage() { | |||
return defaultMessage; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
package com.tuoheng.common.exception; | |||
/** | |||
* 自定义异常类 | |||
*/ | |||
public class CustomException extends RuntimeException { | |||
private static final long serialVersionUID = 1L; | |||
private Integer code; | |||
private String message; | |||
public CustomException(String message) { | |||
this.message = message; | |||
} | |||
public CustomException(String message, Integer code) { | |||
this.message = message; | |||
this.code = code; | |||
} | |||
public CustomException(String message, Throwable e) { | |||
super(message, e); | |||
this.message = message; | |||
} | |||
@Override | |||
public String getMessage() { | |||
return message; | |||
} | |||
public Integer getCode() { | |||
return code; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package com.tuoheng.common.exception; | |||
import com.tuoheng.common.common.ExceptionInterface; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* 业务异常枚举 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2020-09-17 | |||
*/ | |||
@Accessors(chain = true) | |||
@AllArgsConstructor | |||
public enum ExceptionConstantEnum implements ExceptionInterface { | |||
/** | |||
* 存在错误参数 304 | |||
*/ | |||
PARAMS_NOT_RIGHT(304, "存在错误参数"), | |||
/** | |||
* SQL异常 | |||
**/ | |||
SQL_ERROR_EXCEPTION(501, "SQL执行异常!"); | |||
/** | |||
* 结果类型CODE | |||
*/ | |||
@Getter | |||
private final int code; | |||
/** | |||
* 结果类型描述 | |||
*/ | |||
@Getter | |||
private final String message; | |||
} | |||
@@ -0,0 +1,46 @@ | |||
package com.tuoheng.common.exception; | |||
import com.tuoheng.common.common.ExceptionInterface; | |||
/** | |||
* 业务异常类(业务处理时手动抛出异常) | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public class ServiceException extends RuntimeException { | |||
/** | |||
* 异常码 | |||
*/ | |||
public final int code; | |||
/** | |||
* 异常描述,兼容JsonResult | |||
*/ | |||
private String msg; | |||
/** | |||
* 构造器 | |||
* | |||
* @param exceptionInfo | |||
*/ | |||
public ServiceException(ExceptionInterface exceptionInfo) { | |||
super(exceptionInfo.getMessage()); | |||
this.msg = exceptionInfo.getMessage(); | |||
this.code = exceptionInfo.getCode(); | |||
} | |||
/** | |||
* 构造器 | |||
* | |||
* @param code | |||
* @param message | |||
*/ | |||
public ServiceException(int code, String message) { | |||
super(message); | |||
this.msg = message; | |||
this.code = code; | |||
} | |||
} | |||
@@ -0,0 +1,14 @@ | |||
package com.tuoheng.common.exception.user; | |||
/** | |||
* 验证码异常处理类 | |||
*/ | |||
public class CaptchaException extends UserException { | |||
private static final long serialVersionUID = 1L; | |||
public CaptchaException() { | |||
super("user.jcaptcha.error", null); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.common.exception.user; | |||
import com.tuoheng.common.exception.BaseException; | |||
/** | |||
* 用户异常处理类 | |||
*/ | |||
public class UserException extends BaseException { | |||
private static final long serialVersionUID = 1L; | |||
public UserException(String code, Object[] args) { | |||
super("user", code, args, null); | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.tuoheng.common.exception.user; | |||
/** | |||
* 用户不存在异常 | |||
*/ | |||
public class UserNotExistsException extends UserException { | |||
private static final long serialVersionUID = 1L; | |||
public UserNotExistsException() { | |||
super("user.not.exists", null); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.tuoheng.common.service; | |||
import com.tuoheng.common.utils.JsonResult; | |||
import javax.servlet.http.HttpServletRequest; | |||
/** | |||
* <p> | |||
* 文件上传 服务类 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
public interface IUploadService { | |||
/** | |||
* 上传图片 | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
JsonResult uploadImage(HttpServletRequest request, String name); | |||
/** | |||
* 上传文件 | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
JsonResult uploadFile(HttpServletRequest request, String name); | |||
} |
@@ -0,0 +1,66 @@ | |||
package com.tuoheng.common.service.impl; | |||
import com.tuoheng.common.service.IUploadService; | |||
import com.tuoheng.common.utils.CommonUtils; | |||
import com.tuoheng.common.utils.JsonResult; | |||
import com.tuoheng.common.utils.UploadUtils; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* <p> | |||
* 文件上传 服务实现类 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
@Service | |||
public class UploadServiceImpl implements IUploadService { | |||
@Autowired | |||
private UploadUtils uploadUtils; | |||
/** | |||
* 上传图片 | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult uploadImage(HttpServletRequest request, String name) { | |||
UploadUtils uploadUtils = new UploadUtils(); | |||
Map<String, Object> result = uploadUtils.uploadFile(request, name); | |||
List<String> imageList = (List<String>) result.get("image"); | |||
String imageUrl = CommonUtils.getImageURL(imageList.get(imageList.size() - 1)); | |||
return JsonResult.success(imageUrl, "上传成功"); | |||
} | |||
/** | |||
* 上传文件 | |||
* | |||
* @param request 网络请求 | |||
* @param name 目录名 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult uploadFile(HttpServletRequest request, String name) { | |||
UploadUtils uploadUtils = new UploadUtils(); | |||
uploadUtils.setDirName("files"); | |||
Map<String, Object> result = uploadUtils.uploadFile(request, name); | |||
List<String> nameList = (List<String>) result.get("name"); | |||
List<String> imageList = (List<String>) result.get("image"); | |||
String imageUrl = CommonUtils.getImageURL(imageList.get(imageList.size() - 1)); | |||
Map<String, Object> map = new HashMap<>(); | |||
map.put("fileName", nameList.get(nameList.size() - 1)); | |||
map.put("fileUrl", imageUrl); | |||
return JsonResult.success(map, "上传成功"); | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.web.multipart.MultipartFile; | |||
import java.io.*; | |||
public class BASE64DecodedMultipartFile implements MultipartFile { | |||
private final byte[] imgContent; | |||
private final String header; | |||
public BASE64DecodedMultipartFile(byte[] imgContent, String header) { | |||
this.imgContent = imgContent; | |||
this.header = header.split(";")[0]; | |||
} | |||
@Override | |||
public String getName() { | |||
// TODO - implementation depends on your requirements | |||
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1]; | |||
} | |||
@Override | |||
public String getOriginalFilename() { | |||
// TODO - implementation depends on your requirements | |||
return System.currentTimeMillis() + (int)Math.random() * 10000 + "." + header.split("/")[1]; | |||
} | |||
@Override | |||
public String getContentType() { | |||
// TODO - implementation depends on your requirements | |||
return header.split(":")[1]; | |||
} | |||
@Override | |||
public boolean isEmpty() { | |||
return imgContent == null || imgContent.length == 0; | |||
} | |||
@Override | |||
public long getSize() { | |||
return imgContent.length; | |||
} | |||
@Override | |||
public byte[] getBytes() throws IOException { | |||
return imgContent; | |||
} | |||
@Override | |||
public InputStream getInputStream() throws IOException { | |||
return new ByteArrayInputStream(imgContent); | |||
} | |||
@Override | |||
public void transferTo(File dest) throws IOException, IllegalStateException { | |||
new FileOutputStream(dest).write(imgContent); | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
package com.tuoheng.common.utils; | |||
import com.tuoheng.common.common.BaseResponse; | |||
import com.tuoheng.common.common.ExceptionInterface; | |||
import com.tuoheng.common.common.SysExceptionEnum; | |||
import com.tuoheng.common.constant.Constant; | |||
import com.tuoheng.common.exception.ServiceException; | |||
/** | |||
* 响应包装工具类 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public final class BaseResponseUtil { | |||
/** | |||
* 成功编码 | |||
*/ | |||
private static final Integer SUCCESS_CODE = 200; | |||
/** | |||
* 默认操作成功提示 | |||
*/ | |||
private static final String DEFAULT_SUCCESS_MESSAGE = "操作成功"; | |||
/** | |||
* 构造器私有,防止外部实例化 | |||
*/ | |||
private BaseResponseUtil() { | |||
// 防止反射 | |||
throw new ServiceException(SysExceptionEnum.SYS_EXCEPTION); | |||
} | |||
/** | |||
* 包裹响应对象,此方法适合写操作没有数据实体场景下调用 | |||
* | |||
* @return 响应实体 | |||
*/ | |||
public static BaseResponse<Object> success() { | |||
return new BaseResponse<>().setCode(SUCCESS_CODE).setMessage(DEFAULT_SUCCESS_MESSAGE); | |||
} | |||
/** | |||
* 包裹响应对象,此方法适合查询操作有数据实体场景下调用 | |||
* | |||
* @param data 数据实体 | |||
* @return 响应实体 | |||
*/ | |||
public static BaseResponse<Object> success(Object data) { | |||
return success().setData(data); | |||
} | |||
/** | |||
* 包裹响应对象,此方法适合 增、删、改 操作有数据实体场景下调用 | |||
* | |||
* @param operationEnum | |||
* @return | |||
*/ | |||
public static BaseResponse<Object> success(ExceptionInterface operationEnum) { | |||
return success().setCode(operationEnum.getCode()).setMessage(operationEnum.getMessage()); | |||
} | |||
/** | |||
* 包裹响应对象,校验框架异常 场景下调用 | |||
* | |||
* @param message 异常消息 | |||
* @return 响应实体 | |||
*/ | |||
public static BaseResponse<Object> fail(Integer code, String message) { | |||
return new BaseResponse<>().setStatus(Constant.FAIL).setCode(code).setMessage(message); | |||
} | |||
/** | |||
* 包裹响应对象,自定义业务异常 场景下调用 | |||
* | |||
* @param baseResponseCode | |||
* @return | |||
*/ | |||
public static BaseResponse<Object> fail(ExceptionInterface baseResponseCode) { | |||
return new BaseResponse<>().setStatus(Constant.FAIL).setCode(baseResponseCode.getCode()).setMessage(baseResponseCode.getMessage()); | |||
} | |||
/** | |||
* 包裹响应对象,系统异常 场景下调用 | |||
* | |||
* @param sysExceptionEnum | |||
* @return 响应实体 | |||
*/ | |||
public static BaseResponse<Object> error(ExceptionInterface sysExceptionEnum) { | |||
return new BaseResponse<>().setStatus(Constant.ERROR).setCode(sysExceptionEnum.getCode()).setMessage(sysExceptionEnum.getMessage()); | |||
} | |||
} | |||
@@ -0,0 +1,89 @@ | |||
package com.tuoheng.common.utils; | |||
import java.nio.charset.Charset; | |||
import java.nio.charset.StandardCharsets; | |||
/** | |||
* 字符串工具集 | |||
*/ | |||
public class CharsetKit { | |||
/** | |||
* ISO-8859-1 | |||
*/ | |||
public static final String ISO_8859_1 = "ISO-8859-1"; | |||
/** | |||
* UTF-8 | |||
*/ | |||
public static final String UTF_8 = "UTF-8"; | |||
/** | |||
* GBK | |||
*/ | |||
public static final String GBK = "GBK"; | |||
/** | |||
* ISO-8859-1 | |||
*/ | |||
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); | |||
/** | |||
* UTF-8 | |||
*/ | |||
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); | |||
/** | |||
* GBK | |||
*/ | |||
public static final Charset CHARSET_GBK = Charset.forName(GBK); | |||
/** | |||
* 转换为Charset对象 | |||
* | |||
* @param charset 字符集,为空则返回默认字符集 | |||
* @return Charset | |||
*/ | |||
public static Charset charset(String charset) { | |||
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); | |||
} | |||
/** | |||
* 转换字符串的字符集编码 | |||
* | |||
* @param source 字符串 | |||
* @param srcCharset 源字符集,默认ISO-8859-1 | |||
* @param destCharset 目标字符集,默认UTF-8 | |||
* @return 转换后的字符集 | |||
*/ | |||
public static String convert(String source, String srcCharset, String destCharset) { | |||
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); | |||
} | |||
/** | |||
* 转换字符串的字符集编码 | |||
* | |||
* @param source 字符串 | |||
* @param srcCharset 源字符集,默认ISO-8859-1 | |||
* @param destCharset 目标字符集,默认UTF-8 | |||
* @return 转换后的字符集 | |||
*/ | |||
public static String convert(String source, Charset srcCharset, Charset destCharset) { | |||
if (null == srcCharset) { | |||
srcCharset = StandardCharsets.ISO_8859_1; | |||
} | |||
if (null == destCharset) { | |||
srcCharset = StandardCharsets.UTF_8; | |||
} | |||
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { | |||
return source; | |||
} | |||
return new String(source.getBytes(srcCharset), destCharset); | |||
} | |||
/** | |||
* @return 系统字符集编码 | |||
*/ | |||
public static String systemCharset() { | |||
return Charset.defaultCharset().name(); | |||
} | |||
} |
@@ -0,0 +1,297 @@ | |||
package com.tuoheng.common.utils; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.tuoheng.common.config.CommonConfig; | |||
import org.springframework.util.CollectionUtils; | |||
import java.lang.reflect.Field; | |||
import java.security.MessageDigest; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.function.Consumer; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import java.util.stream.Collectors; | |||
/** | |||
* 公共函数类 | |||
*/ | |||
public class CommonUtils { | |||
/** | |||
* 获取到图片域名的地址 | |||
* | |||
* @param imageUrl | |||
* @return | |||
*/ | |||
public static String getImageURL(String imageUrl) { | |||
return CommonConfig.imageURL + imageUrl; | |||
} | |||
/** | |||
* 正则匹配富文本图片 | |||
* | |||
* @param htmlStr 富文本内容 | |||
* @return | |||
*/ | |||
public static List<String> getImgStr(String htmlStr) { | |||
Pattern p_image = Pattern.compile("<img.*src\\s*=\\s*(.*?)[^>]*?>", Pattern.CASE_INSENSITIVE); | |||
Pattern r_image = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)"); | |||
List<String> list = new ArrayList<>(); | |||
Matcher m_image = p_image.matcher(htmlStr); | |||
while (m_image.find()) { | |||
// 得到<img />数据 | |||
String img = m_image.group(); | |||
System.out.println(img); | |||
// 匹配<img>中的src数据 | |||
Matcher m = r_image.matcher(img); | |||
while (m.find()) { | |||
list.add(m.group(1)); | |||
} | |||
} | |||
return list; | |||
} | |||
/** | |||
* 验证邮箱是否正确 | |||
* | |||
* @param email | |||
* @return | |||
*/ | |||
public static boolean isEmail(String email) { | |||
boolean flag = false; | |||
try { | |||
String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; | |||
Pattern regex = Pattern.compile(check); | |||
Matcher matcher = regex.matcher(email); | |||
flag = matcher.matches(); | |||
} catch (Exception e) { | |||
flag = false; | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 验证手机号是否正确 | |||
* | |||
* @param mobile | |||
* @return | |||
*/ | |||
public static boolean isMobile(String mobile) { | |||
boolean flag = false; | |||
try { | |||
Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"); | |||
Matcher m = p.matcher(mobile); | |||
flag = m.matches(); | |||
} catch (Exception e) { | |||
flag = false; | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 生成指定位数的随机字符串 | |||
* | |||
* @param isNum 是否是纯数字 | |||
* @param length 长度 | |||
* @return | |||
*/ | |||
public static String getRandomStr(boolean isNum, int length) { | |||
String resultStr = ""; | |||
String str = isNum ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz"; | |||
int len = str.length(); | |||
boolean isStop = true; | |||
do { | |||
resultStr = ""; | |||
int count = 0; | |||
for (int i = 0; i < length; i++) { | |||
double dblR = Math.random() * len; | |||
int intR = (int) Math.floor(dblR); | |||
char c = str.charAt(intR); | |||
if (('0' <= c) && (c <= '9')) { | |||
count++; | |||
} | |||
resultStr += str.charAt(intR); | |||
} | |||
if (count >= 2) { | |||
isStop = false; | |||
} | |||
} while (isStop); | |||
return resultStr; | |||
} | |||
/** | |||
* 判断是否在数组中 | |||
* | |||
* @param s | |||
* @param array | |||
* @return | |||
*/ | |||
public static boolean inArray(final String s, final String[] array) { | |||
for (String item : array) { | |||
if (item != null && item.equals(s)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 从html中提取纯文本 | |||
* | |||
* @param strHtml | |||
* @return | |||
*/ | |||
public static String stripHtml(String strHtml) { | |||
String content = strHtml.replaceAll("</?[^>]+>", ""); //剔出<html>的标签 | |||
content = content.replaceAll("\\s*|\t|\r|\n", "");//去除字符串中的空格,回车,换行符,制表符 | |||
return content; | |||
} | |||
/** | |||
* 去除字符串中的空格、回车、换行符、制表符等 | |||
* | |||
* @param str 原始字符串 | |||
* @return | |||
*/ | |||
public static String replaceSpecialStr(String str) { | |||
String repl = ""; | |||
if (str != null) { | |||
Pattern p = Pattern.compile("\\s*|\t|\r|\n"); | |||
Matcher m = p.matcher(str); | |||
repl = m.replaceAll(""); | |||
} | |||
return repl; | |||
} | |||
/** | |||
* 判断某个元素是否在数组中 | |||
* | |||
* @param key 元素 | |||
* @param map 数组 | |||
* @return | |||
*/ | |||
public static boolean inArray(String key, Map<String, String> map) { | |||
boolean flag = false; | |||
for (String k : map.keySet()) { | |||
if (k.equals(key)) { | |||
flag = true; | |||
} | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 对象转Map | |||
* | |||
* @param obj 对象 | |||
* @return | |||
* @throws IllegalAccessException | |||
*/ | |||
public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException { | |||
Map<String, Object> map = new HashMap<>(); | |||
Class<?> clazz = obj.getClass(); | |||
for (Field field : clazz.getDeclaredFields()) { | |||
field.setAccessible(true); | |||
String fieldName = field.getName(); | |||
Object value = field.get(obj); | |||
map.put(fieldName, value); | |||
} | |||
return map; | |||
} | |||
/** | |||
* 判断是否是JSON格式 | |||
* | |||
* @param str JSON字符串 | |||
* @return | |||
*/ | |||
private boolean isJson(String str) { | |||
try { | |||
JSONObject jsonStr = JSONObject.parseObject(str); | |||
return true; | |||
} catch (Exception e) { | |||
return false; | |||
} | |||
} | |||
/** | |||
* MD5方法 | |||
* | |||
* @param source | |||
* @return | |||
*/ | |||
public static String md5(byte[] source) { | |||
try { | |||
MessageDigest md = MessageDigest.getInstance("MD5"); | |||
md.update(source); | |||
StringBuffer buf = new StringBuffer(); | |||
for (byte b : md.digest()) { | |||
buf.append(String.format("%02x", b & 0xff)); | |||
} | |||
return buf.toString(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return null; | |||
} | |||
} | |||
/** | |||
* 密码加密 | |||
* | |||
* @param password 密码 | |||
* @return | |||
*/ | |||
public static String password(String password) { | |||
String md51 = md5(password.getBytes()); | |||
String pwd = md5((md51 + "IgtUdEQJyVevaCxQnY").getBytes()); | |||
return pwd; | |||
} | |||
/** | |||
* 对数组进行分组 | |||
* | |||
* @param list 数据源 | |||
* @param size 每组几个 | |||
* @param <T> | |||
* @return | |||
*/ | |||
public static <T> List<List<T>> split(List<T> list, Integer size) { | |||
if (CollectionUtils.isEmpty(list)) { | |||
return new ArrayList<>(); | |||
} | |||
Integer count = list.size() / size; | |||
List<List<T>> arrayList = new ArrayList<>(); | |||
for (int i = 0; i < count; i++) { | |||
List<T> temp = list.subList(i * size, (i + 1) * size); | |||
arrayList.add(temp); | |||
} | |||
Integer extra = list.size() % size; | |||
if (extra != 0) { | |||
List<T> temp = list.subList(count * size, count * size + extra); | |||
arrayList.add(temp); | |||
} | |||
return arrayList; | |||
} | |||
/** | |||
* 批量处理方法 | |||
* | |||
* @param operater 回调方法,有入参无返回值 | |||
* @param sourceList 批量处理list对象 | |||
* @param threshold 阀值,比如有5000个对象,阀值设置1000,就是1000执行一次 | |||
* @param <T> 对象类型 | |||
*/ | |||
public static <T> void batchOperate(Consumer<List<T>> operater, List<T> sourceList, Integer threshold) { | |||
int size = sourceList.size(); | |||
int fromIndex = 0; | |||
List<T> list = null; | |||
while (size > fromIndex){ | |||
list = sourceList.stream().skip(fromIndex).limit(threshold).collect(Collectors.toList()); | |||
operater.accept(list); | |||
fromIndex += threshold; | |||
} | |||
} | |||
} |
@@ -0,0 +1,850 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.util.StringUtils; | |||
import java.math.BigDecimal; | |||
import java.math.BigInteger; | |||
import java.nio.ByteBuffer; | |||
import java.nio.charset.Charset; | |||
import java.text.NumberFormat; | |||
import java.util.Set; | |||
/** | |||
* 类型转换器 | |||
*/ | |||
public class ConvertUtil { | |||
/** | |||
* 转换为字符串<br> | |||
* 如果给定的值为null,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static String toStr(Object value, String defaultValue) { | |||
if (null == value) { | |||
return defaultValue; | |||
} | |||
if (value instanceof String) { | |||
return (String) value; | |||
} | |||
return value.toString(); | |||
} | |||
/** | |||
* 转换为字符串<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static String toStr(Object value) { | |||
return toStr(value, null); | |||
} | |||
/** | |||
* 转换为字符<br> | |||
* 如果给定的值为null,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Character toChar(Object value, Character defaultValue) { | |||
if (null == value) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Character) { | |||
return (Character) value; | |||
} | |||
final String valueStr = toStr(value, null); | |||
return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); | |||
} | |||
/** | |||
* 转换为字符<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Character toChar(Object value) { | |||
return toChar(value, null); | |||
} | |||
/** | |||
* 转换为byte<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Byte toByte(Object value, Byte defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Byte) { | |||
return (Byte) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).byteValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return Byte.parseByte(valueStr); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为byte<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Byte toByte(Object value) { | |||
return toByte(value, null); | |||
} | |||
/** | |||
* 转换为Short<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Short toShort(Object value, Short defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Short) { | |||
return (Short) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).shortValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return Short.parseShort(valueStr.trim()); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为Short<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Short toShort(Object value) { | |||
return toShort(value, null); | |||
} | |||
/** | |||
* 转换为Number<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Number toNumber(Object value, Number defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Number) { | |||
return (Number) value; | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return NumberFormat.getInstance().parse(valueStr); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为Number<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Number toNumber(Object value) { | |||
return toNumber(value, null); | |||
} | |||
/** | |||
* 转换为int<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Integer toInt(Object value, Integer defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Integer) { | |||
return (Integer) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).intValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return Integer.parseInt(valueStr.trim()); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为int<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Integer toInt(Object value) { | |||
return toInt(value, null); | |||
} | |||
/** | |||
* 转换为Integer数组<br> | |||
* | |||
* @param str 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Integer[] toIntArray(String str) { | |||
return toIntArray(",", str); | |||
} | |||
/** | |||
* 转换为Long数组<br> | |||
* | |||
* @param str 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Long[] toLongArray(String str) { | |||
return toLongArray(",", str); | |||
} | |||
/** | |||
* 转换为Integer数组<br> | |||
* | |||
* @param split 分隔符 | |||
* @param split 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Integer[] toIntArray(String split, String str) { | |||
if (StringUtils.isEmpty(str)) { | |||
return new Integer[]{}; | |||
} | |||
String[] arr = str.split(split); | |||
final Integer[] ints = new Integer[arr.length]; | |||
for (int i = 0; i < arr.length; i++) { | |||
final Integer v = toInt(arr[i], 0); | |||
ints[i] = v; | |||
} | |||
return ints; | |||
} | |||
/** | |||
* 转换为Long数组<br> | |||
* | |||
* @param split 分隔符 | |||
* @param str 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Long[] toLongArray(String split, String str) { | |||
if (StringUtils.isEmpty(str)) { | |||
return new Long[]{}; | |||
} | |||
String[] arr = str.split(split); | |||
final Long[] longs = new Long[arr.length]; | |||
for (int i = 0; i < arr.length; i++) { | |||
final Long v = toLong(arr[i], null); | |||
longs[i] = v; | |||
} | |||
return longs; | |||
} | |||
/** | |||
* 转换为String数组<br> | |||
* | |||
* @param str 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static String[] toStrArray(String str) { | |||
return toStrArray(",", str); | |||
} | |||
/** | |||
* 转换为String数组<br> | |||
* | |||
* @param split 分隔符 | |||
* @param split 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static String[] toStrArray(String split, String str) { | |||
return str.split(split); | |||
} | |||
/** | |||
* 转换为long<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Long toLong(Object value, Long defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Long) { | |||
return (Long) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).longValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
// 支持科学计数法 | |||
return new BigDecimal(valueStr.trim()).longValue(); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为long<br> | |||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Long toLong(Object value) { | |||
return toLong(value, null); | |||
} | |||
/** | |||
* 转换为double<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Double toDouble(Object value, Double defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Double) { | |||
return (Double) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).doubleValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
// 支持科学计数法 | |||
return new BigDecimal(valueStr.trim()).doubleValue(); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为double<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Double toDouble(Object value) { | |||
return toDouble(value, null); | |||
} | |||
/** | |||
* 转换为Float<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Float toFloat(Object value, Float defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Float) { | |||
return (Float) value; | |||
} | |||
if (value instanceof Number) { | |||
return ((Number) value).floatValue(); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return Float.parseFloat(valueStr.trim()); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为Float<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Float toFloat(Object value) { | |||
return toFloat(value, null); | |||
} | |||
/** | |||
* 转换为boolean<br> | |||
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static Boolean toBool(Object value, Boolean defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof Boolean) { | |||
return (Boolean) value; | |||
} | |||
String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
valueStr = valueStr.trim().toLowerCase(); | |||
switch (valueStr) { | |||
case "true": | |||
return true; | |||
case "false": | |||
return false; | |||
case "yes": | |||
return true; | |||
case "ok": | |||
return true; | |||
case "no": | |||
return false; | |||
case "1": | |||
return true; | |||
case "0": | |||
return false; | |||
default: | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为boolean<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static Boolean toBool(Object value) { | |||
return toBool(value, null); | |||
} | |||
/** | |||
* 转换为Enum对象<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* | |||
* @param clazz Enum的Class | |||
* @param value 值 | |||
* @param defaultValue 默认值 | |||
* @return Enum | |||
*/ | |||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (clazz.isAssignableFrom(value.getClass())) { | |||
@SuppressWarnings("unchecked") | |||
E myE = (E) value; | |||
return myE; | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return Enum.valueOf(clazz, valueStr); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为Enum对象<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* | |||
* @param clazz Enum的Class | |||
* @param value 值 | |||
* @return Enum | |||
*/ | |||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) { | |||
return toEnum(clazz, value, null); | |||
} | |||
/** | |||
* 转换为BigInteger<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof BigInteger) { | |||
return (BigInteger) value; | |||
} | |||
if (value instanceof Long) { | |||
return BigInteger.valueOf((Long) value); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return new BigInteger(valueStr); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为BigInteger<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static BigInteger toBigInteger(Object value) { | |||
return toBigInteger(value, null); | |||
} | |||
/** | |||
* 转换为BigDecimal<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @param defaultValue 转换错误时的默认值 | |||
* @return 结果 | |||
*/ | |||
public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { | |||
if (value == null) { | |||
return defaultValue; | |||
} | |||
if (value instanceof BigDecimal) { | |||
return (BigDecimal) value; | |||
} | |||
if (value instanceof Long) { | |||
return new BigDecimal((Long) value); | |||
} | |||
if (value instanceof Double) { | |||
return new BigDecimal((Double) value); | |||
} | |||
if (value instanceof Integer) { | |||
return new BigDecimal((Integer) value); | |||
} | |||
final String valueStr = toStr(value, null); | |||
if (StringUtils.isEmpty(valueStr)) { | |||
return defaultValue; | |||
} | |||
try { | |||
return new BigDecimal(valueStr); | |||
} catch (Exception e) { | |||
return defaultValue; | |||
} | |||
} | |||
/** | |||
* 转换为BigDecimal<br> | |||
* 如果给定的值为空,或者转换失败,返回默认值<br> | |||
* 转换失败不会报错 | |||
* | |||
* @param value 被转换的值 | |||
* @return 结果 | |||
*/ | |||
public static BigDecimal toBigDecimal(Object value) { | |||
return toBigDecimal(value, null); | |||
} | |||
/** | |||
* 将对象转为字符串<br> | |||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 | |||
* | |||
* @param obj 对象 | |||
* @return 字符串 | |||
*/ | |||
public static String utf8Str(Object obj) { | |||
return str(obj, CharsetKit.CHARSET_UTF_8); | |||
} | |||
/** | |||
* 将对象转为字符串<br> | |||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 | |||
* | |||
* @param obj 对象 | |||
* @param charsetName 字符集 | |||
* @return 字符串 | |||
*/ | |||
public static String str(Object obj, String charsetName) { | |||
return str(obj, Charset.forName(charsetName)); | |||
} | |||
/** | |||
* 将对象转为字符串<br> | |||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 | |||
* | |||
* @param obj 对象 | |||
* @param charset 字符集 | |||
* @return 字符串 | |||
*/ | |||
public static String str(Object obj, Charset charset) { | |||
if (null == obj) { | |||
return null; | |||
} | |||
if (obj instanceof String) { | |||
return (String) obj; | |||
} else if (obj instanceof byte[] || obj instanceof Byte[]) { | |||
return str((Byte[]) obj, charset); | |||
} else if (obj instanceof ByteBuffer) { | |||
return str((ByteBuffer) obj, charset); | |||
} | |||
return obj.toString(); | |||
} | |||
/** | |||
* 将byte数组转为字符串 | |||
* | |||
* @param bytes byte数组 | |||
* @param charset 字符集 | |||
* @return 字符串 | |||
*/ | |||
public static String str(byte[] bytes, String charset) { | |||
return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); | |||
} | |||
/** | |||
* 解码字节码 | |||
* | |||
* @param data 字符串 | |||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台 | |||
* @return 解码后的字符串 | |||
*/ | |||
public static String str(byte[] data, Charset charset) { | |||
if (data == null) { | |||
return null; | |||
} | |||
if (null == charset) { | |||
return new String(data); | |||
} | |||
return new String(data, charset); | |||
} | |||
/** | |||
* 将编码的byteBuffer数据转换为字符串 | |||
* | |||
* @param data 数据 | |||
* @param charset 字符集,如果为空使用当前系统字符集 | |||
* @return 字符串 | |||
*/ | |||
public static String str(ByteBuffer data, String charset) { | |||
if (data == null) { | |||
return null; | |||
} | |||
return str(data, Charset.forName(charset)); | |||
} | |||
/** | |||
* 将编码的byteBuffer数据转换为字符串 | |||
* | |||
* @param data 数据 | |||
* @param charset 字符集,如果为空使用当前系统字符集 | |||
* @return 字符串 | |||
*/ | |||
public static String str(ByteBuffer data, Charset charset) { | |||
if (null == charset) { | |||
charset = Charset.defaultCharset(); | |||
} | |||
return charset.decode(data).toString(); | |||
} | |||
// ----------------------------------------------------------------------- 全角半角转换 | |||
/** | |||
* 半角转全角 | |||
* | |||
* @param input String. | |||
* @return 全角字符串. | |||
*/ | |||
public static String toSBC(String input) { | |||
return toSBC(input, null); | |||
} | |||
/** | |||
* 半角转全角 | |||
* | |||
* @param input String | |||
* @param notConvertSet 不替换的字符集合 | |||
* @return 全角字符串. | |||
*/ | |||
public static String toSBC(String input, Set<Character> notConvertSet) { | |||
char c[] = input.toCharArray(); | |||
for (int i = 0; i < c.length; i++) { | |||
if (null != notConvertSet && notConvertSet.contains(c[i])) { | |||
// 跳过不替换的字符 | |||
continue; | |||
} | |||
if (c[i] == ' ') { | |||
c[i] = '\u3000'; | |||
} else if (c[i] < '\177') { | |||
c[i] = (char) (c[i] + 65248); | |||
} | |||
} | |||
return new String(c); | |||
} | |||
/** | |||
* 全角转半角 | |||
* | |||
* @param input String. | |||
* @return 半角字符串 | |||
*/ | |||
public static String toDBC(String input) { | |||
return toDBC(input, null); | |||
} | |||
/** | |||
* 替换全角为半角 | |||
* | |||
* @param text 文本 | |||
* @param notConvertSet 不替换的字符集合 | |||
* @return 替换后的字符 | |||
*/ | |||
public static String toDBC(String text, Set<Character> notConvertSet) { | |||
char c[] = text.toCharArray(); | |||
for (int i = 0; i < c.length; i++) { | |||
if (null != notConvertSet && notConvertSet.contains(c[i])) { | |||
// 跳过不替换的字符 | |||
continue; | |||
} | |||
if (c[i] == '\u3000') { | |||
c[i] = ' '; | |||
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { | |||
c[i] = (char) (c[i] - 65248); | |||
} | |||
} | |||
String returnString = new String(c); | |||
return returnString; | |||
} | |||
/** | |||
* 数字金额大写转换 先写个完整的然后将如零拾替换成零 | |||
* | |||
* @param n 数字 | |||
* @return 中文大写数字 | |||
*/ | |||
public static String digitUppercase(double n) { | |||
String[] fraction = {"角", "分"}; | |||
String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; | |||
String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}}; | |||
String head = n < 0 ? "负" : ""; | |||
n = Math.abs(n); | |||
String s = ""; | |||
for (int i = 0; i < fraction.length; i++) { | |||
s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); | |||
} | |||
if (s.length() < 1) { | |||
s = "整"; | |||
} | |||
int integerPart = (int) Math.floor(n); | |||
for (int i = 0; i < unit[0].length && integerPart > 0; i++) { | |||
String p = ""; | |||
for (int j = 0; j < unit[1].length && n > 0; j++) { | |||
p = digit[integerPart % 10] + unit[1][j] + p; | |||
integerPart = integerPart / 10; | |||
} | |||
s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; | |||
} | |||
return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.tuoheng.common.utils; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
* 坐标转换工具类 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021/9/29 | |||
*/ | |||
@Slf4j | |||
public class CoordinateConversionUtil { | |||
} |
@@ -0,0 +1,213 @@ | |||
package com.tuoheng.common.utils; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* 坐标系转换工具类 | |||
* | |||
* @author zhu_zishuang | |||
* @date 6/22/21 | |||
*/ | |||
public class CoordinatesTransformControllerUtil { | |||
private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0; | |||
private static final double PI = 3.14159265358979324; | |||
// 卫星椭球坐标投影到平面地图坐标系的投影因子。 地球长半径 | |||
private static final double EARTH_MAJOR_RADIUS = 6378245.0; | |||
// 椭球的偏心率。 | |||
private static final double ECCENTRICITY_RATIO = 0.00669342162296594323; | |||
/** | |||
* @Discription: 百度坐标(bd09II)转火星坐标(GCJ02) | |||
* @Param lat 百度坐标纬度 | |||
* @Param lon 百度坐标经度 | |||
* @Created on: 2020/12/23 15:40 | |||
* @author muyuanpei | |||
*/ | |||
public static Map<String, Double> baiduTomars(double lat, double lon) { | |||
Map<String, Double> mars_point = new HashMap<>(); | |||
mars_point.put("lon", 0D); | |||
mars_point.put("lat", 0D); | |||
double x = lon - 0.0065; | |||
double y = lat - 0.006; | |||
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI); | |||
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI); | |||
mars_point.put("lon", z * Math.cos(theta)); | |||
mars_point.put("lat", z * Math.sin(theta)); | |||
return mars_point; | |||
} | |||
/** | |||
* @Discription: 火星坐标系(GCJ02)转百度坐标系(bd09II) | |||
* @Param gcjLat 火星坐标纬度 | |||
* @Param gcjLon 火星坐标经度 | |||
* @Created on: 2020/12/23 15:42 | |||
* @author muyuanpei | |||
*/ | |||
public static Map<String, Double> marsTobaidu(double gcjLat, double gcjLon) { | |||
HashMap<String, Double> baidu_point = new HashMap<>(); | |||
baidu_point.put("lon", 0D); | |||
baidu_point.put("lat", 0D); | |||
double x = gcjLon; | |||
double y = gcjLat; | |||
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * X_PI); | |||
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * X_PI); | |||
baidu_point.put("lon", z * Math.cos(theta) + 0.0065); | |||
baidu_point.put("lat", z * Math.sin(theta) + 0.006); | |||
return baidu_point; | |||
} | |||
/** | |||
* @Discription: 火星坐标系(GCJ02)转地球坐标系(WGS84) | |||
* @Param lat 火星坐标纬度 | |||
* @Param lon 火星坐标经度 | |||
* @Created on: 2020/12/23 15:42 | |||
* @author muyuanpei | |||
*/ | |||
public static Map<String, Double> transformGCJ2WGS(double gcjLat, double gcjLon) { | |||
Map<String, Double> map = delta(gcjLat, gcjLon); | |||
double lat = map.get("lat"); | |||
double lon = map.get("lon"); | |||
map.get("lon"); | |||
map.put("lat", gcjLat - lat); | |||
map.put("lon", gcjLon - lon); | |||
return map; | |||
} | |||
private static Map delta(double lat, double lon) { | |||
double dLat = transformLat(lon - 105.0, lat - 35.0, PI); | |||
double dLon = transformLon(lon - 105.0, lat - 35.0, PI); | |||
double radLat = lat / 180.0 * PI; | |||
double magic = Math.sin(radLat); | |||
magic = 1 - ECCENTRICITY_RATIO * magic * magic; | |||
double sqrtMagic = Math.sqrt(magic); | |||
dLat = (dLat * 180.0) / ((EARTH_MAJOR_RADIUS * (1 - ECCENTRICITY_RATIO)) / (magic * sqrtMagic) * PI); | |||
dLon = (dLon * 180.0) / (EARTH_MAJOR_RADIUS / sqrtMagic * Math.cos(radLat) * PI); | |||
Map<String, Double> map = new HashMap<>(); | |||
map.put("lat", dLat); | |||
map.put("lon", dLon); | |||
return map; | |||
} | |||
/** | |||
* @Discription: 地球坐标系(WGS - 84)转火星坐标系(GCJ) | |||
* @Param wgLat 地球坐标纬度 | |||
* @Param wgLon 地球坐标经度 | |||
* @Created on: 2020/12/23 15:42 | |||
* @author muyuanpei | |||
*/ | |||
public static Map<String, Double> transform(double wgLat, double wgLon) { | |||
HashMap<String, Double> mars_point = new HashMap<>(); | |||
mars_point.put("lon", 0D); | |||
mars_point.put("lat", 0D); | |||
// 如果是国外坐标点,则直接返回传进来的坐标点 | |||
if (outOfChina(wgLon, wgLat)) { | |||
mars_point.put("lon", wgLon); | |||
mars_point.put("lat", wgLat); | |||
return mars_point; | |||
} | |||
// 如果为国内坐标点,则计算偏移量 | |||
double dLat = transformLat(wgLon - 105.0, wgLat - 35.0, PI); | |||
double dLon = transformLon(wgLon - 105.0, wgLat - 35.0, PI); | |||
double radLat = wgLat / 180.0 * PI; | |||
double magic = Math.sin(radLat); | |||
magic = 1 - ECCENTRICITY_RATIO * magic * magic; | |||
double sqrtMagic = Math.sqrt(magic); | |||
dLat = (dLat * 180.0) / ((EARTH_MAJOR_RADIUS * (1 - ECCENTRICITY_RATIO)) / (magic * sqrtMagic) * PI); | |||
dLon = (dLon * 180.0) / (EARTH_MAJOR_RADIUS / sqrtMagic * Math.cos(radLat) * PI); | |||
mars_point.put("lon", wgLon + dLon); | |||
mars_point.put("lat", wgLat + dLat); | |||
return mars_point; | |||
} | |||
/** | |||
* 度分秒转度 | |||
* | |||
* @param lng | |||
* @return | |||
*/ | |||
public static Double tranformPos(String lng){ | |||
String[] lntArr = lng | |||
.trim() | |||
.replace("°", ";") | |||
.replace("′", ";") | |||
.replace("'", ";") | |||
.replace("\"", "") | |||
.split(";"); | |||
Double result = 0D; | |||
for (int i = lntArr.length; i >0 ; i--) { | |||
double v = Double.parseDouble(lntArr[i-1]); | |||
if(i==1){ | |||
result=v+result; | |||
}else{ | |||
result=(result+v)/60; | |||
} | |||
} | |||
return result; | |||
} | |||
/** | |||
* 经纬度转换 ,度分秒转度 | |||
* | |||
* @param jwd 需要转换的度分秒字符串,示例:119.0° 3.0' 56.54319999999984" | |||
* @return 示例 119.05 | |||
*/ | |||
public static String Dms2D(String jwd){ | |||
if(StringUtils.isNotEmpty(jwd)&&(jwd.contains("°"))){//如果不为空并且存在度单位 | |||
//计算前进行数据处理 | |||
jwd = jwd.replace("E", "").replace("N", "").replace(":", "").replace(":", ""); | |||
double d=0,m=0,s=0; | |||
d = Double.parseDouble(jwd.split("°")[0]); | |||
//不同单位的分,可扩展 | |||
if(jwd.contains("′")){//正常的′ | |||
m = Double.parseDouble(jwd.split("°")[1].split("′")[0]); | |||
}else if(jwd.contains("'")){//特殊的' | |||
m = Double.parseDouble(jwd.split("°")[1].split("'")[0]); | |||
} | |||
//不同单位的秒,可扩展 | |||
if(jwd.contains("″")){//正常的″ | |||
//有时候没有分 如:112°10.25″ | |||
s = jwd.contains("′")?Double.parseDouble(jwd.split("′")[1].split("″")[0]):Double.parseDouble(jwd.split("°")[1].split("″")[0]); | |||
}else if(jwd.contains("''")){//特殊的'' | |||
//有时候没有分 如:112°10.25'' | |||
s = jwd.contains("'")?Double.parseDouble(jwd.split("'")[1].split("''")[0]):Double.parseDouble(jwd.split("°")[1].split("''")[0]); | |||
} | |||
jwd = String.valueOf(d+m/60+s/60/60);//计算并转换为string | |||
} | |||
return jwd; | |||
} | |||
// 公共方法 | |||
/*判断是否在国内,不在国内则不做偏移*/ | |||
private static boolean outOfChina(double lon, double lat) { | |||
if ((lon < 72.004 || lon > 137.8347) && (lat < 0.8293 || lat > 55.8271)) { | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
private static double transformLat(double x, double y, double pi) { | |||
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); | |||
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; | |||
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0; | |||
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0; | |||
return ret; | |||
} | |||
private static double transformLon(double x, double y, double pi) { | |||
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); | |||
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; | |||
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0; | |||
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0; | |||
return ret; | |||
} | |||
} |
@@ -0,0 +1,164 @@ | |||
package com.tuoheng.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 zhuzishuang | |||
* @date 2021/10/8 | |||
*/ | |||
public final class DateUtils extends org.apache.commons.lang3.time.DateUtils { | |||
public static String YYYY = "yyyy"; | |||
public static String YYYY_MM = "yyyy-MM"; | |||
public static String YYYYMMDD = "yyyyMMdd"; | |||
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" | |||
}; | |||
/** | |||
* 获取当前时间 | |||
* | |||
* @return | |||
*/ | |||
public static Date now() { | |||
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 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 + "分钟"; | |||
} | |||
/** | |||
* 根据时间生成code | |||
*/ | |||
public static String generateCode(String prefix){ | |||
// 根据当前时间,生成任务编码 | |||
Date date = new Date(); | |||
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss"); | |||
return prefix + formatter.format(date); | |||
} | |||
/** | |||
* 生成图片编号,P+年月日时分秒+随机三位数 | |||
* @return | |||
*/ | |||
public static String generateFileCode(){ | |||
String Date = DateUtils.dateTimeNow(DateUtils.YYYYMMDDHHMMSS); | |||
int randomNum = (int)(Math.random() * 900) + 100; | |||
String fileCode = "P" + Date + randomNum; | |||
return fileCode; | |||
} | |||
} |
@@ -0,0 +1,695 @@ | |||
package com.tuoheng.common.utils; | |||
import com.tuoheng.common.annotation.Excel; | |||
import com.tuoheng.common.annotation.Excels; | |||
import com.tuoheng.common.config.UploadFileConfig; | |||
import com.tuoheng.common.exception.CustomException; | |||
import org.apache.poi.hssf.usermodel.HSSFDateUtil; | |||
import org.apache.poi.ss.usermodel.*; | |||
import org.apache.poi.ss.util.CellRangeAddressList; | |||
import org.apache.poi.xssf.streaming.SXSSFWorkbook; | |||
import org.apache.poi.xssf.usermodel.XSSFDataValidation; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.*; | |||
import java.lang.reflect.Field; | |||
import java.lang.reflect.Method; | |||
import java.math.BigDecimal; | |||
import java.text.DecimalFormat; | |||
import java.util.*; | |||
/** | |||
* 导出Excel工具类 | |||
*/ | |||
public class ExcelUtils<T> { | |||
private static final Logger log = LoggerFactory.getLogger(ExcelUtils.class); | |||
/** | |||
* Excel sheet最大行数,默认65536 | |||
*/ | |||
public static final int sheetSize = 65536; | |||
/** | |||
* 工作表名称 | |||
*/ | |||
private String sheetName; | |||
/** | |||
* 导出类型(EXPORT:导出数据;IMPORT:导入模板) | |||
*/ | |||
private Excel.Type type; | |||
/** | |||
* 工作薄对象 | |||
*/ | |||
private Workbook wb; | |||
/** | |||
* 工作表对象 | |||
*/ | |||
private Sheet sheet; | |||
/** | |||
* 样式列表 | |||
*/ | |||
private Map<String, CellStyle> styles; | |||
/** | |||
* 导入导出数据列表 | |||
*/ | |||
private List<T> list; | |||
/** | |||
* 注解列表 | |||
*/ | |||
private List<Object[]> fields; | |||
/** | |||
* 实体对象 | |||
*/ | |||
public Class<T> clazz; | |||
public ExcelUtils(Class<T> clazz) { | |||
this.clazz = clazz; | |||
} | |||
public void init(List<T> list, String sheetName, Excel.Type type) { | |||
if (list == null) { | |||
list = new ArrayList<T>(); | |||
} | |||
this.list = list; | |||
this.sheetName = sheetName; | |||
this.type = type; | |||
createExcelField(); | |||
createWorkbook(); | |||
} | |||
/** | |||
* 对excel表单默认第一个索引名转换成list | |||
* | |||
* @param is 输入流 | |||
* @return 转换后集合 | |||
*/ | |||
public List<T> importExcel(InputStream is) throws Exception { | |||
return importExcel(StringUtils.EMPTY, is); | |||
} | |||
/** | |||
* 对excel表单指定表格索引名转换成list | |||
* | |||
* @param sheetName 表格索引名 | |||
* @param is 输入流 | |||
* @return 转换后集合 | |||
*/ | |||
public List<T> importExcel(String sheetName, InputStream is) throws Exception { | |||
this.type = Excel.Type.IMPORT; | |||
this.wb = WorkbookFactory.create(is); | |||
List<T> list = new ArrayList<T>(); | |||
Sheet sheet = null; | |||
if (StringUtils.isNotEmpty(sheetName)) { | |||
// 如果指定sheet名,则取指定sheet中的内容. | |||
sheet = wb.getSheet(sheetName); | |||
} else { | |||
// 如果传入的sheet名不存在则默认指向第1个sheet. | |||
sheet = wb.getSheetAt(0); | |||
} | |||
if (sheet == null) { | |||
throw new IOException("文件sheet不存在"); | |||
} | |||
int rows = sheet.getPhysicalNumberOfRows(); | |||
if (rows > 0) { | |||
// 定义一个map用于存放excel列的序号和field. | |||
Map<String, Integer> cellMap = new HashMap<String, Integer>(); | |||
// 获取表头 | |||
Row heard = sheet.getRow(0); | |||
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) { | |||
Cell cell = heard.getCell(i); | |||
if (StringUtils.isNotNull(cell != null)) { | |||
String value = this.getCellValue(heard, i).toString(); | |||
cellMap.put(value, i); | |||
} else { | |||
cellMap.put(null, i); | |||
} | |||
} | |||
// 有数据时才处理 得到类的所有field. | |||
Field[] allFields = clazz.getDeclaredFields(); | |||
// 定义一个map用于存放列的序号和field. | |||
Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>(); | |||
for (int col = 0; col < allFields.length; col++) { | |||
Field field = allFields[col]; | |||
Excel attr = field.getAnnotation(Excel.class); | |||
if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type)) { | |||
// 设置类的私有字段属性可访问. | |||
field.setAccessible(true); | |||
Integer column = cellMap.get(attr.name()); | |||
fieldsMap.put(column, field); | |||
} | |||
} | |||
for (int i = 1; i < rows; i++) { | |||
// 从第2行开始取数据,默认第一行是表头. | |||
Row row = sheet.getRow(i); | |||
T entity = null; | |||
for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet()) { | |||
Object val = this.getCellValue(row, entry.getKey()); | |||
// 如果不存在实例则新建. | |||
entity = (entity == null ? clazz.newInstance() : entity); | |||
// 从map中得到对应列的field. | |||
Field field = fieldsMap.get(entry.getKey()); | |||
// 取得类型,并根据对象类型设置值. | |||
Class<?> fieldType = field.getType(); | |||
if (String.class == fieldType) { | |||
String s = ConvertUtil.toStr(val); | |||
if (StringUtils.endsWith(s, ".0")) { | |||
val = StringUtils.substringBefore(s, ".0"); | |||
} else { | |||
val = ConvertUtil.toStr(val); | |||
} | |||
} else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) { | |||
val = ConvertUtil.toInt(val); | |||
} else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) { | |||
val = ConvertUtil.toLong(val); | |||
} else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) { | |||
val = ConvertUtil.toDouble(val); | |||
} else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) { | |||
val = ConvertUtil.toFloat(val); | |||
} else if (BigDecimal.class == fieldType) { | |||
val = ConvertUtil.toBigDecimal(val); | |||
} else if (Date.class == fieldType) { | |||
if (val instanceof String) { | |||
val = DateUtils.parseDate(val); | |||
} else if (val instanceof Double) { | |||
val = DateUtil.getJavaDate((Double) val); | |||
} | |||
} | |||
if (StringUtils.isNotNull(fieldType)) { | |||
Excel attr = field.getAnnotation(Excel.class); | |||
String propertyName = field.getName(); | |||
if (StringUtils.isNotEmpty(attr.targetAttr())) { | |||
propertyName = field.getName() + "." + attr.targetAttr(); | |||
} else if (StringUtils.isNotEmpty(attr.readConverterExp())) { | |||
val = reverseByExp(String.valueOf(val), attr.readConverterExp()); | |||
} | |||
ReflectUtils.invokeSetter(entity, propertyName, val); | |||
} | |||
} | |||
list.add(entity); | |||
} | |||
} | |||
return list; | |||
} | |||
/** | |||
* 对list数据源将其里面的数据导入到excel表单 | |||
* | |||
* @param list 导出数据集合 | |||
* @param sheetName 工作表的名称 | |||
* @return 结果 | |||
*/ | |||
public JsonResult exportExcel(List<T> list, String sheetName) { | |||
this.init(list, sheetName, Excel.Type.EXPORT); | |||
return exportExcel(); | |||
} | |||
/** | |||
* 对list数据源将其里面的数据导入到excel表单 | |||
* | |||
* @param sheetName 工作表的名称 | |||
* @return 结果 | |||
*/ | |||
public JsonResult importTemplateExcel(String sheetName) { | |||
this.init(null, sheetName, Excel.Type.IMPORT); | |||
return exportExcel(); | |||
} | |||
/** | |||
* 对list数据源将其里面的数据导入到excel表单 | |||
* | |||
* @return 结果 | |||
*/ | |||
public JsonResult exportExcel() { | |||
OutputStream out = null; | |||
try { | |||
// 取出一共有多少个sheet. | |||
double sheetNo = Math.ceil(list.size() / sheetSize); | |||
for (int index = 0; index <= sheetNo; index++) { | |||
createSheet(sheetNo, index); | |||
// 产生一行 | |||
Row row = sheet.createRow(0); | |||
int column = 0; | |||
// 写入各个字段的列头名称 | |||
for (Object[] os : fields) { | |||
Excel excel = (Excel) os[1]; | |||
this.createCell(excel, row, column++); | |||
} | |||
if (Excel.Type.EXPORT.equals(type)) { | |||
fillExcelData(index, row); | |||
} | |||
} | |||
String filename = encodingFilename(sheetName); | |||
out = new FileOutputStream(getAbsoluteFile(filename)); | |||
wb.write(out); | |||
return JsonResult.success(filename, "导出成功"); | |||
} catch (Exception e) { | |||
log.error("导出Excel异常{}", e.getMessage()); | |||
throw new CustomException("导出Excel失败,请联系网站管理员!"); | |||
} finally { | |||
if (wb != null) { | |||
try { | |||
wb.close(); | |||
} catch (IOException e1) { | |||
e1.printStackTrace(); | |||
} | |||
} | |||
if (out != null) { | |||
try { | |||
out.close(); | |||
} catch (IOException e1) { | |||
e1.printStackTrace(); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* 填充excel数据 | |||
* | |||
* @param index 序号 | |||
* @param row 单元格行 | |||
*/ | |||
public void fillExcelData(int index, Row row) { | |||
int startNo = index * sheetSize; | |||
int endNo = Math.min(startNo + sheetSize, list.size()); | |||
for (int i = startNo; i < endNo; i++) { | |||
row = sheet.createRow(i + 1 - startNo); | |||
// 得到导出对象. | |||
T vo = (T) list.get(i); | |||
int column = 0; | |||
for (Object[] os : fields) { | |||
Field field = (Field) os[0]; | |||
Excel excel = (Excel) os[1]; | |||
// 设置实体类私有属性可访问 | |||
field.setAccessible(true); | |||
this.addCell(excel, row, vo, field, column++); | |||
} | |||
} | |||
} | |||
/** | |||
* 创建表格样式 | |||
* | |||
* @param wb 工作薄对象 | |||
* @return 样式列表 | |||
*/ | |||
private Map<String, CellStyle> createStyles(Workbook wb) { | |||
// 写入各条记录,每条记录对应excel表中的一行 | |||
Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); | |||
CellStyle style = wb.createCellStyle(); | |||
style.setAlignment(HorizontalAlignment.CENTER); | |||
style.setVerticalAlignment(VerticalAlignment.CENTER); | |||
style.setBorderRight(BorderStyle.THIN); | |||
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); | |||
style.setBorderLeft(BorderStyle.THIN); | |||
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); | |||
style.setBorderTop(BorderStyle.THIN); | |||
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); | |||
style.setBorderBottom(BorderStyle.THIN); | |||
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); | |||
Font dataFont = wb.createFont(); | |||
dataFont.setFontName("Arial"); | |||
dataFont.setFontHeightInPoints((short) 10); | |||
style.setFont(dataFont); | |||
styles.put("data", style); | |||
style = wb.createCellStyle(); | |||
style.cloneStyleFrom(styles.get("data")); | |||
style.setAlignment(HorizontalAlignment.CENTER); | |||
style.setVerticalAlignment(VerticalAlignment.CENTER); | |||
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); | |||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); | |||
Font headerFont = wb.createFont(); | |||
headerFont.setFontName("Arial"); | |||
headerFont.setFontHeightInPoints((short) 10); | |||
headerFont.setBold(true); | |||
headerFont.setColor(IndexedColors.WHITE.getIndex()); | |||
style.setFont(headerFont); | |||
styles.put("header", style); | |||
return styles; | |||
} | |||
/** | |||
* 创建单元格 | |||
*/ | |||
public Cell createCell(Excel attr, Row row, int column) { | |||
// 创建列 | |||
Cell cell = row.createCell(column); | |||
// 写入列信息 | |||
cell.setCellValue(attr.name()); | |||
setDataValidation(attr, row, column); | |||
cell.setCellStyle(styles.get("header")); | |||
return cell; | |||
} | |||
/** | |||
* 设置单元格信息 | |||
* | |||
* @param value 单元格值 | |||
* @param attr 注解相关 | |||
* @param cell 单元格信息 | |||
*/ | |||
public void setCellVo(Object value, Excel attr, Cell cell) { | |||
if (Excel.ColumnType.STRING == attr.cellType()) { | |||
cell.setCellType(CellType.NUMERIC); | |||
cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix()); | |||
} else if (Excel.ColumnType.NUMERIC == attr.cellType()) { | |||
cell.setCellType(CellType.NUMERIC); | |||
cell.setCellValue(Integer.parseInt(value + "")); | |||
} | |||
} | |||
/** | |||
* 创建表格样式 | |||
*/ | |||
public void setDataValidation(Excel attr, Row row, int column) { | |||
if (attr.name().indexOf("注:") >= 0) { | |||
sheet.setColumnWidth(column, 6000); | |||
} else { | |||
// 设置列宽 | |||
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); | |||
row.setHeight((short) (attr.height() * 20)); | |||
} | |||
// 如果设置了提示信息则鼠标放上去提示. | |||
if (StringUtils.isNotEmpty(attr.prompt())) { | |||
// 这里默认设了2-101列提示. | |||
setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column); | |||
} | |||
// 如果设置了combo属性则本列只能选择不能输入 | |||
if (attr.combo().length > 0) { | |||
// 这里默认设了2-101列只能选择不能输入. | |||
setXSSFValidation(sheet, attr.combo(), 1, 100, column, column); | |||
} | |||
} | |||
/** | |||
* 添加单元格 | |||
*/ | |||
public Cell addCell(Excel attr, Row row, T vo, Field field, int column) { | |||
Cell cell = null; | |||
try { | |||
// 设置行高 | |||
row.setHeight((short) (attr.height() * 20)); | |||
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. | |||
if (attr.isExport()) { | |||
// 创建cell | |||
cell = row.createCell(column); | |||
cell.setCellStyle(styles.get("data")); | |||
// 用于读取对象中的属性 | |||
Object value = getTargetValue(vo, field, attr); | |||
String dateFormat = attr.dateFormat(); | |||
String readConverterExp = attr.readConverterExp(); | |||
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { | |||
cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); | |||
} else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { | |||
cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp)); | |||
} else { | |||
// 设置列类型 | |||
setCellVo(value, attr, cell); | |||
} | |||
} | |||
} catch (Exception e) { | |||
log.error("导出Excel失败{}", e); | |||
} | |||
return cell; | |||
} | |||
/** | |||
* 设置 POI XSSFSheet 单元格提示 | |||
* | |||
* @param sheet 表单 | |||
* @param promptTitle 提示标题 | |||
* @param promptContent 提示内容 | |||
* @param firstRow 开始行 | |||
* @param endRow 结束行 | |||
* @param firstCol 开始列 | |||
* @param endCol 结束列 | |||
*/ | |||
public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow, | |||
int firstCol, int endCol) { | |||
DataValidationHelper helper = sheet.getDataValidationHelper(); | |||
DataValidationConstraint constraint = helper.createCustomConstraint("DD1"); | |||
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); | |||
DataValidation dataValidation = helper.createValidation(constraint, regions); | |||
dataValidation.createPromptBox(promptTitle, promptContent); | |||
dataValidation.setShowPromptBox(true); | |||
sheet.addValidationData(dataValidation); | |||
} | |||
/** | |||
* 设置某些列的值只能输入预制的数据,显示下拉框. | |||
* | |||
* @param sheet 要设置的sheet. | |||
* @param textlist 下拉框显示的内容 | |||
* @param firstRow 开始行 | |||
* @param endRow 结束行 | |||
* @param firstCol 开始列 | |||
* @param endCol 结束列 | |||
* @return 设置好的sheet. | |||
*/ | |||
public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) { | |||
DataValidationHelper helper = sheet.getDataValidationHelper(); | |||
// 加载下拉列表内容 | |||
DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist); | |||
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 | |||
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); | |||
// 数据有效性对象 | |||
DataValidation dataValidation = helper.createValidation(constraint, regions); | |||
// 处理Excel兼容性问题 | |||
if (dataValidation instanceof XSSFDataValidation) { | |||
dataValidation.setSuppressDropDownArrow(true); | |||
dataValidation.setShowErrorBox(true); | |||
} else { | |||
dataValidation.setSuppressDropDownArrow(false); | |||
} | |||
sheet.addValidationData(dataValidation); | |||
} | |||
/** | |||
* 解析导出值 0=男,1=女,2=未知 | |||
* | |||
* @param propertyValue 参数值 | |||
* @param converterExp 翻译注解 | |||
* @return 解析后值 | |||
* @throws Exception | |||
*/ | |||
public static String convertByExp(String propertyValue, String converterExp) throws Exception { | |||
try { | |||
String[] convertSource = converterExp.split(","); | |||
for (String item : convertSource) { | |||
String[] itemArray = item.split("="); | |||
if (itemArray[0].equals(propertyValue)) { | |||
return itemArray[1]; | |||
} | |||
} | |||
} catch (Exception e) { | |||
throw e; | |||
} | |||
return propertyValue; | |||
} | |||
/** | |||
* 反向解析值 男=0,女=1,未知=2 | |||
* | |||
* @param propertyValue 参数值 | |||
* @param converterExp 翻译注解 | |||
* @return 解析后值 | |||
* @throws Exception | |||
*/ | |||
public static String reverseByExp(String propertyValue, String converterExp) throws Exception { | |||
try { | |||
String[] convertSource = converterExp.split(","); | |||
for (String item : convertSource) { | |||
String[] itemArray = item.split("="); | |||
if (itemArray[1].equals(propertyValue)) { | |||
return itemArray[0]; | |||
} | |||
} | |||
} catch (Exception e) { | |||
throw e; | |||
} | |||
return propertyValue; | |||
} | |||
/** | |||
* 编码文件名 | |||
*/ | |||
public String encodingFilename(String filename) { | |||
filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; | |||
return filename; | |||
} | |||
/** | |||
* 获取下载路径 | |||
* | |||
* @param filename 文件名称 | |||
*/ | |||
public String getAbsoluteFile(String filename) { | |||
String downloadPath = UploadFileConfig.uploadFolder + filename; | |||
File desc = new File(downloadPath); | |||
if (!desc.getParentFile().exists()) { | |||
desc.getParentFile().mkdirs(); | |||
} | |||
return downloadPath; | |||
} | |||
/** | |||
* 获取bean中的属性值 | |||
* | |||
* @param vo 实体对象 | |||
* @param field 字段 | |||
* @param excel 注解 | |||
* @return 最终的属性值 | |||
* @throws Exception | |||
*/ | |||
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception { | |||
Object o = field.get(vo); | |||
if (StringUtils.isNotEmpty(excel.targetAttr())) { | |||
String target = excel.targetAttr(); | |||
if (target.indexOf(".") > -1) { | |||
String[] targets = target.split("[.]"); | |||
for (String name : targets) { | |||
o = getValue(o, name); | |||
} | |||
} else { | |||
o = getValue(o, target); | |||
} | |||
} | |||
return o; | |||
} | |||
/** | |||
* 以类的属性的get方法方法形式获取值 | |||
* | |||
* @param o | |||
* @param name | |||
* @return value | |||
* @throws Exception | |||
*/ | |||
private Object getValue(Object o, String name) throws Exception { | |||
if (StringUtils.isNotEmpty(name)) { | |||
Class<?> clazz = o.getClass(); | |||
String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); | |||
Method method = clazz.getMethod(methodName); | |||
o = method.invoke(o); | |||
} | |||
return o; | |||
} | |||
/** | |||
* 得到所有定义字段 | |||
*/ | |||
private void createExcelField() { | |||
this.fields = new ArrayList<Object[]>(); | |||
List<Field> tempFields = new ArrayList<>(); | |||
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); | |||
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); | |||
for (Field field : tempFields) { | |||
// 单注解 | |||
if (field.isAnnotationPresent(Excel.class)) { | |||
putToField(field, field.getAnnotation(Excel.class)); | |||
} | |||
// 多注解 | |||
if (field.isAnnotationPresent(Excels.class)) { | |||
Excels attrs = field.getAnnotation(Excels.class); | |||
Excel[] excels = attrs.value(); | |||
for (Excel excel : excels) { | |||
putToField(field, excel); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* 放到字段集合中 | |||
*/ | |||
private void putToField(Field field, Excel attr) { | |||
if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type)) { | |||
this.fields.add(new Object[]{field, attr}); | |||
} | |||
} | |||
/** | |||
* 创建一个工作簿 | |||
*/ | |||
public void createWorkbook() { | |||
this.wb = new SXSSFWorkbook(500); | |||
} | |||
/** | |||
* 创建工作表 | |||
* | |||
* @param sheetNo sheet数量 | |||
* @param index 序号 | |||
*/ | |||
public void createSheet(double sheetNo, int index) { | |||
this.sheet = wb.createSheet(); | |||
this.styles = createStyles(wb); | |||
// 设置工作表的名称. | |||
if (sheetNo == 0) { | |||
wb.setSheetName(index, sheetName); | |||
} else { | |||
wb.setSheetName(index, sheetName + index); | |||
} | |||
} | |||
/** | |||
* 获取单元格值 | |||
* | |||
* @param row 获取的行 | |||
* @param column 获取单元格列号 | |||
* @return 单元格值 | |||
*/ | |||
public Object getCellValue(Row row, int column) { | |||
if (row == null) { | |||
return row; | |||
} | |||
Object val = ""; | |||
try { | |||
Cell cell = row.getCell(column); | |||
if (cell != null) { | |||
if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) { | |||
val = cell.getNumericCellValue(); | |||
if (HSSFDateUtil.isCellDateFormatted(cell)) { | |||
val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 | |||
} else { | |||
if ((Double) val % 1 > 0) { | |||
val = new DecimalFormat("0.00").format(val); | |||
} else { | |||
val = new DecimalFormat("0").format(val); | |||
} | |||
} | |||
} else if (cell.getCellTypeEnum() == CellType.STRING) { | |||
val = cell.getStringCellValue(); | |||
} else if (cell.getCellTypeEnum() == CellType.BOOLEAN) { | |||
val = cell.getBooleanCellValue(); | |||
} else if (cell.getCellTypeEnum() == CellType.ERROR) { | |||
val = cell.getErrorCellValue(); | |||
} | |||
} | |||
} catch (Exception e) { | |||
return val; | |||
} | |||
return val; | |||
} | |||
} |
@@ -0,0 +1,194 @@ | |||
package com.tuoheng.common.utils; | |||
import cn.hutool.core.util.ObjectUtil; | |||
import org.springframework.mock.web.MockMultipartFile; | |||
import org.springframework.web.multipart.MultipartFile; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.io.*; | |||
import java.net.URLEncoder; | |||
/** | |||
* 文件处理工具类 | |||
*/ | |||
public class FileUtils { | |||
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; | |||
/** | |||
* 验证文件名是否含有特殊字符 | |||
* | |||
* @param filename | |||
* @return | |||
*/ | |||
public static boolean isValidFilename(String filename) { | |||
return filename.matches(FILENAME_PATTERN); | |||
} | |||
/** | |||
* 下载文件并重新编码 | |||
* | |||
* @param request 网络请求 | |||
* @param fileName 文件名 | |||
* @return | |||
* @throws UnsupportedEncodingException | |||
*/ | |||
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) | |||
throws UnsupportedEncodingException { | |||
final String agent = request.getHeader("USER-AGENT"); | |||
String filename = fileName; | |||
if (agent.contains("MSIE")) { | |||
// IE浏览器 | |||
filename = URLEncoder.encode(filename, "utf-8"); | |||
filename = filename.replace("+", " "); | |||
} else if (agent.contains("Firefox")) { | |||
// 火狐浏览器 | |||
filename = new String(fileName.getBytes(), "ISO8859-1"); | |||
} else if (agent.contains("Chrome")) { | |||
// google浏览器 | |||
filename = URLEncoder.encode(filename, "utf-8"); | |||
} else { | |||
// 其它浏览器 | |||
filename = URLEncoder.encode(filename, "utf-8"); | |||
} | |||
return filename; | |||
} | |||
/** | |||
* 输出指定文件的Byte数组 | |||
* | |||
* @param filePath | |||
* @param os | |||
* @throws IOException | |||
*/ | |||
public static void writeBytes(String filePath, OutputStream os) throws IOException { | |||
FileInputStream fis = null; | |||
try { | |||
File file = new File(filePath); | |||
if (!file.exists()) { | |||
throw new FileNotFoundException(filePath); | |||
} | |||
fis = new FileInputStream(file); | |||
byte[] b = new byte[1024]; | |||
int length; | |||
while ((length = fis.read(b)) > 0) { | |||
os.write(b, 0, length); | |||
} | |||
} catch (IOException e) { | |||
throw e; | |||
} finally { | |||
if (os != null) { | |||
try { | |||
os.close(); | |||
} catch (IOException e1) { | |||
e1.printStackTrace(); | |||
} | |||
} | |||
if (fis != null) { | |||
try { | |||
fis.close(); | |||
} catch (IOException e1) { | |||
e1.printStackTrace(); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* 删除文件 | |||
* | |||
* @param filePath 文件路径 | |||
* @return | |||
*/ | |||
public static boolean deleteFile(String filePath) { | |||
boolean flag = false; | |||
File file = new File(filePath); | |||
// 路径为文件且不为空则进行删除 | |||
if (file.isFile() && file.exists()) { | |||
file.delete(); | |||
flag = true; | |||
} | |||
return flag; | |||
} | |||
/** | |||
* MultipartFile 转 File | |||
* | |||
* @param mFile | |||
* @param filePathName | |||
*/ | |||
public static File multipartFileToFile(MultipartFile mFile, String filePathName) { | |||
File file = null; | |||
if (ObjectUtil.isNull(mFile)) { | |||
throw new RuntimeException("图片不能为空!"); | |||
} else { | |||
InputStream ins = null; | |||
try { | |||
ins = mFile.getInputStream(); | |||
file = new File(filePathName); | |||
inputStreamToFile(ins, file); | |||
ins.close(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
ThumbnailUtil.cenerateCompression(filePathName,filePathName,300,0.8); | |||
return file; | |||
} | |||
} | |||
/** | |||
* File 转 MultipartFile | |||
* | |||
* @param file | |||
* @throws Exception | |||
*/ | |||
public static void fileToMultipartFile(File file) throws Exception { | |||
FileInputStream fileInput = new FileInputStream(file); | |||
MultipartFile toMultipartFile = new MockMultipartFile("file", file.getName(), "text/plain", getBytesByFile(fileInput)); | |||
toMultipartFile.getInputStream(); | |||
} | |||
public static void inputStreamToFile(InputStream ins, File file) { | |||
try { | |||
OutputStream os = new FileOutputStream(file); | |||
int bytesRead = 0; | |||
byte[] buffer = new byte[8192]; | |||
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { | |||
os.write(buffer, 0, bytesRead); | |||
} | |||
os.close(); | |||
ins.close(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
* 将 File 转换成 Byte数组 | |||
* | |||
* @param fis | |||
* @return | |||
*/ | |||
public static byte[] getBytesByFile(FileInputStream fis) { | |||
try { | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
byte[] b = new byte[1024 * 1024 * 10]; | |||
int n; | |||
while ((n = fis.read(b)) != -1) { | |||
bos.write(b, 0, n); | |||
} | |||
fis.close(); | |||
byte[] data = bos.toByteArray(); | |||
bos.close(); | |||
return data; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
package com.tuoheng.common.utils; | |||
import java.util.Properties; | |||
/** | |||
* 判断当前系统类型 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021/10/8 | |||
*/ | |||
public class HawkOSOperator { | |||
/** | |||
* 判断windows系统 | |||
* | |||
* @return | |||
*/ | |||
public static boolean isWindowsOS() { | |||
Properties props = System.getProperties(); | |||
if (props.getProperty("os.name").toLowerCase().contains("windows")) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 判断linux系统 | |||
* | |||
* @return | |||
*/ | |||
public static boolean isLinuxOS() { | |||
Properties props = System.getProperties(); | |||
if (props.getProperty("os.name").toLowerCase().contains("linux")) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 判断mac系统 | |||
* | |||
* @return | |||
*/ | |||
public static boolean isMacOS() { | |||
Properties props = System.getProperties(); | |||
if (props.getProperty("os.name").toLowerCase().contains("mac")) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,186 @@ | |||
package com.tuoheng.common.utils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import javax.net.ssl.*; | |||
import java.io.*; | |||
import java.net.ConnectException; | |||
import java.net.SocketTimeoutException; | |||
import java.net.URL; | |||
import java.net.URLConnection; | |||
import java.security.cert.X509Certificate; | |||
/** | |||
* 通用http发送方法 | |||
*/ | |||
public class HttpUtils { | |||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); | |||
/** | |||
* 向指定 URL 发送GET方法的请求 | |||
* | |||
* @param url 发送请求的 URL | |||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 | |||
* @return 所代表远程资源的响应结果 | |||
*/ | |||
public static String sendGet(String url, String param) { | |||
StringBuilder result = new StringBuilder(); | |||
BufferedReader in = null; | |||
try { | |||
String urlNameString = url + "?" + param; | |||
log.info("sendGet - {}", urlNameString); | |||
URL realUrl = new URL(urlNameString); | |||
URLConnection connection = realUrl.openConnection(); | |||
connection.setRequestProperty("accept", "*/*"); | |||
connection.setRequestProperty("connection", "Keep-Alive"); | |||
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); | |||
connection.connect(); | |||
in = new BufferedReader(new InputStreamReader(connection.getInputStream())); | |||
String line; | |||
while ((line = in.readLine()) != null) { | |||
result.append(line); | |||
} | |||
log.info("recv - {}", result); | |||
} catch (ConnectException e) { | |||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); | |||
} catch (SocketTimeoutException e) { | |||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); | |||
} catch (IOException e) { | |||
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); | |||
} catch (Exception e) { | |||
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); | |||
} finally { | |||
try { | |||
if (in != null) { | |||
in.close(); | |||
} | |||
} catch (Exception ex) { | |||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); | |||
} | |||
} | |||
return result.toString(); | |||
} | |||
/** | |||
* 向指定 URL 发送POST方法的请求 | |||
* | |||
* @param url 发送请求的 URL | |||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 | |||
* @return 所代表远程资源的响应结果 | |||
*/ | |||
public static String sendPost(String url, String param) { | |||
PrintWriter out = null; | |||
BufferedReader in = null; | |||
StringBuilder result = new StringBuilder(); | |||
try { | |||
String urlNameString = url + "?" + param; | |||
log.info("sendPost - {}", urlNameString); | |||
URL realUrl = new URL(urlNameString); | |||
URLConnection conn = realUrl.openConnection(); | |||
conn.setRequestProperty("accept", "*/*"); | |||
conn.setRequestProperty("connection", "Keep-Alive"); | |||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); | |||
conn.setRequestProperty("Accept-Charset", "utf-8"); | |||
conn.setRequestProperty("contentType", "utf-8"); | |||
conn.setDoOutput(true); | |||
conn.setDoInput(true); | |||
out = new PrintWriter(conn.getOutputStream()); | |||
out.print(param); | |||
out.flush(); | |||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); | |||
String line; | |||
while ((line = in.readLine()) != null) { | |||
result.append(line); | |||
} | |||
log.info("recv - {}", result); | |||
} catch (ConnectException e) { | |||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); | |||
} catch (SocketTimeoutException e) { | |||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); | |||
} catch (IOException e) { | |||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); | |||
} catch (Exception e) { | |||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); | |||
} finally { | |||
try { | |||
if (out != null) { | |||
out.close(); | |||
} | |||
if (in != null) { | |||
in.close(); | |||
} | |||
} catch (IOException ex) { | |||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); | |||
} | |||
} | |||
return result.toString(); | |||
} | |||
public static String sendSSLPost(String url, String param) { | |||
StringBuilder result = new StringBuilder(); | |||
String urlNameString = url + "?" + param; | |||
try { | |||
log.info("sendSSLPost - {}", urlNameString); | |||
SSLContext sc = SSLContext.getInstance("SSL"); | |||
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); | |||
URL console = new URL(urlNameString); | |||
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); | |||
conn.setRequestProperty("accept", "*/*"); | |||
conn.setRequestProperty("connection", "Keep-Alive"); | |||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); | |||
conn.setRequestProperty("Accept-Charset", "utf-8"); | |||
conn.setRequestProperty("contentType", "utf-8"); | |||
conn.setDoOutput(true); | |||
conn.setDoInput(true); | |||
conn.setSSLSocketFactory(sc.getSocketFactory()); | |||
conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); | |||
conn.connect(); | |||
InputStream is = conn.getInputStream(); | |||
BufferedReader br = new BufferedReader(new InputStreamReader(is)); | |||
String ret = ""; | |||
while ((ret = br.readLine()) != null) { | |||
if (ret != null && !ret.trim().equals("")) { | |||
result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8")); | |||
} | |||
} | |||
log.info("recv - {}", result); | |||
conn.disconnect(); | |||
br.close(); | |||
} catch (ConnectException e) { | |||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); | |||
} catch (SocketTimeoutException e) { | |||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); | |||
} catch (IOException e) { | |||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); | |||
} catch (Exception e) { | |||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); | |||
} | |||
return result.toString(); | |||
} | |||
private static class TrustAnyTrustManager implements X509TrustManager { | |||
@Override | |||
public void checkClientTrusted(X509Certificate[] chain, String authType) { | |||
} | |||
@Override | |||
public void checkServerTrusted(X509Certificate[] chain, String authType) { | |||
} | |||
@Override | |||
public X509Certificate[] getAcceptedIssuers() { | |||
return new X509Certificate[]{}; | |||
} | |||
} | |||
private static class TrustAnyHostnameVerifier implements HostnameVerifier { | |||
@Override | |||
public boolean verify(String hostname, SSLSession session) { | |||
return true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,294 @@ | |||
package com.tuoheng.common.utils; | |||
import com.alibaba.fastjson.JSON; | |||
import com.alibaba.fastjson.JSONObject; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.StringUtils; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.io.BufferedReader; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.net.*; | |||
/** | |||
* 获取IP工具类 | |||
*/ | |||
public class IpUtils { | |||
private static final Logger log = LoggerFactory.getLogger(IpUtils.class); | |||
/** | |||
* 通过HttpServletRequest返回IP地址 | |||
* | |||
* @param request HttpServletRequest | |||
* @return ip String | |||
* @throws Exception | |||
*/ | |||
public static String getIpAddr(HttpServletRequest request) { | |||
if (request == null) { | |||
return "unknown"; | |||
} | |||
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(); | |||
} | |||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; | |||
} | |||
public static boolean internalIp(String ip) { | |||
byte[] addr = textToNumericFormatV4(ip); | |||
return internalIp(addr) || "127.0.0.1".equals(ip); | |||
} | |||
private static boolean internalIp(byte[] addr) { | |||
final byte b0 = addr[0]; | |||
final byte b1 = addr[1]; | |||
// 10.x.x.x/8 | |||
final byte SECTION_1 = 0x0A; | |||
// 172.16.x.x/12 | |||
final byte SECTION_2 = (byte) 0xAC; | |||
final byte SECTION_3 = (byte) 0x10; | |||
final byte SECTION_4 = (byte) 0x1F; | |||
// 192.168.x.x/16 | |||
final byte SECTION_5 = (byte) 0xC0; | |||
final byte SECTION_6 = (byte) 0xA8; | |||
switch (b0) { | |||
case SECTION_1: | |||
return true; | |||
case SECTION_2: | |||
if (b1 >= SECTION_3 && b1 <= SECTION_4) { | |||
return true; | |||
} | |||
case SECTION_5: | |||
switch (b1) { | |||
case SECTION_6: | |||
return true; | |||
} | |||
default: | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将IPv4地址转换成字节 | |||
* | |||
* @param text IPv4地址 | |||
* @return byte 字节 | |||
*/ | |||
public static byte[] textToNumericFormatV4(String text) { | |||
if (text.length() == 0) { | |||
return null; | |||
} | |||
byte[] bytes = new byte[4]; | |||
String[] elements = text.split("\\.", -1); | |||
try { | |||
long l; | |||
int i; | |||
switch (elements.length) { | |||
case 1: | |||
l = Long.parseLong(elements[0]); | |||
if ((l < 0L) || (l > 4294967295L)) | |||
return null; | |||
bytes[0] = (byte) (int) (l >> 24 & 0xFF); | |||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); | |||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 2: | |||
l = Integer.parseInt(elements[0]); | |||
if ((l < 0L) || (l > 255L)) | |||
return null; | |||
bytes[0] = (byte) (int) (l & 0xFF); | |||
l = Integer.parseInt(elements[1]); | |||
if ((l < 0L) || (l > 16777215L)) | |||
return null; | |||
bytes[1] = (byte) (int) (l >> 16 & 0xFF); | |||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 3: | |||
for (i = 0; i < 2; ++i) { | |||
l = Integer.parseInt(elements[i]); | |||
if ((l < 0L) || (l > 255L)) | |||
return null; | |||
bytes[i] = (byte) (int) (l & 0xFF); | |||
} | |||
l = Integer.parseInt(elements[2]); | |||
if ((l < 0L) || (l > 65535L)) | |||
return null; | |||
bytes[2] = (byte) (int) (l >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 4: | |||
for (i = 0; i < 4; ++i) { | |||
l = Integer.parseInt(elements[i]); | |||
if ((l < 0L) || (l > 255L)) | |||
return null; | |||
bytes[i] = (byte) (int) (l & 0xFF); | |||
} | |||
break; | |||
default: | |||
return null; | |||
} | |||
} catch (NumberFormatException e) { | |||
return null; | |||
} | |||
return bytes; | |||
} | |||
public static String getHostIp() { | |||
try { | |||
return InetAddress.getLocalHost().getHostAddress(); | |||
} catch (UnknownHostException e) { | |||
} | |||
return "127.0.0.1"; | |||
} | |||
public static String getHostName() { | |||
try { | |||
return InetAddress.getLocalHost().getHostName(); | |||
} catch (UnknownHostException e) { | |||
} | |||
return "未知"; | |||
} | |||
/** | |||
* 通过IP地址获取MAC地址 | |||
* | |||
* @param ip String,127.0.0.1格式 | |||
* @return mac String | |||
* @throws Exception | |||
*/ | |||
public String getMACAddress(String ip) throws Exception { | |||
String line = ""; | |||
String macAddress = ""; | |||
final String MAC_ADDRESS_PREFIX = "MAC Address = "; | |||
final String LOOPBACK_ADDRESS = "127.0.0.1"; | |||
//如果为127.0.0.1,则获取本地MAC地址。 | |||
if (LOOPBACK_ADDRESS.equals(ip)) { | |||
InetAddress inetAddress = InetAddress.getLocalHost(); | |||
//貌似此方法需要JDK1.6。 | |||
byte[] mac = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress(); | |||
//下面代码是把mac地址拼装成String | |||
StringBuilder sb = new StringBuilder(); | |||
for (int i = 0; i < mac.length; i++) { | |||
if (i != 0) { | |||
sb.append("-"); | |||
} | |||
//mac[i] & 0xFF 是为了把byte转化为正整数 | |||
String s = Integer.toHexString(mac[i] & 0xFF); | |||
sb.append(s.length() == 1 ? 0 + s : s); | |||
} | |||
//把字符串所有小写字母改为大写成为正规的mac地址并返回 | |||
macAddress = sb.toString().trim().toUpperCase(); | |||
return macAddress; | |||
} | |||
//获取非本地IP的MAC地址 | |||
try { | |||
Process p = Runtime.getRuntime().exec("nbtstat -A " + ip); | |||
InputStreamReader isr = new InputStreamReader(p.getInputStream()); | |||
BufferedReader br = new BufferedReader(isr); | |||
while ((line = br.readLine()) != null) { | |||
if (line != null) { | |||
int index = line.indexOf(MAC_ADDRESS_PREFIX); | |||
if (index != -1) { | |||
macAddress = line.substring(index + MAC_ADDRESS_PREFIX.length()).trim().toUpperCase(); | |||
} | |||
} | |||
} | |||
br.close(); | |||
} catch (IOException e) { | |||
e.printStackTrace(System.out); | |||
} | |||
return macAddress; | |||
} | |||
/** | |||
* 通过IP获取地址(需要联网,调用淘宝的IP库) | |||
* | |||
* @param ip IP地址 | |||
* @return | |||
*/ | |||
public static String getIpInfo(String ip) { | |||
if ("127.0.0.1".equals(ip)) { | |||
ip = "127.0.0.1"; | |||
} | |||
String info = ""; | |||
try { | |||
URL url = new URL("http://ip.taobao.com/service/getIpInfo.php?ip=" + ip); | |||
HttpURLConnection htpcon = (HttpURLConnection) url.openConnection(); | |||
htpcon.setRequestMethod("GET"); | |||
htpcon.setDoOutput(true); | |||
htpcon.setDoInput(true); | |||
htpcon.setUseCaches(false); | |||
InputStream in = htpcon.getInputStream(); | |||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); | |||
StringBuffer temp = new StringBuffer(); | |||
String line = bufferedReader.readLine(); | |||
while (line != null) { | |||
temp.append(line).append("\r\n"); | |||
line = bufferedReader.readLine(); | |||
} | |||
bufferedReader.close(); | |||
JSONObject obj = (JSONObject) JSON.parse(temp.toString()); | |||
if (obj.getIntValue("code") == 0) { | |||
JSONObject data = obj.getJSONObject("data"); | |||
info += data.getString("country") + " "; | |||
info += data.getString("region") + " "; | |||
info += data.getString("city") + " "; | |||
info += data.getString("isp"); | |||
} | |||
} catch (MalformedURLException e) { | |||
e.printStackTrace(); | |||
} catch (ProtocolException e) { | |||
e.printStackTrace(); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
return info; | |||
} | |||
/** | |||
* 根据IP查询地区 | |||
* | |||
* @param ip IP地址 | |||
* @return | |||
*/ | |||
public static String getRealAddressByIP(String ip) { | |||
String address = "XX XX"; | |||
// 内网不查询 | |||
if (IpUtils.internalIp(ip)) { | |||
return "内网IP"; | |||
} | |||
String rspStr = HttpUtils.sendPost("http://ip.taobao.com/service/getIpInfo.php", "ip=" + ip); | |||
if (StringUtils.isEmpty(rspStr)) { | |||
log.error("获取地理位置异常 {}", ip); | |||
return address; | |||
} | |||
JSONObject obj = JSONObject.parseObject(rspStr); | |||
JSONObject data = obj.getObject("data", JSONObject.class); | |||
String region = data.getString("region"); | |||
String city = data.getString("city"); | |||
address = region + " " + city; | |||
return address; | |||
} | |||
} |
@@ -0,0 +1,133 @@ | |||
package com.tuoheng.common.utils; | |||
import com.fasterxml.jackson.annotation.JsonInclude; | |||
import com.fasterxml.jackson.databind.DeserializationFeature; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import com.fasterxml.jackson.databind.ObjectMapper; | |||
import com.fasterxml.jackson.databind.SerializationFeature; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.StringUtils; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Map; | |||
public class JacksonUtil { | |||
private final static Logger LOGGER = LoggerFactory.getLogger(JacksonUtil.class); | |||
private final static ObjectMapper objectMapper = new ObjectMapper(); | |||
private final static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||
private JacksonUtil(){} | |||
static { | |||
//序列化的时候序列对象的所有属性 | |||
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); | |||
//反序列化的时候如果多了其他属性,不抛出异常 | |||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |||
//如果是空对象的时候,不抛异常 | |||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); | |||
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式 | |||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); | |||
objectMapper.setDateFormat(dateformat); | |||
} | |||
/** | |||
* 转换为 JSON 字符串 | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
public static <T> String obj2json(T obj) { | |||
if(obj != null){ | |||
try { | |||
return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj); | |||
} catch (Exception e) { | |||
LOGGER.error("对象转字符串异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 漂亮的转换为 JSON 字符串 | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
public static <T> String obj2StringPretty(T obj) { | |||
if(obj != null){ | |||
try { | |||
return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); | |||
} catch (Exception e) { | |||
LOGGER.error("对象转字符串异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 转换为 JavaBean | |||
* | |||
* @param str | |||
* @param clazz | |||
* @return | |||
* @throws Exception | |||
*/ | |||
public static <T> T json2pojo(String str, Class<T> clazz) { | |||
if(StringUtils.hasLength(str) && clazz != null){ | |||
try { | |||
return clazz.equals(String.class)? (T)str : objectMapper.readValue(str,clazz); | |||
} catch (Exception e) { | |||
LOGGER.error("字符串转对象异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 将 Map 转换为 JavaBean | |||
* | |||
* @param map | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T map2pojo(Map map, Class<T> clazz) { | |||
return objectMapper.convertValue(map, clazz); | |||
} | |||
/** | |||
* 将 JSON 对象转换为 JavaBean | |||
* | |||
* @param obj | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T obj2pojo(Object obj, Class<T> clazz) { | |||
return objectMapper.convertValue(obj, clazz); | |||
} | |||
/** | |||
* 将指定节点的 JSON 数据转换为 JavaBean | |||
* | |||
* @param str | |||
* @param treeNode | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T json2pojoByTree(String str, String treeNode, Class<T> clazz) { | |||
if(StringUtils.hasLength(str) && StringUtils.hasLength(str) && clazz == null){ | |||
try { | |||
JsonNode jsonNode = objectMapper.readTree(str); | |||
JsonNode data = jsonNode.findPath(treeNode); | |||
return json2pojo(data.toString(), clazz); | |||
} catch (Exception e) { | |||
LOGGER.error("字符串按节点转对象异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
package com.tuoheng.common.utils; | |||
import com.tuoheng.common.constant.CommonConstants; | |||
import java.io.Serializable; | |||
/** | |||
* JSON回应类 | |||
* | |||
* @author 拓恒 | |||
* @date 2019/11/28 | |||
*/ | |||
public class JsonResult<T> implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 成功 | |||
*/ | |||
public static final int SUCCESS = 0;//CommonConstants.SUCCESS; | |||
/** | |||
* 失败 | |||
*/ | |||
public static final int error = CommonConstants.FAIL; | |||
private int code; | |||
private String msg; | |||
private T data; | |||
public static <T> JsonResult<T> success() { | |||
return jsonResult(null, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(T data) { | |||
return jsonResult(data, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(T data, String msg) { | |||
return jsonResult(data, SUCCESS, msg); | |||
} | |||
public static <T> JsonResult<T> error() { | |||
return jsonResult(null, error, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(String msg) { | |||
return jsonResult(null, error, msg); | |||
} | |||
public static <T> JsonResult<T> error(T data) { | |||
return jsonResult(data, error, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(T data, String msg) { | |||
return jsonResult(data, error, msg); | |||
} | |||
public static <T> JsonResult<T> error(int code, String msg) { | |||
return jsonResult(null, code, msg); | |||
} | |||
private static <T> JsonResult<T> jsonResult(T data, int code, String msg) { | |||
JsonResult<T> result = new JsonResult<>(); | |||
result.setCode(code); | |||
result.setData(data); | |||
result.setMsg(msg); | |||
return result; | |||
} | |||
public int getCode() { | |||
return code; | |||
} | |||
public void setCode(int code) { | |||
this.code = code; | |||
} | |||
public String getMsg() { | |||
return msg; | |||
} | |||
public void setMsg(String msg) { | |||
this.msg = msg; | |||
} | |||
public T getData() { | |||
return data; | |||
} | |||
public void setData(T data) { | |||
this.data = data; | |||
} | |||
} |
@@ -0,0 +1,125 @@ | |||
package com.tuoheng.common.utils; | |||
import io.jsonwebtoken.Claims; | |||
import io.jsonwebtoken.JwtBuilder; | |||
import io.jsonwebtoken.Jwts; | |||
import io.jsonwebtoken.SignatureAlgorithm; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.UUID; | |||
/** | |||
* Jwt工具类 | |||
*/ | |||
public class JwtUtil { | |||
/** | |||
* token秘钥 | |||
*/ | |||
public static final String key = "KfxcZITlSBpzE2mgb3eJno"; | |||
/** | |||
* token 过期时间(10个小时) | |||
*/ | |||
public static final long ttlMillis = 3600000 * 10L; | |||
/** | |||
* 用户登录成功后生成Jwt | |||
* 使用Hs256算法 私匙使用用户密码 | |||
* | |||
* @param userId 登录成功userId | |||
* @return | |||
*/ | |||
public static String createJWT(Integer userId) { | |||
//指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。 | |||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; | |||
//生成JWT的时间 | |||
long nowMillis = System.currentTimeMillis(); | |||
Date now = new Date(nowMillis); | |||
//创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的) | |||
Map<String, Object> claims = new HashMap<String, Object>(); | |||
claims.put("id", userId); | |||
//生成签发人 | |||
String subject = "JWT"; | |||
//下面就是在为payload添加各种标准声明和私有声明了 | |||
//这里其实就是new一个JwtBuilder,设置jwt的body | |||
JwtBuilder builder = Jwts.builder() | |||
//如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 | |||
.setClaims(claims) | |||
//设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。 | |||
.setId(UUID.randomUUID().toString()) | |||
//iat: jwt的签发时间 | |||
.setIssuedAt(now) | |||
//代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。 | |||
.setSubject(subject) | |||
//设置签名使用的签名算法和签名使用的秘钥 | |||
.signWith(signatureAlgorithm, key); | |||
if (ttlMillis >= 0) { | |||
long expMillis = nowMillis + ttlMillis; | |||
Date exp = new Date(expMillis); | |||
//设置过期时间 | |||
builder.setExpiration(exp); | |||
} | |||
return builder.compact(); | |||
} | |||
/** | |||
* Token的解密 | |||
* | |||
* @param token 加密后的token | |||
* @return | |||
*/ | |||
public static Claims parseJWT(String token) { | |||
//得到DefaultJwtParser | |||
Claims claims = Jwts.parser() | |||
//设置签名的秘钥 | |||
.setSigningKey(key) | |||
//设置需要解析的jwt | |||
.parseClaimsJws(token).getBody(); | |||
return claims; | |||
} | |||
/** | |||
* 校验token | |||
* 在这里可以使用官方的校验,我这里校验的是token中携带的密码于数据库一致的话就校验通过 | |||
* | |||
* @param token | |||
* @param userId | |||
* @return | |||
*/ | |||
public static Boolean isVerify(String token, Integer userId) { | |||
//得到DefaultJwtParser | |||
Claims claims = Jwts.parser() | |||
//设置签名的秘钥 | |||
.setSigningKey(key) | |||
//设置需要解析的jwt | |||
.parseClaimsJws(token).getBody(); | |||
if (claims.get("id").equals(userId)) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* 获取用户ID | |||
* | |||
* @return | |||
*/ | |||
public static Integer userId() { | |||
// 获取token | |||
String token = ServletUtils.getRequest().getHeader("Authorization"); | |||
// Jwt解密代码如下: | |||
Claims claims = JwtUtil.parseJWT(token); | |||
Integer userId = Integer.valueOf(claims.get("id").toString()); | |||
System.out.println("用户ID:" + userId); | |||
return userId; | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.tuoheng.common.utils; | |||
/** | |||
* 处理并记录日志文件 | |||
*/ | |||
public class LogUtils { | |||
public static String getBlock(Object msg) { | |||
if (msg == null) { | |||
msg = ""; | |||
} | |||
return "[" + msg.toString() + "]"; | |||
} | |||
} |
@@ -0,0 +1,168 @@ | |||
package com.tuoheng.common.utils; | |||
import lombok.extern.slf4j.Slf4j; | |||
import java.lang.reflect.Field; | |||
import java.util.*; | |||
/** | |||
* map工具类 | |||
* | |||
*/ | |||
@Slf4j | |||
public class MapUtils { | |||
/** | |||
* map的参数组装拼接 | |||
* | |||
* @param map | |||
* @return | |||
*/ | |||
public static String MapParamToStr(Map<String, Object> map) { | |||
if (map == null) return ""; | |||
// 定义初始化 | |||
StringBuilder sb = new StringBuilder(); | |||
int total = 1; | |||
int size = map.size(); | |||
for (Map.Entry<String, Object> entry : map.entrySet()) { | |||
String key = entry.getKey(); | |||
Object value = entry.getValue(); | |||
if (total >= 0 && total < size) { | |||
sb.append(key + "=" + value + "&"); | |||
} else if (total == size) { | |||
sb.append(key + "=" + value); | |||
} | |||
total++; | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* 利用反射将map集合封装成bean对象 | |||
* | |||
* @param map | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T mapToBean(Map<String, Object> map, Class<?> clazz) throws Exception { | |||
Object obj = clazz.newInstance(); | |||
if (map != null && !map.isEmpty() && map.size() > 0) { | |||
for (Map.Entry<String, Object> entry : map.entrySet()) { | |||
String propertyName = entry.getKey(); // 属性名 | |||
Object value = entry.getValue(); // 属性值 | |||
String setMethodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); | |||
Field field = getClassField(clazz, propertyName); //获取和map的key匹配的属性名称 | |||
if (field == null) { | |||
continue; | |||
} | |||
Class<?> fieldTypeClass = field.getType(); | |||
value = convertValType(value, fieldTypeClass); | |||
try { | |||
clazz.getMethod(setMethodName, field.getType()).invoke(obj, value); | |||
} catch (NoSuchMethodException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
return (T) obj; | |||
} | |||
/** | |||
* 对象转map | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
public static Map<String, Object> objToMap(Object obj) { | |||
LinkedHashMap<String, Object> map = new LinkedHashMap<>(); | |||
Field[] fields = obj.getClass().getDeclaredFields(); // 获取f对象对应类中的所有属性域 | |||
Field[] superFields = obj.getClass().getSuperclass().getDeclaredFields(); | |||
List<Field> fieldsList = Arrays.asList(fields); | |||
ArrayList<Field> fieldArrayList = new ArrayList<>(fieldsList); | |||
for (Field superField : superFields) { | |||
fieldArrayList.add(superField); | |||
} | |||
Collections.reverse(fieldArrayList); | |||
for (int i = 0; i < fieldArrayList.size(); i++) { | |||
String varName = fieldArrayList.get(i).getName(); | |||
varName = varName.toLowerCase(); // 将key置为小写,默认为对象的属性 | |||
try { | |||
boolean accessFlag = fieldArrayList.get(i).isAccessible(); // 获取原来的访问控制权限 | |||
fieldArrayList.get(i).setAccessible(true); // 修改访问控制权限 | |||
Object o = fieldArrayList.get(i).get(obj); // 获取在对象f中属性fields[i]对应的对象中的变量 | |||
if (o != null) { | |||
map.put(varName, o.toString()); | |||
} | |||
fieldArrayList.get(i).setAccessible(accessFlag); // 恢复访问控制权限 | |||
} catch (IllegalArgumentException ex) { | |||
ex.printStackTrace(); | |||
} catch (IllegalAccessException ex) { | |||
ex.printStackTrace(); | |||
} | |||
} | |||
return map; | |||
} | |||
/** | |||
* 根据给定对象类匹配对象中的特定字段 | |||
* | |||
* @param clazz | |||
* @param fieldName | |||
* @return | |||
*/ | |||
private static Field getClassField(Class<?> clazz, String fieldName) { | |||
if (Object.class.getName().equals(clazz.getName())) { | |||
return null; | |||
} | |||
Field[] declaredFields = clazz.getDeclaredFields(); | |||
for (Field field : declaredFields) { | |||
if (field.getName().equals(fieldName)) { | |||
return field; | |||
} | |||
} | |||
Class<?> superClass = clazz.getSuperclass(); //如果该类还有父类,将父类对象中的字段也取出 | |||
if (superClass != null) { //递归获取 | |||
return getClassField(superClass, fieldName); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 将map的value值转为实体类中字段类型匹配的方法 | |||
* | |||
* @param value | |||
* @param fieldTypeClass | |||
* @return | |||
*/ | |||
private static Object convertValType(Object value, Class<?> fieldTypeClass) { | |||
Object retVal = null; | |||
if (Long.class.getName().equals(fieldTypeClass.getName()) | |||
|| long.class.getName().equals(fieldTypeClass.getName())) { | |||
retVal = Long.parseLong(value.toString()); | |||
} else if (Integer.class.getName().equals(fieldTypeClass.getName()) | |||
|| int.class.getName().equals(fieldTypeClass.getName())) { | |||
retVal = Integer.parseInt(value.toString()); | |||
} else if (Float.class.getName().equals(fieldTypeClass.getName()) | |||
|| float.class.getName().equals(fieldTypeClass.getName())) { | |||
retVal = Float.parseFloat(value.toString()); | |||
} else if (Double.class.getName().equals(fieldTypeClass.getName()) | |||
|| double.class.getName().equals(fieldTypeClass.getName())) { | |||
retVal = Double.parseDouble(value.toString()); | |||
} else { | |||
retVal = value; | |||
} | |||
return retVal; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.context.MessageSource; | |||
import org.springframework.context.i18n.LocaleContextHolder; | |||
/** | |||
* 获取i18n资源文件 | |||
*/ | |||
public class MessageUtils { | |||
/** | |||
* 根据消息键和参数 获取消息 委托给spring messageSource | |||
* | |||
* @param code 消息键 | |||
* @param args 参数 | |||
* @return 获取国际化翻译值 | |||
*/ | |||
public static String message(String code, Object... args) { | |||
MessageSource messageSource = SpringUtils.getBean(MessageSource.class); | |||
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); | |||
} | |||
} |
@@ -0,0 +1,608 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.data.redis.core.RedisTemplate; | |||
import org.springframework.transaction.annotation.Propagation; | |||
import org.springframework.transaction.annotation.Transactional; | |||
import org.springframework.util.CollectionUtils; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.concurrent.TimeUnit; | |||
public class RedisUtils { | |||
private RedisTemplate<String, Object> redisTemplate; | |||
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { | |||
this.redisTemplate = redisTemplate; | |||
} | |||
// =============================common============================ | |||
/** | |||
* 普通缓存获取 | |||
* | |||
* @param | |||
* @return 值 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Set<String> keys(String pattern) { | |||
return redisTemplate.keys(pattern); | |||
} | |||
/** | |||
* 指定缓存失效时间 | |||
* | |||
* @param key 键 | |||
* @param time 时间(秒) | |||
* @return | |||
*/ | |||
public boolean expire(String key, long time) { | |||
try { | |||
if (time > 0) { | |||
redisTemplate.expire(key, time, TimeUnit.SECONDS); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 根据key 获取过期时间 | |||
* | |||
* @param key 键 不能为null | |||
* @return 时间(秒) 返回0代表为永久有效 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public long getExpire(String key) { | |||
return redisTemplate.getExpire(key, TimeUnit.SECONDS); | |||
} | |||
/** | |||
* 判断key是否存在 | |||
* | |||
* @param key 键 | |||
* @return true 存在 false不存在 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public boolean hasKey(String key) { | |||
try { | |||
return redisTemplate.hasKey(key); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 删除缓存 | |||
* | |||
* @param key 可以传一个值 或多个 | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public void del(String... key) { | |||
if (key != null && key.length > 0) { | |||
if (key.length == 1) { | |||
redisTemplate.delete(key[0]); | |||
} else { | |||
redisTemplate.delete(CollectionUtils.arrayToList(key)); | |||
} | |||
} | |||
} | |||
// ============================String============================= | |||
/** | |||
* 普通缓存获取 | |||
* | |||
* @param key 键 | |||
* @return 值 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Object get(String key) { | |||
return key == null ? null : redisTemplate.opsForValue().get(key); | |||
} | |||
/** | |||
* 普通缓存放入 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @return true成功 false失败 | |||
*/ | |||
public boolean set(String key, Object value) { | |||
try { | |||
redisTemplate.opsForValue().set(key, value); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 普通缓存放入并设置时间 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 | |||
* @return true成功 false 失败 | |||
*/ | |||
public boolean set(String key, Object value, long time) { | |||
try { | |||
if (time > 0) { | |||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); | |||
} else { | |||
set(key, value); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 递增 | |||
* | |||
* @param key 键 | |||
* @param delta 要增加几(大于0) | |||
* @return | |||
*/ | |||
public long incr(String key, long delta) { | |||
if (delta < 0) { | |||
throw new RuntimeException("递增因子必须大于0"); | |||
} | |||
return redisTemplate.opsForValue().increment(key, delta); | |||
} | |||
/** | |||
* 递减 | |||
* | |||
* @param key 键 | |||
* @param delta 要减少几(小于0) | |||
* @return | |||
*/ | |||
public long decr(String key, long delta) { | |||
if (delta < 0) { | |||
throw new RuntimeException("递减因子必须大于0"); | |||
} | |||
return redisTemplate.opsForValue().increment(key, -delta); | |||
} | |||
// ================================Map================================= | |||
/** | |||
* HashGet | |||
* | |||
* @param key 键 不能为null | |||
* @param item 项 不能为null | |||
* @return 值 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Object hget(String key, String item) { | |||
return redisTemplate.opsForHash().get(key, item); | |||
} | |||
/** | |||
* 获取hashKey对应的所有键值 | |||
* | |||
* @param key 键 | |||
* @return 对应的多个键值 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Map<Object, Object> hmget(String key) { | |||
return redisTemplate.opsForHash().entries(key); | |||
} | |||
/** | |||
* HashSet | |||
* | |||
* @param key 键 | |||
* @param map 对应多个键值 | |||
* @return true 成功 false 失败 | |||
*/ | |||
public boolean hmset(String key, Map<String, Object> map) { | |||
try { | |||
redisTemplate.opsForHash().putAll(key, map); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* HashSet 并设置时间 | |||
* | |||
* @param key 键 | |||
* @param map 对应多个键值 | |||
* @param time 时间(秒) | |||
* @return true成功 false失败 | |||
*/ | |||
public boolean hmset(String key, Map<String, Object> map, long time) { | |||
try { | |||
redisTemplate.opsForHash().putAll(key, map); | |||
if (time > 0) { | |||
expire(key, time); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 向一张hash表中放入数据,如果不存在将创建 | |||
* | |||
* @param key 键 | |||
* @param item 项 | |||
* @param value 值 | |||
* @return true 成功 false失败 | |||
*/ | |||
public boolean hset(String key, String item, Object value) { | |||
try { | |||
redisTemplate.opsForHash().put(key, item, value); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 向一张hash表中放入数据,如果不存在将创建 | |||
* | |||
* @param key 键 | |||
* @param item 项 | |||
* @param value 值 | |||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 | |||
* @return true 成功 false失败 | |||
*/ | |||
public boolean hset(String key, String item, Object value, long time) { | |||
try { | |||
redisTemplate.opsForHash().put(key, item, value); | |||
if (time > 0) { | |||
expire(key, time); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 删除hash表中的值 | |||
* | |||
* @param key 键 不能为null | |||
* @param item 项 可以使多个 不能为null | |||
*/ | |||
public void hdel(String key, Object... item) { | |||
redisTemplate.opsForHash().delete(key, item); | |||
} | |||
/** | |||
* 判断hash表中是否有该项的值 | |||
* | |||
* @param key 键 不能为null | |||
* @param item 项 不能为null | |||
* @return true 存在 false不存在 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public boolean hHasKey(String key, String item) { | |||
return redisTemplate.opsForHash().hasKey(key, item); | |||
} | |||
/** | |||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回 | |||
* | |||
* @param key 键 | |||
* @param item 项 | |||
* @param by 要增加几(大于0) | |||
* @return | |||
*/ | |||
public double hincr(String key, String item, double by) { | |||
return redisTemplate.opsForHash().increment(key, item, by); | |||
} | |||
/** | |||
* hash递减 | |||
* | |||
* @param key 键 | |||
* @param item 项 | |||
* @param by 要减少记(小于0) | |||
* @return | |||
*/ | |||
public double hdecr(String key, String item, double by) { | |||
return redisTemplate.opsForHash().increment(key, item, -by); | |||
} | |||
// ============================set============================= | |||
/** | |||
* 根据key获取Set中的所有值 | |||
* | |||
* @param key 键 | |||
* @return | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Set<Object> sGet(String key) { | |||
try { | |||
return redisTemplate.opsForSet().members(key); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return null; | |||
} | |||
} | |||
/** | |||
* 根据value从一个set中查询,是否存在 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @return true 存在 false不存在 | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public boolean sHasKey(String key, Object value) { | |||
try { | |||
return redisTemplate.opsForSet().isMember(key, value); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将数据放入set缓存 | |||
* | |||
* @param key 键 | |||
* @param values 值 可以是多个 | |||
* @return 成功个数 | |||
*/ | |||
public long sSet(String key, Object... values) { | |||
try { | |||
return redisTemplate.opsForSet().add(key, values); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
/** | |||
* 将set数据放入缓存 | |||
* | |||
* @param key 键 | |||
* @param time 时间(秒) | |||
* @param values 值 可以是多个 | |||
* @return 成功个数 | |||
*/ | |||
public long sSetAndTime(String key, long time, Object... values) { | |||
try { | |||
Long count = redisTemplate.opsForSet().add(key, values); | |||
if (time > 0) { | |||
expire(key, time); | |||
} | |||
return count; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
/** | |||
* 获取set缓存的长度 | |||
* | |||
* @param key 键 | |||
* @return | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public long sGetSetSize(String key) { | |||
try { | |||
return redisTemplate.opsForSet().size(key); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
/** | |||
* 移除值为value的 | |||
* | |||
* @param key 键 | |||
* @param values 值 可以是多个 | |||
* @return 移除的个数 | |||
*/ | |||
public long setRemove(String key, Object... values) { | |||
try { | |||
Long count = redisTemplate.opsForSet().remove(key, values); | |||
return count; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
// ===============================list================================= | |||
/** | |||
* 获取list缓存的内容 | |||
* | |||
* @param key 键 | |||
* @param start 开始 | |||
* @param end 结束 0 到 -1代表所有值 | |||
* @return | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public List<Object> lGet(String key, long start, long end) { | |||
try { | |||
return redisTemplate.opsForList().range(key, start, end); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return null; | |||
} | |||
} | |||
/** | |||
* 获取list缓存的长度 | |||
* | |||
* @param key 键 | |||
* @return | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public long lGetListSize(String key) { | |||
try { | |||
return redisTemplate.opsForList().size(key); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
/** | |||
* 通过索引 获取list中的值 | |||
* | |||
* @param key 键 | |||
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 | |||
* @return | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public Object lGetIndex(String key, long index) { | |||
try { | |||
return redisTemplate.opsForList().index(key, index); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return null; | |||
} | |||
} | |||
/** | |||
* 将list放入缓存 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @param time 时间(秒) | |||
* @return | |||
*/ | |||
public boolean lSet(String key, Object value) { | |||
try { | |||
redisTemplate.opsForList().rightPush(key, value); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将list放入缓存 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @param time 时间(秒) | |||
* @return | |||
*/ | |||
public boolean lSet(String key, Object value, long time) { | |||
try { | |||
redisTemplate.opsForList().rightPush(key, value); | |||
if (time > 0) { | |||
expire(key, time); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将list放入缓存 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @param time 时间(秒) | |||
* @return | |||
*/ | |||
public boolean lSet(String key, List<Object> value) { | |||
try { | |||
redisTemplate.opsForList().rightPushAll(key, value); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将list放入缓存 | |||
* | |||
* @param key 键 | |||
* @param value 值 | |||
* @param time 时间(秒) | |||
* @return | |||
*/ | |||
public boolean lSet(String key, List<Object> value, long time) { | |||
try { | |||
redisTemplate.opsForList().rightPushAll(key, value); | |||
if (time > 0) { | |||
expire(key, time); | |||
} | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 根据索引修改list中的某条数据 | |||
* | |||
* @param key 键 | |||
* @param index 索引 | |||
* @param value 值 | |||
* @return | |||
*/ | |||
public boolean lUpdateIndex(String key, long index, Object value) { | |||
try { | |||
redisTemplate.opsForList().set(key, index, value); | |||
return true; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return false; | |||
} | |||
} | |||
/** | |||
* 移除N个值为value | |||
* | |||
* @param key 键 | |||
* @param count 移除多少个 | |||
* @param value 值 | |||
* @return 移除的个数 | |||
*/ | |||
public long lRemove(String key, long count, Object value) { | |||
try { | |||
Long remove = redisTemplate.opsForList().remove(key, count, value); | |||
return remove; | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return 0; | |||
} | |||
} | |||
/** | |||
* 按键的排序获取对应的值 | |||
* | |||
* @param keys 多个键 | |||
* @return List<Object> | |||
*/ | |||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) | |||
public List<Object> mget(Collection<String> keys) { | |||
return redisTemplate.opsForValue().multiGet(keys); | |||
} | |||
} |
@@ -0,0 +1,320 @@ | |||
package com.tuoheng.common.utils; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.apache.commons.lang3.Validate; | |||
import org.apache.poi.ss.usermodel.DateUtil; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.lang.reflect.*; | |||
import java.util.Date; | |||
/** | |||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数 | |||
*/ | |||
@SuppressWarnings("rawtypes") | |||
public class ReflectUtils { | |||
private static final String SETTER_PREFIX = "set"; | |||
private static final String GETTER_PREFIX = "get"; | |||
private static final String CGLIB_CLASS_SEPARATOR = "$$"; | |||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); | |||
/** | |||
* 调用Getter方法. | |||
* 支持多级,如:对象名.对象名.方法 | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <E> E invokeGetter(Object obj, String propertyName) { | |||
Object object = obj; | |||
for (String name : StringUtils.split(propertyName, ".")) { | |||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); | |||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); | |||
} | |||
return (E) object; | |||
} | |||
/** | |||
* 调用Setter方法, 仅匹配方法名。 | |||
* 支持多级,如:对象名.对象名.方法 | |||
*/ | |||
public static <E> void invokeSetter(Object obj, String propertyName, E value) { | |||
Object object = obj; | |||
String[] names = StringUtils.split(propertyName, "."); | |||
for (int i = 0; i < names.length; i++) { | |||
if (i < names.length - 1) { | |||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); | |||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); | |||
} else { | |||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); | |||
invokeMethodByName(object, setterMethodName, new Object[]{value}); | |||
} | |||
} | |||
} | |||
/** | |||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <E> E getFieldValue(final Object obj, final String fieldName) { | |||
Field field = getAccessibleField(obj, fieldName); | |||
if (field == null) { | |||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | |||
return null; | |||
} | |||
E result = null; | |||
try { | |||
result = (E) field.get(obj); | |||
} catch (IllegalAccessException e) { | |||
logger.error("不可能抛出的异常{}", e.getMessage()); | |||
} | |||
return result; | |||
} | |||
/** | |||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. | |||
*/ | |||
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) { | |||
Field field = getAccessibleField(obj, fieldName); | |||
if (field == null) { | |||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | |||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | |||
return; | |||
} | |||
try { | |||
field.set(obj, value); | |||
} catch (IllegalAccessException e) { | |||
logger.error("不可能抛出的异常: {}", e.getMessage()); | |||
} | |||
} | |||
/** | |||
* 直接调用对象方法, 无视private/protected修饰符. | |||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. | |||
* 同时匹配方法名+参数类型, | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes, | |||
final Object[] args) { | |||
if (obj == null || methodName == null) { | |||
return null; | |||
} | |||
Method method = getAccessibleMethod(obj, methodName, parameterTypes); | |||
if (method == null) { | |||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | |||
return null; | |||
} | |||
try { | |||
return (E) method.invoke(obj, args); | |||
} catch (Exception e) { | |||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | |||
throw convertReflectionExceptionToUnchecked(msg, e); | |||
} | |||
} | |||
/** | |||
* 直接调用对象方法, 无视private/protected修饰符, | |||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. | |||
* 只匹配函数名,如果有多个同名函数调用第一个。 | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) { | |||
Method method = getAccessibleMethodByName(obj, methodName, args.length); | |||
if (method == null) { | |||
// 如果为空不报错,直接返回空。 | |||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | |||
return null; | |||
} | |||
try { | |||
// 类型转换(将参数数据类型转换为目标方法参数类型) | |||
Class<?>[] cs = method.getParameterTypes(); | |||
for (int i = 0; i < cs.length; i++) { | |||
if (args[i] != null && !args[i].getClass().equals(cs[i])) { | |||
if (cs[i] == String.class) { | |||
args[i] = ConvertUtil.toStr(args[i]); | |||
if (StringUtils.endsWith((String) args[i], ".0")) { | |||
args[i] = StringUtils.substringBefore((String) args[i], ".0"); | |||
} | |||
} else if (cs[i] == Integer.class) { | |||
args[i] = ConvertUtil.toInt(args[i]); | |||
} else if (cs[i] == Long.class) { | |||
args[i] = ConvertUtil.toLong(args[i]); | |||
} else if (cs[i] == Double.class) { | |||
args[i] = ConvertUtil.toDouble(args[i]); | |||
} else if (cs[i] == Float.class) { | |||
args[i] = ConvertUtil.toFloat(args[i]); | |||
} else if (cs[i] == Date.class) { | |||
if (args[i] instanceof String) { | |||
args[i] = DateUtils.parseDate(args[i]); | |||
} else { | |||
args[i] = DateUtil.getJavaDate((Double) args[i]); | |||
} | |||
} | |||
} | |||
} | |||
return (E) method.invoke(obj, args); | |||
} catch (Exception e) { | |||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | |||
throw convertReflectionExceptionToUnchecked(msg, e); | |||
} | |||
} | |||
/** | |||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. | |||
* 如向上转型到Object仍无法找到, 返回null. | |||
*/ | |||
public static Field getAccessibleField(final Object obj, final String fieldName) { | |||
// 为空不报错。直接返回 null | |||
if (obj == null) { | |||
return null; | |||
} | |||
Validate.notBlank(fieldName, "fieldName can't be blank"); | |||
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { | |||
try { | |||
Field field = superClass.getDeclaredField(fieldName); | |||
makeAccessible(field); | |||
return field; | |||
} catch (NoSuchFieldException e) { | |||
continue; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | |||
* 如向上转型到Object仍无法找到, 返回null. | |||
* 匹配函数名+参数类型。 | |||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | |||
*/ | |||
public static Method getAccessibleMethod(final Object obj, final String methodName, | |||
final Class<?>... parameterTypes) { | |||
// 为空不报错。直接返回 null | |||
if (obj == null) { | |||
return null; | |||
} | |||
Validate.notBlank(methodName, "methodName can't be blank"); | |||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { | |||
try { | |||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes); | |||
makeAccessible(method); | |||
return method; | |||
} catch (NoSuchMethodException e) { | |||
continue; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | |||
* 如向上转型到Object仍无法找到, 返回null. | |||
* 只匹配函数名。 | |||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | |||
*/ | |||
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) { | |||
// 为空不报错。直接返回 null | |||
if (obj == null) { | |||
return null; | |||
} | |||
Validate.notBlank(methodName, "methodName can't be blank"); | |||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { | |||
Method[] methods = searchType.getDeclaredMethods(); | |||
for (Method method : methods) { | |||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) { | |||
makeAccessible(method); | |||
return method; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | |||
*/ | |||
public static void makeAccessible(Method method) { | |||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) | |||
&& !method.isAccessible()) { | |||
method.setAccessible(true); | |||
} | |||
} | |||
/** | |||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | |||
*/ | |||
public static void makeAccessible(Field field) { | |||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) | |||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { | |||
field.setAccessible(true); | |||
} | |||
} | |||
/** | |||
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 | |||
* 如无法找到, 返回Object.class. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> Class<T> getClassGenricType(final Class clazz) { | |||
return getClassGenricType(clazz, 0); | |||
} | |||
/** | |||
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型. | |||
* 如无法找到, 返回Object.class. | |||
*/ | |||
public static Class getClassGenricType(final Class clazz, final int index) { | |||
Type genType = clazz.getGenericSuperclass(); | |||
if (!(genType instanceof ParameterizedType)) { | |||
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); | |||
return Object.class; | |||
} | |||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); | |||
if (index >= params.length || index < 0) { | |||
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " | |||
+ params.length); | |||
return Object.class; | |||
} | |||
if (!(params[index] instanceof Class)) { | |||
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); | |||
return Object.class; | |||
} | |||
return (Class) params[index]; | |||
} | |||
public static Class<?> getUserClass(Object instance) { | |||
if (instance == null) { | |||
throw new RuntimeException("Instance must not be null"); | |||
} | |||
Class clazz = instance.getClass(); | |||
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { | |||
Class<?> superClass = clazz.getSuperclass(); | |||
if (superClass != null && !Object.class.equals(superClass)) { | |||
return superClass; | |||
} | |||
} | |||
return clazz; | |||
} | |||
/** | |||
* 将反射时的checked exception转换为unchecked exception. | |||
*/ | |||
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) { | |||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException | |||
|| e instanceof NoSuchMethodException) { | |||
return new IllegalArgumentException(msg, e); | |||
} else if (e instanceof InvocationTargetException) { | |||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); | |||
} | |||
return new RuntimeException(msg, e); | |||
} | |||
} |
@@ -0,0 +1,118 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.web.context.request.RequestAttributes; | |||
import org.springframework.web.context.request.RequestContextHolder; | |||
import org.springframework.web.context.request.ServletRequestAttributes; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.servlet.http.HttpSession; | |||
import java.io.IOException; | |||
/** | |||
* 客户端工具类 | |||
*/ | |||
public class ServletUtils { | |||
/** | |||
* 获取String参数 | |||
*/ | |||
public static String getParameter(String name) { | |||
return getRequest().getParameter(name); | |||
} | |||
/** | |||
* 获取String参数 | |||
*/ | |||
public static String getParameter(String name, String defaultValue) { | |||
return ConvertUtil.toStr(getRequest().getParameter(name), defaultValue); | |||
} | |||
/** | |||
* 获取Integer参数 | |||
*/ | |||
public static Integer getParameterToInt(String name) { | |||
return ConvertUtil.toInt(getRequest().getParameter(name)); | |||
} | |||
/** | |||
* 获取Integer参数 | |||
*/ | |||
public static Integer getParameterToInt(String name, Integer defaultValue) { | |||
return ConvertUtil.toInt(getRequest().getParameter(name), defaultValue); | |||
} | |||
/** | |||
* 获取request | |||
*/ | |||
public static HttpServletRequest getRequest() { | |||
return getRequestAttributes().getRequest(); | |||
} | |||
/** | |||
* 获取response | |||
*/ | |||
public static HttpServletResponse getResponse() { | |||
return getRequestAttributes().getResponse(); | |||
} | |||
/** | |||
* 获取session | |||
*/ | |||
public static HttpSession getSession() { | |||
return getRequest().getSession(); | |||
} | |||
public static ServletRequestAttributes getRequestAttributes() { | |||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); | |||
return (ServletRequestAttributes) attributes; | |||
} | |||
/** | |||
* 将字符串渲染到客户端 | |||
* | |||
* @param response 渲染对象 | |||
* @param string 待渲染的字符串 | |||
* @return null | |||
*/ | |||
public static String renderString(HttpServletResponse response, String string) { | |||
try { | |||
response.setStatus(200); | |||
response.setContentType("application/json"); | |||
response.setCharacterEncoding("utf-8"); | |||
response.getWriter().print(string); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 是否是Ajax异步请求 | |||
* | |||
* @param request | |||
*/ | |||
public static boolean isAjaxRequest(HttpServletRequest request) { | |||
String accept = request.getHeader("accept"); | |||
if (accept != null && accept.indexOf("application/json") != -1) { | |||
return true; | |||
} | |||
String xRequestedWith = request.getHeader("X-Requested-With"); | |||
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) { | |||
return true; | |||
} | |||
String uri = request.getRequestURI(); | |||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { | |||
return true; | |||
} | |||
String ajax = request.getParameter("__ajax"); | |||
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,102 @@ | |||
package com.tuoheng.common.utils; | |||
import org.springframework.aop.framework.AopContext; | |||
import org.springframework.beans.BeansException; | |||
import org.springframework.beans.factory.NoSuchBeanDefinitionException; | |||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; | |||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* spring工具类 方便在非spring管理环境中获取bean | |||
*/ | |||
@Component | |||
public class SpringUtils implements BeanFactoryPostProcessor { | |||
/** | |||
* Spring应用上下文环境 | |||
*/ | |||
private static ConfigurableListableBeanFactory beanFactory; | |||
@Override | |||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { | |||
SpringUtils.beanFactory = beanFactory; | |||
} | |||
/** | |||
* 获取对象 | |||
* | |||
* @param name | |||
* @return Object 一个以所给名字注册的bean的实例 | |||
* @throws org.springframework.beans.BeansException | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T getBean(String name) throws BeansException { | |||
return (T) beanFactory.getBean(name); | |||
} | |||
/** | |||
* 获取类型为requiredType的对象 | |||
* | |||
* @param clz | |||
* @return | |||
* @throws org.springframework.beans.BeansException | |||
*/ | |||
public static <T> T getBean(Class<T> clz) throws BeansException { | |||
T result = (T) beanFactory.getBean(clz); | |||
return result; | |||
} | |||
/** | |||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true | |||
* | |||
* @param name | |||
* @return boolean | |||
*/ | |||
public static boolean containsBean(String name) { | |||
return beanFactory.containsBean(name); | |||
} | |||
/** | |||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) | |||
* | |||
* @param name | |||
* @return boolean | |||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
*/ | |||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { | |||
return beanFactory.isSingleton(name); | |||
} | |||
/** | |||
* @param name | |||
* @return Class 注册对象的类型 | |||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
*/ | |||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { | |||
return beanFactory.getType(name); | |||
} | |||
/** | |||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名 | |||
* | |||
* @param name | |||
* @return | |||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
*/ | |||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { | |||
return beanFactory.getAliases(name); | |||
} | |||
/** | |||
* 获取aop代理对象 | |||
* | |||
* @param invoker | |||
* @return | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T getAopProxy(T invoker) { | |||
return (T) AopContext.currentProxy(); | |||
} | |||
} |
@@ -0,0 +1,434 @@ | |||
package com.tuoheng.common.utils; | |||
import com.tuoheng.common.config.CommonConfig; | |||
import java.util.*; | |||
/** | |||
* 字符串工具类 | |||
*/ | |||
public class StringUtils extends org.apache.commons.lang3.StringUtils { | |||
/** | |||
* 空字符串 | |||
*/ | |||
private static final String NULLSTR = ""; | |||
/** | |||
* 下划线 | |||
*/ | |||
private static final char SEPARATOR = '_'; | |||
/** | |||
* 获取参数不为空值 | |||
* | |||
* @param value defaultValue 要判断的value | |||
* @return value 返回值 | |||
*/ | |||
public static <T> T nvl(T value, T defaultValue) { | |||
return value != null ? value : defaultValue; | |||
} | |||
/** | |||
* * 判断一个Collection是否为空, 包含List,Set,Queue | |||
* | |||
* @param coll 要判断的Collection | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Collection<?> coll) { | |||
return isNull(coll) || coll.isEmpty(); | |||
} | |||
/** | |||
* * 判断一个Collection是否非空,包含List,Set,Queue | |||
* | |||
* @param coll 要判断的Collection | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Collection<?> coll) { | |||
return !isEmpty(coll); | |||
} | |||
/** | |||
* * 判断一个对象数组是否为空 | |||
* | |||
* @param objects 要判断的对象数组 | |||
* * @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Object[] objects) { | |||
return isNull(objects) || (objects.length == 0); | |||
} | |||
/** | |||
* * 判断一个对象数组是否非空 | |||
* | |||
* @param objects 要判断的对象数组 | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Object[] objects) { | |||
return !isEmpty(objects); | |||
} | |||
/** | |||
* * 判断一个Map是否为空 | |||
* | |||
* @param map 要判断的Map | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Map<?, ?> map) { | |||
return isNull(map) || map.isEmpty(); | |||
} | |||
/** | |||
* * 判断一个Map是否为空 | |||
* | |||
* @param map 要判断的Map | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Map<?, ?> map) { | |||
return !isEmpty(map); | |||
} | |||
/** | |||
* * 判断一个字符串是否为空串 | |||
* | |||
* @param str String | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(String str) { | |||
return isNull(str) || NULLSTR.equals(str.trim()); | |||
} | |||
/** | |||
* * 判断一个字符串是否为非空串 | |||
* | |||
* @param str String | |||
* @return true:非空串 false:空串 | |||
*/ | |||
public static boolean isNotEmpty(String str) { | |||
return !isEmpty(str); | |||
} | |||
/** | |||
* * 判断一个对象是否为空 | |||
* | |||
* @param object Object | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isNull(Object object) { | |||
return object == null; | |||
} | |||
/** | |||
* * 判断一个对象是否非空 | |||
* | |||
* @param object Object | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotNull(Object object) { | |||
return !isNull(object); | |||
} | |||
/** | |||
* * 判断一个对象是否是数组类型(Java基本型别的数组) | |||
* | |||
* @param object 对象 | |||
* @return true:是数组 false:不是数组 | |||
*/ | |||
public static boolean isArray(Object object) { | |||
return isNotNull(object) && object.getClass().isArray(); | |||
} | |||
/** | |||
* 去空格 | |||
*/ | |||
public static String trim(String str) { | |||
return (str == null ? "" : str.trim()); | |||
} | |||
/** | |||
* 截取字符串 | |||
* | |||
* @param str 字符串 | |||
* @param start 开始 | |||
* @return 结果 | |||
*/ | |||
public static String substring(final String str, int start) { | |||
if (str == null) { | |||
return NULLSTR; | |||
} | |||
if (start < 0) { | |||
start = str.length() + start; | |||
} | |||
if (start < 0) { | |||
start = 0; | |||
} | |||
if (start > str.length()) { | |||
return NULLSTR; | |||
} | |||
return str.substring(start); | |||
} | |||
/** | |||
* 截取字符串 | |||
* | |||
* @param str 字符串 | |||
* @param start 开始 | |||
* @param end 结束 | |||
* @return 结果 | |||
*/ | |||
public static String substring(final String str, int start, int end) { | |||
if (str == null) { | |||
return NULLSTR; | |||
} | |||
if (end < 0) { | |||
end = str.length() + end; | |||
} | |||
if (start < 0) { | |||
start = str.length() + start; | |||
} | |||
if (end > str.length()) { | |||
end = str.length(); | |||
} | |||
if (start > end) { | |||
return NULLSTR; | |||
} | |||
if (start < 0) { | |||
start = 0; | |||
} | |||
if (end < 0) { | |||
end = 0; | |||
} | |||
return str.substring(start, end); | |||
} | |||
/** | |||
* 截取字符串str中指定字符 strStart、strEnd之间的字符串 | |||
* | |||
* @param str | |||
* @param strStart | |||
* @param strEnd | |||
* @return | |||
*/ | |||
public static String subString(String str, String strStart, String strEnd) { | |||
/* 找出指定的2个字符在 该字符串里面的 位置 */ | |||
int strStartIndex = str.indexOf(strStart); | |||
int strEndIndex = str.indexOf(strEnd); | |||
/* index 为负数 即表示该字符串中 没有该字符 */ | |||
if (strStartIndex < 0) { | |||
return "字符串 :---->" + str + "<---- 中不存在 " + strStart + ", 无法截取目标字符串"; | |||
} | |||
if (strEndIndex < 0) { | |||
return "字符串 :---->" + str + "<---- 中不存在 " + strEnd + ", 无法截取目标字符串"; | |||
} | |||
/* 开始截取 */ | |||
String result = str.substring(strStartIndex, strEndIndex).substring(strStart.length()); | |||
return result; | |||
} | |||
/** | |||
* 格式化文本, {} 表示占位符<br> | |||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br> | |||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> | |||
* 例:<br> | |||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> | |||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> | |||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> | |||
* | |||
* @param template 文本模板,被替换的部分用 {} 表示 | |||
* @param params 参数值 | |||
* @return 格式化后的文本 | |||
*/ | |||
// public static String format(String template, Object... params) { | |||
// if (isEmpty(params) || isEmpty(template)) { | |||
// return template; | |||
// } | |||
// return StrFormatter.format(template, params); | |||
// } | |||
/** | |||
* 字符串转set | |||
* | |||
* @param str 字符串 | |||
* @param sep 分隔符 | |||
* @return set集合 | |||
*/ | |||
public static final Set<String> str2Set(String str, String sep) { | |||
return new HashSet<String>(str2List(str, sep, true, false)); | |||
} | |||
/** | |||
* 字符串转list | |||
* | |||
* @param str 字符串 | |||
* @param sep 分隔符 | |||
* @param filterBlank 过滤纯空白 | |||
* @param trim 去掉首尾空白 | |||
* @return list集合 | |||
*/ | |||
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) { | |||
List<String> list = new ArrayList<String>(); | |||
if (StringUtils.isEmpty(str)) { | |||
return list; | |||
} | |||
// 过滤空白字符串 | |||
if (filterBlank && StringUtils.isBlank(str)) { | |||
return list; | |||
} | |||
String[] split = str.split(sep); | |||
for (String string : split) { | |||
if (filterBlank && StringUtils.isBlank(string)) { | |||
continue; | |||
} | |||
if (trim) { | |||
string = string.trim(); | |||
} | |||
list.add(string); | |||
} | |||
return list; | |||
} | |||
/** | |||
* 下划线转驼峰命名 | |||
*/ | |||
public static String toUnderScoreCase(String str) { | |||
if (str == null) { | |||
return null; | |||
} | |||
StringBuilder sb = new StringBuilder(); | |||
// 前置字符是否大写 | |||
boolean preCharIsUpperCase = true; | |||
// 当前字符是否大写 | |||
boolean curreCharIsUpperCase = true; | |||
// 下一字符是否大写 | |||
boolean nexteCharIsUpperCase = true; | |||
for (int i = 0; i < str.length(); i++) { | |||
char c = str.charAt(i); | |||
if (i > 0) { | |||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); | |||
} else { | |||
preCharIsUpperCase = false; | |||
} | |||
curreCharIsUpperCase = Character.isUpperCase(c); | |||
if (i < (str.length() - 1)) { | |||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); | |||
} | |||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { | |||
sb.append(SEPARATOR); | |||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { | |||
sb.append(SEPARATOR); | |||
} | |||
sb.append(Character.toLowerCase(c)); | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* 是否包含字符串 | |||
* | |||
* @param str 验证字符串 | |||
* @param strs 字符串组 | |||
* @return 包含返回true | |||
*/ | |||
public static boolean inStringIgnoreCase(String str, String... strs) { | |||
if (str != null && strs != null) { | |||
for (String s : strs) { | |||
if (str.equalsIgnoreCase(trim(s))) { | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld | |||
* | |||
* @param name 转换前的下划线大写方式命名的字符串 | |||
* @return 转换后的驼峰式命名的字符串 | |||
*/ | |||
public static String convertToCamelCase(String name) { | |||
StringBuilder result = new StringBuilder(); | |||
// 快速检查 | |||
if (name == null || name.isEmpty()) { | |||
// 没必要转换 | |||
return ""; | |||
} else if (!name.contains("_")) { | |||
// 不含下划线,仅将首字母大写 | |||
return name.substring(0, 1).toUpperCase() + name.substring(1); | |||
} | |||
// 用下划线将原始字符串分割 | |||
String[] camels = name.split("_"); | |||
for (String camel : camels) { | |||
// 跳过原始字符串中开头、结尾的下换线或双重下划线 | |||
if (camel.isEmpty()) { | |||
continue; | |||
} | |||
// 首字母大写 | |||
result.append(camel.substring(0, 1).toUpperCase()); | |||
result.append(camel.substring(1).toLowerCase()); | |||
} | |||
return result.toString(); | |||
} | |||
/** | |||
* 驼峰式命名法 例如:user_name->userName | |||
*/ | |||
public static String toCamelCase(String s) { | |||
if (s == null) { | |||
return null; | |||
} | |||
s = s.toLowerCase(); | |||
StringBuilder sb = new StringBuilder(s.length()); | |||
boolean upperCase = false; | |||
for (int i = 0; i < s.length(); i++) { | |||
char c = s.charAt(i); | |||
if (c == SEPARATOR) { | |||
upperCase = true; | |||
} else if (upperCase) { | |||
sb.append(Character.toUpperCase(c)); | |||
upperCase = false; | |||
} else { | |||
sb.append(c); | |||
} | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* 替换字符串中的域名 | |||
*/ | |||
public static String removeHost(String str, String host) { | |||
if (!StringUtils.isEmpty(str) && str.contains(host)) { | |||
return str.replaceAll(host, ""); | |||
} | |||
return str; | |||
} | |||
/** | |||
* 添加字符串中的域名 | |||
*/ | |||
public static String addHost(String str, String host) { | |||
if (!StringUtils.isEmpty(str) && !str.contains(host) && !str.contains("http")) { | |||
return host + str; | |||
} | |||
return str; | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
package com.tuoheng.common.utils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.util.concurrent.*; | |||
/** | |||
* 线程相关工具类 | |||
*/ | |||
public class ThreadUtils { | |||
private static final Logger logger = LoggerFactory.getLogger(ThreadUtils.class); | |||
/** | |||
* sleep等待,单位为毫秒 | |||
*/ | |||
public static void sleep(long milliseconds) { | |||
try { | |||
Thread.sleep(milliseconds); | |||
} catch (InterruptedException e) { | |||
return; | |||
} | |||
} | |||
/** | |||
* 停止线程池 | |||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. | |||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. | |||
* 如果仍人超時,則強制退出. | |||
* 另对在shutdown时线程本身被调用中断做了处理. | |||
*/ | |||
public static void shutdownAndAwaitTermination(ExecutorService pool) { | |||
if (pool != null && !pool.isShutdown()) { | |||
pool.shutdown(); | |||
try { | |||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { | |||
pool.shutdownNow(); | |||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { | |||
logger.info("Pool did not terminate"); | |||
} | |||
} | |||
} catch (InterruptedException ie) { | |||
pool.shutdownNow(); | |||
Thread.currentThread().interrupt(); | |||
} | |||
} | |||
} | |||
/** | |||
* 打印线程异常信息 | |||
*/ | |||
public static void printException(Runnable r, Throwable t) { | |||
if (t == null && r instanceof Future<?>) { | |||
try { | |||
Future<?> future = (Future<?>) r; | |||
if (future.isDone()) { | |||
future.get(); | |||
} | |||
} catch (CancellationException ce) { | |||
t = ce; | |||
} catch (ExecutionException ee) { | |||
t = ee.getCause(); | |||
} catch (InterruptedException ie) { | |||
Thread.currentThread().interrupt(); | |||
} | |||
} | |||
if (t != null) { | |||
logger.error(t.getMessage(), t); | |||
} | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
package com.tuoheng.common.utils; | |||
import net.coobird.thumbnailator.Thumbnails; | |||
import javax.imageio.ImageIO; | |||
import java.awt.image.BufferedImage; | |||
import java.io.*; | |||
import java.math.BigDecimal; | |||
public class ThumbnailUtil { | |||
/** | |||
* 生成压缩图 | |||
* @param url 原图地址 | |||
* @param desFileSize 指定图片大小,单位kb | |||
* @param accuracy 精度,递归压缩的比率,建议小于0.9 | |||
* @return | |||
*/ | |||
public static String cenerateCompression(String url,String urlThumbnail,long desFileSize , double accuracy){ | |||
//String urlThumbnail= UploadFileConfig.uploadFolder +"Thumbnail"+ imgUrl; | |||
String root= urlThumbnail.substring(0,urlThumbnail.lastIndexOf("/")); | |||
//如果指定的文件夹不存在 则创建 | |||
File filedir=new File(root); | |||
if (!filedir.exists()) { | |||
filedir.mkdirs();//创建文件夹 | |||
} | |||
//图片文件 | |||
File fileThumbnail = new File(urlThumbnail); | |||
if (fileThumbnail.exists()&&fileThumbnail.length() <= desFileSize * 1024) { | |||
System.out.println("已存在压缩文件!"); | |||
return urlThumbnail; | |||
} | |||
//原图 | |||
File file = new File(url); | |||
if (!file.exists() || !file.isFile()) { | |||
System.out.println("文件不存在"); | |||
} | |||
try { | |||
if(!fileThumbnail.exists()) { | |||
copyFileUsingFileStreams(file, new File(urlThumbnail)); | |||
} | |||
}catch (IOException e){ | |||
e.printStackTrace(); | |||
} | |||
urlThumbnail = commpressPicForScale(url, urlThumbnail, desFileSize, accuracy); | |||
return urlThumbnail; | |||
} | |||
/** | |||
* | |||
* @param srcPath 原图片地址 | |||
* @param desPath 目标图片地址 | |||
* @param desFileSize 指定图片大小,单位kb | |||
* @param accuracy 精度,递归压缩的比率,建议小于0.9 | |||
* @return | |||
*/ | |||
public static String commpressPicForScale(String srcPath,String desPath, | |||
long desFileSize , double accuracy){ | |||
try { | |||
File srcFile = new File(srcPath); | |||
long srcFilesize = srcFile.length(); | |||
System.out.println("原图片:"+srcPath + ",大小:" + srcFilesize/1024 + "kb"); | |||
//递归压缩,直到目标文件大小小于desFileSize | |||
commpressPicCycle(desPath, desFileSize, accuracy); | |||
File desFile = new File(desPath); | |||
System.out.println("目标图片:" + desPath + ",大小" + desFile.length()/1024 + "kb"); | |||
System.out.println("图片压缩完成!"); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
return desPath; | |||
} | |||
public static void commpressPicCycle(String desPath , long desFileSize, | |||
double accuracy) throws IOException{ | |||
File imgFile = new File(desPath); | |||
long fileSize = imgFile.length(); | |||
//判断大小,如果小于desFileSizek,不压缩,如果大于等于desFileSizek,压缩 | |||
if(fileSize <= desFileSize * 1024){ | |||
return; | |||
} | |||
//计算宽高 | |||
BufferedImage bim = ImageIO.read(imgFile); | |||
int imgWidth = bim.getWidth(); | |||
int imgHeight = bim.getHeight(); | |||
int desWidth = new BigDecimal(imgWidth).multiply( | |||
new BigDecimal(accuracy)).intValue(); | |||
int desHeight = new BigDecimal(imgHeight).multiply( | |||
new BigDecimal(accuracy)).intValue(); | |||
Thumbnails.of(desPath).size(desWidth, desHeight).outputQuality(accuracy).toFile(desPath); | |||
//如果不满足要求,递归直至满足小于1M的要求 | |||
commpressPicCycle(desPath, desFileSize, accuracy); | |||
} | |||
private static void copyFileUsingFileStreams(File source, File dest) | |||
throws IOException { | |||
InputStream input = null; | |||
OutputStream output = null; | |||
try { | |||
input = new FileInputStream(source); | |||
output = new FileOutputStream(dest); | |||
byte[] buf = new byte[1024]; | |||
int bytesRead; | |||
while ((bytesRead = input.read(buf)) > 0) { | |||
output.write(buf, 0, bytesRead); | |||
} | |||
} finally { | |||
input.close(); | |||
output.close(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,335 @@ | |||
package com.tuoheng.common.utils; | |||
import com.tuoheng.common.config.UploadFileConfig; | |||
import org.apache.commons.fileupload.FileItem; | |||
import org.apache.commons.fileupload.FileUploadException; | |||
import org.apache.commons.fileupload.disk.DiskFileItemFactory; | |||
import org.apache.commons.fileupload.servlet.ServletFileUpload; | |||
import org.springframework.stereotype.Component; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.UnsupportedEncodingException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.*; | |||
/** | |||
* 上传文件工具类 | |||
*/ | |||
@Component | |||
public class UploadUtils { | |||
// 表单字段常量 | |||
public static final String FORM_FIELDS = "form_fields"; | |||
// 文件域常量 | |||
public static final String FILE_FIELDS = "file"; | |||
// 定义允许上传的文件扩展名 | |||
private Map<String, String> extMap = new HashMap<String, String>(); | |||
// 文件保存目录路径 | |||
private String uploadPath = UploadFileConfig.uploadFolder; | |||
// 文件的目录名 | |||
private String dirName = "images"; | |||
// 上传临时路径 | |||
private static final String TEMP_PATH = "temp"; | |||
// 临时存相对路径 | |||
private String tempPath = uploadPath + TEMP_PATH; | |||
// 单个文件最大上传大小(20M) | |||
private long fileMaxSize = 1024 * 1024 * 20; | |||
// 最大文件大小(100M) | |||
private long maxSize = 1024 * 1024 * 100; | |||
// 文件保存目录url | |||
private String saveUrl; | |||
// 文件最终的url包括文件名 | |||
private List<String> fileUrlList = new ArrayList<>(); | |||
// 上传文件原名 | |||
private List<String> fileNameList = new ArrayList<>(); | |||
// 表单参数 | |||
private Map<String,String> paramMap = new HashMap(); | |||
/** | |||
* 构造函数 | |||
*/ | |||
public UploadUtils() { | |||
// 其中images,flashs,medias,files,对应文件夹名称,对应dirName | |||
// key文件夹名称 | |||
// value该文件夹内可以上传文件的后缀名 | |||
extMap.put("images", "gif,jpg,jpeg,png,bmp"); | |||
extMap.put("flashs", "swf,flv"); | |||
extMap.put("medias", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb"); | |||
extMap.put("files", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2,mp3,mp4,mp4,mov"); | |||
} | |||
/** | |||
* 文件上传 | |||
* | |||
* @param request | |||
* @return | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public Map<String, Object> uploadFile(HttpServletRequest request, String name) { | |||
// 验证文件并返回错误信息 | |||
String error = this.validateFields(request, name); | |||
// 初始化表单元素 | |||
Map<String, Object> fieldsMap = new HashMap<String, Object>(); | |||
if (error.equals("")) { | |||
fieldsMap = this.initFields(request); | |||
} | |||
List<FileItem> fiList = (List<FileItem>) fieldsMap.get(UploadUtils.FILE_FIELDS); | |||
paramMap = (Map) fieldsMap.get(UploadUtils.FORM_FIELDS); | |||
if (fiList != null) { | |||
for (FileItem item : fiList) { | |||
// 上传文件并返回错误信息 | |||
error = this.saveFile(item); | |||
} | |||
} | |||
// 返回结果 | |||
Map<String, Object> result = new HashMap<>(); | |||
result.put("error", error); | |||
result.put("image", this.fileUrlList); | |||
result.put("name", this.fileNameList); | |||
result.put("param", this.paramMap); | |||
return result; | |||
} | |||
/** | |||
* 上传验证并初始化目录 | |||
* | |||
* @param request | |||
* @return | |||
*/ | |||
private String validateFields(HttpServletRequest request, String name) { | |||
String errorInfo = ""; | |||
// 获取内容类型 | |||
String contentType = request.getContentType(); | |||
int contentLength = request.getContentLength(); | |||
// 初始化上传路径,不存在则创建 | |||
File uploadDir = new File(uploadPath); | |||
// 目录不存在则创建 | |||
if (!uploadDir.exists()) { | |||
uploadDir.mkdirs(); | |||
} | |||
if (contentType == null || !contentType.startsWith("multipart")) { | |||
// TODO | |||
System.out.println("请求不包含multipart/form-data流"); | |||
errorInfo = "请求不包含multipart/form-data流"; | |||
} else if (maxSize < contentLength) { | |||
// TODO | |||
System.out.println("上传文件大小超出文件最大大小"); | |||
errorInfo = "上传文件大小超出文件最大大小[" + maxSize + "]"; | |||
} else if (!ServletFileUpload.isMultipartContent(request)) { | |||
// TODO | |||
errorInfo = "请选择文件"; | |||
} else if (!uploadDir.isDirectory()) { | |||
// TODO | |||
errorInfo = "上传目录[" + uploadPath + "]不存在"; | |||
} else if (!uploadDir.canWrite()) { | |||
// TODO | |||
errorInfo = "上传目录[" + uploadPath + "]没有写权限"; | |||
} else if (!extMap.containsKey(dirName)) { | |||
// TODO | |||
errorInfo = "目录名不正确"; | |||
} else { | |||
// 上传路径 | |||
uploadPath = UploadFileConfig.uploadFolder + dirName + "/" + name + "/"; | |||
// 保存目录Url | |||
saveUrl = "/" + dirName + "/" + name + "/"; | |||
// 创建一级目录 | |||
File saveDirFile = new File(uploadPath); | |||
if (!saveDirFile.exists()) { | |||
saveDirFile.mkdirs(); | |||
} | |||
// 创建二级目录(格式:年月日) | |||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); | |||
String ymd = sdf.format(new Date()); | |||
uploadPath += ymd + "/"; | |||
saveUrl += ymd + "/"; | |||
File dirFile = new File(uploadPath); | |||
if (!dirFile.exists()) { | |||
dirFile.mkdirs(); | |||
} | |||
// 创建上传临时目录 | |||
File file = new File(tempPath); | |||
if (!file.exists()) { | |||
file.mkdirs(); | |||
} | |||
} | |||
return errorInfo; | |||
} | |||
/** | |||
* 处理上传内容 | |||
* | |||
* @return | |||
*/ | |||
// @SuppressWarnings("unchecked") | |||
private Map<String, Object> initFields(HttpServletRequest request) { | |||
// 存储表单字段和非表单字段 | |||
Map<String, Object> map = new HashMap<String, Object>(); | |||
// 第一步:判断request | |||
boolean isMultipart = ServletFileUpload.isMultipartContent(request); | |||
// 第二步:解析request | |||
if (isMultipart) { | |||
// 设置环境:创建一个DiskFileItemFactory工厂 | |||
DiskFileItemFactory factory = new DiskFileItemFactory(); | |||
// 阀值,超过这个值才会写到临时目录,否则在内存中 | |||
factory.setSizeThreshold(1024 * 1024 * 10); | |||
// 设置上传文件的临时目录 | |||
factory.setRepository(new File(tempPath)); | |||
// 核心操作类:创建一个文件上传解析器。 | |||
ServletFileUpload upload = new ServletFileUpload(factory); | |||
// 设置文件名称编码(解决上传"文件名"的中文乱码) | |||
upload.setHeaderEncoding("UTF-8"); | |||
// 限制单个文件上传大小 | |||
upload.setFileSizeMax(fileMaxSize); | |||
// 限制总上传文件大小 | |||
upload.setSizeMax(maxSize); | |||
// 使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项 | |||
List<FileItem> items = null; | |||
try { | |||
items = upload.parseRequest(request); | |||
} catch (FileUploadException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
// 第3步:处理uploaded items | |||
if (items != null && items.size() > 0) { | |||
Iterator<FileItem> iter = items.iterator(); | |||
// 文件域对象 | |||
List<FileItem> list = new ArrayList<FileItem>(); | |||
// 表单字段 | |||
Map<String, String> fields = new HashMap<String, String>(); | |||
while (iter.hasNext()) { | |||
FileItem item = iter.next(); | |||
// 处理所有表单元素和文件域表单元素 | |||
if (item.isFormField()) { | |||
// 如果fileitem中封装的是普通输入项的数据(输出名、值) | |||
String name = item.getFieldName();// 普通输入项数据的名 | |||
String value = null; | |||
try { | |||
value = item.getString("UTF-8"); | |||
} catch (UnsupportedEncodingException e) { | |||
e.printStackTrace(); | |||
} | |||
fields.put(name, value); | |||
} else { | |||
//如果fileitem中封装的是上传文件,得到上传的文件名称 | |||
// 文件域表单元素 | |||
list.add(item); | |||
} | |||
} | |||
map.put(FORM_FIELDS, fields); | |||
map.put(FILE_FIELDS, list); | |||
} | |||
} | |||
return map; | |||
} | |||
/** | |||
* 保存文件 | |||
* | |||
* @param item | |||
* @return | |||
*/ | |||
private String saveFile(FileItem item) { | |||
String error = ""; | |||
String fileName = item.getName(); | |||
String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); | |||
if (item.getSize() > maxSize) { // 检查文件大小 | |||
// TODO | |||
error = "上传文件大小超过限制"; | |||
} else if (!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)) {// 检查扩展名 | |||
error = "上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。"; | |||
} else { | |||
// 存储文件重命名 | |||
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); | |||
String newFileName = df.format(new Date()) + new Random().nextInt(1000) + "." + fileExt; | |||
// 新增文件原名数组(带后缀) | |||
fileNameList.add(fileName); | |||
// 新增值文件数组 | |||
//String filePath = saveUrl + newFileName; | |||
//fileUrlList.add(filePath); | |||
//add 保存压缩图片地址 | |||
String filePath = saveUrl + "yasuo/"+ newFileName; | |||
fileUrlList.add(filePath); | |||
//end 保存压缩图片地址 | |||
// 写入文件 | |||
try { | |||
File uploadedFile = new File(uploadPath, newFileName); | |||
item.write(uploadedFile); | |||
//uploadedFile2用于压缩图片 | |||
File uploadedFile2 = new File(uploadPath+"yasuo/", newFileName); | |||
File file = new File(uploadPath+"yasuo/"); | |||
if (!file.exists()){ | |||
file.mkdirs(); | |||
} | |||
item.write(uploadedFile2); | |||
ThumbnailUtil.cenerateCompression(uploadPath+newFileName,uploadPath+"yasuo/"+newFileName,1000,0.8); | |||
//end 压缩图片 | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
System.out.println("上传失败了!!!"); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
return error; | |||
} | |||
/** | |||
* *********************get/set方法********************************* | |||
*/ | |||
public String getSaveUrl() { | |||
return saveUrl; | |||
} | |||
public String getUploadPath() { | |||
return uploadPath; | |||
} | |||
public long getMaxSize() { | |||
return maxSize; | |||
} | |||
public void setMaxSize(long maxSize) { | |||
this.maxSize = maxSize; | |||
} | |||
public Map<String, String> getExtMap() { | |||
return extMap; | |||
} | |||
public void setExtMap(Map<String, String> extMap) { | |||
this.extMap = extMap; | |||
} | |||
public String getDirName() { | |||
return dirName; | |||
} | |||
public void setDirName(String dirName) { | |||
this.dirName = dirName; | |||
} | |||
public String getTempPath() { | |||
return tempPath; | |||
} | |||
public void setTempPath(String tempPath) { | |||
this.tempPath = tempPath; | |||
} | |||
public List getfileUrlList() { | |||
return fileUrlList; | |||
} | |||
public void setfileUrlList(List fileUrlList) { | |||
this.fileUrlList = fileUrlList; | |||
} | |||
} |
@@ -0,0 +1,186 @@ | |||
package com.tuoheng.common.utils; | |||
import javax.imageio.ImageIO; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.awt.*; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayOutputStream; | |||
import java.util.Base64; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Random; | |||
/** | |||
* 获取验证码工具类 | |||
*/ | |||
public class VerifyUtil { | |||
private static Random random = new Random(); | |||
/** | |||
* 验证码的宽 | |||
*/ | |||
private int width = 165; | |||
/** | |||
* 验证码的高 | |||
*/ | |||
private int height = 45; | |||
/** | |||
* 验证码中夹杂的干扰线数量 | |||
*/ | |||
private int lineSize = 30; | |||
/** | |||
* 验证码字符个数 | |||
*/ | |||
private int randomStrNum = 4; | |||
private String randomString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ"; | |||
/** | |||
* 字体的设置 | |||
* | |||
* @return | |||
*/ | |||
private Font getFont() { | |||
return new Font("Times New Roman", Font.ROMAN_BASELINE, 40); | |||
} | |||
/** | |||
* 颜色的设置 | |||
* | |||
* @param fc | |||
* @param bc | |||
* @return | |||
*/ | |||
private static Color getRandomColor(int fc, int bc) { | |||
fc = Math.min(fc, 255); | |||
bc = Math.min(bc, 255); | |||
int r = fc + random.nextInt(bc - fc - 16); | |||
int g = fc + random.nextInt(bc - fc - 14); | |||
int b = fc + random.nextInt(bc - fc - 12); | |||
return new Color(r, g, b); | |||
} | |||
/** | |||
* 干扰线的绘制 | |||
* | |||
* @param g | |||
*/ | |||
private void drawLine(Graphics g) { | |||
int x = random.nextInt(width); | |||
int y = random.nextInt(height); | |||
int xl = random.nextInt(20); | |||
int yl = random.nextInt(10); | |||
g.drawLine(x, y, x + xl, y + yl); | |||
} | |||
/** | |||
* 随机字符的获取 | |||
* | |||
* @param num | |||
* @return | |||
*/ | |||
private String getRandomString(int num) { | |||
num = num > 0 ? num : randomString.length(); | |||
return String.valueOf(randomString.charAt(random.nextInt(num))); | |||
} | |||
/** | |||
* 字符串的绘制 | |||
* | |||
* @param g | |||
* @param randomStr | |||
* @param i | |||
* @return | |||
*/ | |||
private String drawString(Graphics g, String randomStr, int i) { | |||
g.setFont(getFont()); | |||
g.setColor(getRandomColor(108, 190)); | |||
String rand = getRandomString(random.nextInt(randomString.length())); | |||
randomStr += rand; | |||
g.translate(random.nextInt(3), random.nextInt(6)); | |||
g.drawString(rand, 40 * i + 10, 25); | |||
return randomStr; | |||
} | |||
/** | |||
* 生成随机图片 | |||
* | |||
* @param response | |||
*/ | |||
public void getRandomCodeImage(HttpServletResponse response) { | |||
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类 | |||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); | |||
Graphics g = image.getGraphics(); | |||
g.fillRect(0, 0, width, height); | |||
g.setColor(getRandomColor(105, 189)); | |||
g.setFont(getFont()); | |||
// 干扰线 | |||
for (int i = 0; i < lineSize; i++) { | |||
drawLine(g); | |||
} | |||
// 随机字符 | |||
String randomStr = ""; | |||
for (int i = 0; i < randomStrNum; i++) { | |||
randomStr = drawString(g, randomStr, i); | |||
} | |||
System.out.println("随机字符:" + randomStr); | |||
g.dispose(); | |||
try { | |||
// 将图片以png格式返回,返回的是图片 | |||
ImageIO.write(image, "PNG", response.getOutputStream()); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
* 生成随机图片的base64编码字符串 | |||
* | |||
* @return | |||
*/ | |||
public Map<String, String> getRandomCodeBase64() { | |||
Map<String, String> result = new HashMap<>(); | |||
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类 | |||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); | |||
Graphics g = image.getGraphics(); | |||
g.fillRect(0, 0, width, height); | |||
g.setColor(getRandomColor(105, 189)); | |||
g.setFont(getFont()); | |||
//干扰线 | |||
for (int i = 0; i < lineSize; i++) { | |||
drawLine(g); | |||
} | |||
//随机字符 | |||
String randomStr = ""; | |||
for (int i = 0; i < randomStrNum; i++) { | |||
randomStr = drawString(g, randomStr, i); | |||
} | |||
g.dispose(); | |||
String base64String = ""; | |||
try { | |||
// 直接返回图片 | |||
// 返回 base64 | |||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |||
ImageIO.write(image, "PNG", bos); | |||
byte[] bytes = bos.toByteArray(); | |||
Base64.Encoder encoder = Base64.getEncoder(); | |||
base64String = encoder.encodeToString(bytes); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
} | |||
result.put("randomStr", randomStr); | |||
result.put("img", base64String); | |||
return result; | |||
} | |||
} |