钉钉群机器人发送消息;
This commit is contained in:
parent
046527c76e
commit
35b5798f9b
221
msgdispatcher/pom.xml
Normal file
221
msgdispatcher/pom.xml
Normal file
@ -0,0 +1,221 @@
|
||||
<?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.msgdispatcher</groupId>
|
||||
<artifactId>msgdispatcher</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<hutool.version>5.8.18</hutool.version>
|
||||
<maven.plugin.version>3.8.1</maven.plugin.version>
|
||||
<mybatis.plus.spring.boot>3.5.5</mybatis.plus.spring.boot>
|
||||
|
||||
<hutool.version>5.8.25</hutool.version>
|
||||
<postgresql.version>42.7.3</postgresql.version>
|
||||
<easyexcel.version>3.2.1</easyexcel.version>
|
||||
<annotations.version>4.8.6</annotations.version>
|
||||
<undertow.version>2.3.14.Final</undertow.version>
|
||||
<apache.poi>5.3.0</apache.poi>
|
||||
<taosdata.verson>3.2.10</taosdata.verson>
|
||||
<disruptor.version>3.4.4</disruptor.version>
|
||||
<aviator.version>5.4.3</aviator.version>
|
||||
<alibaba-dingtalk-service-sdk.version>2.0.0</alibaba-dingtalk-service-sdk.version>
|
||||
<commons-codec.version>1.11</commons-codec.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- web 容器使用 undertow 代替 tomcat-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 定时任务支持-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--spring 配置支持-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${apache.poi}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-poi</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>${taosdata.verson}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 提供Redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--mybatis-plus-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis.plus.spring.boot}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- hutool 的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-captcha</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--redis 操作-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>${disruptor.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>${aviator.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--aop切面-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 临时修复MAYBE枚举量未定义问题,等待Spring6.2正式发行-->
|
||||
<dependency>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-annotations</artifactId>
|
||||
<version>${annotations.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>${easyexcel.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml-schemas</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,22 @@
|
||||
package com.msgdispatcher.common.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "dingtalk.webhook")
|
||||
public class DingTalkProperties {
|
||||
|
||||
|
||||
private String url;
|
||||
|
||||
private String token;
|
||||
|
||||
private String secret;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.msgdispatcher.common.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "dingtalk-user")
|
||||
public class DingTalkUserProperties {
|
||||
|
||||
|
||||
private String getTokenUrl;
|
||||
|
||||
private String getUserUrl;
|
||||
|
||||
private String appKey;
|
||||
|
||||
private String appSecret;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.msgdispatcher.common.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "msg-file")
|
||||
public class MsgFileProperties {
|
||||
|
||||
/**
|
||||
* 消息文件目录
|
||||
*/
|
||||
private String directoryPath;
|
||||
|
||||
/**
|
||||
* 基础备份目录
|
||||
*/
|
||||
private String baseBackupDir;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.msgdispatcher.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.msgdispatcher.common.constant;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public interface AdminConstant {
|
||||
|
||||
/**
|
||||
* 基础包路径
|
||||
*/
|
||||
String BASE_PACKAGE = "com.msgdispatcher";
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.CLIENT_UN_AUTHORIZED.getCode();
|
||||
|
||||
private String msg = ResultCode.CLIENT_UN_AUTHORIZED.getMessage();
|
||||
|
||||
public BusinessException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BusinessException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public BusinessException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public BusinessException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public BusinessException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public BusinessException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public class EasyExcelException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.FAILURE.getCode();
|
||||
|
||||
private String msg = ResultCode.FAILURE.getMessage();
|
||||
|
||||
public EasyExcelException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public EasyExcelException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EasyExcelException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EasyExcelException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EasyExcelException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EasyExcelException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public class OssException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.FAILURE.getCode();
|
||||
|
||||
private String msg = ResultCode.FAILURE.getMessage();
|
||||
|
||||
public OssException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public OssException(String msg) {
|
||||
super(msg);
|
||||
this.setMsg(msg);
|
||||
}
|
||||
|
||||
public OssException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.setCode(resultCode.getCode());
|
||||
this.setMsg(msg);
|
||||
}
|
||||
|
||||
public OssException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.setMsg(msg);
|
||||
}
|
||||
|
||||
public OssException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.setCode(resultCode.getCode());
|
||||
this.setMsg(msg);
|
||||
}
|
||||
|
||||
public OssException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
@Getter
|
||||
public class RateLimiterException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.FAILURE.getCode();
|
||||
|
||||
private String msg = ResultCode.FAILURE.getMessage();
|
||||
|
||||
public RateLimiterException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RateLimiterException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RateLimiterException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RateLimiterException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RateLimiterException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RateLimiterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
@Getter
|
||||
public class RepeatSubmitException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.FAILURE.getCode();
|
||||
|
||||
private String msg = ResultCode.FAILURE.getMessage();
|
||||
|
||||
public RepeatSubmitException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RepeatSubmitException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RepeatSubmitException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RepeatSubmitException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RepeatSubmitException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public RepeatSubmitException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.msgdispatcher.common.exceptions;
|
||||
|
||||
|
||||
|
||||
import com.msgdispatcher.common.result.IResultCode;
|
||||
import com.msgdispatcher.common.result.ResultCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = ResultCode.FAILURE.getCode();
|
||||
|
||||
private String msg = ResultCode.FAILURE.getMessage();
|
||||
|
||||
public ServiceException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ServiceException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ServiceException(IResultCode resultCode, String msg) {
|
||||
super(msg);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ServiceException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ServiceException(IResultCode resultCode, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.code = resultCode.getCode();
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ServiceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package com.msgdispatcher.common.handler;
|
||||
|
||||
|
||||
import com.msgdispatcher.common.constant.AdminConstant;
|
||||
import com.msgdispatcher.common.exceptions.*;
|
||||
import com.msgdispatcher.common.exceptions.*;
|
||||
import com.msgdispatcher.common.result.R;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.ValidationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
* <p>
|
||||
* 如果我同时捕获了父类和子类,那么到底能够被那个异常处理器捕获呢?比如 Exception 和 BusinessException
|
||||
* 当然是 BusinessException 的异常处理器捕获了,精确匹配,如果没有 BusinessException 的异常处理器才会轮到它的 父亲 ,
|
||||
* 父亲 没有才会到 祖父 。总之一句话, 精准匹配,找那个关系最近的
|
||||
* </p>
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* @param businessException 业务异常
|
||||
* @return @ResponseBody
|
||||
*
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handle(BusinessException businessException) {
|
||||
// 获取指定包名前缀的异常信息,减少不必要的日志
|
||||
String stackTraceByPn = getStackTraceByPn(businessException, AdminConstant.BASE_PACKAGE);
|
||||
log.error("记录业务异常信息, 消息:{} 编码:{} {}", businessException.getMessage(), businessException.getCode(), stackTraceByPn);
|
||||
return R.fail(businessException.getCode(), businessException.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截限流异常信息
|
||||
* */
|
||||
@ExceptionHandler(RateLimiterException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handle(RateLimiterException rateLimiterException) {
|
||||
// 获取指定包名前缀的异常信息,减少不必要的日志
|
||||
// String stackTraceByPn = getStackTraceByPn(rateLimiterException, AdminConstant.BASE_PACKAGE);
|
||||
log.error("拦截限流异常信息, 消息:{} 编码:{}", rateLimiterException.getMessage(), rateLimiterException.getCode());
|
||||
return R.fail(rateLimiterException.getCode(), rateLimiterException.getMessage());
|
||||
}
|
||||
/**
|
||||
* 重复提交异常信息
|
||||
* */
|
||||
@ExceptionHandler(RepeatSubmitException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handle(RepeatSubmitException repeatSubmitException) {
|
||||
// 获取指定包名前缀的异常信息,减少不必要的日志
|
||||
// String stackTraceByPn = getStackTraceByPn(rateLimiterException, AdminConstant.BASE_PACKAGE);
|
||||
log.error("重复提交异常信息, 消息:{} 编码:{}", repeatSubmitException.getMessage(), repeatSubmitException.getCode());
|
||||
return R.fail(repeatSubmitException.getCode(), repeatSubmitException.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handle(ServiceException serviceException) {
|
||||
// 这里记录所有堆栈信息
|
||||
log.error("记录业务异常信息, 消息:{} 编码:{}", serviceException.getMessage(), serviceException.getCode(), serviceException);
|
||||
return R.fail(serviceException.getCode(), serviceException.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(OssException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handle(OssException ossException) {
|
||||
// 这里记录所有堆栈信息
|
||||
log.error("oss异常信息, 消息:{} 编码:{}", ossException.getMessage(), ossException.getCode(), ossException);
|
||||
return R.fail(ossException.getCode(), ossException.getMessage());
|
||||
}
|
||||
|
||||
private String getStackTraceByPn(Throwable e, String packagePrefix) {
|
||||
StringBuilder append = new StringBuilder("\n").append(e);
|
||||
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
|
||||
if (stackTraceElement.getClassName().startsWith(packagePrefix)) {
|
||||
append.append("\n\tat ").append(stackTraceElement);
|
||||
}
|
||||
}
|
||||
return append.toString();
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handleValidatedException(Exception exception) {
|
||||
BindingResult bindingResult = null;
|
||||
if (exception instanceof MethodArgumentNotValidException e){
|
||||
bindingResult = e.getBindingResult();
|
||||
if (bindingResult.hasErrors()) {
|
||||
FieldError fieldError = bindingResult.getFieldError();
|
||||
if (fieldError != null) {
|
||||
return R.fail(fieldError.getField()+ ":" + fieldError.getDefaultMessage());
|
||||
}
|
||||
}
|
||||
}else if (exception instanceof ConstraintViolationException e){
|
||||
String collect = e.getConstraintViolations().stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining(";"));
|
||||
return R.fail(collect);
|
||||
}else if (exception instanceof BindException e){
|
||||
bindingResult = e.getBindingResult();
|
||||
if (bindingResult.hasErrors()) {
|
||||
FieldError fieldError = bindingResult.getFieldError();
|
||||
if (fieldError != null) {
|
||||
return R.fail(fieldError.getField()+ ":" + fieldError.getDefaultMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return R.fail(exception.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {MissingServletRequestParameterException.class})
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handlerMissingServletRequestParameterException(MissingServletRequestParameterException exception) {
|
||||
String message = exception.getMessage();
|
||||
log.error("全局捕获MissingServletRequestParameterException错误信息: {}", message, exception);
|
||||
return R.fail("缺少必要参数");
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获空指针异常
|
||||
**/
|
||||
@ExceptionHandler(value = NullPointerException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handlerNullPointException(NullPointerException exception) {
|
||||
String message = exception.getMessage();
|
||||
log.error("全局捕获null错误信息", exception);
|
||||
return R.fail(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获 404 异常
|
||||
* @param exception 异常
|
||||
* @return R
|
||||
*/
|
||||
@ExceptionHandler({NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class})
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public R handle(Exception exception) {
|
||||
String message = exception.getMessage();
|
||||
log.error("404捕获错误信息: {}", message);
|
||||
return R.fail("找不到对应资源");
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获最大异常
|
||||
**/
|
||||
@ExceptionHandler(value = Throwable.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public R handlerBindException(Exception exception) {
|
||||
String message = exception.getMessage();
|
||||
log.error("全局捕获错误信息", exception);
|
||||
return R.fail(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.msgdispatcher.common.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public interface IResultCode extends Serializable {
|
||||
/**
|
||||
* 获取结果消息
|
||||
* @return 结果消息
|
||||
*/
|
||||
String getMessage();
|
||||
|
||||
/**
|
||||
* 获取返回状态码
|
||||
* @return 结果状态码
|
||||
*/
|
||||
int getCode();
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
package com.msgdispatcher.common.result;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 承载数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 返回消息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
private R(IResultCode resultCode) {
|
||||
this(resultCode, null, resultCode.getMessage());
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, String msg) {
|
||||
this(resultCode, null, msg);
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, T data) {
|
||||
this(resultCode, data, resultCode.getMessage());
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, T data, String msg) {
|
||||
this(resultCode.getCode(), data, msg);
|
||||
}
|
||||
|
||||
private R(int code, T data, String msg) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.msg = msg;
|
||||
this.success = ResultCode.SUCCESS.code == code;
|
||||
}
|
||||
|
||||
public static boolean isSuccess(@Nullable R<?> result) {
|
||||
return Optional.ofNullable(result).map((x) -> ResultCode.SUCCESS.code == x.code).orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
public static boolean isNotSuccess(@Nullable R<?> result) {
|
||||
return !isSuccess(result);
|
||||
}
|
||||
|
||||
public static <T> R<T> data(T data) {
|
||||
return data(data, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> data(T data, String msg) {
|
||||
return data(200, data, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> data(int code, T data, String msg) {
|
||||
return new R(code, data, data == null ? "暂无承载数据" : msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> success(String msg) {
|
||||
return new R(ResultCode.SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> success(T data) {
|
||||
return new R(ResultCode.SUCCESS, data,"操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> success() {
|
||||
return new R(ResultCode.SUCCESS,null, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> success(IResultCode resultCode) {
|
||||
return new R(resultCode);
|
||||
}
|
||||
|
||||
public static <T> R<T> success(IResultCode resultCode, String msg) {
|
||||
return new R(resultCode, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return new R(ResultCode.FAILURE, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(int code, String msg) {
|
||||
return new R(code, null, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(IResultCode resultCode) {
|
||||
return new R(resultCode);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(IResultCode resultCode, String msg) {
|
||||
return new R(resultCode, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> status(boolean flag) {
|
||||
return flag ? success("操作成功") : fail("操作失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "R(code=" + this.getCode() + ", success=" + this.isSuccess() + ", data=" + this.getData() + ", msg=" + this.getMsg() + ")";
|
||||
}
|
||||
|
||||
public R() {
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.msgdispatcher.common.result;
|
||||
|
||||
/**
|
||||
* @author chenhaojie
|
||||
*
|
||||
*/
|
||||
public enum ResultCode implements IResultCode {
|
||||
/**
|
||||
* 返回状态码,以及返回消息
|
||||
*/
|
||||
SUCCESS(200, "操作成功"),
|
||||
FAILURE(400, "业务异常"),
|
||||
UN_AUTHORIZED(401, "请求未授权"),
|
||||
CLIENT_UN_AUTHORIZED(401, "客户端请求未授权"),
|
||||
NOT_FOUND(404, "404 没找到请求"),
|
||||
MSG_NOT_READABLE(400, "消息不能读取"),
|
||||
METHOD_NOT_SUPPORTED(405, "不支持当前请求方法"),
|
||||
MEDIA_TYPE_NOT_SUPPORTED(415, "不支持当前媒体类型"),
|
||||
REQ_REJECT(403, "请求被拒绝"),
|
||||
INTERNAL_SERVER_ERROR(500, "服务器异常"),
|
||||
PARAM_MISS(400, "缺少必要的请求参数"),
|
||||
PARAM_TYPE_ERROR(400, "请求参数类型错误"),
|
||||
PARAM_BIND_ERROR(400, "请求参数绑定错误"),
|
||||
PARAM_VALID_ERROR(400, "参数校验失败");
|
||||
|
||||
final int code;
|
||||
final String message;
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
ResultCode(final int code, final String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.msgdispatcher.modules.ddsend.controller;
|
||||
|
||||
import com.msgdispatcher.common.result.R;
|
||||
import com.msgdispatcher.modules.ddsend.job.SendDingTalkMsgJob;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@RequestMapping("/api/dingTalk")
|
||||
@RestController
|
||||
public class DingTalkController {
|
||||
|
||||
|
||||
|
||||
@Autowired
|
||||
private SendDingTalkMsgJob sendDingTalkMsgJob;
|
||||
|
||||
/**
|
||||
* 钉钉机器人,发送消息
|
||||
*/
|
||||
@GetMapping("/sendMessage")
|
||||
public R<String> sendMessage() {
|
||||
sendDingTalkMsgJob.sendMessage();
|
||||
return R.success("发送成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.msgdispatcher.modules.ddsend.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class MsgFileDto {
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
private String filePath;
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private List<Map<String, Object>> mgsList;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.msgdispatcher.modules.ddsend.job;
|
||||
|
||||
import com.msgdispatcher.modules.ddsend.domain.dto.MsgFileDto;
|
||||
import com.msgdispatcher.modules.ddsend.service.DingTalkService;
|
||||
import com.msgdispatcher.modules.ddsend.service.ParseFileService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SendDingTalkMsgJob {
|
||||
|
||||
@Autowired
|
||||
private DingTalkService dingTalkService;
|
||||
|
||||
@Autowired
|
||||
private ParseFileService parseFileService;
|
||||
|
||||
|
||||
|
||||
//@Scheduled(cron = "0 0/10 * * * ?")
|
||||
public void sendMessage() {
|
||||
//获取目录下所有文件的解析结果
|
||||
List<MsgFileDto> msgFileList= null;
|
||||
try {
|
||||
msgFileList = parseFileService.scanAndParseAllFiles();
|
||||
} catch (Exception e) {
|
||||
log.error("解析文件错误:"+e.getMessage());
|
||||
}
|
||||
if (msgFileList ==null){
|
||||
log.info("目录中没有文件,无需发送消息");
|
||||
return;
|
||||
}
|
||||
//发送消息
|
||||
for (MsgFileDto msgFileDto : msgFileList) {
|
||||
String filePath = null;
|
||||
try {
|
||||
filePath = msgFileDto.getFilePath();
|
||||
List<Map<String, Object>> mgsList = msgFileDto.getMgsList();
|
||||
for (Map<String, Object> map : mgsList) {
|
||||
String phone = (String) map.get("接收人手机号");
|
||||
String msgContent = (String) map.get("消息内容");
|
||||
String wind = (String) map.get("风机");
|
||||
StringBuilder stb = new StringBuilder();
|
||||
stb.append(wind).append("风机,").append(msgContent);
|
||||
String user =null;
|
||||
if (phone !=null){
|
||||
stb.append(" @").append(phone);
|
||||
user = dingTalkService.getUserIdByMobile(phone);
|
||||
}
|
||||
dingTalkService.sendMessage(stb.toString(),user);
|
||||
}
|
||||
//文件发送成功后,把文件移动到备份文件夹
|
||||
parseFileService.moveFileToBackupDirectory(filePath);
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息错误:{},文件路径:{}",e,filePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package com.msgdispatcher.modules.ddsend.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dingtalk.api.DefaultDingTalkClient;
|
||||
import com.dingtalk.api.DingTalkClient;
|
||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
||||
import com.dingtalk.api.response.OapiRobotSendResponse;
|
||||
import com.msgdispatcher.common.config.DingTalkProperties;
|
||||
import com.msgdispatcher.common.config.DingTalkUserProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DingTalkService {
|
||||
|
||||
|
||||
@Autowired
|
||||
private DingTalkProperties dingTalkProperties;
|
||||
|
||||
@Autowired
|
||||
private DingTalkUserProperties dingTalkUserProperties;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 发送钉钉消息
|
||||
* @param message 内容
|
||||
*/
|
||||
public void sendMessage(String message,String user) {
|
||||
try {
|
||||
Long timestamp = System.currentTimeMillis();
|
||||
String secret = dingTalkProperties.getSecret();
|
||||
String stringToSign = timestamp + "\n" + secret;
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
|
||||
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
|
||||
String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
|
||||
//sign字段和timestamp字段必须拼接到请求URL上,否则会出现 310000 的错误信息
|
||||
String serverUrl = String.format("%s%s%s%s", dingTalkProperties.getUrl(), sign, "×tamp=", timestamp);
|
||||
DingTalkClient client = new DefaultDingTalkClient(serverUrl);
|
||||
OapiRobotSendRequest req = new OapiRobotSendRequest();
|
||||
//定义文本内容
|
||||
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
|
||||
text.setContent(message);
|
||||
//定义 @ 对象
|
||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||
at.setAtUserIds(Arrays.asList(user));
|
||||
//设置消息类型
|
||||
req.setMsgtype("text");
|
||||
req.setText(text);
|
||||
req.setAt(at);
|
||||
OapiRobotSendResponse rsp = client.execute(req, dingTalkProperties.getToken());
|
||||
log.info("rsp:"+rsp.getBody());
|
||||
} catch (Exception e) {
|
||||
log.error("机器人发送消息错误"+e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据手机号获取用户ID
|
||||
* @param mobile
|
||||
* @return
|
||||
*/
|
||||
public String getUserIdByMobile(String mobile) {
|
||||
try {
|
||||
String accessToken = getAccessToken();
|
||||
String url = String.format("%s%s&mobile=%s",dingTalkUserProperties.getGetUserUrl(),accessToken,mobile);
|
||||
JSONObject response = restTemplate.getForObject(url, JSONObject.class);
|
||||
if (response != null && response.getIntValue("errcode") == 0) {
|
||||
return response.getString("userid");
|
||||
} else {
|
||||
log.info("无法获取访问令牌"+ response.toJSONString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("根据手机号获取用户ID失败"+ e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AccessToken
|
||||
* @return access_token
|
||||
*/
|
||||
public String getAccessToken() {;
|
||||
String url = String.format("%s%s&appsecret=%s",dingTalkUserProperties.getGetTokenUrl(),
|
||||
dingTalkUserProperties.getAppKey(),dingTalkUserProperties.getAppSecret());
|
||||
JSONObject response = restTemplate.getForObject(url, JSONObject.class);
|
||||
if (response != null && response.getIntValue("errcode") == 0) {
|
||||
return response.getString("access_token");
|
||||
} else {
|
||||
throw new RuntimeException("无法获取访问令牌: " + response.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package com.msgdispatcher.modules.ddsend.service;
|
||||
|
||||
import com.msgdispatcher.common.config.MsgFileProperties;
|
||||
import com.msgdispatcher.modules.ddsend.domain.dto.MsgFileDto;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class ParseFileService {
|
||||
|
||||
|
||||
@Autowired
|
||||
private MsgFileProperties msgFileProperties;
|
||||
|
||||
/**
|
||||
* 扫描目录下的所有.e文件并解析E语言格式文件
|
||||
*
|
||||
* @return 所有文件的解析结果
|
||||
* @throws IOException 如果读取过程中出现错误
|
||||
*/
|
||||
public List<MsgFileDto> scanAndParseAllFiles() throws IOException {
|
||||
File directory = new File(msgFileProperties.getDirectoryPath());
|
||||
if (!directory.exists()) {
|
||||
throw new RuntimeException("目录不存在: " + msgFileProperties.getDirectoryPath());
|
||||
}
|
||||
|
||||
File[] files = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".e")); // 过滤出.e文件
|
||||
if (files == null || files.length == 0) {
|
||||
throw new RuntimeException("目录中找不到 .e 文件,目录: " + msgFileProperties.getDirectoryPath());
|
||||
}
|
||||
|
||||
List<MsgFileDto> listData = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
// 获取文件的路径名
|
||||
String filePath = file.getPath();
|
||||
List<Map<String, Object>> fileData = parseELanguageFile(file);
|
||||
MsgFileDto msgFileDto = new MsgFileDto();
|
||||
msgFileDto.setFilePath(filePath);
|
||||
msgFileDto.setMgsList(fileData);
|
||||
listData.add(msgFileDto);
|
||||
}
|
||||
return listData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析E语言格式文件并返回List<Map<String, Object>>
|
||||
*
|
||||
* @param file 文件对象
|
||||
* @return 解析后的数据
|
||||
* @throws IOException 如果读取过程中出现错误
|
||||
*/
|
||||
private List<Map<String, Object>> parseELanguageFile(File file) throws IOException {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||
String line;
|
||||
boolean isDataSection = false;
|
||||
List<String> headers = new ArrayList<>();
|
||||
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.startsWith("<!")) {
|
||||
// 跳过头部信息
|
||||
continue;
|
||||
} else if (line.startsWith("@")) {
|
||||
// 解析表头
|
||||
headers = Arrays.asList(line.substring(1).trim().split("\t"));
|
||||
isDataSection = true;
|
||||
continue;
|
||||
} else if (line.startsWith("#") && isDataSection) {
|
||||
// 解析数据行
|
||||
String[] values = line.substring(1).trim().split("\t");
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
row.put(headers.get(i), values[i]);
|
||||
}
|
||||
result.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移动文件到指定目录
|
||||
*
|
||||
* @param sourcePath 源文件路径
|
||||
* @throws IOException 如果移动过程中出现错误
|
||||
*/
|
||||
public void moveFileToBackupDirectory(String sourcePath) throws IOException {
|
||||
Path source = Paths.get(sourcePath);
|
||||
if (!Files.exists(source)) {
|
||||
throw new IOException("Source file does not exist: " + sourcePath);
|
||||
}
|
||||
//获取备份目录路径
|
||||
String backupDir = createBackupDirectory();
|
||||
Path target = Paths.get(backupDir, source.getFileName().toString());
|
||||
|
||||
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据当前日期创建备份目录(1个月一个目录)
|
||||
*
|
||||
* @return 备份目录路径
|
||||
* @throws IOException 如果创建目录过程中出现错误
|
||||
*/
|
||||
private String createBackupDirectory() throws IOException {
|
||||
LocalDate now = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
String backupDirName = now.format(formatter);
|
||||
Path backupDirPath = Paths.get(msgFileProperties.getBaseBackupDir(), backupDirName);
|
||||
|
||||
if (!Files.exists(backupDirPath)) {
|
||||
Files.createDirectories(backupDirPath);
|
||||
}
|
||||
|
||||
return backupDirPath.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移动文件到指定目录
|
||||
*
|
||||
* @param sourcePath 源文件路径
|
||||
* @param targetDir 目标目录路径
|
||||
* @throws IOException 如果移动过程中出现错误
|
||||
*/
|
||||
public void moveFile(String sourcePath, String targetDir) throws IOException {
|
||||
Path source = Paths.get(sourcePath);
|
||||
Path target = Paths.get(targetDir, source.getFileName().toString());
|
||||
|
||||
if (!Files.exists(source)) {
|
||||
throw new IOException("Source file does not exist: " + sourcePath);
|
||||
}
|
||||
|
||||
if (!Files.exists(target.getParent())) {
|
||||
Files.createDirectories(target.getParent());
|
||||
}
|
||||
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
|
||||
}
|
83
msgdispatcher/src/main/resources/application-dev.yml
Normal file
83
msgdispatcher/src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,83 @@
|
||||
server:
|
||||
port: 8081
|
||||
# SpringBoot中我们既可以使用Tomcat作为Http服务,也可以用Undertow来代替。Undertow在高并发业务场景中,性能优于Tomcat
|
||||
undertow:
|
||||
threads:
|
||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
||||
io: 16
|
||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||
worker: 400
|
||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
||||
# 每块buffer的空间大小,越小的空间被利用越充分
|
||||
buffer-size: 1024
|
||||
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
||||
max-http-post-size: -1
|
||||
# 是否分配的直接内存
|
||||
direct-buffers: true
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: msgdispatcher
|
||||
#json格式化全局配置,相当于@JsonFormat
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
# 指定默认包含的熟悉,NON_NULL表示只序列化非空属性
|
||||
default-property-inclusion: non_null
|
||||
# 配置文件上传大小限制
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件最大大小
|
||||
max-file-size: 1024MB
|
||||
# 多个文件总大小
|
||||
max-request-size: 2048MB
|
||||
datasource:
|
||||
url: jdbc:postgresql://192.168.109.102:5432/das
|
||||
username: das
|
||||
password: qwaszx12
|
||||
# # redis相关配置
|
||||
data:
|
||||
redis:
|
||||
host: 192.168.109.195
|
||||
database: 0
|
||||
port: 6379
|
||||
password:
|
||||
client-type: lettuce
|
||||
|
||||
|
||||
# 配置 xml 文件所在位置 配置全局的 主键策略,默认为 ASSIGN_ID 默认为 【雪花算法】 , auto 自增
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.das.**.entity
|
||||
global-config:
|
||||
# 关闭MP3.0自带的banner
|
||||
banner: false
|
||||
db-config:
|
||||
id-type: ASSIGN_ID
|
||||
# 逻辑删除
|
||||
logic-not-delete-value: 0
|
||||
logic-delete-value: 1
|
||||
#字段策略
|
||||
insert-strategy: not_null
|
||||
update-strategy: not_null
|
||||
where-strategy: not_empty
|
||||
#驼峰下划线转换
|
||||
table-underline: true
|
||||
# 开启驼峰命名 默认开启驼峰命名
|
||||
# mybatis-plus配置控制台打印完整带参数SQL语句
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
cache-enabled: false
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
|
||||
logging:
|
||||
level:
|
||||
com:
|
||||
das: DEBUG
|
||||
|
||||
tdengine:
|
||||
password: taosdata
|
||||
url: jdbc:TAOS-RS://192.168.109.160:6041/das
|
||||
username: root
|
106
msgdispatcher/src/main/resources/application.yml
Normal file
106
msgdispatcher/src/main/resources/application.yml
Normal file
@ -0,0 +1,106 @@
|
||||
server:
|
||||
port: 8087
|
||||
# SpringBoot中我们既可以使用Tomcat作为Http服务,也可以用Undertow来代替。Undertow在高并发业务场景中,性能优于Tomcat
|
||||
undertow:
|
||||
threads:
|
||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
||||
io: 16
|
||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||
worker: 400
|
||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
||||
# 每块buffer的空间大小,越小的空间被利用越充分
|
||||
buffer-size: 1024
|
||||
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
||||
max-http-post-size: -1
|
||||
# 是否分配的直接内存
|
||||
direct-buffers: true
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: msgdispatcher
|
||||
#json格式化全局配置,相当于@JsonFormat
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
# 指定默认包含的熟悉,NON_NULL表示只序列化非空属性
|
||||
default-property-inclusion: non_null
|
||||
# 配置文件上传大小限制
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件最大大小
|
||||
max-file-size: 1024MB
|
||||
# 多个文件总大小
|
||||
max-request-size: 2048MB
|
||||
datasource:
|
||||
url: jdbc:postgresql://192.168.109.102:5432/das
|
||||
username: das
|
||||
password: qwaszx12
|
||||
# # redis相关配置
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
database: 0
|
||||
port: 6379
|
||||
password:
|
||||
client-type: lettuce
|
||||
|
||||
|
||||
# 配置 xml 文件所在位置 配置全局的 主键策略,默认为 ASSIGN_ID 默认为 【雪花算法】 , auto 自增
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.msgdispatcher.**.entity
|
||||
global-config:
|
||||
# 关闭MP3.0自带的banner
|
||||
banner: false
|
||||
db-config:
|
||||
id-type: ASSIGN_ID
|
||||
# 逻辑删除
|
||||
logic-not-delete-value: 0
|
||||
logic-delete-value: 1
|
||||
#字段策略
|
||||
insert-strategy: not_null
|
||||
update-strategy: not_null
|
||||
where-strategy: not_empty
|
||||
#驼峰下划线转换
|
||||
table-underline: true
|
||||
# 开启驼峰命名 默认开启驼峰命名
|
||||
# mybatis-plus配置控制台打印完整带参数SQL语句
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
cache-enabled: false
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: ERROR
|
||||
com:
|
||||
das: ERROR
|
||||
|
||||
tdengine:
|
||||
password: taosdata
|
||||
url: jdbc:TAOS-RS://192.168.109.160:6041/das
|
||||
username: root
|
||||
|
||||
|
||||
dingtalk:
|
||||
webhook:
|
||||
url: https://oapi.dingtalk.com/robot/send?sign=
|
||||
token: 5d3897e1b170ff89b641d136e96582931374b6dbc39e333ffc54d4d5c735b879
|
||||
secret: SECc871ad4be9c0f5f5b72f8a1519a196185d3bcc865ef9d1f184c9cb1a6d9f4649
|
||||
|
||||
dingtalk-user :
|
||||
#获取token接口
|
||||
getTokenUrl: https://oapi.dingtalk.com/gettoken?appkey=
|
||||
#获取用户信息接口
|
||||
getUserUrl: https://oapi.dingtalk.com/user/get?access_token=
|
||||
#开发者中心-应用列表中,应用的AppKey和AppSecret
|
||||
appKey:
|
||||
appSecret:
|
||||
|
||||
msg-file:
|
||||
#消息文件目录
|
||||
directoryPath: /home/dingding
|
||||
#备份目录
|
||||
baseBackupDir: /home/dingding/bak
|
Loading…
Reference in New Issue
Block a user