Merge branch 'master' into feat/auth_dev_v1

This commit is contained in:
xiang
2025-08-23 22:24:17 +08:00
10 changed files with 116 additions and 40 deletions

16
pom.xml
View File

@@ -125,10 +125,11 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <!-- <dependency>-->
<groupId>org.springframework.boot</groupId> <!-- <groupId>org.springframework.boot</groupId>-->
<artifactId>spring-boot-starter-actuator</artifactId> <!-- <artifactId>spring-boot-starter-actuator</artifactId>-->
</dependency> <!-- <version>${spring.boot.version}</version>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
@@ -174,6 +175,13 @@
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<!-- ssh -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -8,7 +8,7 @@
<artifactId>xservice-basic</artifactId> <artifactId>xservice-basic</artifactId>
<version>1.1</version> <version>1.1</version>
</parent> </parent>
<version>1.2</version> <version>1.3</version>
<artifactId>xservice-common</artifactId> <artifactId>xservice-common</artifactId>

View File

@@ -0,0 +1,39 @@
package com.xiang.xservice.basic.utils;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import java.util.concurrent.ConcurrentHashMap;
public class SSHManager {
private static final ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
public static void createTunnel(String key, String sshHost, int sshPort,
String sshUser, String sshPassword,
int localPort, String remoteHost, int remotePort) throws Exception {
if (sessionMap.containsKey(key) && sessionMap.get(key).isConnected()) {
return; // 已存在
}
JSch jsch = new JSch();
Session session = jsch.getSession(sshUser, sshHost, sshPort);
session.setPassword(sshPassword);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
session.setPortForwardingL(localPort, remoteHost, remotePort);
sessionMap.put(key, session);
System.out.println("SSH Tunnel for [" + key + "] established.");
}
public static void closeTunnel(String key) {
Session session = sessionMap.get(key);
if (session != null && session.isConnected()) {
session.disconnect();
sessionMap.remove(key);
System.out.println("SSH Tunnel for [" + key + "] closed.");
}
}
}

View File

@@ -10,7 +10,7 @@
</parent> </parent>
<artifactId>xservice-message-starter</artifactId> <artifactId>xservice-message-starter</artifactId>
<version>1.1</version> <version>2.0</version>
<properties> <properties>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
@@ -22,7 +22,7 @@
<dependency> <dependency>
<groupId>com.xiang</groupId> <groupId>com.xiang</groupId>
<artifactId>xservice-common</artifactId> <artifactId>xservice-common</artifactId>
<version>1.0</version> <version>1.2</version>
</dependency> </dependency>
<!-- 钉钉jar包 --> <!-- 钉钉jar包 -->

View File

@@ -12,6 +12,7 @@ import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response; import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.dingtalk.api.response.OapiRobotSendResponse; import com.dingtalk.api.response.OapiRobotSendResponse;
import com.xiang.xservice.basic.xservice.dingTalk.enums.DingTalkUrlEnum; import com.xiang.xservice.basic.xservice.dingTalk.enums.DingTalkUrlEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -46,53 +47,40 @@ public class DingTalkService {
private static final String CLIENT_SECRET = "wyapsH6y8P1K_wuTPKGKwG0mquj1uth9Dxn6HcRpta3sh8Syukl0C8nOmR1PeBzs"; private static final String CLIENT_SECRET = "wyapsH6y8P1K_wuTPKGKwG0mquj1uth9Dxn6HcRpta3sh8Syukl0C8nOmR1PeBzs";
private static final String GRANT_TYPE = "client_credentials"; private static final String MSG_TYPE_TEXT = "text";
private static final String USER_ID = "450841600726084717";
private static final String MSG_TYPE = "text";
/** /**
* 自定义机器人token * 发送机器人消息到指定的群
*/ * @param robotSecret 机器人密钥
private static final String CUSTOM_ROBOT_TOKEN = "4709b708d961846e0aee523c5abc3b67e8fa424ee292501d85efd4e504f15a8b"; * @param robotToken 机器人token
* @param userIds @对象
/** * @param msg 消息内容
* 密钥
*/
private static final String SECRET = "SEC768ed578c0fb31a9aec84b1c1db4f195f5aca203985bbb9d549e23e41c8874d1";
/**
* 发送机器人消息
* @param msg
* @return * @return
* @throws Exception * @throws Exception
*/ */
public String sendRobotMessage(String msg) throws Exception { public String sendRobotMessage(String robotSecret, String robotToken, List<String> userIds, String msg) throws Exception {
Long timestamp = System.currentTimeMillis(); Long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + SECRET; String stringToSign = timestamp + "\n" + robotSecret;
Mac mac = Mac.getInstance("HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); mac.init(new SecretKeySpec(robotSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
String sign = URLEncoder.encode(Base64.getEncoder().encodeToString(signData), StandardCharsets.UTF_8); String sign = URLEncoder.encode(Base64.getEncoder().encodeToString(signData), StandardCharsets.UTF_8);
//sign字段和timestamp字段必须拼接到请求URL上否则会出现 310000 的错误信息 //sign字段和timestamp字段必须拼接到请求URL上否则会出现 310000 的错误信息
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign=" + sign + "&timestamp=" + timestamp); DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign=" + sign + "&timestamp=" + timestamp);
OapiRobotSendRequest req = new OapiRobotSendRequest(); OapiRobotSendRequest req = new OapiRobotSendRequest();
/**
* 发送文本消息
*/
//定义文本内容 //定义文本内容
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent(msg); text.setContent(msg);
//定义 @ 对象 //定义 @ 对象
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At(); OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
at.setAtUserIds(List.of(USER_ID)); at.setAtUserIds(userIds);
//设置消息类型 //设置消息类型
req.setMsgtype("text"); req.setMsgtype(MSG_TYPE_TEXT);
req.setText(text); req.setText(text);
req.setAt(at); req.setAt(at);
OapiRobotSendResponse rsp = client.execute(req, CUSTOM_ROBOT_TOKEN); OapiRobotSendResponse rsp = client.execute(req, robotToken);
return rsp.getBody(); return rsp.getBody();
} }
@@ -110,7 +98,7 @@ public class DingTalkService {
OapiChatSendRequest.Text text = new OapiChatSendRequest.Text(); OapiChatSendRequest.Text text = new OapiChatSendRequest.Text();
text.setContent(message); text.setContent(message);
msg.setText(text); msg.setText(text);
msg.setMsgtype("text"); msg.setMsgtype(MSG_TYPE_TEXT);
req.setMsg(msg); req.setMsg(msg);
OapiChatSendResponse rsp = client.execute(req, token); OapiChatSendResponse rsp = client.execute(req, token);
log.info("[DingTalk] send chat message, req:{}, token:{}, response:{}", JSONObject.toJSONString(req), token, JSONObject.toJSONString(rsp)); log.info("[DingTalk] send chat message, req:{}, token:{}, response:{}", JSONObject.toJSONString(req), token, JSONObject.toJSONString(rsp));
@@ -133,7 +121,7 @@ public class DingTalkService {
req.setUseridList(userId); req.setUseridList(userId);
req.setToAllUser(false); req.setToAllUser(false);
OapiMessageCorpconversationAsyncsendV2Request.Msg obj1 = new OapiMessageCorpconversationAsyncsendV2Request.Msg(); OapiMessageCorpconversationAsyncsendV2Request.Msg obj1 = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
obj1.setMsgtype(MSG_TYPE); obj1.setMsgtype(MSG_TYPE_TEXT);
OapiMessageCorpconversationAsyncsendV2Request.Text obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Text(); OapiMessageCorpconversationAsyncsendV2Request.Text obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Text();
obj2.setContent(message); obj2.setContent(message);
obj1.setText(obj2); obj1.setText(obj2);

View File

@@ -10,11 +10,19 @@
</parent> </parent>
<artifactId>xservice-mysql-starter</artifactId> <artifactId>xservice-mysql-starter</artifactId>
<version>1.1</version> <version>2.0</version>
<properties> <properties>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies>
<dependency>
<groupId>com.xiang</groupId>
<artifactId>xservice-common</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
</project> </project>

View File

@@ -29,7 +29,7 @@ public class DynamicDataSourceConfig {
targetDataSources.put(key, builder.build()); targetDataSources.put(key, builder.build());
}); });
DynamicRoutingDataSource routing = new DynamicRoutingDataSource(); DynamicRoutingDataSource routing = new DynamicRoutingDataSource(props);
routing.setDefaultTargetDataSource(targetDataSources.get(props.getPrimary())); routing.setDefaultTargetDataSource(targetDataSources.get(props.getPrimary()));
routing.setTargetDataSources(targetDataSources); routing.setTargetDataSources(targetDataSources);
return routing; return routing;

View File

@@ -12,5 +12,12 @@ public class DataSourceProperty {
private String username; private String username;
private String password; private String password;
private String driverClassName = "com.mysql.cj.jdbc.Driver"; private String driverClassName = "com.mysql.cj.jdbc.Driver";
private Boolean sshConnect;
private String sshHost;
private Integer sshPort = 22;
private String sshUser;
private String sshPassword;
private Integer localPort; // 本地转发端口
private String remoteHost; // 远端数据库 host
private Integer remotePort; // 远端数据库 port
} }

View File

@@ -1,11 +1,37 @@
package com.xiang.xservice.mysql.service; package com.xiang.xservice.mysql.service;
import com.xiang.xservice.basic.utils.SSHManager;
import com.xiang.xservice.mysql.config.DynamicDataSourceContext; import com.xiang.xservice.mysql.config.DynamicDataSourceContext;
import com.xiang.xservice.mysql.entity.DataSourceProperty;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import com.xiang.xservice.mysql.entity.DynamicDataSourceProperties;
@RequiredArgsConstructor
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private final DynamicDataSourceProperties dynamicDataSourceProperties;
@Override @Override
protected Object determineCurrentLookupKey() { protected Object determineCurrentLookupKey() {
return DynamicDataSourceContext.get(); String key = DynamicDataSourceContext.get();
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(key);
if (Boolean.TRUE.equals(dataSourceProperty.getSshConnect())) {
try {
SSHManager.createTunnel(
key,
dataSourceProperty.getSshHost(),
dataSourceProperty.getSshPort(),
dataSourceProperty.getSshUser(),
dataSourceProperty.getSshPassword(),
dataSourceProperty.getLocalPort(),
dataSourceProperty.getRemoteHost(),
dataSourceProperty.getRemotePort()
);
} catch (Exception e) {
throw new RuntimeException("Failed to establish SSH tunnel for " + key, e);
}
}
return key;
} }
} }

View File

@@ -22,7 +22,7 @@
<dependency> <dependency>
<groupId>com.xiang</groupId> <groupId>com.xiang</groupId>
<artifactId>xservice-common</artifactId> <artifactId>xservice-common</artifactId>
<version>1.0</version> <version>1.2</version>
</dependency> </dependency>
</dependencies> </dependencies>