first commit
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
150
pom.xml
Normal file
150
pom.xml
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<?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.xiang</groupId>
|
||||||
|
<artifactId>script-common</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<spring.boot.version>2.7.18</spring.boot.version>
|
||||||
|
<spring.cloud.version>2021.0.8</spring.cloud.version>
|
||||||
|
<spring.cloud.alibaba.version>2021.1</spring.cloud.alibaba.version>
|
||||||
|
<mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
|
||||||
|
<rocketmq.version>5.1.4</rocketmq.version>
|
||||||
|
<mysql.version>8.0.33</mysql.version>
|
||||||
|
<fastjson2.version>2.0.51</fastjson2.version>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<guava.version>16.0.1</guava.version>
|
||||||
|
<mybatis-plus-spring-boot.version>3.5.14</mybatis-plus-spring-boot.version>
|
||||||
|
<mysql.version>8.0.33</mysql.version>
|
||||||
|
<lombok.version>1.18.30</lombok.version>
|
||||||
|
<spring-data-redis.version>3.23.6</spring-data-redis.version>
|
||||||
|
<rocketmq.version>2.2.3</rocketmq.version>
|
||||||
|
<spring.authorization.server.version>0.4.0</spring.authorization.server.version>
|
||||||
|
<redisson.version>3.15.1</redisson.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- -mysql -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>${mysql.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- druid 数据源 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>1.2.14</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis-Plus 核心依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus-spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 常用工具 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.18.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
<version>1.5.5.Final</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- http -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.5.13</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- collections -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>32.1.3-jre</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
<version>4.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.15.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- json -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>2.0.51</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 钉钉jar包 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun</groupId>
|
||||||
|
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
17
src/main/java/com/xiang/Application.java
Normal file
17
src/main/java/com/xiang/Application.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.xiang;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Application.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
log.info("script application start up!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 15:24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "dingtalk.robot")
|
||||||
|
public class DingTalkRobotProperties {
|
||||||
|
private Map<String, RobotConfig> properties;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2025-07-25 15:56
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class GlobalJacksonConfig {
|
||||||
|
|
||||||
|
public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_FORMAT);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ObjectMapper objectMapper() {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
// 设置null字段也序列化
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
// 设置LocalDateTime序列化
|
||||||
|
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||||
|
javaTimeModule.addSerializer(LocalDateTime .class, new LocalDateTimeSerializer(DATETIME_FORMATTER));
|
||||||
|
mapper.registerModule(javaTimeModule);
|
||||||
|
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/main/java/com/xiang/common/config/HttpConfig.java
Normal file
51
src/main/java/com/xiang/common/config/HttpConfig.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class HttpConfig {
|
||||||
|
@Bean
|
||||||
|
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
|
||||||
|
// 连接池管理器
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
|
||||||
|
// 整个连接池最大连接数
|
||||||
|
connectionManager.setMaxTotal(100);
|
||||||
|
// 每个主机的最大连接数
|
||||||
|
connectionManager.setDefaultMaxPerRoute(20);
|
||||||
|
|
||||||
|
// 请求配置
|
||||||
|
RequestConfig requestConfig = RequestConfig.custom()
|
||||||
|
// 建立连接的超时时间
|
||||||
|
.setConnectTimeout(5000)
|
||||||
|
// 响应超时时间
|
||||||
|
.setSocketTimeout(5000)
|
||||||
|
// 从连接池获取连接的超时时间
|
||||||
|
.setConnectionRequestTimeout(5000)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 创建 HttpClient
|
||||||
|
CloseableHttpClient httpClient = HttpClients.custom()
|
||||||
|
.setConnectionManager(connectionManager)
|
||||||
|
.setDefaultRequestConfig(requestConfig)
|
||||||
|
// 清理空闲连接
|
||||||
|
.evictIdleConnections(30, TimeUnit.SECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 配置给 RestTemplate 使用
|
||||||
|
return new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) {
|
||||||
|
return new RestTemplate(factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/main/java/com/xiang/common/config/RedisConfig.java
Normal file
49
src/main/java/com/xiang/common/config/RedisConfig.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.xiang.common.utils.RedisService;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(factory);
|
||||||
|
|
||||||
|
// 设置 key 和 hashKey 的序列化方式为 String
|
||||||
|
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||||
|
template.setKeySerializer(stringRedisSerializer);
|
||||||
|
template.setHashKeySerializer(stringRedisSerializer);
|
||||||
|
|
||||||
|
// 设置 value 和 hashValue 的序列化方式为 JSON 或其他合适格式
|
||||||
|
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
|
||||||
|
new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
|
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
|
||||||
|
|
||||||
|
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||||
|
|
||||||
|
template.afterPropertiesSet();
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
|
||||||
|
return new StringRedisTemplate(factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/main/java/com/xiang/common/config/RedisProperties.java
Normal file
22
src/main/java/com/xiang/common/config/RedisProperties.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2025-12-08 14:38
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "spring.data.redis")
|
||||||
|
public class RedisProperties {
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
private String port;
|
||||||
|
private String password;
|
||||||
|
private Integer database = 0;
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return "redis://" + host + ":" + port;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/com/xiang/common/config/RedissonConfig.java
Normal file
30
src/main/java/com/xiang/common/config/RedissonConfig.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import org.redisson.Redisson;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.redisson.config.Config;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2025-12-08 14:37
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(RedisProperties.class)
|
||||||
|
public class RedissonConfig {
|
||||||
|
@Bean(destroyMethod = "shutdown")
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public RedissonClient redissonClient(RedisProperties properties) {
|
||||||
|
|
||||||
|
Config config = new Config();
|
||||||
|
config.useSingleServer()
|
||||||
|
.setAddress(properties.getAddress())
|
||||||
|
.setDatabase(properties.getDatabase())
|
||||||
|
.setPassword(properties.getPassword());
|
||||||
|
|
||||||
|
return Redisson.create(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/com/xiang/common/config/RobotConfig.java
Normal file
17
src/main/java/com/xiang/common/config/RobotConfig.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 15:36
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RobotConfig {
|
||||||
|
private String name;
|
||||||
|
private String token;
|
||||||
|
private String secret;
|
||||||
|
private List<String> users;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.xiang.common.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class ScriptThreadFactory implements ThreadFactory {
|
||||||
|
private final String threadName;
|
||||||
|
private final boolean daemon;
|
||||||
|
|
||||||
|
private final AtomicInteger threadNum = new AtomicInteger();
|
||||||
|
|
||||||
|
public ScriptThreadFactory(String threadName, boolean daemon) {
|
||||||
|
this.threadName = threadName;
|
||||||
|
this.daemon = daemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Thread newThread(Runnable r) {
|
||||||
|
Thread thread = new Thread(r, this.threadName + "[#" + threadNum.incrementAndGet() + "]");
|
||||||
|
thread.setDaemon(this.daemon);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.xiang.common.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 16:13
|
||||||
|
*/
|
||||||
|
public interface BaseDingTalkBizType {
|
||||||
|
|
||||||
|
String getBizName();
|
||||||
|
String getDesc();
|
||||||
|
}
|
||||||
26
src/main/java/com/xiang/common/enums/DingTalkUrlEnum.java
Normal file
26
src/main/java/com/xiang/common/enums/DingTalkUrlEnum.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.xiang.common.enums;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum DingTalkUrlEnum {
|
||||||
|
/**
|
||||||
|
* 钉钉接口枚举
|
||||||
|
*/
|
||||||
|
DING_TALK_GET_ENTERPRISE_INTER_TOKEN("https://oapi.dingtalk.com/gettoken", "获取企业内部应用Token"),
|
||||||
|
DING_TALK_ASYNC_SEND_MESSAGE("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2", "异步发送工作通知"),
|
||||||
|
DING_TALK_CHAR_MESSAGE("https://oapi.dingtalk.com/chat/send", "发送消息到企业群旧版SDK"),
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
final String desc;
|
||||||
|
|
||||||
|
DingTalkUrlEnum(String url, String desc) {
|
||||||
|
this.url = url;
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.xiang.common.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
|
||||||
|
private final String ERROR_CODE = "500";
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
public BusinessException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = ERROR_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(String code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
146
src/main/java/com/xiang/common/utils/HttpService.java
Normal file
146
src/main/java/com/xiang/common/utils/HttpService.java
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package com.xiang.common.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2025-05-08 14:39
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class HttpService {
|
||||||
|
|
||||||
|
private static final int socketTimeOut = 10000;
|
||||||
|
private static final int connectTimeout = 10000;
|
||||||
|
private static final int connectionRequestTimeout = 3000;
|
||||||
|
private static final int defaultMaxPerRoute = 100;
|
||||||
|
private static final int maxTotal = 200;
|
||||||
|
|
||||||
|
private static final int LIVE_TIME = 5000;
|
||||||
|
|
||||||
|
private static final int ALIVE_STRATEGY = 30000;
|
||||||
|
|
||||||
|
private static final RequestConfig requestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(connectTimeout)
|
||||||
|
.setSocketTimeout(socketTimeOut)
|
||||||
|
.setConnectionRequestTimeout(connectionRequestTimeout)
|
||||||
|
.build();
|
||||||
|
// 使用连接池
|
||||||
|
private static final PoolingHttpClientConnectionManager connectionManager;
|
||||||
|
|
||||||
|
private static final CloseableHttpClient httpClient;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 确保使用 TLSv1.2
|
||||||
|
System.setProperty("https.protocols", "TLSv1.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
connectionManager = new PoolingHttpClientConnectionManager();
|
||||||
|
// 最大连接数
|
||||||
|
connectionManager.setMaxTotal(maxTotal);
|
||||||
|
// 每个主机的最大连接数
|
||||||
|
connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
|
||||||
|
connectionManager.setValidateAfterInactivity(LIVE_TIME);
|
||||||
|
|
||||||
|
|
||||||
|
httpClient = HttpClients.custom()
|
||||||
|
.setConnectionManager(connectionManager)
|
||||||
|
.setDefaultRequestConfig(requestConfig)
|
||||||
|
// 清理空闲连接
|
||||||
|
.evictIdleConnections(30, TimeUnit.SECONDS)
|
||||||
|
.setKeepAliveStrategy((response, context) -> ALIVE_STRATEGY)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doPost(String url, Map<String, String> header, String jsonParams) {
|
||||||
|
CloseableHttpResponse response = null;
|
||||||
|
String result = "";
|
||||||
|
try {
|
||||||
|
log.info("HTTP请求,请求地址===>{}, 请求头===>{}, 请求参数===>{}", url, JSON.toJSONString(header), jsonParams);
|
||||||
|
HttpPost httpPost = new HttpPost(url);
|
||||||
|
httpPost.addHeader("Content-Type", "application/json");
|
||||||
|
// 创建请求内容
|
||||||
|
StringEntity entity = new StringEntity(jsonParams, "utf-8");
|
||||||
|
httpPost.setEntity(entity);
|
||||||
|
// 设置请求头
|
||||||
|
if (null != header && !header.isEmpty()) {
|
||||||
|
Set<Map.Entry<String, String>> entries = header.entrySet();
|
||||||
|
for (Map.Entry<String, String> e : entries) {
|
||||||
|
httpPost.setHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = httpClient.execute(httpPost);
|
||||||
|
result = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||||
|
log.info("【POST请求】 请求地址===>{}, 响应结果==={}", url, result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("doPost异常", e);
|
||||||
|
} finally {
|
||||||
|
// 不关闭 httpClient
|
||||||
|
closeResource(response);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doGet(String url, Map<String, String> header, Map<String, String> param) {
|
||||||
|
CloseableHttpResponse response = null;
|
||||||
|
String result = "";
|
||||||
|
try {
|
||||||
|
String request = "";
|
||||||
|
if (MapUtils.isNotEmpty(param)) {
|
||||||
|
StringBuilder req = new StringBuilder("?");
|
||||||
|
for (Map.Entry<String, String> entry : param.entrySet()) {
|
||||||
|
req.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
||||||
|
}
|
||||||
|
request = req.substring(0, req.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet(url + request);
|
||||||
|
if (MapUtils.isNotEmpty(header)) {
|
||||||
|
for (Map.Entry<String, String> entry : header.entrySet()) {
|
||||||
|
httpGet.setHeader(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("doGet请求:请求头:{},请求地址:{}", header, url + request);
|
||||||
|
response = httpClient.execute(httpGet);
|
||||||
|
result = EntityUtils.toString(response.getEntity(), "utf-8");
|
||||||
|
log.info("【GET请求】, 请求地址===>{}, 响应结果===>{}", url + request, result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("doGet异常:", e);
|
||||||
|
} finally {
|
||||||
|
closeResource(response);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description 关闭资源
|
||||||
|
*/
|
||||||
|
private static void closeResource(Closeable... resources) {
|
||||||
|
try {
|
||||||
|
for (Closeable resource : resources) {
|
||||||
|
if (resource != null) {
|
||||||
|
resource.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
190
src/main/java/com/xiang/common/utils/RedisService.java
Normal file
190
src/main/java/com/xiang/common/utils/RedisService.java
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
package com.xiang.common.utils;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RedisService {
|
||||||
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Value("${spring.application.name}")
|
||||||
|
private String group;
|
||||||
|
|
||||||
|
public void set(String key, String value) {
|
||||||
|
redisTemplate.opsForValue().set(buildKey(group, key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String key, String value, long timeout, TimeUnit unit) {
|
||||||
|
redisTemplate.opsForValue().set(buildKey(group, key), value, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String group, String key, Object value) {
|
||||||
|
redisTemplate.opsForValue().set(buildKey(group, key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String group, String key, String value, long timeout, TimeUnit unit) {
|
||||||
|
redisTemplate.opsForValue().set(buildKey(group, key), value, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(String key) {
|
||||||
|
return redisTemplate.opsForValue().get(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(String group, String key) {
|
||||||
|
return redisTemplate.opsForValue().get(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean delKey(String group, String key) {
|
||||||
|
return redisTemplate.delete(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean delKey(String key) {
|
||||||
|
return delKey(group, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean deleteObject(Collection collection) {
|
||||||
|
return redisTemplate.delete(collection) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hasKey(String key) {
|
||||||
|
return hasKey(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hasKey(String group, String key) {
|
||||||
|
return redisTemplate.hasKey(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean expire(String key, long timeout, TimeUnit unit) {
|
||||||
|
return expire(group, key, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean expire(String group, String key, long timeout, TimeUnit unit) {
|
||||||
|
return redisTemplate.expire(buildKey(group, key), timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hSet(String key, String field, Object value) {
|
||||||
|
redisTemplate.opsForHash().put(buildKey(group, key), field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hSet(String group, String key, String field, Object value) {
|
||||||
|
redisTemplate.opsForHash().put(buildKey(group, key), field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object hGet(String key, String field) {
|
||||||
|
return redisTemplate.opsForHash().get(buildKey(group, key), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object hGet(String group, String key, String field) {
|
||||||
|
return redisTemplate.opsForHash().get(buildKey(group, key), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Object, Object> hGetAll(String key) {
|
||||||
|
return redisTemplate.opsForHash().entries(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Object, Object> hGetAll(String group, String key) {
|
||||||
|
return redisTemplate.opsForHash().entries(buildKey(group, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hDel(String key, Object... fields) {
|
||||||
|
return redisTemplate.opsForHash().delete(buildKey(group, key), fields) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hDel(String group, String key, Object... fields) {
|
||||||
|
return redisTemplate.opsForHash().delete(buildKey(group, key), fields) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hHasKey(String key, String field) {
|
||||||
|
return redisTemplate.opsForHash().hasKey(buildKey(group, key), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hHasKey(String group, String key, String field) {
|
||||||
|
return redisTemplate.opsForHash().hasKey(buildKey(group, key), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
|
||||||
|
return tryLock(group, key, value, timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean tryLock(String group, String key, String value, long timeout, TimeUnit unit) {
|
||||||
|
String redisKey = buildKey(group, key);
|
||||||
|
Boolean success = redisTemplate.opsForValue()
|
||||||
|
.setIfAbsent(redisKey, value, timeout, unit);
|
||||||
|
return Boolean.TRUE.equals(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean unlock(String key, String value) {
|
||||||
|
return unlock(group, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean unlock(String group, String key, String value) {
|
||||||
|
if (hasKey(buildKey(group, key), value)) {
|
||||||
|
String val = (String) get(buildKey(group, key), value);
|
||||||
|
if (StringUtils.equals(val, value)) {
|
||||||
|
return delKey(group, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean unlockLura(String key, String value) {
|
||||||
|
return unlockLura(group, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean unlockLura(String group, String key, String value) {
|
||||||
|
String redisKey = buildKey(group, key);
|
||||||
|
|
||||||
|
String luaScript =
|
||||||
|
"if redis.call('get', KEYS[1]) == ARGV[1] " +
|
||||||
|
"then return redis.call('del', KEYS[1]) " +
|
||||||
|
"else return 0 end";
|
||||||
|
|
||||||
|
Long result = executeLua(
|
||||||
|
luaScript,
|
||||||
|
Collections.singletonList(redisKey),
|
||||||
|
Collections.singletonList(value),
|
||||||
|
Long.class
|
||||||
|
);
|
||||||
|
|
||||||
|
return Objects.nonNull(result) && result > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T executeLua(String script, List<String> keys, List<Object> args, Class<T> resultType) {
|
||||||
|
DefaultRedisScript<T> redisScript = new DefaultRedisScript<>();
|
||||||
|
redisScript.setScriptText(script);
|
||||||
|
redisScript.setResultType(resultType);
|
||||||
|
|
||||||
|
return redisTemplate.execute(redisScript, keys, args.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildKey(String group, String key) {
|
||||||
|
return String.format("%s:%s", group, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的基本对象列表
|
||||||
|
*
|
||||||
|
* @param pattern 字符串前缀
|
||||||
|
*
|
||||||
|
* @return 对象列表
|
||||||
|
*/
|
||||||
|
public Collection<String> keys(String pattern) {
|
||||||
|
return keys(group, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> keys(String group, String pattern) {
|
||||||
|
return redisTemplate.keys(buildKey(group, pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/main/java/com/xiang/common/utils/RedissonService.java
Normal file
75
src/main/java/com/xiang/common/utils/RedissonService.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package com.xiang.common.utils;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.redisson.api.RLock;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2025-12-08 14:44
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RedissonService {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(RedissonService.class);
|
||||||
|
|
||||||
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
|
public <T> T tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, Supplier<T> supplier) {
|
||||||
|
RLock lock = redissonClient.getLock(key);
|
||||||
|
boolean flag = false;
|
||||||
|
try {
|
||||||
|
flag = lock.tryLock(waitTime, leaseTime, unit);
|
||||||
|
if (!flag) {
|
||||||
|
log.info("key:{}未拿到锁", key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return supplier.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (flag && lock.isHeldByCurrentThread()) {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, Supplier<T> supplier, Supplier<T> fallback) {
|
||||||
|
RLock lock = redissonClient.getLock(key);
|
||||||
|
boolean locked = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
locked = lock.tryLock(waitTime, leaseTime, unit);
|
||||||
|
if (!locked) {
|
||||||
|
// 拿不到锁 = fallback
|
||||||
|
return fallback.get();
|
||||||
|
}
|
||||||
|
return supplier.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return fallback.get();
|
||||||
|
} finally {
|
||||||
|
if (locked && lock.isHeldByCurrentThread()) {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T lock(String key, Supplier<T> supplier) {
|
||||||
|
RLock lock = redissonClient.getLock(key);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return supplier.get();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.xiang.common.utils.dingTalk;
|
||||||
|
|
||||||
|
import com.xiang.common.config.DingTalkRobotProperties;
|
||||||
|
import com.xiang.common.config.RobotConfig;
|
||||||
|
import com.xiang.common.enums.BaseDingTalkBizType;
|
||||||
|
import com.xiang.common.exception.BusinessException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 16:11
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public abstract class AbstractDingTalkFactory {
|
||||||
|
|
||||||
|
private final DingTalkRobotProperties dingTalkRobotProperties;
|
||||||
|
private final DingTalkSender dingTalkSender;
|
||||||
|
|
||||||
|
public abstract void sendMsg(String msg);
|
||||||
|
|
||||||
|
protected BizDingTalkClient getClient(BaseDingTalkBizType dingTalkBizTypeEnum) {
|
||||||
|
Map<String, RobotConfig> properties = dingTalkRobotProperties.getProperties();
|
||||||
|
if (MapUtils.isEmpty(properties)) {
|
||||||
|
throw new BusinessException("钉钉群聊配置信息错误");
|
||||||
|
}
|
||||||
|
RobotConfig robotConfig = properties.get(dingTalkBizTypeEnum.getBizName());
|
||||||
|
return new RobotBizDingTalkClient(robotConfig, dingTalkSender);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.xiang.common.utils.dingTalk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 15:28
|
||||||
|
*/
|
||||||
|
public interface BizDingTalkClient {
|
||||||
|
|
||||||
|
void sendDingTalkMsg(String msg);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.xiang.common.utils.dingTalk;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.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.xiang.common.config.RobotConfig;
|
||||||
|
import com.xiang.common.exception.BusinessException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉消息工具类
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 15:16
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class DingTalkSender {
|
||||||
|
|
||||||
|
private static final String MSG_TYPE_TEXT = "text";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送机器人消息到指定的群
|
||||||
|
* @param robotConfig 机器人配置文件
|
||||||
|
* @param msg 消息内容
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String sendRobotMessage(RobotConfig robotConfig, String msg) {
|
||||||
|
try {
|
||||||
|
return doSendMsg(robotConfig, msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("钉钉机器人消息发送失败,业务线===>{}", robotConfig.getName());
|
||||||
|
throw new BusinessException("钉钉消息发送失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送
|
||||||
|
* @param robotConfig 机器人配置信息
|
||||||
|
* @param msg 发送的消息
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private String doSendMsg(RobotConfig robotConfig, String msg) throws Exception {
|
||||||
|
String robotSecret = robotConfig.getSecret().trim();
|
||||||
|
String robotToken = robotConfig.getToken().trim();
|
||||||
|
List<String> userIds = robotConfig.getUsers();
|
||||||
|
Long timestamp = System.currentTimeMillis();
|
||||||
|
String stringToSign = timestamp + "\n" + robotSecret;
|
||||||
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
|
mac.init(new SecretKeySpec(robotSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||||
|
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
|
||||||
|
String sign = URLEncoder.encode(Base64.getEncoder().encodeToString(signData), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
//sign字段和timestamp字段必须拼接到请求URL上,否则会出现 310000 的错误信息
|
||||||
|
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign=" + sign + "×tamp=" + timestamp);
|
||||||
|
OapiRobotSendRequest req = new OapiRobotSendRequest();
|
||||||
|
|
||||||
|
//定义文本内容
|
||||||
|
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
|
||||||
|
text.setContent(msg);
|
||||||
|
//定义 @ 对象
|
||||||
|
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||||
|
at.setAtUserIds(userIds);
|
||||||
|
//设置消息类型
|
||||||
|
req.setMsgtype(MSG_TYPE_TEXT);
|
||||||
|
req.setText(text);
|
||||||
|
req.setAt(at);
|
||||||
|
OapiRobotSendResponse rsp = client.execute(req, robotToken);
|
||||||
|
return rsp.getBody();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.xiang.common.utils.dingTalk;
|
||||||
|
|
||||||
|
import com.xiang.common.config.RobotConfig;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: xiang
|
||||||
|
* @Date: 2026-01-04 15:28
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RobotBizDingTalkClient implements BizDingTalkClient {
|
||||||
|
|
||||||
|
private final RobotConfig robotConfig;
|
||||||
|
private final DingTalkSender dingTalkSender;
|
||||||
|
@Override
|
||||||
|
public void sendDingTalkMsg(String msg) {
|
||||||
|
try {
|
||||||
|
dingTalkSender.sendRobotMessage(robotConfig, msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("钉钉消息发送失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/resources/application-local.yml
Normal file
25
src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://120.27.153.87:3306/script_common?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: root
|
||||||
|
password: sdkljfikdfn@123
|
||||||
|
druid:
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 60000
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com
|
||||||
|
port: 6379
|
||||||
|
password: Xiang0000
|
||||||
|
database: 11
|
||||||
|
timeout: 3000
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 20
|
||||||
|
max-idle: 10
|
||||||
|
min-idle: 2
|
||||||
|
max-wait: 3000
|
||||||
7
src/main/resources/application.yml
Normal file
7
src/main/resources/application.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
|
application:
|
||||||
|
name: script
|
||||||
Reference in New Issue
Block a user