Browse Source

代码初始化

tags/V1.6.0
wanghaoran 2 years ago
parent
commit
8ec696ec5f
100 changed files with 11415 additions and 0 deletions
  1. +40
    -0
      .gitignore
  2. +84
    -0
      pom.xml
  3. +247
    -0
      tuoheng-admin/pom.xml
  4. +21
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/AdminApplication.java
  5. +15
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/controller/TestController.java
  6. +159
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/GaodeUtil.java
  7. +182
    -0
      tuoheng-admin/src/main/resources/application-dev.yml
  8. +188
    -0
      tuoheng-admin/src/main/resources/application-local.yml
  9. +188
    -0
      tuoheng-admin/src/main/resources/application-prod.yml
  10. +187
    -0
      tuoheng-admin/src/main/resources/application-test.yml
  11. +25
    -0
      tuoheng-admin/src/main/resources/application.yml
  12. +4
    -0
      tuoheng-admin/src/main/resources/log4j.properties
  13. +67
    -0
      tuoheng-admin/src/main/resources/logback.xml
  14. +47
    -0
      tuoheng-admin/src/main/resources/redis.properties
  15. BIN
      tuoheng-admin/src/main/resources/template/ExportReport.docx
  16. +239
    -0
      tuoheng-api/pom.xml
  17. +21
    -0
      tuoheng-api/src/main/java/com/tuoheng/api/ApiApplication.java
  18. +187
    -0
      tuoheng-api/src/main/resources/application-dev.yml
  19. +188
    -0
      tuoheng-api/src/main/resources/application-local.yml
  20. +192
    -0
      tuoheng-api/src/main/resources/application-prod.yml
  21. +187
    -0
      tuoheng-api/src/main/resources/application-test.yml
  22. +24
    -0
      tuoheng-api/src/main/resources/application.yml
  23. +4
    -0
      tuoheng-api/src/main/resources/log4j.properties
  24. +62
    -0
      tuoheng-api/src/main/resources/logback.xml
  25. +173
    -0
      tuoheng-common/pom.xml
  26. +112
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excel.java
  27. +18
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excels.java
  28. +34
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/LockAction.java
  29. +36
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Log.java
  30. +4
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseController.java
  31. +57
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseEntity.java
  32. +19
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseQuery.java
  33. +37
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseResponse.java
  34. +188
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseServiceImpl.java
  35. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/ExceptionInterface.java
  36. +100
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/IBaseService.java
  37. +52
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/OperationEnum.java
  38. +96
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/SysExceptionEnum.java
  39. +42
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/AliSmsConfig.java
  40. +71
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/CommonConfig.java
  41. +40
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/I18nConfig.java
  42. +26
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/MybatisPlusConfig.java
  43. +49
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/QrImage.java
  44. +164
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/RedisConfig.java
  45. +58
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/ThreadPoolConfig.java
  46. +40
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/UploadFileConfig.java
  47. +83
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/WebMvcConfig.java
  48. +43
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/http/DefaultClientHttpRequestInterceptor.java
  49. +32
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/http/HeadClientHttpRequestInterceptor.java
  50. +64
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/http/RestProperties.java
  51. +176
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/http/RestTemplateConfig.java
  52. +109
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/redisson/RedissonDistributedLockAspectConfiguration.java
  53. +86
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/xxl/XxlJobConfig.java
  54. +108
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/CommonConstants.java
  55. +60
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/Constant.java
  56. +30
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/RedisConstant.java
  57. +50
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/controller/CommonController.java
  58. +52
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/controller/UploadController.java
  59. +16
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/LockType.java
  60. +18
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/LogStatus.java
  61. +68
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/LogType.java
  62. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/OperType.java
  63. +84
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/BaseException.java
  64. +37
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/CustomException.java
  65. +39
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/ExceptionConstantEnum.java
  66. +46
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/ServiceException.java
  67. +14
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/CaptchaException.java
  68. +16
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserException.java
  69. +14
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserNotExistsException.java
  70. +35
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/service/IUploadService.java
  71. +66
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/service/impl/UploadServiceImpl.java
  72. +59
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/BASE64DecodedMultipartFile.java
  73. +94
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/BaseResponseUtil.java
  74. +89
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CharsetKit.java
  75. +297
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CommonUtils.java
  76. +850
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ConvertUtil.java
  77. +15
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CoordinateConversionUtil.java
  78. +213
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CoordinatesTransformControllerUtil.java
  79. +164
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/DateUtils.java
  80. +695
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ExcelUtils.java
  81. +194
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/FileUtils.java
  82. +50
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/HawkOSOperator.java
  83. +186
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/HttpUtils.java
  84. +294
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/IpUtils.java
  85. +133
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/JacksonUtil.java
  86. +97
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/JsonResult.java
  87. +125
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/JwtUtil.java
  88. +15
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/LogUtils.java
  89. +168
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/MapUtils.java
  90. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/MessageUtils.java
  91. +608
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/RedisUtils.java
  92. +320
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ReflectUtils.java
  93. +118
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ServletUtils.java
  94. +102
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/SpringUtils.java
  95. +434
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/StringUtils.java
  96. +73
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ThreadUtils.java
  97. +112
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ThumbnailUtil.java
  98. +335
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/UploadUtils.java
  99. +186
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/VerifyUtil.java
  100. +0
    -0
      tuoheng-common/src/main/resources/i18n/messages.properties

+ 40
- 0
.gitignore View File

@@ -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

+ 84
- 0
pom.xml View File

@@ -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>

+ 247
- 0
tuoheng-admin/pom.xml View File

@@ -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>

+ 21
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/AdminApplication.java View File

@@ -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);
}

}

+ 15
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/controller/TestController.java View File

@@ -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";
}
}

+ 159
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/GaodeUtil.java View File

@@ -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;
}


}

+ 182
- 0
tuoheng-admin/src/main/resources/application-dev.yml View File

@@ -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_

+ 188
- 0
tuoheng-admin/src/main/resources/application-local.yml View File

@@ -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_

+ 188
- 0
tuoheng-admin/src/main/resources/application-prod.yml View File

@@ -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_

+ 187
- 0
tuoheng-admin/src/main/resources/application-test.yml View File

@@ -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_

+ 25
- 0
tuoheng-admin/src/main/resources/application.yml View File

@@ -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


+ 4
- 0
tuoheng-admin/src/main/resources/log4j.properties View File

@@ -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

+ 67
- 0
tuoheng-admin/src/main/resources/logback.xml View File

@@ -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>

+ 47
- 0
tuoheng-admin/src/main/resources/redis.properties View File

@@ -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

BIN
tuoheng-admin/src/main/resources/template/ExportReport.docx View File


+ 239
- 0
tuoheng-api/pom.xml View File

@@ -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>

+ 21
- 0
tuoheng-api/src/main/java/com/tuoheng/api/ApiApplication.java View File

@@ -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启动成功 ლ(´ڡ`ლ)゙");
}

}

+ 187
- 0
tuoheng-api/src/main/resources/application-dev.yml View File

@@ -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_

+ 188
- 0
tuoheng-api/src/main/resources/application-local.yml View File

@@ -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_

+ 192
- 0
tuoheng-api/src/main/resources/application-prod.yml View File

@@ -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

+ 187
- 0
tuoheng-api/src/main/resources/application-test.yml View File

@@ -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_

+ 24
- 0
tuoheng-api/src/main/resources/application.yml View File

@@ -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

+ 4
- 0
tuoheng-api/src/main/resources/log4j.properties View File

@@ -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

+ 62
- 0
tuoheng-api/src/main/resources/logback.xml View File

@@ -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>

+ 173
- 0
tuoheng-common/pom.xml View File

@@ -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>

+ 112
- 0
tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excel.java View File

@@ -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;
}
}

}

+ 18
- 0
tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excels.java View File

@@ -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();

}

+ 34
- 0
tuoheng-common/src/main/java/com/tuoheng/common/annotation/LockAction.java View File

@@ -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;
}

+ 36
- 0
tuoheng-common/src/main/java/com/tuoheng/common/annotation/Log.java View File

@@ -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;

}

+ 4
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/BaseController.java View File

@@ -0,0 +1,4 @@
package com.tuoheng.common.common;

public class BaseController {
}

+ 57
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/BaseEntity.java View File

@@ -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;
}

+ 19
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/BaseQuery.java View File

@@ -0,0 +1,19 @@
package com.tuoheng.common.common;

import lombok.Data;

/**
* 查询对象基类
*/
@Data
public class BaseQuery {
/**
* 页码
*/
private Integer page;

/**
* 每页数
*/
private Integer limit;
}

+ 37
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/BaseResponse.java View File

@@ -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;
}


+ 188
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/BaseServiceImpl.java View File

@@ -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;
}
}

+ 23
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/ExceptionInterface.java View File

@@ -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();
}

+ 100
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/IBaseService.java View File

@@ -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();

}

+ 52
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/OperationEnum.java View File

@@ -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;
}

+ 96
- 0
tuoheng-common/src/main/java/com/tuoheng/common/common/SysExceptionEnum.java View File

@@ -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;
}

+ 42
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/AliSmsConfig.java View File

@@ -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;

}

+ 71
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/CommonConfig.java View File

@@ -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;
}

}

+ 40
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/I18nConfig.java View File

@@ -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());
}

}

+ 26
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/MybatisPlusConfig.java View File

@@ -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;
}
}

+ 49
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/QrImage.java View File

@@ -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;

}

+ 164
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/RedisConfig.java View File

@@ -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);
// }
}

+ 58
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/ThreadPoolConfig.java View File

@@ -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);
}
};
}

}

+ 40
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/UploadFileConfig.java View File

@@ -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;
}
}

+ 83
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/WebMvcConfig.java View File

@@ -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);
}

}

+ 43
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/http/DefaultClientHttpRequestInterceptor.java View File

@@ -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 ========");
}
}

+ 32
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/http/HeadClientHttpRequestInterceptor.java View File

@@ -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;
}
}

+ 64
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/http/RestProperties.java View File

@@ -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;
}

+ 176
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/http/RestTemplateConfig.java View File

@@ -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;
}
};
}
}

+ 109
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/redisson/RedissonDistributedLockAspectConfiguration.java View File

@@ -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());
// }
// }
//}

+ 86
- 0
tuoheng-common/src/main/java/com/tuoheng/common/config/xxl/XxlJobConfig.java View File

@@ -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();
*/


}

+ 108
- 0
tuoheng-common/src/main/java/com/tuoheng/common/constant/CommonConstants.java View File

@@ -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;

}

+ 60
- 0
tuoheng-common/src/main/java/com/tuoheng/common/constant/Constant.java View File

@@ -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";
}

+ 30
- 0
tuoheng-common/src/main/java/com/tuoheng/common/constant/RedisConstant.java View File

@@ -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;


}

+ 50
- 0
tuoheng-common/src/main/java/com/tuoheng/common/controller/CommonController.java View File

@@ -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);
}
}

}

+ 52
- 0
tuoheng-common/src/main/java/com/tuoheng/common/controller/UploadController.java View File

@@ -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);
}

}

+ 16
- 0
tuoheng-common/src/main/java/com/tuoheng/common/enums/LockType.java View File

@@ -0,0 +1,16 @@
package com.tuoheng.common.enums;

public enum LockType {

/** 可重入锁*/
REENTRANT_LOCK,

/** 公平锁*/
FAIR_LOCK,

/** 读锁*/
READ_LOCK,

/** 写锁*/
WRITE_LOCK;
}

+ 18
- 0
tuoheng-common/src/main/java/com/tuoheng/common/enums/LogStatus.java View File

@@ -0,0 +1,18 @@
package com.tuoheng.common.enums;

/**
* 操作状态
*/
public enum LogStatus {

/**
* 成功
*/
SUCCESS,

/**
* 失败
*/
FAIL,

}

+ 68
- 0
tuoheng-common/src/main/java/com/tuoheng/common/enums/LogType.java View File

@@ -0,0 +1,68 @@
package com.tuoheng.common.enums;

/**
* 日志类型
*/
public enum LogType {

/**
* 其它
*/
OTHER,

/**
* 新增
*/
INSERT,

/**
* 修改
*/
UPDATE,

/**
* 删除
*/
DELETE,

/**
* 授权
*/
GRANT,

/**
* 导出
*/
EXPORT,

/**
* 导入
*/
IMPORT,

/**
* 强退
*/
FORCE,

/**
* 生成代码
*/
GENCODE,

/**
* 清空数据
*/
CLEAN,

/**
* 状态
*/
STATUS,

/**
* 重置密码
*/
RESETPWD,

}

+ 23
- 0
tuoheng-common/src/main/java/com/tuoheng/common/enums/OperType.java View File

@@ -0,0 +1,23 @@
package com.tuoheng.common.enums;

/**
* 操作类型
*/
public enum OperType {

/**
* 其它
*/
OTHER,

/**
* 后台用户
*/
MANAGE,

/**
* 手机端用户
*/
MOBILE

}

+ 84
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/BaseException.java View File

@@ -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;
}

}

+ 37
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/CustomException.java View File

@@ -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;
}

}

+ 39
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/ExceptionConstantEnum.java View File

@@ -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;
}


+ 46
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/ServiceException.java View File

@@ -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;
}

}


+ 14
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/user/CaptchaException.java View File

@@ -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);
}

}

+ 16
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserException.java View File

@@ -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);
}

}

+ 14
- 0
tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserNotExistsException.java View File

@@ -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);
}

}

+ 35
- 0
tuoheng-common/src/main/java/com/tuoheng/common/service/IUploadService.java View File

@@ -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);

}

+ 66
- 0
tuoheng-common/src/main/java/com/tuoheng/common/service/impl/UploadServiceImpl.java View File

@@ -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, "上传成功");
}

}

+ 59
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/BASE64DecodedMultipartFile.java View File

@@ -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);
}
}

+ 94
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/BaseResponseUtil.java View File

@@ -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());
}
}


+ 89
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/CharsetKit.java View File

@@ -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();
}

}

+ 297
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/CommonUtils.java View File

@@ -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;
}
}
}

+ 850
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ConvertUtil.java View File

@@ -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("^整$", "零元整");
}

}

+ 15
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/CoordinateConversionUtil.java View File

@@ -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 {


}

+ 213
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/CoordinatesTransformControllerUtil.java View File

@@ -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;
}

}

+ 164
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/DateUtils.java View File

@@ -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;
}

}

+ 695
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ExcelUtils.java View File

@@ -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;
}

}

+ 194
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/FileUtils.java View File

@@ -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;
}

}

+ 50
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/HawkOSOperator.java View File

@@ -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;
}
}

+ 186
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/HttpUtils.java View File

@@ -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;
}
}

}

+ 294
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/IpUtils.java View File

@@ -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;
}

}

+ 133
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/JacksonUtil.java View File

@@ -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;
}
}

+ 97
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/JsonResult.java View File

@@ -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;
}

}

+ 125
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/JwtUtil.java View File

@@ -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;
}
}

+ 15
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/LogUtils.java View File

@@ -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() + "]";
}

}

+ 168
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/MapUtils.java View File

@@ -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;
}


}

+ 23
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/MessageUtils.java View File

@@ -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());
}

}

+ 608
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/RedisUtils.java View File

@@ -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);
}

}

+ 320
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ReflectUtils.java View File

@@ -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);
}

}

+ 118
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ServletUtils.java View File

@@ -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;
}

}

+ 102
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/SpringUtils.java View File

@@ -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();
}

}

+ 434
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/StringUtils.java View File

@@ -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;
}
}

+ 73
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ThreadUtils.java View File

@@ -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);
}
}

}

+ 112
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/ThumbnailUtil.java View File

@@ -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();
}
}


}

+ 335
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/UploadUtils.java View File

@@ -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;
}
}

+ 186
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/VerifyUtil.java View File

@@ -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;
}

}

+ 0
- 0
tuoheng-common/src/main/resources/i18n/messages.properties View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save