feat:first commit

This commit is contained in:
Zhujx
2025-07-16 15:05:59 +08:00
parent ec437bb088
commit 767bce5ce8
16 changed files with 649 additions and 0 deletions

58
domain/pom.xml Executable file
View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xiang</groupId>
<artifactId>xservice-scirpt</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>domain</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.xiang</groupId>
<artifactId>facade</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alidns20150109</artifactId>
<version>3.4.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.0.RELEASE</version>
<configuration>
<classifier>exec</classifier>
<!-- 指定该Main Class为全局的唯一入口 -->
<mainClass>com.xiang.DomainApplication</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package com.xiang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @Author: xiang
* @Date: 2025-06-10 16:43
*/
@SpringBootApplication
@EnableScheduling
public class DomainApplication {
public static void main(String[] args) {
SpringApplication.run(DomainApplication.class, args);
}
}

View File

@@ -0,0 +1,51 @@
package com.xiang.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
/**
* @Author: xiang
* @Date: 2025-05-09 14:09
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private String code;
private String message;
private List<T> data;
public static Result<Void> success(String message) {
return new Result<Void>("200", message, null);
}
public static <T> Result<T> success(String message, List<T> data) {
return new Result<T>("200", message, data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<T>("200", message, Collections.singletonList(data));
}
public static <T> Result<T> error(String message) {
return new Result<T>("500", message, null);
}
public static <T> Result<T> error(String message, T data) {
return new Result<T>("500", message, Collections.singletonList(data));
}
public static <T> Result<T> error(String message, List<T> data) {
return new Result<T>("500", message, data);
}
public static Result<Void> error(String code, String message) {
return new Result<Void>(code, message, null);
}
}

View File

@@ -0,0 +1,54 @@
package com.xiang.controller;
import com.xiang.common.Result;
import com.xiang.service.DomainService;
import com.xiang.utils.IpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.time.LocalDateTime;
/**
* 域名动态解析
*
* @Author: xiang
* @Date: 2025-06-10 16:44
*/
@RestController
@RequestMapping("/system/domain")
@Slf4j
@RequiredArgsConstructor
public class DynamicDomainController {
private final DomainService domainService;
@GetMapping("/getIp")
public Result<String> getPublicIp() {
String publicIp;
try {
publicIp = IpUtils.getPublicIp();
} catch (IOException e) {
log.error("获取公网IP失败 time:{}", LocalDateTime.now(), e);
return Result.error("获取公网IP失败");
}
return Result.success("获取公网IP成功", publicIp);
}
@PostMapping("/ddns")
public Result<Void> dynamicDomainAnalysis() {
try {
String publicIp = IpUtils.getPublicIp();
log.info("获取公网IP成功time:{}, ip:{}", LocalDateTime.now(), publicIp);
domainService.dynamicDomainAnalysis(publicIp);
return Result.success("获取公网IP成功");
} catch (Exception e) {
log.error("获取公网IP失败 time:{}", LocalDateTime.now(), e);
return Result.error("获取公网IP失败");
}
}
}

View File

@@ -0,0 +1,22 @@
package com.xiang.entity.resp;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @Author: xiang
* @Date: 2025-06-11 15:55
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PublicIpResp {
private String code;
private String message;
private List<String> data;
}

View File

@@ -0,0 +1,38 @@
package com.xiang.schedule;
import com.xiang.service.DomainServiceImpl;
import com.xiang.utils.IpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @Author: xiang
* @Date: 2025-06-10 17:21
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class DynamicDomainSchedule {
private final DomainServiceImpl domainService;
@Scheduled(cron = "0 0/5 * * * ? ")
public void dynamicDomainSchedule() {
String publicIp = "";
try {
publicIp = IpUtils.getPublicIp();
} catch (Exception e) {
log.error("获取公网ip失败", e);
}
if (StringUtils.isNotBlank(publicIp)) {
try {
domainService.dynamicDomainAnalysis(publicIp);
} catch (Exception e) {
log.error("动态解析公网ip失败, ip:{}", publicIp, e);
}
}
}
}

View File

@@ -0,0 +1,14 @@
package com.xiang.service;
/**
* @Author: xiang
* @Date: 2025-06-10 16:48
*/
public interface DomainService {
/**
* 动态域名解析
* @param publicIp 动态ip
*/
void dynamicDomainAnalysis(String publicIp) throws Exception;
}

View File

@@ -0,0 +1,101 @@
package com.xiang.service;
import com.aliyun.alidns20150109.Client;
import com.aliyun.alidns20150109.models.AddDomainRecordRequest;
import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsRequest;
import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsResponse;
import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsResponseBody;
import com.aliyun.alidns20150109.models.UpdateDomainRecordRequest;
import com.aliyun.teaopenapi.models.Config;
import com.xiang.dingTalk.service.DingTalkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: xiang
* @Date: 2025-06-10 16:48
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class DomainServiceImpl implements DomainService {
private static final String ACCESS_KEY_ID = "LTAI5tDMjaVF8Bbqcpp4dmvP";
private static final String ACCESS_KEY_SECRET = "nkmnaNjWQy5984C5kjyS0oDmdMKGQd";
/**
* 根域名
*/
private static final String DOMAIN_NAME = "xiangtech.xyz";
/**
* 主机记录,例如 home.example.com
*/
@Value("${aliyun.dns.RR}")
private String rr;
private static final String TYPE = "A";
private final DingTalkService dingTalkService;
@Value("${DingTalk.chatId}")
private String chatId;
@Override
public void dynamicDomainAnalysis(String publicIp) throws Exception {
Client client = createClient();
// 查询记录
DescribeSubDomainRecordsRequest query = new DescribeSubDomainRecordsRequest()
.setSubDomain(rr + "." + DOMAIN_NAME)
.setType(TYPE);
DescribeSubDomainRecordsResponse response = client.describeSubDomainRecords(query);
List<DescribeSubDomainRecordsResponseBody.DescribeSubDomainRecordsResponseBodyDomainRecordsRecord> records =
response.getBody().getDomainRecords().getRecord();
if (records.isEmpty()) {
log.info("未找到记录,添加记录..., ip:{}", publicIp);
addDnsRecord(client, publicIp);
dingTalkService.sendChatMessage(chatId, "动态解析公网ip成功域名" + rr + "." + DOMAIN_NAME + ", 新ip:" + publicIp);
} else {
String recordId = records.get(0).getRecordId();
String currentValue = records.get(0).getValue();
if (!publicIp.equals(currentValue)) {
log.info("IP变更更新记录...,ip:{}", publicIp);
updateDnsRecord(client, recordId, publicIp);
dingTalkService.sendChatMessage(chatId, "动态解析公网ip成功域名" + rr + "." + DOMAIN_NAME + ", 新ip:" + publicIp);
} else {
log.info("ip未变更无需修改ip:{}", publicIp);
}
}
}
private Client createClient() throws Exception {
Config config = new Config()
.setAccessKeyId(ACCESS_KEY_ID)
.setAccessKeySecret(ACCESS_KEY_SECRET)
.setEndpoint("alidns.cn-hangzhou.aliyuncs.com");
return new Client(config);
}
private void updateDnsRecord(Client client, String recordId, String newIp) throws Exception {
UpdateDomainRecordRequest request = new UpdateDomainRecordRequest()
.setRecordId(recordId)
.setRR(rr)
.setType(TYPE)
.setValue(newIp);
client.updateDomainRecord(request);
log.info("更新成功: newIP:{}", newIp);
}
private void addDnsRecord(Client client, String ip) throws Exception {
AddDomainRecordRequest request = new AddDomainRecordRequest()
.setDomainName(DOMAIN_NAME)
.setRR(rr)
.setType(TYPE)
.setValue(ip);
client.addDomainRecord(request);
log.info("添加成功: ip:{}", ip);
}
}

View File

@@ -0,0 +1,121 @@
package com.xiang.utils;
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.util.EntityUtils;
import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
/**
* @Author: xiang
* @Date: 2025-05-08 14:39
*/
@Slf4j
public class HttpUtils {
private final static int socketTimeOut = 60 * 1000;
private final static int connectTimeout = 60 * 1000;
private final static int connectionRequestTimeout = 15 * 1000;
private final static int defaultMaxPerRoute = 500;
private final static int maxTotal = 2000;
public static String doPost(String url, Map<String, String> header, String jsonParams) {
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间
.setConnectTimeout(connectTimeout)
// 设置Socket超时时间
.setSocketTimeout(socketTimeOut)
.setConnectionRequestTimeout(connectionRequestTimeout)
.build();
//创建httpClient对象
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response = null;
String result = "";
try {
// 创建http请求
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/json");
// 创建请求内容
StringEntity entity = new StringEntity(jsonParams, "utf-8");
entity.setContentType("application/json");
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");
} catch (Exception e) {
log.error("doPost异常", e);
} finally {
//关闭资源
closeResource(response, httpClient);
}
return result;
}
public static String doGet(String url, Map<String, String> header, Map<String, String> param) {
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间
.setConnectTimeout(connectTimeout)
// 设置Socket超时时间
.setSocketTimeout(socketTimeOut)
.setConnectionRequestTimeout(connectionRequestTimeout)
.build();
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
CloseableHttpResponse response;
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);
httpGet.addHeader("Content-Type", "application/json");
if (MapUtils.isNotEmpty(header)) {
for (Map.Entry<String, String> entry : header.entrySet()) {
httpGet.setHeader(entry.getKey(), entry.getValue());
}
}
log.info("doGet请求请求地址{}", url + request);
response = httpClient.execute(httpGet);
result = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
log.error("doGet异常", e);
}
return result;
}
/**
* @Description 关闭资源
*/
private static void closeResource(Closeable... resources) {
try {
for (Closeable resource : resources) {
if (resource != null) {
resource.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,22 @@
package com.xiang.utils;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.util.Map;
/**
* @Author: xiang
* @Date: 2025-06-10 16:50
*/
public class IpUtils {
private final static String PUBLIC_IP_URL = "https://api-ipv4.ip.sb/ip";
public static String getPublicIp() throws IOException {
Map<String, String> header = Maps.newHashMap();
header.put("User-Agent", "Mozilla/5.0");
return HttpUtils.doGet(PUBLIC_IP_URL, header, null).trim();
}
}

View File

@@ -0,0 +1,10 @@
spring:
datasource:
url: jdbc:mysql:///xservice-script?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
aliyun:
dns:
RR: test21

View File

@@ -0,0 +1,10 @@
spring:
datasource:
url: jdbc:mysql://172.28.159.213:3306/xservice-script?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
aliyun:
dns:
RR: client

View File

@@ -0,0 +1,10 @@
spring:
datasource:
url: jdbc:mysql://172.28.159.213:3306/xservice-script?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
aliyun:
dns:
RR: general

View File

@@ -0,0 +1,18 @@
mybatis:
mapper-locations:
- classpath*:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
DingTalk:
# 钉钉消息用户,用逗号隔开
userList: "450841600726084717"
# 钉钉消息群ID需要调用/chat/create api创建群返回
chatId: "chatd16d8daeea33b36b73588c676d508096"
server:
port: 8080
spring:
profiles:
active: local

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 应用名称:和统一配置中的项目代码保持一致(小写) -->
<property name="APP_NAME" value="xservice-script"/>
<contextName>${APP_NAME}</contextName>
<!--日志文件保留天数 -->
<property name="LOG_MAX_HISTORY" value="30"/>
<!--应用日志文件保存路径 -->
<!--在没有定义${LOG_HOME}系统变量的时候,可以设置此本地变量。提交测试、上线时,要将其注释掉,使用系统变量。 -->
<property name="LOG_HOME" value="logs/${APP_NAME}"/>
<!--<property name="LOG_HOME" msg="/home/logs/${APP_NAME}" />-->
<!--控制台输出appender-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--设置输出格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>%boldGreen(%contextName): %boldCyan(%d{yyyy-MM-dd HH:mm:ss:SSS}) %highlight([%c]) %boldMagenta([%t]) %boldCyan([%L]) %highlight([traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}]) %boldGreen([%p]) - %msg%n
</pattern>
<!--设置编码-->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件:主项目日志 -->
<appender name="APP_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>${LOG_MAX_HISTORY}</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%c类名%t表示线程名%L行 %p日志级别 %msg日志消息%n是换行符 -->
<pattern>%contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 此日志文件只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 按照每天生成日志文件:主项目日志 -->
<appender name="APP_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>${LOG_MAX_HISTORY}</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%c类名%t表示线程名%L行 %p日志级别 %msg日志消息%n是换行符 -->
<pattern>%contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 按照每天生成日志文件:主项目日志 -->
<appender name="APP_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>${LOG_MAX_HISTORY}</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%c类名%t表示线程名%L行 %p日志级别 %msg日志消息%n是换行符 -->
<pattern>%contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 此日志文件只记录error级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--日志输出到文件-->
<root level="info">
<appender-ref ref="APP_DEBUG"/>
<appender-ref ref="APP_INFO"/>
<appender-ref ref="APP_ERROR"/>
<appender-ref ref="console"/>
</root>
<!-- mybatis 日志级别 -->
<logger name="com.xiang" level="debug"/>
</configuration>

View File

@@ -244,6 +244,7 @@ public class FundInfoQueryJob {
if (Objects.equals(type, 1)) {
code = Arrays.stream(codeArr.split(", ")).collect(Collectors.toList());
}
return Lists.newArrayList();
} else {
code = lists.stream().map(XbFundList::getCode).collect(Collectors.toList());
}