Initial commit

This commit is contained in:
曾文豪
2022-12-22 17:25:10 +08:00
commit 7cec4f3f35
61 changed files with 3696 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/logs/
target/
rebel.xml
/.idea/
*.iml
/static/

28
demo/pom.xml Normal file
View 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>

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

View File

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

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

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

View File

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

View File

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

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

View File

@@ -0,0 +1,12 @@
package com.tiesheng.migration;
import org.springframework.context.annotation.ComponentScan;
/**
* @author hao
*/
@ComponentScan({
"com.tiesheng.migration.**.*"
})
public class MigrationAutoImportSelector {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() +
"&timestamp=" + 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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("账号或密码错误");
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

Binary file not shown.

88
tiesheng-web/pom.xml Normal file
View 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>

View File

@@ -0,0 +1,13 @@
package com.tiesheng.core;
import org.springframework.context.annotation.ComponentScan;
/**
* @author hao
*/
@ComponentScan({
"com.tiesheng.core.**.*",
})
public class CoreAutoImportSelector {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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