Browse Source

first commit

release
wubin 1 year ago
commit
f567c4e792
100 changed files with 11264 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +3
    -0
      README.md
  3. +109
    -0
      pom.xml
  4. +5
    -0
      tuoheng-admin/README.md
  5. +206
    -0
      tuoheng-admin/pom.xml
  6. +26
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/AdminApplication.java
  7. +227
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/AbstractLikeSqlConverter.java
  8. +79
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/MapLikeSqlConverter.java
  9. +153
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/MybatisPlusInterceptor.java
  10. +25
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/ObjectLikeSqlConverter.java
  11. +24
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/controller/HealthController.java
  12. +78
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/enums/MQTTTopicEnum.java
  13. +30
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/enums/ServiceExceptionEnum.java
  14. +14
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/mapper/AirportMapper.java
  15. +100
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/pojo/entity/Airport.java
  16. +15
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/IAirportService.java
  17. +19
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/impl/AirportServiceImpl.java
  18. +295
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/HttpURLConnectionUtil.java
  19. +34
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/MqHttp.java
  20. +249
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/consumer/MqttConsumerCallBack.java
  21. +49
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/send/MqttProviderCallBack.java
  22. +201
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/send/MqttProviderConfig.java
  23. +230
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/AliyunOSSUtil.java
  24. +29
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/AliyunOssResult.java
  25. +61
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/CronUtils.java
  26. +40
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/GenerateDroneSerialNumber.java
  27. +295
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/HttpURLConnectionUtil.java
  28. +66
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/RedisLock.java
  29. +60
    -0
      tuoheng-admin/src/main/java/com/tuoheng/admin/utils/SpringUtil.java
  30. +217
    -0
      tuoheng-admin/src/main/resources/application-dev.yml
  31. +196
    -0
      tuoheng-admin/src/main/resources/application-local.yml
  32. +216
    -0
      tuoheng-admin/src/main/resources/application-prod.yml
  33. +217
    -0
      tuoheng-admin/src/main/resources/application-test.yml
  34. +37
    -0
      tuoheng-admin/src/main/resources/application.yml
  35. +32
    -0
      tuoheng-admin/src/main/resources/logback.xml
  36. +26
    -0
      tuoheng-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
  37. +24
    -0
      tuoheng-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
  38. +153
    -0
      tuoheng-common/pom.xml
  39. +148
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/advice/CustomExceptionHandler.java
  40. +112
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excel.java
  41. +18
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Excels.java
  42. +36
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/annotation/Log.java
  43. +4
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseController.java
  44. +64
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseEntity.java
  45. +19
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseQuery.java
  46. +37
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseResponse.java
  47. +188
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/BaseServiceImpl.java
  48. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/ExceptionInterface.java
  49. +100
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/IBaseService.java
  50. +52
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/OperationEnum.java
  51. +96
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/common/SysExceptionEnum.java
  52. +42
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/AliSmsConfig.java
  53. +26
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/CommonConfig.java
  54. +27
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/MybatisPlusConfig.java
  55. +49
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/QrImage.java
  56. +187
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/RedisConfig.java
  57. +58
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/ThreadPoolConfig.java
  58. +40
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/UploadFileConfig.java
  59. +83
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/config/WebMvcConfig.java
  60. +103
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/CommonConstants.java
  61. +49
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/Constant.java
  62. +30
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/constant/RedisConstant.java
  63. +105
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/controller/CommonController.java
  64. +52
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/controller/UploadController.java
  65. +18
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/LogStatus.java
  66. +68
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/LogType.java
  67. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/enums/OperType.java
  68. +84
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/BaseException.java
  69. +37
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/CustomException.java
  70. +39
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/ExceptionConstantEnum.java
  71. +65
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/ServiceException.java
  72. +14
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/CaptchaException.java
  73. +16
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserException.java
  74. +14
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/exception/user/UserNotExistsException.java
  75. +35
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/service/IUploadService.java
  76. +71
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/service/impl/UploadServiceImpl.java
  77. +59
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/BASE64DecodedMultipartFile.java
  78. +94
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/BaseResponseUtil.java
  79. +89
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CharsetKit.java
  80. +253
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/CommonUtils.java
  81. +850
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ConvertUtil.java
  82. +136
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/DateUtils.java
  83. +61
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/DroneCodeUtils.java
  84. +695
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ExcelUtils.java
  85. +110
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/FileUtils.java
  86. +186
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/HttpUtils.java
  87. +294
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/IpUtils.java
  88. +132
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/JacksonUtil.java
  89. +97
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/JsonResult.java
  90. +15
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/LogUtils.java
  91. +168
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/MapUtils.java
  92. +23
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/MessageUtils.java
  93. +608
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/RedisUtils.java
  94. +320
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ReflectUtils.java
  95. +118
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ServletUtils.java
  96. +102
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/SpringUtils.java
  97. +387
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/StringUtils.java
  98. +73
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/ThreadUtils.java
  99. +319
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/UploadUtils.java
  100. +0
    -0
      tuoheng-common/src/main/java/com/tuoheng/common/utils/VerifyUtil.java

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
/.idea
/tuoheng-admin/target/classes
/tuoheng-system/target/classes/mapper

+ 3
- 0
README.md View File

@@ -0,0 +1,3 @@
# tuoheng_airport

## 拓恒机场统一管理平台

+ 109
- 0
pom.xml View File

@@ -0,0 +1,109 @@
<?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.7.1</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>
</modules>

<properties>
<java.version>1.8</java.version>
<!-- 表示打包时跳过mvn test -->
<maven.test.skip>true</maven.test.skip>
<!--全局配置项目版本号-->
<version>0.0.1-SNAPSHOT</version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>

<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- SpringCloud依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 子模块依赖 -->
<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>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>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

</project>

+ 5
- 0
tuoheng-admin/README.md View File

@@ -0,0 +1,5 @@
# 拓恒机场统一管理后台

## 统一管理和硬件相关,机场地面站以及控制面板的交互
## 对业务系统提供Restful API暴漏飞行相关的能力

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

@@ -0,0 +1,206 @@
<?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>
<!-- Consul注册中心起始依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</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>

<!-- 阿里云OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>

<!--mqtt 相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.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_airport_admin</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!-- <excludes>-->
<!-- <exclude>application-dev.yml</exclude>-->
<!-- <exclude>application-test.yml</exclude>-->
<!-- <exclude>application-prod.yml</exclude>-->
<!-- </excludes>-->
</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>

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

@@ -0,0 +1,26 @@
package com.tuoheng.admin;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;


@SpringBootApplication(scanBasePackages = {"com.tuoheng.*"})
@EnableDiscoveryClient
@MapperScan("com.tuoheng.**.mapper")
@EnableTransactionManagement
// 开启定时任务支持
@EnableScheduling
@EnableAsync
public class AdminApplication {

public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
System.out.println("tuoheng-airport-admin 启动成功~");
}

}

+ 227
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/AbstractLikeSqlConverter.java View File

@@ -0,0 +1,227 @@
package com.tuoheng.admin.config.sql;


import com.tuoheng.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: ike字符转义
* @Version: 1.0
*/
@Slf4j
public abstract class AbstractLikeSqlConverter<T> {

/**
* SQL语句like使用关键字%
*/
private final static String LIKE_SQL_KEY = "%";

/**
* SQL语句需要转义的关键字
*/
private final static String[] ESCAPE_CHAR = new String[]{LIKE_SQL_KEY, "_", "\\"};

/**
* mybatis-plus中like的SQL语句样式
*/
private final static String MYBATIS_PLUS_LIKE_SQL = " like ?";

/**
* mybatis-plus中参数前缀
*/
private final static String MYBATIS_PLUS_WRAPPER_PREFIX = "ew.paramNameValuePairs.";

/**
* mybatis-plus中参数键
*/
final static String MYBATIS_PLUS_WRAPPER_KEY = "ew";

/**
* mybatis-plus中参数分隔符
*/
final static String MYBATIS_PLUS_WRAPPER_SEPARATOR = ".";

/**
* mybatis-plus中参数分隔符替换器
*/
final static String MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX = "\\.";

/**
* 已经替换过的标记
*/
final static String REPLACED_LIKE_KEYWORD_MARK = "replaced.keyword";

/**
* 转义特殊字符
*
* @param sql SQL语句
* @param fields 字段列表
* @param parameter 参数对象
*/
public void convert(String sql, Set<String> fields, T parameter) {
for (String field : fields) {
if (this.hasMybatisPlusLikeSql(sql)) {
if (this.hasWrapper(field)) {
// 第一种情况:在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper
this.transferWrapper(field, parameter);
} else {
// 第二种情况:未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
this.transferSelf(field, parameter);
}
} else {
// 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接
this.transferSplice(field, parameter);
}
}
}

/**
* 转义条件构造的特殊字符
* 在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper
*
* @param field 字段名称
* @param parameter 参数对象
*/
public abstract void transferWrapper(String field, T parameter);

/**
* 转义自定义条件拼接的特殊字符
* 未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
*
* @param field 字段名称
* @param parameter 参数对象
*/
public abstract void transferSelf(String field, T parameter);

/**
* 转义自定义条件拼接的特殊字符
* 在Mapper类的注解SQL中进行了模糊查询的拼接
*
* @param field 字段名称
* @param parameter 参数对象
*/
public abstract void transferSplice(String field, T parameter);

/**
* 转义通配符
*
* @param before 待转义字符串
* @return 转义后字符串
*/
String escapeChar(String before) {
if (StringUtils.isNotBlank(before)) {
before = before.replaceAll("\\\\", "\\\\\\\\");
before = before.replaceAll("_", "\\\\_");
before = before.replaceAll("%", "\\\\%");
}
return before;
}

/**
* 是否包含需要转义的字符
*
* @param obj 待判断的对象
* @return true/false
*/
boolean hasEscapeChar(Object obj) {
if (!(obj instanceof String)) {
return false;
}
return this.hasEscapeChar((String) obj);
}

/**
* 处理对象like问题
*
* @param field 对象字段
* @param parameter 对象
*/
void resolveObj(String field, Object parameter) {
if (parameter == null || StringUtils.isBlank(field)) {
return;
}
try {
PropertyDescriptor descriptor = new PropertyDescriptor(field, parameter.getClass());
Method readMethod = descriptor.getReadMethod();
Object param = readMethod.invoke(parameter);
if (this.hasEscapeChar(param)) {
Method setMethod = descriptor.getWriteMethod();
setMethod.invoke(parameter, this.escapeChar(param.toString()));
} else if (this.cascade(field)) {
int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR) + 1;
this.resolveObj(field.substring(index), param);
}
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
log.error("反射 {} 的 {} get/set方法出现异常", parameter, field, e);
}

}

/**
* 判断是否是级联属性
*
* @param field 字段名
* @return true/false
*/
boolean cascade(String field) {
if (StringUtils.isBlank(field)) {
return false;
}
return field.contains(MYBATIS_PLUS_WRAPPER_SEPARATOR) && !this.hasWrapper(field);
}

/**
* 是否包含mybatis-plus的包含like的SQL语句格式
*
* @param sql 完整SQL语句
* @return true/false
*/

private boolean hasMybatisPlusLikeSql(String sql) {
if (StringUtils.isBlank(sql)) {
return false;
}
return sql.toLowerCase().contains(MYBATIS_PLUS_LIKE_SQL);
}

/**
* 判断是否使用mybatis-plus条件构造器
*
* @param field 字段
* @return true/false
*/

private boolean hasWrapper(String field) {
if (StringUtils.isBlank(field)) {
return false;
}
return field.contains(MYBATIS_PLUS_WRAPPER_PREFIX);
}

/**
* 判断字符串是否含有需要转义的字符
*
* @param str 待判断的字符串
* @return true/false
*/
private boolean hasEscapeChar(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
for (String s : ESCAPE_CHAR) {
if (str.contains(s)) {
return true;
}
}
return false;
}

}

+ 79
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/MapLikeSqlConverter.java View File

@@ -0,0 +1,79 @@
package com.tuoheng.admin.config.sql;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;

import java.util.Map;
import java.util.Objects;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: ike字符转义
* @Version: 1.0
*/
public class MapLikeSqlConverter extends AbstractLikeSqlConverter<Map> {

@Override
public void transferWrapper(String field, Map parameter) {
AbstractWrapper wrapper = (AbstractWrapper) parameter.get(MYBATIS_PLUS_WRAPPER_KEY);
parameter = wrapper.getParamNameValuePairs();
String[] keys = field.split(MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX);
// ew.paramNameValuePairs.param1,截取字符串之后,获取第三个,即为参数名
String paramName = keys[2];
String mapKey = String.format("%s.%s", REPLACED_LIKE_KEYWORD_MARK, paramName);
if (parameter.containsKey(mapKey) && Objects.equals(parameter.get(mapKey), true)) {
return;
}
if (this.cascade(field)) {
this.resolveCascadeObj(field, parameter);
} else {
Object param = parameter.get(paramName);
if (this.hasEscapeChar(param)) {
String paramStr = param.toString();
parameter.put(keys[2], String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1))));
}
}
parameter.put(mapKey, true);
}

@Override
public void transferSelf(String field, Map parameter) {
if (this.cascade(field)) {
this.resolveCascadeObj(field, parameter);
return;
}
Object param = parameter.get(field);
if (this.hasEscapeChar(param)) {
String paramStr = param.toString();
parameter.put(field, String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1))));
}
}

@Override
public void transferSplice(String field, Map parameter) {
if (this.cascade(field)) {
this.resolveCascadeObj(field, parameter);
return;
}
Object param = parameter.get(field);
if (this.hasEscapeChar(param)) {
parameter.put(field, this.escapeChar(param.toString()));
}
}

/**
* 处理级联属性
*
* @param field 级联字段名
* @param parameter 参数Map对象
*/
private void resolveCascadeObj(String field, Map parameter) {
int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR);
Object param = parameter.get(field.substring(0, index));
if (param == null) {
return;
}
this.resolveObj(field.substring(index + 1), param);
}

}

+ 153
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/MybatisPlusInterceptor.java View File

@@ -0,0 +1,153 @@
package com.tuoheng.admin.config.sql;

import com.tuoheng.common.utils.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.*;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: like字符转义 防止sql注入
* @Version: 1.0
*/
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class})})
public class MybatisPlusInterceptor implements Interceptor {

/**
* SQL语句like
*/
private final static String SQL_LIKE = " like ";

/**
* SQL语句占位符
*/
private final static String SQL_PLACEHOLDER = "?";

/**
* SQL语句占位符分隔
*/
private final static String SQL_PLACEHOLDER_REGEX = "\\?";

/**
* 所有的转义器
*/
private static Map<Class, AbstractLikeSqlConverter> converterMap = new HashMap<>(4);

static {
converterMap.put(Map.class, new MapLikeSqlConverter());
// converterMap.put(Object.class, new ObjectLikeSqlConverter());
}

@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取sql及参数信息
Object[] args = invocation.getArgs();
MappedStatement statement = (MappedStatement) args[0];
Object parameterObject = args[1];
BoundSql boundSql = statement.getBoundSql(parameterObject);
String sql = boundSql.getSql();
this.transferLikeSql(sql, parameterObject, boundSql);
return invocation.proceed();
}

@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

@Override
public void setProperties(Properties arg0) {
}

/**
* 修改包含like的SQL语句
*
* @param sql SQL语句
* @param parameterObject 参数对象
* @param boundSql 绑定SQL对象
*/
private void transferLikeSql(String sql, Object parameterObject, BoundSql boundSql) {
// 判断是否有like关键字
if (!isEscape(sql)) {
return;
}
sql = sql.replaceAll(" {2}", " ");
// 获取关键字的个数(去重)
Set<String> fields = this.getKeyFields(sql, boundSql);
if (fields == null) {
return;
}
// 此处可以增强,不止是支持Map对象,Map对象仅用于传入的条件为Map或者使用@Param传入的对象被Mybatis转为的Map
AbstractLikeSqlConverter converter;
// 对关键字进行特殊字符“清洗”,如果有特殊字符的,在特殊字符前添加转义字符(\)
if (parameterObject instanceof Map) {
converter = converterMap.get(Map.class);
} else {
converter = converterMap.get(Object.class);
}
converter.convert(sql, fields, parameterObject);
}

/**
* 是否需要转义
*
* @param sql SQL语句
* @return true/false
*/
private boolean isEscape(String sql) {
return this.hasLike(sql) && this.hasPlaceholder(sql);
}

/**
* 判断SQL语句中是否含有like关键字
*
* @param str SQL语句
* @return true/false
*/
private boolean hasLike(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
return str.toLowerCase().contains(SQL_LIKE);
}

/**
* 判断SQL语句中是否包含SQL占位符
*
* @param str SQL语句
* @return true/false
*/
private boolean hasPlaceholder(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
return str.toLowerCase().contains(SQL_PLACEHOLDER);
}

/**
* 获取需要替换的所有字段集合
*
* @param sql 完整SQL语句
* @param boundSql 绑定的SQL对象
* @return 字段集合列表
*/
private Set<String> getKeyFields(String sql, BoundSql boundSql) {
String[] params = sql.split(SQL_PLACEHOLDER_REGEX);
Set<String> fields = new HashSet<>();
for (int i = 0; i < params.length; i++) {
if (this.hasLike(params[i])) {
String field = boundSql.getParameterMappings().get(i).getProperty();
fields.add(field);
}
}
return fields;
}

}

+ 25
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/config/sql/ObjectLikeSqlConverter.java View File

@@ -0,0 +1,25 @@
package com.tuoheng.admin.config.sql;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 通用参数的转换器
* @Version: 1.0
*/
public class ObjectLikeSqlConverter extends AbstractLikeSqlConverter<Object> {

@Override
public void transferWrapper(String field, Object parameter) {
// 尚未发现这种情况
}

@Override
public void transferSelf(String field, Object parameter) {
// 尚未发现这种情况
}

@Override
public void transferSplice(String field, Object parameter) {
this.resolveObj(field, parameter);
}
}

+ 24
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/controller/HealthController.java View File

@@ -0,0 +1,24 @@
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;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 服务健康检查
* @Version: 1.0
*/
@RestController
@RequestMapping("/health")
public class HealthController {

/**
* 健康检查接口调用
*/
@GetMapping("/hello")
public String health() {
return "ok";
}
}

+ 78
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/enums/MQTTTopicEnum.java View File

@@ -0,0 +1,78 @@
package com.tuoheng.admin.enums;

import lombok.Getter;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-18 19:09
* @Description: MQTT topic 枚举。将所有和MQTT交互的topic统一维护
* %S:设备编号占位符;qos:为传输等级定义的时候自行指定;desc:描述信息方便记录日志等
* @Version: 1.0
*/
public enum MQTTTopicEnum {

TOPIC_DATA_DOORMOTOR("/v1/%s/data/DoorMotor", 0, ""),
TOPIC_DATA_LIFTMOTOR("/v1/%s/data/LiftMotor", 0, ""),
TOPIC_DATA_FIXEDMOTOR("/v1/%s/data/FixedMotor", 0, ""),
TOPIC_DATA_DRONEGOAWAY("/v1/%s/data/DroneGoAway", 0, ""),
TOPIC_DATA_DRONEGOHOME("/v1/%s/data/DroneGoHome", 0, ""),
TOPIC_DATA_DRONEGOSTOP("/v1/%s/data/DroneGoStop", 0, ""),
TOPIC_DATA_DRONESTOP("/v1/%s/data/DroneStop", 0, ""),
TOPIC_CONFIRM_DOORMOTOR("/v1/%s/confirm/DoorMotor", 0, ""),
TOPIC_CONFIRM_LIFTMOTOR("/v1/%s/confirm/LiftMotor", 0, ""),
TOPIC_CONFIRM_FIXEDMOTOR1("/v1/%s/confirm/FixedMotor1", 0, ""),
TOPIC_CONFIRM_FIXEDMOTOR2("/v1/%s/confirm/FixedMotor2", 0, ""),
TOPIC_CONFIRM_DRONEGOAWAY("/v1/%s/confirm/DroneGoAway", 0, ""),
TOPIC_CONFIRM_DRONEGOHOME("/v1/%s/confirm/DroneGoHome", 0, ""),
TOPIC_CONFIRM_DRONEGOSTOP("/v1/%s/confirm/DroneGoStop", 0, ""),
TOPIC_CONFIRM_DRONESTOP("/v1/%s/confirm/DroneStop", 0, ""),
TOPIC_CONFIRM_EDGE("/v1/%s/confirm/Edge", 0, ""),
TOPIC_CONFIRM_CHGR("/v1/%s/confirm/CHGR", 0, ""),
TOPIC_CONFIRM_LIGHT("/v1/%s/confirm/Light", 0, ""),
TOPIC_CONTROL_DOORMOTOR("/v1/%s/control/DoorMotor", 0, ""),
TOPIC_CONTROL_LIFTMOTOR("/v1/%s/control/LiftMotor", 0, ""),
TOPIC_CONTROL_FIXEDMOTOR("/v1/%s/control/FixedMotor", 0, ""),
TOPIC_CONTROL_DRONEGOAWAY("/v1/%s/control/DroneGoAway", 0, ""),
TOPIC_CONTROL_DRONEGOHOME("/v1/%s/control/DroneGoHome", 0, ""),
TOPIC_CONTROL_DRONEGOSTOP("/v1/%s/control/DroneGoStop", 0, ""),
TOPIC_CONTROL_DRONESTOP("/v1/%s/control/DroneStop", 0, ""),
TOPIC_CONTROL_CHGR("/v1/%s/control/CHGR", 0, ""),
TOPIC_CONTROL_LIGHT("/v1/%s/control/Light", 0, ""),
TOPIC_DATA_TAH("/v1/%s/data/TAH", 0, ""),
TOPIC_DATA_WTH("/v1/%s/data/WTH", 0, ""),
TOPIC_DATA_ACD("/v1/%s/data/ACD", 0, ""),
TOPIC_DATA_UPS("/v1/%s/data/UPS", 0, ""),
TOPIC_DATA_CHGR("/v1/%s/data/CHGR", 0, ""),
TOPIC_DATA_LIGHT("/v1/%s/data/Light", 0, ""),
TOPIC_ALARM_TAH("/v1/%s/alarm/TAH", 0, ""),
TOPIC_ALARM_WTH("/v1/%s/alarm/WTH", 0, ""),
TOPIC_ALARM_ACD("/v1/%s/alarm/ACD", 0, ""),
TOPIC_ALARM_UPS("/v1/%s/alarm/UPS", 0, ""),
TOPIC_PULL_WTH("/v1/%s/pull/WTH", 0, ""),
TOPIC_POST_WTH("/v1/%s/post/WTH", 0, "");

MQTTTopicEnum(String topic, int qos, String desc) {
this.topic = topic;
this.qos = qos;
this.desc = desc;
}

/**
* MQTT topic
*/
@Getter
private String topic;

/**
* 对应qos等级 0-至多一次;1-至少一次;2-确保只有一次
*/
@Getter
private int qos;

/**
* 描述
*/
@Getter
private String desc;

}

+ 30
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/enums/ServiceExceptionEnum.java View File

@@ -0,0 +1,30 @@
package com.tuoheng.admin.enums;

import com.tuoheng.common.common.ExceptionInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 10:17
* @Description: 业务异常 枚举
* 异常全局唯一,从5555依次递减
* @Version: 1.0
*/
@AllArgsConstructor
public enum ServiceExceptionEnum implements ExceptionInterface {

/**
* 机场环境检查异常!
*/
AIRPORT_ENVIORMENT_ERROR(5555, "机场环境检查异常!"),

;

@Getter
private final int code;

@Getter
private final String message;

}

+ 14
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/mapper/AirportMapper.java View File

@@ -0,0 +1,14 @@
package com.tuoheng.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tuoheng.admin.pojo.entity.Airport;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 机场表 Mapper 接口
* @Version: 1.0
*/
public interface AirportMapper extends BaseMapper<Airport> {

}

+ 100
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/pojo/entity/Airport.java View File

@@ -0,0 +1,100 @@
package com.tuoheng.admin.pojo.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.tuoheng.common.common.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 机场表
* @Version: 1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("th_airport")
public class Airport extends BaseEntity {

private static final long serialVersionUID = 1L;

/**
* 设备编号
*/
private String code;

/**
* 设备名称
*/
private String name;

/**
* 机场照片
*/
private String image;

/**
* 外部监控地址
*/
private String externalMonitorUrl;
/**
* 外部监控地址flv
*/
private String flvExternalMonitorUrl;

/**
* 内部监控地址
*/
private String internalMonitorUrl;

/**
* 覆盖范围
*/
private String coverage;

/**
* 排序
*/
private Integer sort;

/**
* 备注
*/
private String note;

/**
* 经度
*/
private String longitude;

/**
* 纬度
*/
private String latitude;

/**
* 无人机ID
*/
private Integer droneId;

/**
* 地面站url
*/
private String groundStationUrl;
/**
* 控制板Id
*/
private String edgeId;

/**
* mqtt控制板客户端Id
*/
private String mqClientId;

/*机场运行状态*/
private int astatus;

private String address;
}

+ 15
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/IAirportService.java View File

@@ -0,0 +1,15 @@
package com.tuoheng.admin.service;

import com.tuoheng.admin.pojo.entity.Airport;
import com.tuoheng.common.common.IBaseService;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 机场表 服务类
* @Version: 1.0
*/
public interface IAirportService extends IBaseService<Airport> {


}

+ 19
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/impl/AirportServiceImpl.java View File

@@ -0,0 +1,19 @@
package com.tuoheng.admin.service.impl;

import com.tuoheng.admin.mapper.AirportMapper;
import com.tuoheng.admin.pojo.entity.Airport;
import com.tuoheng.admin.service.IAirportService;
import com.tuoheng.common.common.BaseServiceImpl;
import org.springframework.stereotype.Service;


/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 机场表 服务实现类
* @Version: 1.0
*/
@Service
public class AirportServiceImpl extends BaseServiceImpl<AirportMapper, Airport> implements IAirportService {

}

+ 295
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/HttpURLConnectionUtil.java View File

@@ -0,0 +1,295 @@
package com.tuoheng.admin.service.mqttService;
import org.springframework.lang.Nullable;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
* @author ww
* @date 2022/07/8 10:42
*/
public class HttpURLConnectionUtil {

/**
* Http get请求
* @param httpUrl 连接
* @return 响应数据
*/
public static String doGet(String httpUrl){
//链接
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
StringBuffer result = new StringBuffer();
try {
//创建连接
URL url = new URL(httpUrl);
connection = (HttpURLConnection) url.openConnection();
//设置请求方式
connection.setRequestMethod("GET");

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.setRequestProperty("Content-Type", "application/json;charset=utf-8");
connection.setRequestProperty("Authorization", "Basic YWRtaW46cHVibGlj");
connection.setRequestProperty("Cookie", "jenkins-timestamper-offset=-28800000; order=id%20desc; serverType=nginx; ltd_end=-1; pro_end=-1; memSize=7768; bt_user_info=%7B%22status%22%3Atrue%2C%22msg%22%3A%22%u83B7%u53D6%u6210%u529F%21%22%2C%22data%22%3A%7B%22username%22%3A%22139****4314%22%7D%7D; sites_path=/www/wwwroot; distribution=centos8; p-1=1; lang=en-US; force=0; b6e922e8cf76dea67efb3a9292724aee=acc59469-46e3-4576-b216-81b907d80a7f.rB3Plw7uc301E0qFoEBeoP_fL4w; load_search=undefined; remember-me=YWRtaW46MTY1ODIxMDYwODA2MDoxY2ZhOTBhMzFlN2IzZTg1YzM5MWQ3Y2M4MWEzYzVlZmYzM2UzOGQ4MTVjZDZhNTUwNjQ4YjFhMjVlYTM3NWJj; JSESSIONID.fc8332ab=node01bo1jsydkd8ql1oo3srksa2k6w288.node0; screenResolution=1920x1080; b2651c13496a99bc2899b024b99bca4f=b4e55bb9-4a3e-44b6-ae8b-c054e7115182.SEaDzMgHxI4d2CGoGwNPnj3L2y4; request_token=hH1LwVtazrgz4sMI4CE1SwEVpCYgiQcHNp4EfyvPni9C32PJ; soft_remarks=%7B%22list%22%3A%5B%22%u4F01%u4E1A%u7248%u3001%u4E13%u4E1A%u7248%u63D2%u4EF6%22%2C%2215%u5929%u65E0%u7406%u7531%u9000%u6B3E%22%2C%22%u53EF%u66F4%u6362IP%22%2C%22%u5E74%u4ED8%u8D60%u90012%u5F20SSL%u8BC1%u4E66%22%2C%22%u5E74%u4ED8%u8D60%u90011000%u6761%u77ED%u4FE1%22%2C%22%u4F4E%u81F32.43%u5143/%u5929%22%2C%22%u5546%u7528%u9632%u706B%u5899%u6388%u6743%22%2C%22%u5E74%u4ED8%u53EF%u5165%u4F01%u4E1A%u7248%u670D%u52A1%u7FA4%22%2C%22%u4EA7%u54C1%u6388%u6743%u8BC1%u4E66%22%5D%2C%22pro_list%22%3A%5B%22%u4E13%u4E1A%u7248%u63D2%u4EF6%22%2C%2215%u5929%u65E0%u7406%u7531%u9000%u6B3E%22%2C%22%u53EF%u66F4%u6362IP%22%2C%22%u4F4E%u81F31.18%u5143/%u5929%22%2C%22%u5546%u7528%u9632%u706B%u5899%u6388%u6743%22%2C%22%u4EA7%u54C1%u6388%u6743%u8BC1%u4E66%22%5D%2C%22kfqq%22%3A%223007255432%22%2C%22kf%22%3A%22http%3A//q.url.cn/CDfQPS%3F_type%3Dwpa%26qidian%3Dtrue%22%2C%22qun%22%3A%22%22%2C%22kf_list%22%3A%5B%7B%22qq%22%3A%223007255432%22%2C%22kf%22%3A%22http%3A//q.url.cn/CDfQPS%3F_type%3Dwpa%26qidian%3Dtrue%22%7D%2C%7B%22qq%22%3A%222927440070%22%2C%22kf%22%3A%22http%3A//wpa.qq.com/msgrd%3Fv%3D3%26uin%3D2927440070%26site%3Dqq%26menu%3Dyes%26from%3Dmessage%26isappinstalled%3D0%22%7D%5D%2C%22wx_list%22%3A%5B%7B%22ps%22%3A%22%u552E%u524D%u54A8%u8BE2%22%2C%22kf%22%3A%22https%3A//work.weixin.qq.com/kfid/kfc72fcbde93e26a6f3%22%7D%5D%7D; load_page=null; load_type=null");
//设置连接超时时间
connection.setReadTimeout(15000);
//开始连接
connection.connect();
//获取响应数据
if (connection.getResponseCode() == 200) {
//获取返回的数据
is = connection.getInputStream();
if (null != is) {
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
}
}
}
} 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.toString();
}

/**
* Http post请求
* @param httpUrl 连接
* @param param 参数
* @return
*/
public static String doPost(String httpUrl, @Nullable String param) {
StringBuffer result = new StringBuffer();
//连接
HttpURLConnection connection = null;
OutputStream os = null;
InputStream is = null;
BufferedReader br = null;
trustAllHosts();
try {
//创建连接对象
URL url = new URL(httpUrl);
//创建连接
connection = (HttpURLConnection) url.openConnection();
//设置请求方法
connection.setRequestMethod("POST");
//设置连接超时时间
connection.setConnectTimeout(15000);
//设置读取超时时间
connection.setReadTimeout(15000);
//DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//设置是否可读取
connection.setDoOutput(true);
connection.setDoInput(true);
//设置通用的请求属性
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.setRequestProperty("Content-Type", "application/json;charset=utf-8");

//拼装参数
if (null != param && (!param.equals(""))) {
//设置参数
os = connection.getOutputStream();
//拼装参数
os.write(param.getBytes("UTF-8"));
}
//设置权限
//设置请求头等
//开启连接
//connection.connect();
//读取响应
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
if (null != is) {
//br = new BufferedReader(new InputStreamReader(is, "GBK"));
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
result.append("\r\n");
}
}
}

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭连接
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接
connection.disconnect();
}
return result.toString();
}
public static String doPut(String httpUrl, @Nullable String param) {
StringBuffer result = new StringBuffer();
//连接
HttpURLConnection connection = null;
OutputStream os = null;
InputStream is = null;
BufferedReader br = null;
try {
//创建连接对象
URL url = new URL(httpUrl);
//创建连接
connection = (HttpURLConnection) url.openConnection();
//设置请求方法
connection.setRequestMethod("PUT");
//设置连接超时时间
connection.setConnectTimeout(15000);
//设置读取超时时间
connection.setReadTimeout(15000);
//DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//设置是否可读取
connection.setDoOutput(true);
connection.setDoInput(true);
//设置通用的请求属性
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.setRequestProperty("Content-Type", "application/json;charset=utf-8");

//拼装参数
if (null != param && (!param.equals(""))) {
//设置参数
os = connection.getOutputStream();
//拼装参数
os.write(param.getBytes("UTF-8"));
}
//设置权限
//设置请求头等
//开启连接
//connection.connect();
//读取响应
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
if (null != is) {
//br = new BufferedReader(new InputStreamReader(is, "GBK"));
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
result.append("\r\n");
}
}
}

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭连接
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接
connection.disconnect();
}
return result.toString();
}
public static void main(String[] args) {
String message = doPost("https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13026194071", "");
System.out.println(message);
}

/**
* Trust every server - dont check for any certificate
*/
private static void trustAllHosts() {
final String TAG = "trustAllHosts";
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Log.i(TAG, "checkClientTrusted");
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Log.i(TAG, "checkServerTrusted");
}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}

}

+ 34
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/MqHttp.java View File

@@ -0,0 +1,34 @@
package com.tuoheng.admin.service.mqttService;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class MqHttp {

@Value("${spring.mqtt.onlineUrl}")
String url;
@RequestMapping("/doGetClient")
@ResponseBody
public String doGetClient(){
HttpURLConnectionUtil httpURLConnectionUtil = new HttpURLConnectionUtil();
//String urls=url.replace("tcp","http").replace("1883","18083");
//String urls="https://mqtt.t-aaron.com";
String a = httpURLConnectionUtil.doGet(""+url+"/api/v4/clients?_page=1&_limit=100&_=1657249001745");
return a;
}
//http://124.223.218.130:18083/api/v4/nodes/emqx@127.0.0.1/subscriptions/001497326DAA2502?_=1657257768467
@RequestMapping("/doGetClientById")
@ResponseBody
public String doGetClientById(String clientId){
HttpURLConnectionUtil httpURLConnectionUtil = new HttpURLConnectionUtil();
//String urls=url.replace("tcp","http").replace("1883","18083");
//String urls="https://mqtt.t-aaron.com";
String a = httpURLConnectionUtil.doGet(""+url+"/api/v4/clients/"+clientId+"?_=1657257768467");
return a;
}
}


+ 249
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/consumer/MqttConsumerCallBack.java View File

@@ -0,0 +1,249 @@
package com.tuoheng.admin.service.mqttService.consumer;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tuoheng.admin.pojo.entity.Airport;
import com.tuoheng.admin.service.IAirportService;
import com.tuoheng.admin.utils.SpringUtil;
import com.tuoheng.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: MQTT 消息监听回调
* @Version: 1.0
*/
@Slf4j
public class MqttConsumerCallBack implements MqttCallback{

/**
* 客户端断开连接的回调
*/
@Override
public void connectionLost(Throwable throwable) {
System.out.println("与服务器断开连接,可重连");
/*MqttProviderConfig client = SpringUtil.getBean(MqttProviderConfig.class);
System.out.println(client.isconnect());
if (!client.isconnect()) {
client.connect();
System.out.println("重连成功!");
}*/
}
DateFormat bf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 消息到达的回调
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
try {
log.info("接收消息主题 : {}\n{}",topic,JSONObject.parse(message.getPayload()));
RedisUtils redisUtils = SpringUtil.getBean(RedisUtils.class);
String edgId = topic.split("/")[2];
String command = topic.substring(edgId.length() + 4, topic.length());
if (!(command.contains("data/")||command.contains("alarm/"))) {
redisUtils.hset(edgId, command, message, 100);
}else{
//上报数据换算
message = getMathValue(message,command,redisUtils,edgId);
redisUtils.hset(edgId, command, message);
redisUtils.hset(edgId+"_data", command, message);
if(command.contains("alarm")) {
command = command.replace("alarm", "data");
redisUtils.hset(edgId, command, message);
redisUtils.hset(edgId+"_data", command, message);
}
getWth(command,redisUtils,edgId, message);
}
//saveStatus(edgId,command,message);
//消息日志入库
//saveLog(edgId, command, message);
//保存机场的运行状态
saveAirportRedisStatus(command,redisUtils,edgId, message);
}catch (Exception e){
log.error("",e);
e.printStackTrace();
}
}

/**
* 保存机场的运行状态
* @param command
* @param redisUtils
* @param edgId
* @param message
*/
private void saveAirportRedisStatus(String command, RedisUtils redisUtils, String edgId, MqttMessage message) {
JSONObject jsonObject = (JSONObject)JSONObject.parse(message.getPayload());
IAirportService airportService = SpringUtil.getBean(IAirportService.class);
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("edge_id",edgId);
queryWrapper.eq("mark",1);
List<Airport> list = airportService.list(queryWrapper);

if (list.size()>0){
edgId = list.get(0).getCode();
}
/**
* 1,2 舱门(命令接收,动作完成)
* 3,4 升降器(命令接收,动作完成)
* 5,6 固定器(命令接收,动作完成)
* 7,8 出舱(命令接收,动作完成)
* 9,10 回舱(命令接收,动作完成)
* 11 任务中
* 12 返航中
* 13 悬停中
*/
if(command.contains("confirm/DoorMotor")&&jsonObject.get("msg").toString().contains("SUCCESS")) {
if(jsonObject.get("msg")!=null&&jsonObject.get("msg").toString().contains("SUCCESS")){
redisUtils.hset("airportRunStatus",edgId,"1");
}
}else if (command.contains("data/DoorMotor")){
if(jsonObject.get("parm")!=null&&(JSONObject.toJSONString(jsonObject.get("parm"))).contains("true")){
redisUtils.hset("airportRunStatus",edgId,"2");
}
}else if(command.contains("confirm/LiftMotor")) {
if(jsonObject.get("msg")!=null&&jsonObject.get("msg").toString().contains("SUCCESS")){
redisUtils.hset("airportRunStatus",edgId,"3");
}
}else if (command.contains("data/LiftMotor")){
if(jsonObject.get("parm")!=null&&(JSONObject.toJSONString(jsonObject.get("parm"))).contains("true")){
redisUtils.hset("airportRunStatus",edgId,"4");
}
}else if(command.contains("confirm/FixedMotor")) {
if(jsonObject.get("msg")!=null&&jsonObject.get("msg").toString().contains("SUCCESS")){
redisUtils.hset("airportRunStatus",edgId,"5");
}
}else if (command.contains("data/FixedMotor")){
if(jsonObject.get("parm")!=null&&(JSONObject.toJSONString(jsonObject.get("parm"))).contains("true")){
redisUtils.hset("airportRunStatus",edgId,"6");
}
}else if(command.contains("confirm/DroneGoAway")) {
if(jsonObject.get("msg")!=null&&jsonObject.get("msg").toString().contains("SUCCESS")){
redisUtils.hset("airportRunStatus",edgId,"7");
}
}else if (command.contains("data/DroneGoAway")){
if(jsonObject.get("parm")!=null&&(JSONObject.toJSONString(jsonObject.get("parm"))).contains("true")){
redisUtils.hset("airportRunStatus",edgId,"8");
}
}else if(command.contains("confirm/DroneGoHome")) {
if(jsonObject.get("msg")!=null&&jsonObject.get("msg").toString().contains("SUCCESS")){
redisUtils.hset("airportRunStatus",edgId,"9");
}
}else if (command.contains("data/DroneGoHome")){
if(jsonObject.get("parm")!=null&&(JSONObject.toJSONString(jsonObject.get("parm"))).contains("true")){
redisUtils.hset("airportRunStatus",edgId,"10");
}
}
}

//上报数据换算封装
private MqttMessage getMathValue(MqttMessage message,String command,RedisUtils redisUtils,String edgeId) {
JSONObject jsonObject = (JSONObject)JSONObject.parse(message.getPayload());

JSONObject parmObjectList = (JSONObject)jsonObject.get("parm");
if(command.contains("/WTH")) {
JSONObject parmNew = (JSONObject) parmObjectList.clone();
parmNew.put("WDIRNAME", getMathValueWDIR(parmObjectList.get("WDIR")==null?"0":parmObjectList.get("WDIR").toString()));
parmNew.put("WDIR", String.format("%.2f",Double.parseDouble(parmObjectList.get("WDIR")==null?"0":parmObjectList.get("WDIR").toString())/10));
parmNew.put("WSPD", String.format("%.2f", Double.parseDouble(parmObjectList.get("WSPD")==null?"0":parmObjectList.get("WSPD").toString()) / 100));
parmNew.put("Rainfull", String.format("%.2f",Double.parseDouble(parmObjectList.get("Rainfull")==null?"0":parmObjectList.get("Rainfull").toString())*0.3));
parmNew.put("Hpa", String.format("%.2f",Double.parseDouble(parmObjectList.get("Hpa")==null?"0":parmObjectList.get("Hpa").toString())*0.001));
parmNew.put("Hum", String.format("%.2f",Double.parseDouble(parmObjectList.get("Hum")==null?"0":parmObjectList.get("Hum").toString())*0.1));
parmNew.put("Tmp", String.format("%.2f",Double.parseDouble(parmObjectList.get("Tmp")==null?"0":parmObjectList.get("Tmp").toString())*0.1));
parmNew.put("Lux", String.format("%.2f",Double.parseDouble(parmObjectList.get("Lux")==null?"0":parmObjectList.get("Lux").toString())*0.1));
parmNew.put("Nosie", String.format("%.2f",Double.parseDouble(parmObjectList.get("Nosie")==null?"0":parmObjectList.get("Nosie").toString())*0.1));
parmNew.put("Dew", String.format("%.2f",Double.parseDouble(parmObjectList.get("Dew")==null?"0":parmObjectList.get("Dew").toString())*0.1));
String acd = redisUtils.hget("acd",edgeId)==null?"":redisUtils.hget("acd",edgeId).toString();
parmNew.put("ACDTmp",acd);
jsonObject.put("parmNew",parmNew);
}else if(command.contains("/TAH")) {
JSONObject parmNew = (JSONObject) parmObjectList.clone();
parmNew.put("Hum", String.format("%.2f",Double.parseDouble(parmObjectList.get("Hum")==null?"0":parmObjectList.get("Hum").toString())*0.1));
parmNew.put("Tmp", String.format("%.2f",Double.parseDouble(parmObjectList.get("Tmp")==null?"0":parmObjectList.get("Tmp").toString())*0.1));
jsonObject.put("parmNew",parmNew);
}
if (command.contains("/ACD")){
JSONObject parmNew = (JSONObject) parmObjectList.clone();
parmNew.put("ACDTmp", String.format("%.2f",Double.parseDouble(parmObjectList.get("Tmp")==null?"0":parmObjectList.get("Tmp").toString())*0.1));
jsonObject.put("parmNew",parmNew);

redisUtils.hset("acd",edgeId,String.format("%.2f",Double.parseDouble(parmObjectList.get("Tmp")==null?"0":parmObjectList.get("Tmp").toString())*0.1),180);
}
if (command.contains("DoorMotor")||command.contains("DroneGoAway")){

}

message.setPayload(jsonObject.toJSONString().getBytes());
return message;
}

private Object getMathValueWDIR(String wdir) {
double dr = Double.parseDouble(wdir)/10;
double resultde=dr-180;
if (resultde==0){
return "北风";
}
if (resultde>0&&resultde<90){
return "东北风";
}
if (resultde==90){
return "东风";
}
if (resultde>90&&resultde<180){
return "东南风";
}
if (resultde==180){
return "南风";
}
if (resultde>-90&&resultde<0){
return "西北风";
}
if (resultde==-90){
return "西风";
}
if (resultde>-180&&resultde<-90){
return "西南风";
}
return "";
}

/**
* 消息发布成功的回调
*/
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

}

public void getWth(String command,RedisUtils redisUtils,String edgId,MqttMessage message){
if(command.contains("data/WTH")||command.contains("data/TAH")){

JSONObject json = (JSONObject) JSON.parse(message.getPayload());
List list = (List) redisUtils.get(edgId+command+"list");
if(list==null) {
list = new ArrayList();
}
if(list.size()>3){
list.remove(0);
}
JSONObject map = (JSONObject)json.get("parm");
map.put("date", new Date());
list.add(map);
redisUtils.set(edgId+command+"list",list);
System.out.println(json.get("parm"));
}
}
}


+ 49
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/send/MqttProviderCallBack.java View File

@@ -0,0 +1,49 @@
package com.tuoheng.admin.service.mqttService.send;

import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: MQTT 消息发送回调
* @Version: 1.0
*/
@Configuration
public class MqttProviderCallBack implements MqttCallback{

@Value("${spring.mqtt.client.id}")
private String clientId;
/**
* 与服务器断开的回调
*/
@Override
public void connectionLost(Throwable cause) {
System.out.println(clientId+"与服务器断开连接");
}

/**
* 消息到达的回调
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {


}

/**
* 消息发布成功的回调
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
IMqttAsyncClient client = token.getClient();
System.out.println(client.getClientId()+"发布消息成功!");

}

}


+ 201
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/service/mqttService/send/MqttProviderConfig.java View File

@@ -0,0 +1,201 @@
package com.tuoheng.admin.service.mqttService.send;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tuoheng.admin.enums.MQTTTopicEnum;
import com.tuoheng.admin.pojo.entity.Airport;
import com.tuoheng.admin.service.IAirportService;
import com.tuoheng.admin.service.mqttService.consumer.MqttConsumerCallBack;
import com.tuoheng.common.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: MQTT配置类
* @Version: 1.0
*/
@Configuration
@Slf4j
public class MqttProviderConfig {
@Value("${spring.mqtt.username}")
private String username;

@Value("${spring.mqtt.password}")
private String password;

@Value("${spring.mqtt.url}")
private String hostUrl;

@Value("${spring.mqtt.client.id}")
private String clientId;

@Value("${spring.mqtt.default.topic}")
private String defaultTopic;

/**
* 客户端对象
*/
private MqttClient client;

@Autowired
private IAirportService airportService;
/**
* 在bean初始化后连接到服务器
*/
@PostConstruct
public void init(){
connect();
}

/**
* 客户端连接服务端
*/
public void connect(){
try{
String ip = IpUtils.getHostIp();
//创建MQTT客户端对象
client = new MqttClient(hostUrl,ip,new MemoryPersistence());
//连接设置
MqttConnectOptions options = new MqttConnectOptions();
//是否清空session,设置false表示服务器会保留客户端的连接记录(订阅主题,qos),客户端重连之后能获取到服务器在客户端断开连接期间推送的消息
//设置为true表示每次连接服务器都是以新的身份
options.setCleanSession(true);
//设置连接用户名
options.setUserName(username);
//设置连接密码
options.setPassword(password.toCharArray());
//设置超时时间,单位为秒
options.setConnectionTimeout(100);
//设置心跳时间 单位为秒,表示服务器每隔 1.5*20秒的时间向客户端发送心跳判断客户端是否在线
options.setKeepAliveInterval(20);
//设置遗嘱消息的话题,若客户端和服务器之间的连接意外断开,服务器将发布客户端的遗嘱信息
options.setWill("willTopic",(ip + "与服务器断开连接").getBytes(),0,false);

//防止 ERROR o.e.p.c.mqttv3.internal.ClientState - Timed out as no activity 错误
options.setConnectionTimeout(0);

//mqttClient.reconnect(); 这个方法或者回调已经设置了重连
//options.setAutomaticReconnect(true);
//设置回调
client.setCallback(new MqttConsumerCallBack());
client.connect(options);


QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("mark",1);
List airportList = airportService.list(wrapper);

/**
* 遍历所有的设备,一次订阅每个设备的对应事件的topic
*/
airportList.forEach(item ->{
Airport airport = (Airport)item;
Arrays.stream(MQTTTopicEnum.values()).forEach(mqttTopicEnum -> {
try {
client.subscribe(String.format(mqttTopicEnum.getTopic(),airport.getEdgeId()),mqttTopicEnum.getQos());
} catch (MqttException e) {
log.error("订阅设备:{},对应topic:{}异常:{}",airport.getEdgeId(),mqttTopicEnum.getTopic(),e);
}
});
});
} catch(MqttException e){
log.error("MQTT链接异常:{}",e);
}
}

public boolean isconnect() {
try {
String ip = IpUtils.getHostIp();
//创建MQTT客户端对象
client = new MqttClient(hostUrl,ip,new MemoryPersistence());
}catch (Exception e){
log.error("MQTT链接异常:{}",e);
}
return client.isConnected();
}

/**
* MQTT发布消息
* @param qos
* @param retained
* @param topic
* @param message
* @return
*/
public MqttDeliveryToken publish(int qos,boolean retained,String topic,String message){
if (!client.isConnected()){
connect();
}
MqttDeliveryToken token=null;
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setQos(qos);
mqttMessage.setRetained(retained);
mqttMessage.setPayload(message.getBytes());
//主题的目的地,用于发布/订阅信息
MqttTopic mqttTopic = client.getTopic(topic);
//提供一种机制来跟踪消息的传递进度
//用于在以非阻塞方式(在后台运行)执行发布是跟踪消息的传递进度

try {
//将指定消息发布到主题,但不等待消息传递完成,返回的token可用于跟踪消息的传递状态
//一旦此方法干净地返回,消息就已被客户端接受发布,当连接可用,将在后台完成消息传递。
token = mqttTopic.publish(mqttMessage);
token.waitForCompletion();
} catch (MqttException e) {
log.error("MQTT topic:{},message:{},消息发送异常:{}",topic,message,e);
}
return token;
}


/**
* 断开连接
*/
public void disConnect(){
try {
client.disconnect();
} catch (MqttException e) {
log.error("MQTT断开链接异常:{}",e);
}
}
/**
* 订阅主题
*/
public void subscribe(String topic,int qos){
try {
client.subscribe(topic,qos);
} catch (MqttException e) {
log.error("MQTT订阅topic:{}异常:{}",topic,e);
}
}

public MqttClient getClient(){
return client;
}

/**
* 订阅集成列表
*/
public void subscribeAllAirport(Airport airport) throws MqttException {
if (!client.isConnected()){
connect();
}
Arrays.stream(MQTTTopicEnum.values()).forEach(mqttTopicEnum -> {
try {
client.subscribe(String.format(mqttTopicEnum.getTopic(),airport.getEdgeId()),mqttTopicEnum.getQos());
} catch (MqttException e) {
log.error("订阅设备:{},对应topic:{}异常:{}",airport.getEdgeId(),mqttTopicEnum.getTopic(),e);
}
});
}
}


+ 230
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/AliyunOSSUtil.java View File

@@ -0,0 +1,230 @@
package com.tuoheng.admin.utils;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;

/**
* 描述: 阿里云OSS工具类
* 版权: Copyright (c) 2020
* 公司: XXX
* 作者: yanghj
* 版本: 4.0
* 创建日期: 2020/9/16 11:38
*/
@Component
@Slf4j
public class AliyunOSSUtil {

@Value("${aliyun.oss.endpoint:}")
private String endpoint;

@Value("${aliyun.oss.accessKeyId:}")
private String accessKeyId;

@Value("${aliyun.oss.accessKeySecret:}")
private String accessKeySecret;

@Value("${aliyun.oss.bucketName:}")
private String bucketName;

//文件存储目录
@Value("${aliyun.oss.filedir:}")
private String filedir;

//-----------------对外功能----------------
/**
* 1、单个文件上传
* @param file
* @return 返回完整URL地址
*/
public String uploadFile(MultipartFile file)
{
String fileUrl = uploadImg2Oss(file);
String str = getFileUrl(fileUrl);
return str.trim();
}
/**
* 1、单个文件上传(指定文件名(带后缀))
* @param file
* @return 返回完整URL地址
*/
public String uploadFile(MultipartFile file,String fileName)
{
try
{
InputStream inputStream = file.getInputStream();
this.uploadFile2OSS(inputStream, fileName);
return fileName;
}
catch (Exception e)
{
return "上传失败";
}
}

/**
* 2、多文件上传
* @param fileList
* @return 返回完整URL,逗号分隔
*/
public String uploadFile(List<MultipartFile> fileList) {
String fileUrl = "";
String str = "";
String photoUrl = "";
for(int i = 0;i< fileList.size();i++){
fileUrl = uploadImg2Oss(fileList.get(i));
str = getFileUrl(fileUrl);
if(i == 0){
photoUrl = str;
}else {
photoUrl += "," + str;
}
}
return photoUrl.trim();
}

/**
* 3、通过文件名获取文完整件路径
* @param fileUrl
* @return 完整URL路径
*/
public String getFileUrl(String fileUrl)
{
if (fileUrl !=null && fileUrl.length()>0) {
String[] split = fileUrl.split("/");
String url = this.getUrl(this.filedir + split[split.length - 1]);
return url;
}
return null;
}


//-----------内部辅助功能------------------------
//获取去掉参数的完整路径
private String getShortUrl(String url)
{
String[] imgUrls = url.split("\\?");
return imgUrls[0].trim();
}
// 获得url链接
private String getUrl(String key) {
// 设置URL过期时间为20年 3600l* 1000*24*365*20
Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 20);
// 生成URL
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
if (url != null)
{
return getShortUrl(url.toString());
}
return null;
}

// 上传文件
private String uploadImg2Oss(MultipartFile file)
{
//1、限制最大文件为20M
if (file.getSize() > 1024 * 1024 *20)
{
return "图片太大";
}
String fileName = file.getOriginalFilename();
String suffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); //文件后缀
String uuid = UUID.randomUUID().toString();
String name = uuid + suffix;

try
{
InputStream inputStream = file.getInputStream();
this.uploadFile2OSS(inputStream, name);
return name;
}
catch (Exception e)
{
return "上传失败";
}
}


// 上传文件(指定文件名)

public String uploadFile2OSS(InputStream instream, String fileName)
{
String ret = "";
try {
//创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(instream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("inline;filename=" + fileName);
//上传文件

OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
PutObjectResult putResult = ossClient.putObject(bucketName, filedir + fileName, instream, objectMetadata);
ret = putResult.getETag();
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
try {
if (instream != null) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}

private static String getcontentType(String FilenameExtension)
{
if (FilenameExtension.equalsIgnoreCase(".bmp")) {
return "image/bmp";
}
if (FilenameExtension.equalsIgnoreCase(".gif")) {
return "image/gif";
}
if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
FilenameExtension.equalsIgnoreCase(".jpg") ||
FilenameExtension.equalsIgnoreCase(".png")) {
return "image/jpeg";
}
if (FilenameExtension.equalsIgnoreCase(".html")) {
return "text/html";
}
if (FilenameExtension.equalsIgnoreCase(".txt")) {
return "text/plain";
}
if (FilenameExtension.equalsIgnoreCase(".vsd")) {
return "application/vnd.visio";
}
if (FilenameExtension.equalsIgnoreCase(".pptx") ||
FilenameExtension.equalsIgnoreCase(".ppt")) {
return "application/vnd.ms-powerpoint";
}
if (FilenameExtension.equalsIgnoreCase(".docx") ||
FilenameExtension.equalsIgnoreCase(".doc")) {
return "application/msword";
}
if (FilenameExtension.equalsIgnoreCase(".xml")) {
return "text/xml";
}
//PDF
if (FilenameExtension.equalsIgnoreCase(".pdf")) {
return "application/pdf";
}
return "image/jpeg";
}

}

+ 29
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/AliyunOssResult.java View File

@@ -0,0 +1,29 @@
package com.tuoheng.admin.utils;

import lombok.Data;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/11/23 16:50
*/
@Data
public class AliyunOssResult {

/**
* code:200成功
* code: 400失败
*/
private int code;

/**
* 上传成功的返回url
*/
private String url;

/**
* 提示信息
*/
private String msg;

}

+ 61
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/CronUtils.java View File

@@ -0,0 +1,61 @@
package com.tuoheng.admin.utils;


import org.quartz.CronExpression;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

/**
*
*/
public class CronUtils {

private static final SimpleDateFormat sdf = new SimpleDateFormat("ss mm HH dd MM ?");

/***
* 功能描述:日期转换cron表达式
* @param date
* @return
*/
public static String formatDateByPattern(Date date) {
String formatTimeStr = null;
if (Objects.nonNull(date)) {
formatTimeStr = sdf.format(date);
}
return formatTimeStr;
}

/***
* convert Date to cron, eg "0 07 10 15 1 ?"
* @param date : 时间点
* @return
*/
public static String getCron(Date date) {
return formatDateByPattern(date);
}

public static String getSdf(String datestr) throws ParseException {


// 创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


// 将日期字符串解析为Date对象
Date date = sdf.parse(datestr);

// 创建Cron表达式生成器
CronExpression cronExpression = new CronExpression(String.valueOf(date));

// 获取生成的Cron表达式
String expression = cronExpression.getCronExpression();

return expression;
}


}


+ 40
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/GenerateDroneSerialNumber.java View File

@@ -0,0 +1,40 @@
package com.tuoheng.admin.utils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
* @author chenjiandong
* @description: 生成随机序列号
* @date 2022/11/9 15:59
*/
public class GenerateDroneSerialNumber {

public static String[] chars = new String[] {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
};


public static String generateShortUuid() {
StringBuffer shortBuffer = new StringBuffer();
String uuid = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 8; i++) {
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 34]);
}
return shortBuffer.toString();

}

public static String getDateCode() {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("YYMM");
String sdfDateCode = sdf.format(d);
System.out.println("格式化后的日期年月code:" + sdfDateCode);
return sdfDateCode;
}

}

+ 295
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/HttpURLConnectionUtil.java View File

@@ -0,0 +1,295 @@
package com.tuoheng.admin.utils;
import org.springframework.lang.Nullable;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
* @author ww
* @date 2022/07/8 10:42
*/
public class HttpURLConnectionUtil {

/**
* Http get请求
* @param httpUrl 连接
* @return 响应数据
*/
public static String doGet(String httpUrl){
//链接
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
StringBuffer result = new StringBuffer();
try {
//创建连接
URL url = new URL(httpUrl);
connection = (HttpURLConnection) url.openConnection();
//设置请求方式
connection.setRequestMethod("GET");

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.setRequestProperty("Content-Type", "application/json;charset=utf-8");
connection.setRequestProperty("Authorization", "Basic YWRtaW46cHVibGlj");
connection.setRequestProperty("Cookie", "jenkins-timestamper-offset=-28800000; order=id%20desc; serverType=nginx; ltd_end=-1; pro_end=-1; memSize=7768; bt_user_info=%7B%22status%22%3Atrue%2C%22msg%22%3A%22%u83B7%u53D6%u6210%u529F%21%22%2C%22data%22%3A%7B%22username%22%3A%22139****4314%22%7D%7D; sites_path=/www/wwwroot; distribution=centos8; p-1=1; lang=en-US; force=0; b6e922e8cf76dea67efb3a9292724aee=acc59469-46e3-4576-b216-81b907d80a7f.rB3Plw7uc301E0qFoEBeoP_fL4w; load_search=undefined; remember-me=YWRtaW46MTY1ODIxMDYwODA2MDoxY2ZhOTBhMzFlN2IzZTg1YzM5MWQ3Y2M4MWEzYzVlZmYzM2UzOGQ4MTVjZDZhNTUwNjQ4YjFhMjVlYTM3NWJj; JSESSIONID.fc8332ab=node01bo1jsydkd8ql1oo3srksa2k6w288.node0; screenResolution=1920x1080; b2651c13496a99bc2899b024b99bca4f=b4e55bb9-4a3e-44b6-ae8b-c054e7115182.SEaDzMgHxI4d2CGoGwNPnj3L2y4; request_token=hH1LwVtazrgz4sMI4CE1SwEVpCYgiQcHNp4EfyvPni9C32PJ; soft_remarks=%7B%22list%22%3A%5B%22%u4F01%u4E1A%u7248%u3001%u4E13%u4E1A%u7248%u63D2%u4EF6%22%2C%2215%u5929%u65E0%u7406%u7531%u9000%u6B3E%22%2C%22%u53EF%u66F4%u6362IP%22%2C%22%u5E74%u4ED8%u8D60%u90012%u5F20SSL%u8BC1%u4E66%22%2C%22%u5E74%u4ED8%u8D60%u90011000%u6761%u77ED%u4FE1%22%2C%22%u4F4E%u81F32.43%u5143/%u5929%22%2C%22%u5546%u7528%u9632%u706B%u5899%u6388%u6743%22%2C%22%u5E74%u4ED8%u53EF%u5165%u4F01%u4E1A%u7248%u670D%u52A1%u7FA4%22%2C%22%u4EA7%u54C1%u6388%u6743%u8BC1%u4E66%22%5D%2C%22pro_list%22%3A%5B%22%u4E13%u4E1A%u7248%u63D2%u4EF6%22%2C%2215%u5929%u65E0%u7406%u7531%u9000%u6B3E%22%2C%22%u53EF%u66F4%u6362IP%22%2C%22%u4F4E%u81F31.18%u5143/%u5929%22%2C%22%u5546%u7528%u9632%u706B%u5899%u6388%u6743%22%2C%22%u4EA7%u54C1%u6388%u6743%u8BC1%u4E66%22%5D%2C%22kfqq%22%3A%223007255432%22%2C%22kf%22%3A%22http%3A//q.url.cn/CDfQPS%3F_type%3Dwpa%26qidian%3Dtrue%22%2C%22qun%22%3A%22%22%2C%22kf_list%22%3A%5B%7B%22qq%22%3A%223007255432%22%2C%22kf%22%3A%22http%3A//q.url.cn/CDfQPS%3F_type%3Dwpa%26qidian%3Dtrue%22%7D%2C%7B%22qq%22%3A%222927440070%22%2C%22kf%22%3A%22http%3A//wpa.qq.com/msgrd%3Fv%3D3%26uin%3D2927440070%26site%3Dqq%26menu%3Dyes%26from%3Dmessage%26isappinstalled%3D0%22%7D%5D%2C%22wx_list%22%3A%5B%7B%22ps%22%3A%22%u552E%u524D%u54A8%u8BE2%22%2C%22kf%22%3A%22https%3A//work.weixin.qq.com/kfid/kfc72fcbde93e26a6f3%22%7D%5D%7D; load_page=null; load_type=null");
//设置连接超时时间
connection.setReadTimeout(15000);
//开始连接
connection.connect();
//获取响应数据
if (connection.getResponseCode() == 200) {
//获取返回的数据
is = connection.getInputStream();
if (null != is) {
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
}
}
}
} 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.toString();
}

/**
* Http post请求
* @param httpUrl 连接
* @param param 参数
* @return
*/
public static String doPost(String httpUrl, @Nullable String param) {
StringBuffer result = new StringBuffer();
//连接
HttpURLConnection connection = null;
OutputStream os = null;
InputStream is = null;
BufferedReader br = null;
trustAllHosts();
try {
//创建连接对象
URL url = new URL(httpUrl);
//创建连接
connection = (HttpURLConnection) url.openConnection();
//设置请求方法
connection.setRequestMethod("POST");
//设置连接超时时间
connection.setConnectTimeout(15000);
//设置读取超时时间
connection.setReadTimeout(15000);
//DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//设置是否可读取
connection.setDoOutput(true);
connection.setDoInput(true);
//设置通用的请求属性
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.setRequestProperty("Content-Type", "application/json;charset=utf-8");

//拼装参数
if (null != param && (!param.equals(""))) {
//设置参数
os = connection.getOutputStream();
//拼装参数
os.write(param.getBytes("UTF-8"));
}
//设置权限
//设置请求头等
//开启连接
//connection.connect();
//读取响应
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
if (null != is) {
//br = new BufferedReader(new InputStreamReader(is, "GBK"));
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
result.append("\r\n");
}
}
}

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭连接
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接
connection.disconnect();
}
return result.toString();
}
public static String doPut(String httpUrl, @Nullable String param) {
StringBuffer result = new StringBuffer();
//连接
HttpURLConnection connection = null;
OutputStream os = null;
InputStream is = null;
BufferedReader br = null;
try {
//创建连接对象
URL url = new URL(httpUrl);
//创建连接
connection = (HttpURLConnection) url.openConnection();
//设置请求方法
connection.setRequestMethod("PUT");
//设置连接超时时间
connection.setConnectTimeout(15000);
//设置读取超时时间
connection.setReadTimeout(15000);
//DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
//设置是否可读取
connection.setDoOutput(true);
connection.setDoInput(true);
//设置通用的请求属性
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.setRequestProperty("Content-Type", "application/json;charset=utf-8");

//拼装参数
if (null != param && (!param.equals(""))) {
//设置参数
os = connection.getOutputStream();
//拼装参数
os.write(param.getBytes("UTF-8"));
}
//设置权限
//设置请求头等
//开启连接
//connection.connect();
//读取响应
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
if (null != is) {
//br = new BufferedReader(new InputStreamReader(is, "GBK"));
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String temp = null;
while (null != (temp = br.readLine())) {
result.append(temp);
result.append("\r\n");
}
}
}

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭连接
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接
connection.disconnect();
}
return result.toString();
}
public static void main(String[] args) {
String message = doPost("https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13026194071", "");
System.out.println(message);
}

/**
* Trust every server - dont check for any certificate
*/
private static void trustAllHosts() {
final String TAG = "trustAllHosts";
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Log.i(TAG, "checkClientTrusted");
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//Log.i(TAG, "checkServerTrusted");
}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}

}

+ 66
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/RedisLock.java View File

@@ -0,0 +1,66 @@
package com.tuoheng.admin.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
* redis分布式锁
* @author :小肖
* @date :Created in 2022/2/10 13:28
*/
@Component
@Slf4j
public class RedisLock {

@Autowired
private StringRedisTemplate redisTemplate;

/**
* 加锁
* @param key
* @param value
* @return
*/
public boolean lock(String key,String value){
if(redisTemplate.opsForValue().setIfAbsent(key,value)){
// 加锁成功
return true;
}

// 如果锁过期
String currentValue = redisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) &&
Long.parseLong(currentValue) < System.currentTimeMillis()){

String oldValue = redisTemplate.opsForValue().getAndSet(key,value);
if(!StringUtils.isEmpty(oldValue) &&
oldValue.equals(oldValue)){
return true;
}
}

// 锁已经被其它线程获取
return false;
}

/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key,String value){
try{
String currentValue = redisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) &&
currentValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e){
log.error("【redis 分布式锁】 解锁异常,{}",e);
}
}
}


+ 60
- 0
tuoheng-admin/src/main/java/com/tuoheng/admin/utils/SpringUtil.java View File

@@ -0,0 +1,60 @@
package com.tuoheng.admin.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
* 实现ApplicationContextAware接口,并加入Component注解,让spring扫描到该bean
* 该类用于在普通Java类中注入bean,普通Java类中用@Autowired是无法注入bean的
*/
@Component
public class SpringUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}

/**
* 获取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}

/**
*通过name获取 Bean.
* @param name
* @return
*/
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}

/**
* 通过class获取Bean.
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}

/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}

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

@@ -0,0 +1,217 @@
# 自定义配置
tuoheng:
# 图片域名
# 后期要修改
#image-url: http://images.airport.dev.taauav.com
image-url: #http://192.168.11.22:30079/airport/uploads/

spring:
# 注册中心consul地址
cloud:
consul:
host: 192.168.11.13 # consul 所在服务地址
port: 8500 # consul 服务端口
discovery:
# 是否启用服务发现
enabled: true
# 配置服务注册
register: true
deregister: true
## consul ip地址
hostname: 192.168.11.13
# 注册到consul的服务名称
service-name: ${spring.application.name} # 服务提供者名称
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #实例ID
heartbeat:
enabled: true
prefer-ip-address: true
health-check-path: /actuator/health #健康检查
health-check-interval: 10s
# 配置数据源
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_airport?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
mqtt:
onlineUrl: http://192.168.11.22:18083
#MQTT服务地址,端口号默认11883,如果有多个,用逗号隔开
url: tcp://192.168.11.22:1883
#用户名
username: admin
#密码
password: public
#客户端id(不能重复)
client:
id: provider-id
#MQTT默认的消息推送主题,实际可在调用接口是指定
default:
topic: topic
# Redis数据源
redis:
# 缓存库默认索引0
database: 9
# 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: 100MB
# 上传文件总的最大值
max-request-size: 500MB

#邮件配置
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: /home/airport/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
#踢出之前用户还是之后用户
kickoutAfter: true
# 最大同时在线人数,-1 表示无限制
maxSession: 1
# 代码生成
generate:
# 作者
author: 拓恒
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.tuoheng.system
# 模块名
moduleName: tuoheng-system
# 自动去除表前缀,默认是true
autoRemovePre: true
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_

# 阿里云存储参数配置
aliyun:
oss:
endpoint: "https://oss-cn-shanghai.aliyuncs.com"
accessKeyId: "LTAI5tCMz1nQ3U6jQj8tR8XQ"
accessKeySecret: "qH9CwhQwKhNY2g8MyZnHFhhIYqQgTB"
bucketName: "th-airport"
filedir: "devFile/"

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

@@ -0,0 +1,196 @@
# 自定义配置
tuoheng:
# 图片域名
# 后期要修改
#image-url: http://images.airport.dev.taauav.com
image-url: http://192.168.144.88:30079/

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_airport?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false
username: th
password: th
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
mqtt:
onlineUrl: http://127.0.0.1:18083
#MQTT服务地址,端口号默认11883,如果有多个,用逗号隔开
url: tcp://127.0.0.1:1883
#用户名
username: admin
#密码
password: admin##123
#客户端id(不能重复)
client:
id: provider-id
#MQTT默认的消息推送主题,实际可在调用接口是指定
default:
topic: topic
# Redis数据源
redis:
# 缓存库默认索引0
database: 9
# 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: 100MB
# 上传文件总的最大值
max-request-size: 500MB

#邮件配置
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: /home/airport/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
#踢出之前用户还是之后用户
kickoutAfter: true
# 最大同时在线人数,-1 表示无限制
maxSession: 1
# 代码生成
generate:
# 作者
author: 拓恒
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.tuoheng.system
# 模块名
moduleName: tuoheng-system
# 自动去除表前缀,默认是true
autoRemovePre: true
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_

# 阿里云存储参数配置
aliyun:
oss:
endpoint: "https://oss-cn-shanghai.aliyuncs.com"
accessKeyId: "LTAI5tCMz1nQ3U6jQj8tR8XQ"
accessKeySecret: "qH9CwhQwKhNY2g8MyZnHFhhIYqQgTB"
bucketName: "th-airport"
filedir: "devFile/"

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

@@ -0,0 +1,216 @@
# 自定义配置
tuoheng:
# 图片域名
# 后期要修改
#image-url: http://images.airport.dev.taauav.com
image-url: #http://192.168.11.22:30079/airport/uploads/

spring:
# 注册中心consul地址
cloud:
consul:
host: 172.16.5.12 # consul 所在服务地址
port: 8500 # consul 服务端口
discovery: # 是否启用服务发现
enabled: true
# 配置服务注册
register: true
deregister: true
## consul ip地址
hostname: 172.16.5.12
# 注册到consul的服务名称
service-name: ${spring.application.name} # 服务提供者名称
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #实例ID
heartbeat:
enabled: true
prefer-ip-address: true
health-check-path: /actuator/health #健康检查
health-check-interval: 10s
# 配置数据源
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_airport?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
mqtt:
onlineUrl: https://mqtt.t-aaron.com
#MQTT服务地址,端口号默认11883,如果有多个,用逗号隔开
url: tcp://mqtt.t-aaron.com:10883
#用户名
username: admin
#密码
password: admin##123
#客户端id(不能重复)
client:
id: provider-id
#MQTT默认的消息推送主题,实际可在调用接口是指定
default:
topic: topic
# Redis数据源
redis:
# 缓存库默认索引0
database: 9
# 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: 100MB
# 上传文件总的最大值
max-request-size: 500MB

#邮件配置
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: /home/airport/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
#踢出之前用户还是之后用户
kickoutAfter: true
# 最大同时在线人数,-1 表示无限制
maxSession: 1
# 代码生成
generate:
# 作者
author: 拓恒
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.tuoheng.system
# 模块名
moduleName: tuoheng-system
# 自动去除表前缀,默认是true
autoRemovePre: true
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_

# 阿里云存储参数配置
aliyun:
oss:
endpoint: "https://oss-cn-shanghai.aliyuncs.com"
accessKeyId: "LTAI5tCMz1nQ3U6jQj8tR8XQ"
accessKeySecret: "qH9CwhQwKhNY2g8MyZnHFhhIYqQgTB"
bucketName: "th-airport"
filedir: "prodFile/"

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

@@ -0,0 +1,217 @@
# 自定义配置
tuoheng:
# 图片域名
# 后期要修改
#image-url: http://images.airport.dev.taauav.com
image-url: #http://192.168.11.22:30079/airport/uploads/

spring:
# 注册中心consul地址
cloud:
consul:
host: 172.15.1.11 # consul 所在服务地址
port: 8500 # consul 服务端口
discovery:
# 是否启用服务发现
enabled: true
# 配置服务注册
register: true
deregister: true
## consul ip地址
hostname: 172.15.1.11
# 注册到consul的服务名称
service-name: ${spring.application.name} # 服务提供者名称
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} #实例ID
heartbeat:
enabled: true
prefer-ip-address: true
health-check-path: /actuator/health #健康检查
health-check-interval: 10s
# 配置数据源
datasource:
# 使用阿里的Druid连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
url: jdbc:mysql://rm-uf6z740323e8053pj.mysql.rds.aliyuncs.com:3306/tuoheng_airport?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
mqtt:
onlineUrl: http://106.15.120.154:18083
#MQTT服务地址,端口号默认11883,如果有多个,用逗号隔开
url: tcp://106.15.120.154:1883
#用户名
username: admin
#密码
password: admin##123
#客户端id(不能重复)
client:
id: provider-id
#MQTT默认的消息推送主题,实际可在调用接口是指定
default:
topic: topic
# Redis数据源
redis:
# 缓存库默认索引0
database: 9
# Redis服务器地址
host: r-uf6cdzjifj20jszykr.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: 100MB
# 上传文件总的最大值
max-request-size: 500MB

#邮件配置
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: /home/airport/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
#踢出之前用户还是之后用户
kickoutAfter: true
# 最大同时在线人数,-1 表示无限制
maxSession: 1
# 代码生成
generate:
# 作者
author: 拓恒
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.tuoheng.system
# 模块名
moduleName: tuoheng-system
# 自动去除表前缀,默认是true
autoRemovePre: true
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_

# 阿里云存储参数配置
aliyun:
oss:
endpoint: "https://oss-cn-shanghai.aliyuncs.com"
accessKeyId: "LTAI5tCMz1nQ3U6jQj8tR8XQ"
accessKeySecret: "qH9CwhQwKhNY2g8MyZnHFhhIYqQgTB"
bucketName: "th-airport"
filedir: "testFile/"

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

@@ -0,0 +1,37 @@
# 端口配置
server:
port: 9160

spring:
profiles:
active: @package.environment@
application:
name: tuoheng-airport-platform
main:
allow-bean-definition-overriding: true
mvc:
view:
prefix: classpath:/templates/
suffix: .html
# 服务模块
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
call-setters-on-nulls: true

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

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/data/java/logs/tuoheng_airport_platform/tuoheng_airport.log</file>
<!-- <file>/Users/zhuzs/Documents/file/logs/tuoheng_5gai.log</file>-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- <fileNamePattern>/Users/zhuzs/Documents/file/logs/tuoheng_5gai.log.%d{yyyy-MM-dd}.log</fileNamePattern>-->
<fileNamePattern>/data/java/logs/tuoheng_airport_platform/tuoheng_airport.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<!-- project default level -->
<logger name="com.tuoheng" level="DEBUG" />

<!--log4jdbc -->
<logger name="jdbc.sqltiming" level="DEBUG"/>

<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="rollingFile" />
</root>
</configuration>

+ 26
- 0
tuoheng-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst View File

@@ -0,0 +1,26 @@
com\tuoheng\admin\utils\GenerateDroneSerialNumber.class
com\tuoheng\admin\pojo\entity\Airport.class
com\tuoheng\admin\config\sql\AbstractLikeSqlConverter.class
com\tuoheng\admin\service\mqttService\send\MqttProviderCallBack.class
com\tuoheng\admin\AdminApplication.class
com\tuoheng\admin\utils\CronUtils.class
com\tuoheng\admin\config\sql\ObjectLikeSqlConverter.class
com\tuoheng\admin\enums\ServiceExceptionEnum.class
com\tuoheng\admin\config\sql\MapLikeSqlConverter.class
com\tuoheng\admin\utils\AliyunOSSUtil.class
com\tuoheng\admin\utils\RedisLock.class
com\tuoheng\admin\utils\SpringUtil.class
com\tuoheng\admin\service\impl\AirportServiceImpl.class
com\tuoheng\admin\service\mqttService\HttpURLConnectionUtil$1.class
com\tuoheng\admin\utils\HttpURLConnectionUtil.class
com\tuoheng\admin\utils\AliyunOssResult.class
com\tuoheng\admin\mapper\AirportMapper.class
com\tuoheng\admin\service\mqttService\MqHttp.class
com\tuoheng\admin\service\IAirportService.class
com\tuoheng\admin\service\mqttService\HttpURLConnectionUtil.class
com\tuoheng\admin\enums\MQTTTopicEnum.class
com\tuoheng\admin\service\mqttService\consumer\MqttConsumerCallBack.class
com\tuoheng\admin\utils\HttpURLConnectionUtil$1.class
com\tuoheng\admin\controller\HealthController.class
com\tuoheng\admin\config\sql\MybatisPlusInterceptor.class
com\tuoheng\admin\service\mqttService\send\MqttProviderConfig.class

+ 24
- 0
tuoheng-admin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst View File

@@ -0,0 +1,24 @@
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\AliyunOssResult.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\mqttService\MqHttp.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\mqttService\consumer\MqttConsumerCallBack.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\enums\MQTTTopicEnum.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\enums\ServiceExceptionEnum.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\SpringUtil.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\config\sql\MapLikeSqlConverter.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\config\sql\AbstractLikeSqlConverter.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\mapper\AirportMapper.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\controller\HealthController.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\GenerateDroneSerialNumber.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\config\sql\ObjectLikeSqlConverter.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\pojo\entity\Airport.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\IAirportService.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\mqttService\HttpURLConnectionUtil.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\impl\AirportServiceImpl.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\CronUtils.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\config\sql\MybatisPlusInterceptor.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\HttpURLConnectionUtil.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\AliyunOSSUtil.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\AdminApplication.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\mqttService\send\MqttProviderConfig.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\utils\RedisLock.java
D:\tuohengWorkSpace\tuoheng_airport_platform\tuoheng-admin\src\main\java\com\tuoheng\admin\service\mqttService\send\MqttProviderCallBack.java

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

@@ -0,0 +1,153 @@
<?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>
<!--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>
<!-- 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>
</dependencies>

</project>

+ 148
- 0
tuoheng-common/src/main/java/com/tuoheng/common/advice/CustomExceptionHandler.java View File

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

import com.tuoheng.common.common.SysExceptionEnum;
import com.tuoheng.common.exception.ServiceException;
import com.tuoheng.common.utils.JacksonUtil;
import com.tuoheng.common.utils.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 自定义异常处理器
* @Version: 1.0
*/
@RestControllerAdvice
@Slf4j
public class CustomExceptionHandler {


/**
* 密码校验异常
*/
@ExceptionHandler(BindException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleAuthenticationException(BindException e) {
BindingResult bindingResult = e.getBindingResult();
StringBuilder errorMessage = new StringBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
// errorMessage.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(" ");
errorMessage.append(fieldError.getDefaultMessage()).append(" ");
}
log.error("参数格式错误:{}", errorMessage);
return new ResponseEntity<>(JsonResult.error(JsonResult.ERROR, errorMessage.toString()), HttpStatus.OK);
}

/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<JsonResult<Void>> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return new ResponseEntity<>(JsonResult.error(SysExceptionEnum.SYS_EXCEPTION.getCode(),
e.getMessage()), HttpStatus.BAD_REQUEST);
}

/**
* 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是ConstraintViolationException
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleConstraintViolationException(ConstraintViolationException e) {
String message = e.getConstraintViolations()
.stream()
.map(constraintViolation -> constraintViolation.getMessage())
.collect(Collectors.joining(" "));
log.error("参数格式错误:{}", message);
return new ResponseEntity<>(JsonResult.error(JsonResult.ERROR, message), HttpStatus.OK);
}

/**
* 校验异常处理
* 处理请求参数格式错误 @RequestBody上使用@Valid 实体上使用@NotNull等
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
JsonResult<Void> error = JsonResult.error(SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getCode(),
SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getMessage());
BindingResult result = e.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
if (!CollectionUtils.isEmpty(fieldErrors)) {
error.setMsg(fieldErrors.stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(" ")));
} else {
if (!CollectionUtils.isEmpty(result.getAllErrors())) {
ObjectError currError = result.getAllErrors().get(0);
// error.setMsg(currError.getObjectName() + currError.getDefaultMessage());
error.setMsg(currError.getDefaultMessage());
}
}
log.error("参数格式错误:{}", JacksonUtil.obj2StringPretty(error));
return new ResponseEntity<>(error, HttpStatus.OK);
}

/**
* 校验异常处理
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
log.error("参数格式错误:", e);
return new ResponseEntity<>(JsonResult.error(JsonResult.ERROR, e.getMessage()), HttpStatus.OK);
}

/**
* 系统业务异常处理
*/
@ExceptionHandler(ServiceException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleServiceException(ServiceException e) {
// 打印业务异常日志
log.warn("系统业务逻辑异常,异常状态码 {},异常信息:{}", e.code, e.getMessage());
return new ResponseEntity<>(JsonResult.error(e.getCode(), e.getMsg()), HttpStatus.OK);
}

/**
* 系统数据访问异常处理
*/
@ExceptionHandler(DataAccessException.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleDataAccessException(DataAccessException e) {
log.error("系统数据访问异常,异常信息:", e);
return new ResponseEntity<>(JsonResult.error(SysExceptionEnum.DATAACCESS_EXCEPTION.getCode(),
SysExceptionEnum.DATAACCESS_EXCEPTION.getMessage()), HttpStatus.BAD_REQUEST);
}

/**
* 系统异常处理
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<JsonResult<Void>> handleException(Exception e) {
log.error("系统异常,异常信息:", e);
return new ResponseEntity<>(JsonResult.error(SysExceptionEnum.SYS_EXCEPTION.getCode(),
SysExceptionEnum.SYS_EXCEPTION.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

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

}

+ 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 {
}

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

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

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
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.Date;

/**
* 基类实体对象
*
* @author 牧羊人
* @date 2019/11/28
*/
@Data
public class BaseEntity implements Serializable {

/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;

/**
* 添加人
*/
private Integer createUser;

/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@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;

/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@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;
/**
* 租户id
*/
private Integer tenantId;

}

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

}

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

@@ -0,0 +1,26 @@
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;

/**
* 图片域名赋值
*
* @param url 域名地址
*/
@Value("${tuoheng.image-url}")
public void setImageURL(String url) {
imageURL = url;
}

}

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

@@ -0,0 +1,27 @@
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));
paginationInterceptor.setLimit(Integer.MAX_VALUE);
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;

}

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

@@ -0,0 +1,187 @@
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.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@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;
}
/**
* 使CacheComponent的redisTemplate组件的key使用StringRedisSerializer而非默认的JdkSerializationRedisSerializer
* 避免key出现字节码的情况
* @param factory redis链接
* @return RedisTemplate
*/
/*@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//key使用StringRedisSerializer序列化
redisTemplate.setKeySerializer(redisSerializer);
//value使用jackson2JsonRedisSerializer序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}*/

/**
* 创建Redis消息监听者容器
* @param factory
* @return
*/
@Bean("redisMessageListenerContainer")
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
return container;
}
}

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

}

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

@@ -0,0 +1,103 @@
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";

}

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

@@ -0,0 +1,49 @@
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 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";
}

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


}

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

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

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.tuoheng.common.config.CommonConfig;
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.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

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

/**
* 下载文件
*
* @param fileName 文件名
* @param response
* @return
*/
@GetMapping("/downloadFile/{fileName}")
public OutputStream downloadFile(@PathVariable("fileName") String fileName, HttpServletResponse response) {
// 替换域名
if (fileName.contains(CommonConfig.imageURL)) {
fileName = fileName.replaceAll(CommonConfig.imageURL, "");
}
// 文件地址
String filePath = UploadFileConfig.uploadFolder + fileName;
if (fileName != null) {
//设置文件路径
File file = new File(filePath);
if (file.exists()) {
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
return os;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}

}

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

}

+ 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: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 业务异常枚举
* @Version: 1.0
*/
@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;
}


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

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

import com.tuoheng.common.common.ExceptionInterface;
import com.tuoheng.common.utils.JsonResult;
import lombok.Getter;
import lombok.Setter;

/**
* @Author: 吴彬
* @CreateTime: 2023-05-22 19:09
* @Description: 业务异常类(业务处理时手动抛出异常)
* @Version: 1.0
*/
@Getter
@Setter
public class ServiceException extends RuntimeException {

static final long serialVersionUID = -7034897190745766938L;
/**
* 异常码
*/
public 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 msg
*/
public ServiceException(int code, String msg) {
super(msg);
this.msg = msg;
this.code = code;
}

/**
* 构造器
*
* @param msg
*/
public ServiceException(String msg) {
super(msg);
this.msg = msg;
this.code = JsonResult.ERROR;
}

}


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

}

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

@@ -0,0 +1,71 @@
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) {
Map<String, Object> map = new HashMap<>();
try {
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.put("fileName", nameList.get(nameList.size() - 1));
map.put("fileUrl", imageUrl);
}catch (Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
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();
}

}

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

@@ -0,0 +1,253 @@
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.regex.Matcher;
import java.util.regex.Pattern;

/**
* 公共函数类
*/
public class CommonUtils {

/**
* 获取到图片域名的地址
*
* @param imageUrl
* @return
*/
public static String getImageURL(String imageUrl) {
return CommonConfig.imageURL + imageUrl;
}

/**
* 验证邮箱是否正确
*
* @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;
}

}

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

}

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

@@ -0,0 +1,136 @@
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;

/**
* 时间工具类
*/
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 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 + "分钟";
}

}

+ 61
- 0
tuoheng-common/src/main/java/com/tuoheng/common/utils/DroneCodeUtils.java View File

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

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;

/**
* 生成无人机产品识别码
*/
public class DroneCodeUtils {

public static final String[] X36_ARRAY = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(",");

/**
* @param brand 品牌 eg:THJS
* @param series 系列 eg:Y0
* @param type 型号 eg:A 一代
* @return
*/
public String createDroneCode(String brand, String series, String type) throws InterruptedException {
return brand + series + type + createCode();
}

/**
* 生成不重复的36进制字符串
* @return
* @throws InterruptedException
*/
public String createCode() throws InterruptedException {
String createCode="";
synchronized (this) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmssSS");
SimpleDateFormat dateFormaty = new SimpleDateFormat("yyyy");
String code = dateFormat.format(date);
String nowYear = dateFormaty.format(date);
long addSum = (long) ((Integer.parseInt(nowYear) - 2021) * 12 * Math.pow(10, 11));
Thread.sleep(100);
String string36 = tenTo36(Long.parseLong(code) + addSum);
createCode=nowYear + string36;
}
return createCode;
}

/**
* 十进制转36精制
* @param num
* @return
*/
public static String tenTo36(long num) {
StringBuffer sBuffer = new StringBuffer();
if (num == 0) {
sBuffer.append("0");
}
while (num > 0) {
sBuffer.append(X36_ARRAY[Integer.parseInt(String.valueOf(num % 36))]);
num = num / 36;
}
return sBuffer.reverse().toString();
}
}

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

}

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

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

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

}

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

}

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

@@ -0,0 +1,132 @@
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 = -1;

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

}

+ 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((Collection<String>) 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();
}

}

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

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

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

/**
* 格式化文本, {} 表示占位符<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();
}

}

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

}

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

@@ -0,0 +1,319 @@
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.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;
// 单个文件最大上传大小(10M)
private long fileMaxSize = 1024 * 1024 * 10;
// 最大文件大小(100M)
private long maxSize = 1024 * 1024 * 100;
// 文件保存目录url
private String saveUrl;
// 文件最终的url包括文件名
private List<String> fileUrlList = new ArrayList<>();
// 上传文件原名
private List<String> fileNameList = new ArrayList<>();

/**
* 构造函数
*/
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,kml,mp3,wma,wav,avi,acc,waypoints");
}

/**
* 文件上传
*
* @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);
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);
return result;
}

/**
* 上传验证并初始化目录
*
* @param request
* @return
*/
private String validateFields(HttpServletRequest request, String name) {
String errorInfo = "";
// 获取内容类型
String contentType = request.getContentType();
int contentLength = request.getContentLength();
// 初始化上传路径,不存在则创建
try {
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();
}
}
}catch (Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
}
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 = item.getString();
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);

// 写入文件
try {
File uploadedFile = new File(uploadPath, newFileName);
item.write(uploadedFile);
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
System.out.println("上传失败了!!!");
} catch (Exception e) {
e.printStackTrace();
}
}
return error;
}

/**
* *********************get/set方法*********************************
*/
public String getSaveUrl() {
return saveUrl;
}

public String getUploadPath() {
return uploadPath;
}

public long getMaxSize() {
return maxSize;
}

public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}

public Map<String, String> getExtMap() {
return extMap;
}

public void setExtMap(Map<String, String> extMap) {
this.extMap = extMap;
}

public String getDirName() {
return dirName;
}

public void setDirName(String dirName) {
this.dirName = dirName;
}

public String getTempPath() {
return tempPath;
}

public void setTempPath(String tempPath) {
this.tempPath = tempPath;
}

public List getfileUrlList() {
return fileUrlList;
}

public void setfileUrlList(List fileUrlList) {
this.fileUrlList = fileUrlList;
}
}

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


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

Loading…
Cancel
Save