Initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/logs/
|
||||||
|
target/
|
||||||
|
rebel.xml
|
||||||
|
/.idea/
|
||||||
|
*.iml
|
||||||
|
/static/
|
||||||
28
demo/pom.xml
Normal file
28
demo/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>demo</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-web</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
21
demo/src/main/java/com/tiesheng/demo/DemoApplication.java
Normal file
21
demo/src/main/java/com/tiesheng/demo/DemoApplication.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.tiesheng.demo;
|
||||||
|
|
||||||
|
|
||||||
|
import com.tiesheng.core.EnableTieshengWeb;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@EnableTransactionManagement
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableTieshengWeb
|
||||||
|
public class DemoApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DemoApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.tiesheng.demo.controller;
|
||||||
|
|
||||||
|
import com.tiesheng.annotation.token.TokenIgnore;
|
||||||
|
import com.tiesheng.util.pojos.ApiResp;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/test")
|
||||||
|
public class TestController {
|
||||||
|
|
||||||
|
|
||||||
|
@RequestMapping("/index")
|
||||||
|
@TokenIgnore
|
||||||
|
public ApiResp<String> index() {
|
||||||
|
return ApiResp.respOK("hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
demo/src/main/resources/application.yml
Normal file
22
demo/src/main/resources/application.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
## Spring配置
|
||||||
|
spring:
|
||||||
|
|
||||||
|
## 数据库配置
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://47.96.30.85:3306/com_tiesheng_web?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&queryTimeout=5400&allowMultiQueries=true&serverTimezone=GMT%2B8
|
||||||
|
username: com_tiesheng_web
|
||||||
|
password: 4Xo$XheGFc
|
||||||
|
|
||||||
|
platform:
|
||||||
|
wxmp:
|
||||||
|
app-id: wx7830e1085881b432
|
||||||
|
app-secret: d7757f980b38b5bd8125d45767c40e04
|
||||||
|
|
||||||
|
tiesheng:
|
||||||
|
token:
|
||||||
|
list:
|
||||||
|
"1111":
|
||||||
|
id: "1111"
|
||||||
|
global:
|
||||||
|
version: 2
|
||||||
56
pom.xml
Normal file
56
pom.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>tiesheng</name>
|
||||||
|
<description>杭州铁晟科技有限公司基础依赖</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.6.4</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>demo</module>
|
||||||
|
<module>tiesheng-db-migration</module>
|
||||||
|
<module>tiesheng-login</module>
|
||||||
|
<module>tiesheng-web</module>
|
||||||
|
<module>tiesheng-util</module>
|
||||||
|
<module>tiesheng-poi</module>
|
||||||
|
<module>tiesheng-ding</module>
|
||||||
|
<module>tiesheng-wxmp</module>
|
||||||
|
<module>tiesheng-message</module>
|
||||||
|
<module>tiesheng-encrypt</module>
|
||||||
|
<module>tiesheng-annotation</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<name>zeng_wenhao</name>
|
||||||
|
<email>980287353@qq.com</email>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
17
publish.sh
Executable file
17
publish.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
VERSION=$1
|
||||||
|
MESSAGE=$2
|
||||||
|
|
||||||
|
|
||||||
|
## 推送现有代码
|
||||||
|
git add .
|
||||||
|
git commit -m "publish $VERSION"
|
||||||
|
git push
|
||||||
|
|
||||||
|
## 打标签并推送
|
||||||
|
git tag -a $VERSION -m $MESSAGE
|
||||||
|
if [ $? -ne 0 ];then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git push origin $VERSION
|
||||||
20
tiesheng-annotation/pom.xml
Normal file
20
tiesheng-annotation/pom.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-annotation</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.tiesheng.annotation.encrypt;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface EncryptedRespBody {
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.tiesheng.annotation.token;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TokenIgnore {
|
||||||
|
}
|
||||||
34
tiesheng-db-migration/pom.xml
Normal file
34
tiesheng-db-migration/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-db-migration</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.tiesheng.migration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@ComponentScan({
|
||||||
|
"com.tiesheng.migration.**.*"
|
||||||
|
})
|
||||||
|
public class MigrationAutoImportSelector {
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.tiesheng.migration.config;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表版本对比
|
||||||
|
*
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "tiesheng.db-migration")
|
||||||
|
public class DbMigrationConfig {
|
||||||
|
|
||||||
|
private String table = "ts_db_migration";
|
||||||
|
private List<String> locations = CollUtil.newArrayList("classpath:db/migration/*.sql");
|
||||||
|
private String ignoreSqls = "drop,delete";
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getTable() {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTable(String table) {
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getLocations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocations(List<String> locations) {
|
||||||
|
this.locations = locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIgnoreSqls() {
|
||||||
|
return ignoreSqls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIgnoreSqls(String ignoreSqls) {
|
||||||
|
this.ignoreSqls = ignoreSqls;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package com.tiesheng.migration.service;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.lang.func.VoidFunc1;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.db.Db;
|
||||||
|
import cn.hutool.db.Entity;
|
||||||
|
import cn.hutool.log.LogFactory;
|
||||||
|
import com.tiesheng.migration.config.DbMigrationConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表版本对比
|
||||||
|
*
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DbMigrationInitializer implements ServletContextInitializer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DataSource dataSource;
|
||||||
|
@Autowired
|
||||||
|
DbMigrationConfig dbMigrationConfig;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(ServletContext servletContext) {
|
||||||
|
try {
|
||||||
|
startDeal();
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始处理数据
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void startDeal() throws Exception {
|
||||||
|
Db coreDb = Db.use(dataSource);
|
||||||
|
checkDbMigration(coreDb);
|
||||||
|
|
||||||
|
PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
|
||||||
|
for (String location : dbMigrationConfig.getLocations()) {
|
||||||
|
Resource[] resources = patternResolver.getResources(location);
|
||||||
|
if (ArrayUtil.isEmpty(resources)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
migrationByResource(resource, coreDb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据资源合并数据库结构
|
||||||
|
*
|
||||||
|
* @param resource
|
||||||
|
* @param db
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void migrationByResource(Resource resource, Db db) throws Exception {
|
||||||
|
InputStream inputStream = resource.getInputStream();
|
||||||
|
String readUtf8 = IoUtil.readUtf8(inputStream);
|
||||||
|
String filename = resource.getFilename();
|
||||||
|
Checksum checksum = IoUtil.checksum(resource.getInputStream(), null);
|
||||||
|
Entity entity = fileChecksum(db, filename, checksum.getValue());
|
||||||
|
if (entity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.tx((VoidFunc1<Db>) parameter -> {
|
||||||
|
List<String> split = StrUtil.split(readUtf8, ";");
|
||||||
|
for (String sql : split) {
|
||||||
|
sql = sql.trim();
|
||||||
|
if (StrUtil.isEmpty(sql)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (StrUtil.startWithAnyIgnoreCase(sql, StrUtil.splitToArray(dbMigrationConfig.getIgnoreSqls(), ","))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parameter.execute(sql);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.set("checksum", checksum.getValue());
|
||||||
|
entity.set("update_time", DateUtil.date());
|
||||||
|
db.update(entity, Entity.create(dbMigrationConfig.getTable()).set("id", entity.get("id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否执行
|
||||||
|
*
|
||||||
|
* @param db
|
||||||
|
* @param fileName
|
||||||
|
* @param checksum
|
||||||
|
*/
|
||||||
|
private Entity fileChecksum(Db db, String fileName, long checksum) throws SQLException {
|
||||||
|
Entity entity = db.get(dbMigrationConfig.getTable(), "file", fileName);
|
||||||
|
if (entity == null) {
|
||||||
|
entity = new Entity(dbMigrationConfig.getTable());
|
||||||
|
entity.set("id", IdUtil.getSnowflakeNextId());
|
||||||
|
entity.set("create_time", DateUtil.date());
|
||||||
|
entity.set("update_time", DateUtil.date());
|
||||||
|
entity.set("is_deleted", 0);
|
||||||
|
entity.set("file", fileName);
|
||||||
|
entity.set("checksum", 0);
|
||||||
|
db.insert(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.getLong("checksum") != checksum) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查数据库是否存在
|
||||||
|
*
|
||||||
|
* @param db
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private void checkDbMigration(Db db) throws SQLException {
|
||||||
|
try {
|
||||||
|
db.count(Entity.create(dbMigrationConfig.getTable()));
|
||||||
|
} catch (SQLException ignored) {
|
||||||
|
db.execute("CREATE TABLE `" + dbMigrationConfig.getTable() + "` (\n" +
|
||||||
|
" `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,\n" +
|
||||||
|
" `create_time` datetime(0) NOT NULL,\n" +
|
||||||
|
" `update_time` datetime(0) NOT NULL,\n" +
|
||||||
|
" `is_deleted` int(6) NOT NULL DEFAULT 0,\n" +
|
||||||
|
" `file` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件名称',\n" +
|
||||||
|
" `checksum` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件checksum',\n" +
|
||||||
|
" PRIMARY KEY (`id`) USING BTREE\n" +
|
||||||
|
") ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'db-合并' ROW_FORMAT = Dynamic;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
tiesheng-ding/pom.xml
Normal file
41
tiesheng-ding/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-ding</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-annotation</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
package com.tiesheng.ding.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import cn.hutool.log.LogFactory;
|
||||||
|
import com.tiesheng.ding.pojos.*;
|
||||||
|
import com.tiesheng.util.TimedCacheHelper;
|
||||||
|
import com.tiesheng.util.exception.ApiException;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "tiesheng.ding")
|
||||||
|
public class DingConfig {
|
||||||
|
|
||||||
|
private static final String CACHE_ACCESS_TOKEN = "cache_ding_access_token_";
|
||||||
|
private static final String CACHE_JSAPI_TICKET = "cache_ding_jsapi_ticket_";
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, DingConfigBean> configs = MapUtil.newHashMap();
|
||||||
|
private DingConfigBean global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个DingConfigBean
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DingConfigBean getConfigBean(String service) {
|
||||||
|
DingConfigBean bean = configs.get(service);
|
||||||
|
if (bean == null) {
|
||||||
|
bean = global;
|
||||||
|
}
|
||||||
|
if (bean == null) {
|
||||||
|
throw new ApiException("该服务未配置钉钉授权");
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 逻辑方法
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取accessToken
|
||||||
|
*
|
||||||
|
* @return accessToken
|
||||||
|
* @see <a href="https://open.dingtalk.com/document/orgapp-server/obtain-orgapp-token" />
|
||||||
|
*/
|
||||||
|
private String getAccessToken(String service) {
|
||||||
|
DingConfigBean dingConfigBean = getConfigBean(service);
|
||||||
|
String accessToken = TimedCacheHelper.getTimedCache().get(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), false);
|
||||||
|
if (!StrUtil.isEmpty(accessToken)) {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新获取新的token
|
||||||
|
Map<String, Object> query = new HashMap<>(10);
|
||||||
|
query.put("appkey", dingConfigBean.getAppKey());
|
||||||
|
query.put("appsecret", dingConfigBean.getAppSecret());
|
||||||
|
String response = HttpUtil.get("https://oapi.dingtalk.com/gettoken", query);
|
||||||
|
JSONObject respJson = JSONUtil.parseObj(response);
|
||||||
|
accessToken = respJson.getStr("access_token");
|
||||||
|
TimedCacheHelper.getTimedCache().put(CACHE_ACCESS_TOKEN + dingConfigBean.getAppKey(), accessToken, respJson.getLong("expires_in"));
|
||||||
|
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取jsapi_ticket
|
||||||
|
*
|
||||||
|
* @return ticket
|
||||||
|
* @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-jsapi_ticket" />
|
||||||
|
*/
|
||||||
|
private String getJsapiTicket(String service) {
|
||||||
|
DingConfigBean dingConfigBean = getConfigBean(service);
|
||||||
|
String jsapiTicket = TimedCacheHelper.getTimedCache().get(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), false);
|
||||||
|
if (StrUtil.isEmpty(jsapiTicket)) {
|
||||||
|
String response = HttpUtil.get("https://oapi.dingtalk.com/get_jsapi_ticket?access_token=" + getAccessToken(service));
|
||||||
|
LogFactory.get().info("getJsapiTicket: " + response);
|
||||||
|
JSONObject respJson = JSONUtil.parseObj(response);
|
||||||
|
if (!Objects.equals(respJson.getStr("errcode"), "0")) {
|
||||||
|
throw new ApiException(respJson.getStr("errmsg"));
|
||||||
|
}
|
||||||
|
jsapiTicket = respJson.getStr("ticket");
|
||||||
|
TimedCacheHelper.getTimedCache().put(CACHE_JSAPI_TICKET + dingConfigBean.getAppKey(), jsapiTicket, respJson.getLong("expires_in"));
|
||||||
|
}
|
||||||
|
return jsapiTicket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成jssdk配置
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DingJsapiSignature createJsapiSignature(String service, String url) {
|
||||||
|
DingConfigBean dingConfigBean = getConfigBean(service);
|
||||||
|
if (StrUtil.contains(url, "#")) {
|
||||||
|
url = StrUtil.sub(url, 0, StrUtil.indexOf(url, '#'));
|
||||||
|
}
|
||||||
|
return DingJsapiSignature.create(dingConfigBean.getCorpId(), dingConfigBean.getAgentId(), url, getJsapiTicket(service));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过code获取userId
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
* @see <a href="https://open.dingtalk.com/document/isvapp-server/obtain-the-userid-of-a-user-by-using-the-log-free"></a>
|
||||||
|
*/
|
||||||
|
public String getUserIdByCode(String service, String code) {
|
||||||
|
String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token=" + getAccessToken(service))
|
||||||
|
.body(JSONUtil.createObj().putOpt("code", code).toString()).execute().body();
|
||||||
|
return JSONUtil.parseObj(response).getJSONObject("result").getStr("userid");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过userId获取用户详情
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param ddUserId
|
||||||
|
* @return
|
||||||
|
* @see <a href="https://open.dingtalk.com/document/isvapp-server/query-user-details"></a>
|
||||||
|
*/
|
||||||
|
public DingUserInfo topapiV2UserGet(String service, String ddUserId) {
|
||||||
|
DingConfigBean dingConfigBean = getConfigBean(service);
|
||||||
|
String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/get?access_token=" + getAccessToken(service))
|
||||||
|
.body(JSONUtil.createObj().putOpt("userid", ddUserId).toString()).execute().body();
|
||||||
|
JSONObject resultJson = JSONUtil.parseObj(response).getJSONObject("result");
|
||||||
|
DingUserInfo userInfo = JSONUtil.toBean(resultJson, DingUserInfo.class);
|
||||||
|
|
||||||
|
// 设置一下job_number
|
||||||
|
userInfo.setJobNumber(resultJson.getStr("job_number"));
|
||||||
|
userInfo.setAppId(dingConfigBean.getAppKey());
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取部门列表
|
||||||
|
*
|
||||||
|
* @param deptId 部门id
|
||||||
|
* @return
|
||||||
|
* @see <a href='https://open.dingtalk.com/document/orgapp-server/obtain-the-department-list-v2'></a>
|
||||||
|
*/
|
||||||
|
public List<DingDeptVo> topapiV2DepartmentListsub(String service, String deptId) {
|
||||||
|
String token = getAccessToken(service);
|
||||||
|
if (StrUtil.isEmpty(token)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=" + token)
|
||||||
|
.body(JSONUtil.createObj().putOpt("dept_id", deptId).toString()).execute().body();
|
||||||
|
DingResponse<List<DingDeptVo>> result = JSONUtil.toBean(response, new TypeReference<DingResponse<List<DingDeptVo>>>() {
|
||||||
|
}.getType(), true);
|
||||||
|
|
||||||
|
if (!result.isOk()) {
|
||||||
|
result.setResult(new ArrayList<>());
|
||||||
|
}
|
||||||
|
return result.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取部门用户详情
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @see <a href='https://open.dingtalk.com/document/orgapp-server/queries-the-complete-information-of-a-department-user'></a>
|
||||||
|
*/
|
||||||
|
public DingUserListVo topapiV2UserList(String service, String deptId, Integer cursor) {
|
||||||
|
String token = getAccessToken(service);
|
||||||
|
if (StrUtil.isEmpty(token)) {
|
||||||
|
return DingUserListVo.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
String response = HttpRequest.post("https://oapi.dingtalk.com/topapi/v2/user/list?access_token=" + token)
|
||||||
|
.body(JSONUtil.createObj().putOpt("dept_id", deptId).putOpt("cursor", cursor).putOpt("size", 100).toString())
|
||||||
|
.execute().body();
|
||||||
|
DingResponse<DingUserListVo> result = JSONUtil.toBean(response, new TypeReference<DingResponse<DingUserListVo>>() {
|
||||||
|
}.getType(), true);
|
||||||
|
if (!result.isOk()) {
|
||||||
|
result.setResult(DingUserListVo.fail());
|
||||||
|
}
|
||||||
|
return result.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步钉钉的通讯录
|
||||||
|
*
|
||||||
|
* @param deptVo 为null时从第一级获取
|
||||||
|
* @param consumer 回调
|
||||||
|
*/
|
||||||
|
public void syncDeptUser(String service, DingDeptVo deptVo, Consumer<DingUserInfo> consumer) {
|
||||||
|
|
||||||
|
if (deptVo == null) {
|
||||||
|
deptVo = new DingDeptVo();
|
||||||
|
deptVo.setDeptId("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步当前部门的用户
|
||||||
|
DingUserListVo userListVo = new DingUserListVo();
|
||||||
|
userListVo.setNextCursor(0);
|
||||||
|
userListVo.setHasMore(true);
|
||||||
|
while (userListVo.getHasMore()) {
|
||||||
|
userListVo = topapiV2UserList(service, deptVo.getDeptId(), userListVo.getNextCursor());
|
||||||
|
for (DingUserInfo dingUserInfo : userListVo.getList()) {
|
||||||
|
dingUserInfo.setDeptVo(deptVo);
|
||||||
|
consumer.accept(dingUserInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步下级部门
|
||||||
|
List<DingDeptVo> dingDeptVos = topapiV2DepartmentListsub(service, deptVo.getDeptId());
|
||||||
|
for (DingDeptVo dingDeptVo : dingDeptVos) {
|
||||||
|
syncDeptUser(service, dingDeptVo, consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送工作通知-
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param content
|
||||||
|
* @param actionUrl
|
||||||
|
* @param userIds
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String messageNotification(String service, String title, String content, String actionUrl, List<String> userIds) {
|
||||||
|
|
||||||
|
if (CollUtil.isEmpty(userIds)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
DingConfigBean configBean = getConfigBean(service);
|
||||||
|
|
||||||
|
JSONObject actionCard = new JSONObject();
|
||||||
|
actionCard.set("title", title);
|
||||||
|
actionCard.set("markdown", "### " + title + "\n" + content);
|
||||||
|
actionCard.set("single_title", "点击查看");
|
||||||
|
actionCard.set("single_url", actionUrl);
|
||||||
|
|
||||||
|
JSONObject msg = new JSONObject();
|
||||||
|
msg.set("msgtype", "action_card");
|
||||||
|
msg.set("action_card", actionCard);
|
||||||
|
|
||||||
|
HashMap<String, Object> body = new HashMap<>(10);
|
||||||
|
body.put("agent_id", configBean.getAgentId());
|
||||||
|
body.put("userid_list", CollUtil.join(userIds, ","));
|
||||||
|
body.put("msg", msg);
|
||||||
|
return HttpUtil.post("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=" + getAccessToken(service),
|
||||||
|
JSONUtil.toJsonStr(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Map<String, DingConfigBean> getConfigs() {
|
||||||
|
return configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigs(Map<String, DingConfigBean> configs) {
|
||||||
|
this.configs = configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DingConfigBean getGlobal() {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGlobal(DingConfigBean global) {
|
||||||
|
this.global = global;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.tiesheng.ding.controller;
|
||||||
|
|
||||||
|
import com.tiesheng.annotation.token.TokenIgnore;
|
||||||
|
import com.tiesheng.ding.config.DingConfig;
|
||||||
|
import com.tiesheng.ding.pojos.DingJsapiSignature;
|
||||||
|
import com.tiesheng.util.pojos.ApiResp;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/platform/ding")
|
||||||
|
public class PlatformDingController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DingConfig dingConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉授权jssdk
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/jssdk/{service}")
|
||||||
|
@TokenIgnore
|
||||||
|
public ApiResp<DingJsapiSignature> dingJssdk(@PathVariable String service, String url) {
|
||||||
|
DingJsapiSignature jsapiSignature = dingConfig.createJsapiSignature(service, url);
|
||||||
|
return ApiResp.respOK(jsapiSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
public class DingConfigBean {
|
||||||
|
|
||||||
|
private String corpId;
|
||||||
|
private String agentId;
|
||||||
|
private String appKey;
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getCorpId() {
|
||||||
|
return corpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorpId(String corpId) {
|
||||||
|
this.corpId = corpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAgentId() {
|
||||||
|
return agentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAgentId(String agentId) {
|
||||||
|
this.agentId = agentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppKey() {
|
||||||
|
return appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppKey(String appKey) {
|
||||||
|
this.appKey = appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppSecret() {
|
||||||
|
return appSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppSecret(String appSecret) {
|
||||||
|
this.appSecret = appSecret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
public class DingDeptVo {
|
||||||
|
|
||||||
|
private String deptId;
|
||||||
|
private String name;
|
||||||
|
private String parentId;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getDeptId() {
|
||||||
|
return deptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeptId(String deptId) {
|
||||||
|
this.deptId = deptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jssdk配置
|
||||||
|
*
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
public class DingJsapiSignature {
|
||||||
|
|
||||||
|
private String corpId;
|
||||||
|
private String agentId;
|
||||||
|
private String nonceStr;
|
||||||
|
private long timestamp;
|
||||||
|
private String url;
|
||||||
|
private String signature;
|
||||||
|
|
||||||
|
public static DingJsapiSignature create(String corpId, String agentId, String url, String ticket) {
|
||||||
|
DingJsapiSignature wxJsapiSignature = new DingJsapiSignature();
|
||||||
|
wxJsapiSignature.setCorpId(corpId);
|
||||||
|
wxJsapiSignature.setAgentId(agentId);
|
||||||
|
wxJsapiSignature.setUrl(url);
|
||||||
|
wxJsapiSignature.setTimestamp(DateUtil.currentSeconds());
|
||||||
|
wxJsapiSignature.setNonceStr(RandomUtil.randomString(10));
|
||||||
|
|
||||||
|
// 生成签名
|
||||||
|
String builder = "jsapi_ticket=" + ticket +
|
||||||
|
"&noncestr=" + wxJsapiSignature.getNonceStr() +
|
||||||
|
"×tamp=" + wxJsapiSignature.getTimestamp() +
|
||||||
|
"&url=" + wxJsapiSignature.getUrl();
|
||||||
|
wxJsapiSignature.setSignature(SecureUtil.sha256(builder));
|
||||||
|
|
||||||
|
return wxJsapiSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public String getCorpId() {
|
||||||
|
return corpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorpId(String corpId) {
|
||||||
|
this.corpId = corpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAgentId() {
|
||||||
|
return agentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAgentId(String agentId) {
|
||||||
|
this.agentId = agentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNonceStr() {
|
||||||
|
return nonceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNonceStr(String nonceStr) {
|
||||||
|
this.nonceStr = nonceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignature(String signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class DingResponse<T> {
|
||||||
|
|
||||||
|
private String errcode;
|
||||||
|
private String errmsg;
|
||||||
|
private T result;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 逻辑方法
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public boolean isOk() {
|
||||||
|
return Objects.equals(errcode, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getErrcode() {
|
||||||
|
return errcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrcode(String errcode) {
|
||||||
|
this.errcode = errcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrmsg() {
|
||||||
|
return errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrmsg(String errmsg) {
|
||||||
|
this.errmsg = errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(T result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
public class DingUserInfo {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String userid;
|
||||||
|
private String avatar;
|
||||||
|
private String mobile;
|
||||||
|
private String email;
|
||||||
|
private String jobNumber;
|
||||||
|
private String nickname;
|
||||||
|
private String title;
|
||||||
|
private String remark;
|
||||||
|
private String unionid;
|
||||||
|
private String appId;
|
||||||
|
private List<String> deptIdList;
|
||||||
|
private boolean leader;
|
||||||
|
private boolean boss;
|
||||||
|
private boolean admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该用户所在的部门
|
||||||
|
*/
|
||||||
|
private DingDeptVo deptVo;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnionid() {
|
||||||
|
return unionid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnionid(String unionid) {
|
||||||
|
this.unionid = unionid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserid() {
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserid(String userid) {
|
||||||
|
this.userid = userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatar() {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatar(String avatar) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMobile() {
|
||||||
|
return mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMobile(String mobile) {
|
||||||
|
this.mobile = mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJobNumber() {
|
||||||
|
return jobNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJobNumber(String jobNumber) {
|
||||||
|
this.jobNumber = jobNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickname(String nickname) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemark() {
|
||||||
|
return remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemark(String remark) {
|
||||||
|
this.remark = remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DingDeptVo getDeptVo() {
|
||||||
|
return deptVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeptVo(DingDeptVo deptVo) {
|
||||||
|
this.deptVo = deptVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeader() {
|
||||||
|
return leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeader(boolean leader) {
|
||||||
|
this.leader = leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBoss() {
|
||||||
|
return boss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBoss(boolean boss) {
|
||||||
|
this.boss = boss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdmin() {
|
||||||
|
return admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdmin(boolean admin) {
|
||||||
|
this.admin = admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDeptIdList() {
|
||||||
|
return deptIdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeptIdList(List<String> deptIdList) {
|
||||||
|
this.deptIdList = deptIdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppId() {
|
||||||
|
return appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppId(String appId) {
|
||||||
|
this.appId = appId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.tiesheng.ding.pojos;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DingUserListVo {
|
||||||
|
|
||||||
|
private Integer nextCursor;
|
||||||
|
private Boolean hasMore;
|
||||||
|
private List<DingUserInfo> list;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败数据
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DingUserListVo fail() {
|
||||||
|
DingUserListVo listVo = new DingUserListVo();
|
||||||
|
listVo.setHasMore(false);
|
||||||
|
listVo.setNextCursor(0);
|
||||||
|
listVo.setList(new ArrayList<>());
|
||||||
|
return listVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Integer getNextCursor() {
|
||||||
|
return nextCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextCursor(Integer nextCursor) {
|
||||||
|
this.nextCursor = nextCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getHasMore() {
|
||||||
|
return hasMore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasMore(Boolean hasMore) {
|
||||||
|
this.hasMore = hasMore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DingUserInfo> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<DingUserInfo> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
tiesheng-encrypt/pom.xml
Normal file
47
tiesheng-encrypt/pom.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-encrypt</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
|
<version>1.68</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-annotation</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.tiesheng.encrypt;
|
||||||
|
|
||||||
|
|
||||||
|
import com.tiesheng.encrypt.config.EncryptConfig;
|
||||||
|
import com.tiesheng.encrypt.config.EncryptRequestBodyAdvice;
|
||||||
|
import com.tiesheng.encrypt.config.EncryptResponseBodyAdvice;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackageClasses = {
|
||||||
|
EncryptConfig.class,
|
||||||
|
EncryptRequestBodyAdvice.class,
|
||||||
|
EncryptResponseBodyAdvice.class,
|
||||||
|
})
|
||||||
|
public class EnableEncryptConfig {
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.tiesheng.encrypt.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "tiesheng.encrypt")
|
||||||
|
public class EncryptConfig {
|
||||||
|
|
||||||
|
public String publicD;
|
||||||
|
public String privateQ;
|
||||||
|
private boolean enable = false;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public boolean isEnable() {
|
||||||
|
return enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnable(boolean enable) {
|
||||||
|
this.enable = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicD() {
|
||||||
|
return publicD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicD(String publicD) {
|
||||||
|
this.publicD = publicD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateQ() {
|
||||||
|
return privateQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateQ(String privateQ) {
|
||||||
|
this.privateQ = privateQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.tiesheng.encrypt.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.ECKeyUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class EncryptRequestBodyAdvice implements RequestBodyAdvice {
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
EncryptConfig encryptConfig;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
|
||||||
|
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
|
||||||
|
if (!encryptConfig.isEnable()) {
|
||||||
|
return inputMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new DecryptHttpInputMessage(inputMessage, encryptConfig.getPrivateQ());
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||||
|
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||||
|
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class DecryptHttpInputMessage implements HttpInputMessage {
|
||||||
|
|
||||||
|
private HttpHeaders headers;
|
||||||
|
private InputStream body;
|
||||||
|
|
||||||
|
DecryptHttpInputMessage(HttpInputMessage inputMessage, String privateQ) throws Exception {
|
||||||
|
this.headers = inputMessage.getHeaders();
|
||||||
|
String bodyStr = IoUtil.read(inputMessage.getBody(), CharsetUtil.CHARSET_UTF_8);
|
||||||
|
String encryptData = JSONUtil.parseObj(bodyStr).getStr("encryptData");
|
||||||
|
if (!StrUtil.isEmpty(encryptData)) {
|
||||||
|
// 部分语言加密之后缺少04前缀,如果解密失败,可尝试增加04
|
||||||
|
ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toSm2PrivateParams(privateQ);
|
||||||
|
ECPublicKeyParameters publicKeyParameters = ECKeyUtil.getPublicParams(privateKeyParameters);
|
||||||
|
SM2 sm2 = SmUtil.sm2(privateKeyParameters, publicKeyParameters);
|
||||||
|
String decrypt = sm2.decryptStr(encryptData, KeyType.PrivateKey);
|
||||||
|
this.body = IoUtil.toStream(decrypt, Charset.defaultCharset());
|
||||||
|
} else {
|
||||||
|
this.body = IoUtil.toStream(bodyStr, Charset.defaultCharset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
return this.headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.tiesheng.encrypt.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.ECKeyUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import cn.hutool.log.LogFactory;
|
||||||
|
import com.tiesheng.annotation.encrypt.EncryptedRespBody;
|
||||||
|
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServerHttpResponse;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
EncryptConfig encryptConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends
|
||||||
|
HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||||
|
|
||||||
|
if (!encryptConfig.isEnable()) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean encrypt = AnnotationUtil.getAnnotation(returnType.getContainingClass(), EncryptedRespBody.class) != null;
|
||||||
|
if (!encrypt) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String content = JSONUtil.toJsonStr(body);
|
||||||
|
String respData = JSONUtil.parseObj(content).getStr("data");
|
||||||
|
|
||||||
|
if (StrUtil.isEmpty(respData)) {
|
||||||
|
// 无需加密
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject resp = JSONUtil.parseObj(content);
|
||||||
|
resp.set("encrypted", true);
|
||||||
|
if (resp.getInt("code") == 200) {
|
||||||
|
// 用公钥进行加密
|
||||||
|
ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toSm2PrivateParams(encryptConfig.getPrivateQ());
|
||||||
|
ECPublicKeyParameters publicKeyParameters = ECKeyUtil.getPublicParams(privateKeyParameters);
|
||||||
|
SM2 sm2 = SmUtil.sm2(privateKeyParameters, publicKeyParameters);
|
||||||
|
String decrypt = sm2.encryptHex(respData, KeyType.PublicKey);
|
||||||
|
resp.set("data", decrypt.substring(2));
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
} catch (Exception var17) {
|
||||||
|
LogFactory.get().info("加密数据异常", var17);
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
tiesheng-login/pom.xml
Normal file
35
tiesheng-login/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-login</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-ding</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-wxmp</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.tiesheng.login.config.zust;
|
||||||
|
|
||||||
|
public class CasLoginDTO {
|
||||||
|
|
||||||
|
private String data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工号
|
||||||
|
*/
|
||||||
|
private String casUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户容器
|
||||||
|
*/
|
||||||
|
private String casUserContainerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户姓名
|
||||||
|
*/
|
||||||
|
private String casUserCn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户别名
|
||||||
|
*/
|
||||||
|
private String casUserAlias;
|
||||||
|
/**
|
||||||
|
* 用户所在组
|
||||||
|
*/
|
||||||
|
private String casUserMemberOf;
|
||||||
|
/**
|
||||||
|
* 用户性别
|
||||||
|
*/
|
||||||
|
private String casUserGender;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUser() {
|
||||||
|
return casUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUser(String casUser) {
|
||||||
|
this.casUser = casUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUserContainerId() {
|
||||||
|
return casUserContainerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUserContainerId(String casUserContainerId) {
|
||||||
|
this.casUserContainerId = casUserContainerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUserCn() {
|
||||||
|
return casUserCn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUserCn(String casUserCn) {
|
||||||
|
this.casUserCn = casUserCn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUserAlias() {
|
||||||
|
return casUserAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUserAlias(String casUserAlias) {
|
||||||
|
this.casUserAlias = casUserAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUserMemberOf() {
|
||||||
|
return casUserMemberOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUserMemberOf(String casUserMemberOf) {
|
||||||
|
this.casUserMemberOf = casUserMemberOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCasUserGender() {
|
||||||
|
return casUserGender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCasUserGender(String casUserGender) {
|
||||||
|
this.casUserGender = casUserGender;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
tiesheng-message/pom.xml
Normal file
35
tiesheng-message/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-message</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-ding</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-wxmp</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
35
tiesheng-poi/pom.xml
Normal file
35
tiesheng-poi/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-poi</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
<version>3.1.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.tiesheng.poi.read;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.read.listener.ReadListener;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class PoiReadBase {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* @param readListener
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> String read(File file, ReadListener<T> readListener) {
|
||||||
|
Class<?> aClass = ClassUtil.getTypeArgument(readListener.getClass(), 0);
|
||||||
|
EasyExcel.read(file, aClass, readListener).autoTrim(true).headRowNumber(1).sheet().doRead();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.tiesheng.poi.write;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.annotation.write.style.*;
|
||||||
|
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
|
||||||
|
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
|
||||||
|
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@HeadRowHeight(24)
|
||||||
|
@HeadFontStyle(fontHeightInPoints = 13)
|
||||||
|
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT)
|
||||||
|
@ContentRowHeight(20)
|
||||||
|
@ContentFontStyle(fontHeightInPoints = 12)
|
||||||
|
@ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER)
|
||||||
|
@ColumnWidth(20)
|
||||||
|
public class PoiWriteBase {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出数据
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> boolean export(List<T> list, String absPath, String sheetName) {
|
||||||
|
Class<?> aClass = ClassUtil.getTypeArgument(list.getClass(), 0);
|
||||||
|
EasyExcel.write(absPath, aClass).excelType(ExcelTypeEnum.XLSX)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(list);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
tiesheng-util/pom.xml
Normal file
35
tiesheng-util/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.10</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- fastJson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>1.2.47</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.tiesheng.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import com.tiesheng.util.exception.ApiException;
|
||||||
|
|
||||||
|
public class PasswordUtils {
|
||||||
|
|
||||||
|
private static final int PREFIX_SIZE = 8;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取加密密码
|
||||||
|
*
|
||||||
|
* @param password
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String buildPassword(String password) {
|
||||||
|
String prefix = RandomUtil.randomString(PREFIX_SIZE);
|
||||||
|
return prefix + SecureUtil.sha1(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证密码
|
||||||
|
*
|
||||||
|
* @param userInput
|
||||||
|
* @param encrypted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static void verifyPassword(String userInput, String encrypted) {
|
||||||
|
String userEncrypted = buildPassword(userInput);
|
||||||
|
|
||||||
|
userEncrypted = StrUtil.subSuf(userEncrypted, PREFIX_SIZE);
|
||||||
|
encrypted = StrUtil.subSuf(encrypted, PREFIX_SIZE);
|
||||||
|
|
||||||
|
if (!StrUtil.equals(userEncrypted, encrypted)) {
|
||||||
|
throw new ApiException("账号或密码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.tiesheng.util;
|
||||||
|
|
||||||
|
import cn.hutool.cache.CacheUtil;
|
||||||
|
import cn.hutool.cache.impl.TimedCache;
|
||||||
|
|
||||||
|
public class TimedCacheHelper {
|
||||||
|
|
||||||
|
private static volatile TimedCache<String, String> timedCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个默认2分钟过期的缓存
|
||||||
|
*/
|
||||||
|
public static TimedCache<String, String> getTimedCache() {
|
||||||
|
if (timedCache == null) {
|
||||||
|
synchronized (TimedCacheHelper.class) {
|
||||||
|
if (timedCache == null) {
|
||||||
|
timedCache = CacheUtil.newTimedCache(2 * 60 * 1000);
|
||||||
|
timedCache.schedulePrune(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return timedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.tiesheng.util.exception;
|
||||||
|
|
||||||
|
import com.tiesheng.util.pojos.ApiResp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huang
|
||||||
|
* @ProjectName health
|
||||||
|
* @Copyright Hangzhou ShuoChuang Technology Co.,Ltd All Right Reserved
|
||||||
|
* @Description 这里是对文件的描述
|
||||||
|
* @data 2017/8/8
|
||||||
|
* @note 这里写文件的详细功能和改动
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
public class ApiException extends RuntimeException {
|
||||||
|
|
||||||
|
private ApiResp<String> apiResp;
|
||||||
|
|
||||||
|
public ApiException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.apiResp = ApiResp.respCust(102, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiException(int code, String message) {
|
||||||
|
this.apiResp = ApiResp.respCust(code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiException(ApiResp<String> apiResp) {
|
||||||
|
this.apiResp = apiResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiResp<String> getApiResp() {
|
||||||
|
return apiResp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.tiesheng.util.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常
|
||||||
|
*
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum ApiRespEnum {
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 200
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
OK(200, "请求成功"),
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 400
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
BadRequest(400, ""),
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 500
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
ServerError(500, "服务器忙,请稍后再试"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
ApiRespEnum(int code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.tiesheng.util.ip2region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data block class
|
||||||
|
*
|
||||||
|
* @author chenxin<chenxin619315 @ gmail.com>
|
||||||
|
*/
|
||||||
|
public class DataBlock {
|
||||||
|
/**
|
||||||
|
* city id
|
||||||
|
*/
|
||||||
|
private int cityId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* region address
|
||||||
|
*/
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* region ptr in the db file
|
||||||
|
*/
|
||||||
|
private int dataPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct method
|
||||||
|
*
|
||||||
|
* @param cityId
|
||||||
|
* @param region region string
|
||||||
|
* @param dataPtr data ptr
|
||||||
|
*/
|
||||||
|
public DataBlock(int cityId, String region, int dataPtr) {
|
||||||
|
this.cityId = cityId;
|
||||||
|
this.region = region;
|
||||||
|
this.dataPtr = dataPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBlock(int cityId, String region) {
|
||||||
|
this(cityId, region, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCityId() {
|
||||||
|
return cityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBlock setCityId(int cityId) {
|
||||||
|
this.cityId = cityId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegion() {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBlock setRegion(String region) {
|
||||||
|
this.region = region;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataPtr() {
|
||||||
|
return dataPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBlock setDataPtr(int dataPtr) {
|
||||||
|
this.dataPtr = dataPtr;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(cityId).append('|').append(region).append('|').append(dataPtr);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.tiesheng.util.ip2region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* database configuration class
|
||||||
|
*
|
||||||
|
* @author chenxin<chenxin619315 @ gmail.com>
|
||||||
|
*/
|
||||||
|
public class DbConfig {
|
||||||
|
/**
|
||||||
|
* total header data block size
|
||||||
|
*/
|
||||||
|
private int totalHeaderSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* max index data block size
|
||||||
|
* u should always choice the fastest read block size
|
||||||
|
*/
|
||||||
|
private int indexBlockSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct method
|
||||||
|
*
|
||||||
|
* @param totalHeaderSize
|
||||||
|
*/
|
||||||
|
public DbConfig(int totalHeaderSize) {
|
||||||
|
this.totalHeaderSize = totalHeaderSize;
|
||||||
|
this.indexBlockSize = 8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbConfig() {
|
||||||
|
this(8 * 2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalHeaderSize() {
|
||||||
|
return totalHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbConfig setTotalHeaderSize(int totalHeaderSize) {
|
||||||
|
this.totalHeaderSize = totalHeaderSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndexBlockSize() {
|
||||||
|
return indexBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbConfig setIndexBlockSize(int dataBlockSize) {
|
||||||
|
this.indexBlockSize = dataBlockSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.tiesheng.util.ip2region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* item index class
|
||||||
|
*
|
||||||
|
* @author chenxin<chenxin619315 @ gmail.com>
|
||||||
|
*/
|
||||||
|
public class IndexBlock {
|
||||||
|
|
||||||
|
private static int LENGTH = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start ip address
|
||||||
|
*/
|
||||||
|
private long startIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* end ip address
|
||||||
|
*/
|
||||||
|
private long endIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data ptr and data length
|
||||||
|
*/
|
||||||
|
private int dataPtr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data length
|
||||||
|
*/
|
||||||
|
private int dataLen;
|
||||||
|
|
||||||
|
public IndexBlock(long startIp, long endIp, int dataPtr, int dataLen) {
|
||||||
|
this.startIp = startIp;
|
||||||
|
this.endIp = endIp;
|
||||||
|
this.dataPtr = dataPtr;
|
||||||
|
this.dataLen = dataLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getIndexBlockLength() {
|
||||||
|
return LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStartIp() {
|
||||||
|
return startIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexBlock setStartIp(long startIp) {
|
||||||
|
this.startIp = startIp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEndIp() {
|
||||||
|
return endIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexBlock setEndIp(long endIp) {
|
||||||
|
this.endIp = endIp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataPtr() {
|
||||||
|
return dataPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexBlock setDataPtr(int dataPtr) {
|
||||||
|
this.dataPtr = dataPtr;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataLen() {
|
||||||
|
return dataLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexBlock setDataLen(int dataLen) {
|
||||||
|
this.dataLen = dataLen;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the bytes for storage
|
||||||
|
*
|
||||||
|
* @return byte[]
|
||||||
|
*/
|
||||||
|
public byte[] getBytes() {
|
||||||
|
/*
|
||||||
|
* +------------+-----------+-----------+
|
||||||
|
* | 4bytes | 4bytes | 4bytes |
|
||||||
|
* +------------+-----------+-----------+
|
||||||
|
* start ip end ip data ptr + len
|
||||||
|
*/
|
||||||
|
byte[] b = new byte[12];
|
||||||
|
|
||||||
|
IpUtil.writeIntLong(b, 0, startIp); //start ip
|
||||||
|
IpUtil.writeIntLong(b, 4, endIp); //end ip
|
||||||
|
|
||||||
|
//write the data ptr and the length
|
||||||
|
long mix = dataPtr | ((dataLen << 24) & 0xFF000000L);
|
||||||
|
IpUtil.writeIntLong(b, 8, mix);
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
package com.tiesheng.util.ip2region;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.io.resource.ClassPathResource;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ip db searcher class (Not thread safe)
|
||||||
|
*
|
||||||
|
* @author chenxin<chenxin619315 @ gmail.com>
|
||||||
|
*/
|
||||||
|
public class Ip2Region {
|
||||||
|
|
||||||
|
|
||||||
|
private static Ip2Region ip2Region;
|
||||||
|
/**
|
||||||
|
* db config
|
||||||
|
*/
|
||||||
|
private final DbConfig dbConfig;
|
||||||
|
/**
|
||||||
|
* db file access handler
|
||||||
|
*/
|
||||||
|
private RandomAccessFile raf = null;
|
||||||
|
/**
|
||||||
|
* header blocks buffer
|
||||||
|
*/
|
||||||
|
private long[] headerSip = null;
|
||||||
|
private int[] headerPtr = null;
|
||||||
|
private int headerLength;
|
||||||
|
/**
|
||||||
|
* super blocks info
|
||||||
|
*/
|
||||||
|
private long firstIndexPtr = 0;
|
||||||
|
private long lastIndexPtr = 0;
|
||||||
|
private int totalIndexBlocks = 0;
|
||||||
|
/**
|
||||||
|
* for memory mode
|
||||||
|
* the original db binary string
|
||||||
|
*/
|
||||||
|
private byte[] dbBinStr = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct class
|
||||||
|
*/
|
||||||
|
public Ip2Region() {
|
||||||
|
this.dbConfig = new DbConfig();
|
||||||
|
try {
|
||||||
|
ClassPathResource cpr = new ClassPathResource("ipdb/ip2region.db");
|
||||||
|
InputStream inputStream = cpr.getStream();
|
||||||
|
File tempFile = File.createTempFile("ip2region", ".db");
|
||||||
|
try {
|
||||||
|
FileUtil.writeFromStream(inputStream, tempFile);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(inputStream);
|
||||||
|
}
|
||||||
|
raf = new RandomAccessFile(tempFile, "r");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Ip2Region getInstance() {
|
||||||
|
if (ip2Region == null) {
|
||||||
|
synchronized (Ip2Region.class) {
|
||||||
|
if (ip2Region == null) {
|
||||||
|
ip2Region = new Ip2Region();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ip2Region;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region with an int ip address with memory binary search algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock memorySearch(long ip) throws IOException {
|
||||||
|
int blen = IndexBlock.getIndexBlockLength();
|
||||||
|
if (dbBinStr == null) {
|
||||||
|
dbBinStr = new byte[(int) raf.length()];
|
||||||
|
raf.seek(0L);
|
||||||
|
raf.readFully(dbBinStr, 0, dbBinStr.length);
|
||||||
|
|
||||||
|
//initialize the global vars
|
||||||
|
firstIndexPtr = IpUtil.getIntLong(dbBinStr, 0);
|
||||||
|
lastIndexPtr = IpUtil.getIntLong(dbBinStr, 4);
|
||||||
|
totalIndexBlocks = (int) ((lastIndexPtr - firstIndexPtr) / blen) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//search the index blocks to define the data
|
||||||
|
int l = 0, h = totalIndexBlocks;
|
||||||
|
long sip, eip, dataptr = 0;
|
||||||
|
while (l <= h) {
|
||||||
|
int m = (l + h) >> 1;
|
||||||
|
int p = (int) (firstIndexPtr + m * blen);
|
||||||
|
|
||||||
|
sip = IpUtil.getIntLong(dbBinStr, p);
|
||||||
|
if (ip < sip) {
|
||||||
|
h = m - 1;
|
||||||
|
} else {
|
||||||
|
eip = IpUtil.getIntLong(dbBinStr, p + 4);
|
||||||
|
if (ip > eip) {
|
||||||
|
l = m + 1;
|
||||||
|
} else {
|
||||||
|
dataptr = IpUtil.getIntLong(dbBinStr, p + 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//not matched
|
||||||
|
if (dataptr == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the data
|
||||||
|
int dataLen = (int) ((dataptr >> 24) & 0xFF);
|
||||||
|
int dataPtr = (int) ((dataptr & 0x00FFFFFF));
|
||||||
|
int cityId = (int) IpUtil.getIntLong(dbBinStr, dataPtr);
|
||||||
|
String region = new String(dbBinStr, dataPtr + 4, dataLen - 4, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
return new DataBlock(cityId, region, dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region throught the ip address with memory binary search algorithm
|
||||||
|
*
|
||||||
|
* @return DataBlock
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock memorySearch() throws IOException {
|
||||||
|
return memorySearch(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region throught the ip address with memory binary search algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return DataBlock
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock memorySearch(String ip) throws IOException {
|
||||||
|
return memorySearch(IpUtil.ip2long(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get by index ptr
|
||||||
|
*
|
||||||
|
* @param ptr
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock getByIndexPtr(long ptr) throws IOException {
|
||||||
|
raf.seek(ptr);
|
||||||
|
byte[] buffer = new byte[12];
|
||||||
|
raf.readFully(buffer, 0, buffer.length);
|
||||||
|
long extra = IpUtil.getIntLong(buffer, 8);
|
||||||
|
|
||||||
|
int dataLen = (int) ((extra >> 24) & 0xFF);
|
||||||
|
int dataPtr = (int) ((extra & 0x00FFFFFF));
|
||||||
|
|
||||||
|
raf.seek(dataPtr);
|
||||||
|
byte[] data = new byte[dataLen];
|
||||||
|
raf.readFully(data, 0, data.length);
|
||||||
|
|
||||||
|
int cityId = (int) IpUtil.getIntLong(data, 0);
|
||||||
|
String region = new String(data, 4, data.length - 4, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
return new DataBlock(cityId, region, dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region with an int ip address with b-tree algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock btreeSearch(long ip) throws IOException {
|
||||||
|
//check and load the header
|
||||||
|
if (headerSip == null) {
|
||||||
|
raf.seek(8L);
|
||||||
|
byte[] b = new byte[dbConfig.getTotalHeaderSize()];
|
||||||
|
raf.readFully(b, 0, b.length);
|
||||||
|
|
||||||
|
//fill the header
|
||||||
|
int len = b.length >> 3, idx = 0;
|
||||||
|
headerSip = new long[len];
|
||||||
|
headerPtr = new int[len];
|
||||||
|
long startIp, dataPtr;
|
||||||
|
for (int i = 0; i < b.length; i += 8) {
|
||||||
|
startIp = IpUtil.getIntLong(b, i);
|
||||||
|
dataPtr = IpUtil.getIntLong(b, i + 4);
|
||||||
|
if (dataPtr == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerSip[idx] = startIp;
|
||||||
|
headerPtr[idx] = (int) dataPtr;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerLength = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
//1. define the index block with the binary search
|
||||||
|
if (ip == headerSip[0]) {
|
||||||
|
return getByIndexPtr(headerPtr[0]);
|
||||||
|
} else if (ip == headerSip[headerLength - 1]) {
|
||||||
|
return getByIndexPtr(headerPtr[headerLength - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int l = 0, h = headerLength, sptr = 0, eptr = 0;
|
||||||
|
while (l <= h) {
|
||||||
|
int m = (l + h) >> 1;
|
||||||
|
|
||||||
|
//perfetc matched, just return it
|
||||||
|
if (ip == headerSip[m]) {
|
||||||
|
if (m > 0) {
|
||||||
|
sptr = headerPtr[m - 1];
|
||||||
|
eptr = headerPtr[m];
|
||||||
|
} else {
|
||||||
|
sptr = headerPtr[m];
|
||||||
|
eptr = headerPtr[m + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//less then the middle value
|
||||||
|
if (ip < headerSip[m]) {
|
||||||
|
if (m == 0) {
|
||||||
|
sptr = headerPtr[m];
|
||||||
|
eptr = headerPtr[m + 1];
|
||||||
|
break;
|
||||||
|
} else if (ip > headerSip[m - 1]) {
|
||||||
|
sptr = headerPtr[m - 1];
|
||||||
|
eptr = headerPtr[m];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
h = m - 1;
|
||||||
|
} else {
|
||||||
|
if (m == headerLength - 1) {
|
||||||
|
sptr = headerPtr[m - 1];
|
||||||
|
eptr = headerPtr[m];
|
||||||
|
break;
|
||||||
|
} else if (ip <= headerSip[m + 1]) {
|
||||||
|
sptr = headerPtr[m];
|
||||||
|
eptr = headerPtr[m + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
l = m + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//match nothing just stop it
|
||||||
|
if (sptr == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//2. search the index blocks to define the data
|
||||||
|
int blockLen = eptr - sptr, blen = IndexBlock.getIndexBlockLength();
|
||||||
|
//include the right border block
|
||||||
|
byte[] iBuffer = new byte[blockLen + blen];
|
||||||
|
raf.seek(sptr);
|
||||||
|
raf.readFully(iBuffer, 0, iBuffer.length);
|
||||||
|
|
||||||
|
l = 0;
|
||||||
|
h = blockLen / blen;
|
||||||
|
long sip, eip, dataptr = 0;
|
||||||
|
while (l <= h) {
|
||||||
|
int m = (l + h) >> 1;
|
||||||
|
int p = m * blen;
|
||||||
|
sip = IpUtil.getIntLong(iBuffer, p);
|
||||||
|
if (ip < sip) {
|
||||||
|
h = m - 1;
|
||||||
|
} else {
|
||||||
|
eip = IpUtil.getIntLong(iBuffer, p + 4);
|
||||||
|
if (ip > eip) {
|
||||||
|
l = m + 1;
|
||||||
|
} else {
|
||||||
|
dataptr = IpUtil.getIntLong(iBuffer, p + 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//not matched
|
||||||
|
if (dataptr == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//3. get the data
|
||||||
|
int dataLen = (int) ((dataptr >> 24) & 0xFF);
|
||||||
|
int dataPtr = (int) ((dataptr & 0x00FFFFFF));
|
||||||
|
|
||||||
|
raf.seek(dataPtr);
|
||||||
|
byte[] data = new byte[dataLen];
|
||||||
|
raf.readFully(data, 0, data.length);
|
||||||
|
|
||||||
|
int cityId = (int) IpUtil.getIntLong(data, 0);
|
||||||
|
String region = new String(data, 4, data.length - 4, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
return new DataBlock(cityId, region, dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region throught the ip address with b-tree search algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return DataBlock
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock btreeSearch(String ip) {
|
||||||
|
try {
|
||||||
|
return btreeSearch(IpUtil.ip2long(ip));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
return new DataBlock(0, "未知IP");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region with a int ip address with binary search algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock binarySearch(long ip) throws IOException {
|
||||||
|
int blen = IndexBlock.getIndexBlockLength();
|
||||||
|
if (totalIndexBlocks == 0) {
|
||||||
|
raf.seek(0L);
|
||||||
|
byte[] superBytes = new byte[8];
|
||||||
|
raf.readFully(superBytes, 0, superBytes.length);
|
||||||
|
//initialize the global vars
|
||||||
|
firstIndexPtr = IpUtil.getIntLong(superBytes, 0);
|
||||||
|
lastIndexPtr = IpUtil.getIntLong(superBytes, 4);
|
||||||
|
totalIndexBlocks = (int) ((lastIndexPtr - firstIndexPtr) / blen) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//search the index blocks to define the data
|
||||||
|
int l = 0, h = totalIndexBlocks;
|
||||||
|
byte[] buffer = new byte[blen];
|
||||||
|
long sip, eip, dataptr = 0;
|
||||||
|
while (l <= h) {
|
||||||
|
int m = (l + h) >> 1;
|
||||||
|
raf.seek(firstIndexPtr + m * blen);
|
||||||
|
raf.readFully(buffer, 0, buffer.length);
|
||||||
|
sip = IpUtil.getIntLong(buffer, 0);
|
||||||
|
if (ip < sip) {
|
||||||
|
h = m - 1;
|
||||||
|
} else {
|
||||||
|
eip = IpUtil.getIntLong(buffer, 4);
|
||||||
|
if (ip > eip) {
|
||||||
|
l = m + 1;
|
||||||
|
} else {
|
||||||
|
dataptr = IpUtil.getIntLong(buffer, 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//not matched
|
||||||
|
if (dataptr == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the data
|
||||||
|
int dataLen = (int) ((dataptr >> 24) & 0xFF);
|
||||||
|
int dataPtr = (int) ((dataptr & 0x00FFFFFF));
|
||||||
|
|
||||||
|
raf.seek(dataPtr);
|
||||||
|
byte[] data = new byte[dataLen];
|
||||||
|
raf.readFully(data, 0, data.length);
|
||||||
|
|
||||||
|
int cityId = (int) IpUtil.getIntLong(data, 0);
|
||||||
|
String region = new String(data, 4, data.length - 4, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
return new DataBlock(cityId, region, dataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the region throught the ip address with binary search algorithm
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return DataBlock
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public DataBlock binarySearch(String ip) throws IOException {
|
||||||
|
return binarySearch(IpUtil.ip2long(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the db config
|
||||||
|
*
|
||||||
|
* @return DbConfig
|
||||||
|
*/
|
||||||
|
public DbConfig getDbConfig() {
|
||||||
|
return dbConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close the db
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
headerSip = null;
|
||||||
|
headerPtr = null;
|
||||||
|
dbBinStr = null;
|
||||||
|
if (raf != null) {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.tiesheng.util.ip2region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* util class
|
||||||
|
*
|
||||||
|
* @author chenxin<chenxin619315 @ gmail.com>
|
||||||
|
*/
|
||||||
|
public class IpUtil {
|
||||||
|
/**
|
||||||
|
* write specfield bytes to a byte array start from offset
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* @param offset
|
||||||
|
* @param v
|
||||||
|
* @param bytes
|
||||||
|
*/
|
||||||
|
public static void write(byte[] b, int offset, long v, int bytes) {
|
||||||
|
for (int i = 0; i < bytes; i++) {
|
||||||
|
b[offset++] = (byte) ((v >>> (8 * i)) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write a int to a byte array
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* @param offset
|
||||||
|
* @param v
|
||||||
|
*/
|
||||||
|
public static void writeIntLong(byte[] b, int offset, long v) {
|
||||||
|
b[offset++] = (byte) ((v >> 0) & 0xFF);
|
||||||
|
b[offset++] = (byte) ((v >> 8) & 0xFF);
|
||||||
|
b[offset++] = (byte) ((v >> 16) & 0xFF);
|
||||||
|
b[offset] = (byte) ((v >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a int from a byte array start from the specifiled offset
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* @param offset
|
||||||
|
*/
|
||||||
|
public static long getIntLong(byte[] b, int offset) {
|
||||||
|
return (
|
||||||
|
((b[offset++] & 0x000000FFL)) |
|
||||||
|
((b[offset++] << 8) & 0x0000FF00L) |
|
||||||
|
((b[offset++] << 16) & 0x00FF0000L) |
|
||||||
|
((b[offset] << 24) & 0xFF000000L)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a int from a byte array start from the specifield offset
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* @param offset
|
||||||
|
*/
|
||||||
|
public static int getInt3(byte[] b, int offset) {
|
||||||
|
return (
|
||||||
|
(b[offset++] & 0x000000FF) |
|
||||||
|
(b[offset++] & 0x0000FF00) |
|
||||||
|
(b[offset] & 0x00FF0000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt2(byte[] b, int offset) {
|
||||||
|
return (
|
||||||
|
(b[offset++] & 0x000000FF) |
|
||||||
|
(b[offset] & 0x0000FF00)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt1(byte[] b, int offset) {
|
||||||
|
return (
|
||||||
|
(b[offset] & 0x000000FF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* string ip to long ip
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return long
|
||||||
|
*/
|
||||||
|
public static long ip2long(String ip) {
|
||||||
|
String[] p = ip.split("\\.");
|
||||||
|
if (p.length != 4) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int p1 = ((Integer.valueOf(p[0]) << 24) & 0xFF000000);
|
||||||
|
int p2 = ((Integer.valueOf(p[1]) << 16) & 0x00FF0000);
|
||||||
|
int p3 = ((Integer.valueOf(p[2]) << 8) & 0x0000FF00);
|
||||||
|
int p4 = ((Integer.valueOf(p[3]) << 0) & 0x000000FF);
|
||||||
|
|
||||||
|
return ((p1 | p2 | p3 | p4) & 0xFFFFFFFFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int to ip string
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static String long2ip(long ip) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb
|
||||||
|
.append((ip >> 24) & 0xFF).append('.')
|
||||||
|
.append((ip >> 16) & 0xFF).append('.')
|
||||||
|
.append((ip >> 8) & 0xFF).append('.')
|
||||||
|
.append((ip >> 0) & 0xFF);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
174
tiesheng-util/src/main/java/com/tiesheng/util/pojos/ApiResp.java
Normal file
174
tiesheng-util/src/main/java/com/tiesheng/util/pojos/ApiResp.java
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package com.tiesheng.util.pojos;
|
||||||
|
|
||||||
|
import com.tiesheng.util.exception.ApiRespEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huang
|
||||||
|
* @ProjectName health
|
||||||
|
* @Copyright Hangzhou ShuoChuang Technology Co.,Ltd All Right Reserved
|
||||||
|
* @Description 这里是对文件的描述
|
||||||
|
* @data 2017/9/4
|
||||||
|
* @note 这里写文件的详细功能和改动
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
public class ApiResp<T> {
|
||||||
|
|
||||||
|
public static int CODE_OK = 200;
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
private long recordsTotal = 0;
|
||||||
|
private boolean encrypt = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求成功
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respOK(T data) {
|
||||||
|
ApiRespEnum okResp = ApiRespEnum.OK;
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = okResp.getCode();
|
||||||
|
result.message = okResp.getMessage();
|
||||||
|
result.data = data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求成功,返回总条数
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param recordsTotal
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respOK(T data, long recordsTotal) {
|
||||||
|
ApiRespEnum okResp = ApiRespEnum.OK;
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = okResp.getCode();
|
||||||
|
result.message = okResp.getMessage();
|
||||||
|
result.data = data;
|
||||||
|
result.recordsTotal = recordsTotal;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义的错误
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param msg
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respCust(int code, String msg) {
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = code;
|
||||||
|
result.message = msg;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义的错误
|
||||||
|
*
|
||||||
|
* @param respEnum
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respCust(ApiRespEnum respEnum) {
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = respEnum.getCode();
|
||||||
|
result.message = respEnum.getMessage();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户未登录
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respNeedLogin(String msg) {
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = 110;
|
||||||
|
result.message = msg;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作失败
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respDoFail() {
|
||||||
|
ApiResp<T> result = new ApiResp<>();
|
||||||
|
result.code = 121;
|
||||||
|
result.message = "操作失败";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据bool返回数据
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> ApiResp<T> respBool(T data, boolean isOk) {
|
||||||
|
ApiResp<T> result = respOK(data);
|
||||||
|
if (!isOk) {
|
||||||
|
result = respDoFail();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRecordsTotal() {
|
||||||
|
return recordsTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordsTotal(long recordsTotal) {
|
||||||
|
this.recordsTotal = recordsTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEncrypt() {
|
||||||
|
return encrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncrypt(boolean encrypt) {
|
||||||
|
this.encrypt = encrypt;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
tiesheng-util/src/main/resources/ipdb/ip2region.db
Normal file
BIN
tiesheng-util/src/main/resources/ipdb/ip2region.db
Normal file
Binary file not shown.
88
tiesheng-web/pom.xml
Normal file
88
tiesheng-web/pom.xml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-web</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>log4j-to-slf4j</artifactId>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>spring-boot-starter-json</artifactId>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- aspect -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySql驱动 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>8.0.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mybatis-plus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-util</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-db-migration</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-poi</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-annotation</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-login</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.tiesheng.core;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
|
||||||
|
@ComponentScan({
|
||||||
|
"com.tiesheng.core.**.*",
|
||||||
|
})
|
||||||
|
public class CoreAutoImportSelector {
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.tiesheng.core;
|
||||||
|
|
||||||
|
import com.tiesheng.migration.MigrationAutoImportSelector;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Documented
|
||||||
|
@Import({
|
||||||
|
CoreAutoImportSelector.class,
|
||||||
|
MigrationAutoImportSelector.class
|
||||||
|
})
|
||||||
|
public @interface EnableTieshengWeb {
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.tiesheng.core.config.exception;
|
||||||
|
|
||||||
|
import cn.hutool.core.exceptions.ValidateException;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.hutool.log.LogFactory;
|
||||||
|
import com.tiesheng.util.exception.ApiException;
|
||||||
|
import com.tiesheng.util.exception.ApiRespEnum;
|
||||||
|
import com.tiesheng.util.pojos.ApiResp;
|
||||||
|
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||||
|
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||||
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huang
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class SpringExceptionHandler {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常捕获
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ApiResp<String> handle(Exception e) {
|
||||||
|
|
||||||
|
// 自定义异常
|
||||||
|
if (e instanceof ApiException) {
|
||||||
|
return ((ApiException) e).getApiResp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求异常
|
||||||
|
if (e instanceof ValidateException) {
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
if (e instanceof BindException) {
|
||||||
|
FieldError error = ((BindException) e).getFieldError();
|
||||||
|
String msg = "请求参数不合法";
|
||||||
|
if (error != null) {
|
||||||
|
msg = error.getDefaultMessage();
|
||||||
|
}
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), msg);
|
||||||
|
}
|
||||||
|
if (e instanceof MethodArgumentTypeMismatchException) {
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), "请求参数不合法");
|
||||||
|
}
|
||||||
|
if (e instanceof HttpRequestMethodNotSupportedException) {
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), "不支持的请求方式");
|
||||||
|
}
|
||||||
|
if (e instanceof NoHandlerFoundException) {
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), "请求路径不存在");
|
||||||
|
}
|
||||||
|
if (e instanceof MaxUploadSizeExceededException) {
|
||||||
|
MultipartProperties property = SpringUtil.getBean(MultipartProperties.class);
|
||||||
|
return ApiResp.respCust(ApiRespEnum.BadRequest.getCode(), String.format("上传文件不得超过%dMB", property.getMaxFileSize().toMegabytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务器内部异常
|
||||||
|
if (e instanceof IOException) {
|
||||||
|
return ApiResp.respCust(ApiRespEnum.ServerError.getCode(), "IO异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiResp<String> apiResp = ApiResp.respCust(ApiRespEnum.ServerError);
|
||||||
|
LogFactory.get().info(e);
|
||||||
|
return apiResp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.tiesheng.core.config.global;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "tiesheng.global")
|
||||||
|
public class GlobalConfig {
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
private String service;
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// 逻辑方法
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
String context = SpringUtil.getProperty("server.servlet.context-path");
|
||||||
|
if (StrUtil.isEmpty(context)) {
|
||||||
|
context = "";
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整的路径
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String buildPath(String path) {
|
||||||
|
if (StrUtil.isEmpty(path)) {
|
||||||
|
path = "";
|
||||||
|
}
|
||||||
|
if (!StrUtil.isEmpty(host) && !StrUtil.startWith(path, "http")) {
|
||||||
|
path = host + getContextPath() + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setService(String service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.tiesheng.core.config.json;
|
||||||
|
|
||||||
|
import cn.hutool.log.LogFactory;
|
||||||
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
|
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||||
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||||
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class FastJsonMessageConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置FastJson
|
||||||
|
*
|
||||||
|
* @return HttpMessageConverters
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public HttpMessageConverters fastJsonHttpMessageConverters() {
|
||||||
|
FastJsonConfig config = new FastJsonConfig();
|
||||||
|
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
|
||||||
|
SerializerFeature.WriteNullStringAsEmpty,
|
||||||
|
SerializerFeature.WriteEnumUsingName,
|
||||||
|
SerializerFeature.WriteNullNumberAsZero);
|
||||||
|
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
|
||||||
|
fastConverter.setFastJsonConfig(config);
|
||||||
|
fastConverter.setDefaultCharset(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
List<MediaType> mediaTypes = new ArrayList<>();
|
||||||
|
mediaTypes.add(MediaType.APPLICATION_JSON);
|
||||||
|
fastConverter.setSupportedMediaTypes(mediaTypes);
|
||||||
|
|
||||||
|
return new HttpMessageConverters(fastConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.tiesheng.core.config.mybatis;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
|
||||||
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||||
|
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MybatisPlusCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页器配置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||||
|
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* map对象自动转驼峰
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ConfigurationCustomizer configurationCustomizer() {
|
||||||
|
return i -> {
|
||||||
|
i.setObjectWrapperFactory(new MybatisMapWrapperFactory());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* map中保留null值
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MybatisConfiguration mybatisConfiguration() {
|
||||||
|
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||||
|
configuration.setCallSettersOnNulls(true);
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.tiesheng.core.config.mybatis;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MybatisTimeMetaHandler implements MetaObjectHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在执行mybatisPlus的insert()时,为我们自动给某些字段填充值,这样的话,我们就不需要手动给insert()里的实体类赋值了
|
||||||
|
*
|
||||||
|
* @param metaObject
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void insertFill(MetaObject metaObject) {
|
||||||
|
// 其中方法参数中第一个是前面自动填充所对应的字段,第二个是要自动填充的值。第三个是指定实体类的对象
|
||||||
|
this.setFieldValByName("createTime", DateUtil.date(), metaObject);
|
||||||
|
this.setFieldValByName("updateTime", DateUtil.date(), metaObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在执行mybatisPlus的update()时,为我们自动给某些字段填充值,这样的话,我们就不需要手动给update()里的实体类赋值了
|
||||||
|
*
|
||||||
|
* @param metaObject
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateFill(MetaObject metaObject) {
|
||||||
|
this.setFieldValByName("updateTime", DateUtil.date(), metaObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.tiesheng.core.config.token;
|
||||||
|
|
||||||
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.hutool.jwt.JWT;
|
||||||
|
import com.tiesheng.core.config.token.bean.TokenBean;
|
||||||
|
import com.tiesheng.core.util.servlet.ServletKit;
|
||||||
|
import com.tiesheng.util.exception.ApiException;
|
||||||
|
import com.tiesheng.util.pojos.ApiResp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
public class TokenParse {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登陆信息
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TokenBean get(String token) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
TokenValidConfig tokenValidConfig = SpringUtil.getBean(TokenValidConfig.class);
|
||||||
|
TokenBean tokenBean = tokenValidConfig.getTokenBean(token);
|
||||||
|
if (tokenBean == null) {
|
||||||
|
JWT decode = JWT.of(token);
|
||||||
|
String id = decode.getPayload("id").toString();
|
||||||
|
String environmentType = decode.getPayload("environmentType").toString();
|
||||||
|
String service = decode.getPayload("service").toString();
|
||||||
|
String extra = decode.getPayload("extra").toString();
|
||||||
|
tokenBean = new TokenBean(id, environmentType, service);
|
||||||
|
tokenBean.setExtra(extra);
|
||||||
|
}
|
||||||
|
return tokenBean;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ApiException(ApiResp.respNeedLogin("请重新登录"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录信息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TokenBean get() {
|
||||||
|
String headerToken = ServletUtil.getHeader(ServletKit.getRequest(), "token", "utf-8");
|
||||||
|
return get(headerToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户id
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TokenBean getWithoutThr() {
|
||||||
|
TokenBean tokenBean = null;
|
||||||
|
try {
|
||||||
|
tokenBean = get();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return tokenBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.tiesheng.core.config.token;
|
||||||
|
|
||||||
|
import com.tiesheng.annotation.token.TokenIgnore;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
* @ProjectName CmccSpring
|
||||||
|
* @Copyright Hangzhou ShuoChuang Technology Co.,Ltd All Right Reserved
|
||||||
|
* @Description 这里是对文件的描述
|
||||||
|
* @data 2019-07-15
|
||||||
|
* @note 这里写文件的详细功能和改动
|
||||||
|
* @note
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class TokenValidAspect {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切入点
|
||||||
|
*/
|
||||||
|
@Pointcut("execution(* com..controller..*.*(..))")
|
||||||
|
public void methodArgs() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取操作日志说明
|
||||||
|
*
|
||||||
|
* @param joinPoint
|
||||||
|
*/
|
||||||
|
@Before("methodArgs()")
|
||||||
|
public void before(JoinPoint joinPoint) {
|
||||||
|
|
||||||
|
// 过滤不要需要验证的接口
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
TokenIgnore apiTokenIgnore = method.getAnnotation(TokenIgnore.class);
|
||||||
|
if (apiTokenIgnore != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// token验证
|
||||||
|
TokenParse.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.tiesheng.core.config.token;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import com.tiesheng.core.config.token.bean.TokenBean;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hao
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties("tiesheng.token")
|
||||||
|
public class TokenValidConfig {
|
||||||
|
|
||||||
|
private Map<String, TokenBean> list = MapUtil.newHashMap();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证token
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TokenBean getTokenBean(String token) {
|
||||||
|
if (list == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Map<String, TokenBean> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(Map<String, TokenBean> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.tiesheng.core.config.token.bean;
|
||||||
|
|
||||||
|
import cn.hutool.jwt.JWT;
|
||||||
|
|
||||||
|
public class TokenBean {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String environmentType;
|
||||||
|
private String service;
|
||||||
|
private String extra;
|
||||||
|
|
||||||
|
public TokenBean() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenBean(String id, String environmentType, String service) {
|
||||||
|
this.id = id;
|
||||||
|
this.environmentType = environmentType;
|
||||||
|
this.service = service;
|
||||||
|
this.extra = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置token
|
||||||
|
*/
|
||||||
|
public String toToken() {
|
||||||
|
byte[] key = "%kIp9frQCu".getBytes();
|
||||||
|
return JWT.create()
|
||||||
|
.setPayload("id", id)
|
||||||
|
.setPayload("environmentType", environmentType)
|
||||||
|
.setPayload("service", service)
|
||||||
|
.setPayload("extra", extra)
|
||||||
|
.setPayload("time", System.currentTimeMillis())
|
||||||
|
.setKey(key)
|
||||||
|
.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// setter\getter
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnvironmentType() {
|
||||||
|
return environmentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvironmentType(String environmentType) {
|
||||||
|
this.environmentType = environmentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setService(String service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExtra() {
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtra(String extra) {
|
||||||
|
this.extra = extra;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.tiesheng.core.util.servlet;
|
||||||
|
|
||||||
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public class ServletKit extends ServletUtil {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前线程的request
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getRequest() {
|
||||||
|
ServletRequestAttributes attributes = (ServletRequestAttributes)
|
||||||
|
RequestContextHolder.getRequestAttributes();
|
||||||
|
return attributes.getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
21
tiesheng-web/src/main/resources/application.yml
Normal file
21
tiesheng-web/src/main/resources/application.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
server:
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
## Spring配置
|
||||||
|
spring:
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 20MB
|
||||||
|
web:
|
||||||
|
resources:
|
||||||
|
static-locations: classpath:/static/,file:static/
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
|
|
||||||
|
## 日志
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: logs/tiesheng.log
|
||||||
20
tiesheng-wxmp/pom.xml
Normal file
20
tiesheng-wxmp/pom.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.tiesheng</groupId>
|
||||||
|
<artifactId>tiesheng-parent</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>tiesheng-wxmp</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
||||||
Reference in New Issue
Block a user