feat: 初始化 rkutils 项目
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+29
@@ -24,3 +24,32 @@
|
|||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
|
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.log
|
||||||
|
*.flattened-pom.xml
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/
|
||||||
|
nbbuild/
|
||||||
|
dist/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
### Mac files ###
|
||||||
|
*.DS_Store
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<?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>org.noear</groupId>
|
||||||
|
<artifactId>solon-parent</artifactId>
|
||||||
|
<version>3.9.0</version>
|
||||||
|
<relativePath />
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.renkang</groupId>
|
||||||
|
<artifactId>rkutils</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<description>rkutils project for Solon</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-lib</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-scheduling-simple</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-logging-logback-jakarta</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Source: https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>2.0.60</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-net-httputils</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.noear</groupId>
|
||||||
|
<artifactId>solon-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>tencent</id>
|
||||||
|
<url>https://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.renkang.rkutils;
|
||||||
|
|
||||||
|
import com.renkang.rkutils.service.LogFileService;
|
||||||
|
import org.noear.solon.Solon;
|
||||||
|
import org.noear.solon.annotation.SolonMain;
|
||||||
|
import org.noear.solon.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@EnableScheduling
|
||||||
|
@SolonMain
|
||||||
|
public class App {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Solon.start(App.class, args);
|
||||||
|
LogFileService logFileService = Solon.context().getBean(LogFileService.class);
|
||||||
|
logFileService.downloadLogFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.renkang.rkutils.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.noear.solon.annotation.Configuration;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Inject("${base}")
|
||||||
|
@Data
|
||||||
|
public class BaseConfig {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String gatewayUrl;
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.renkang.rkutils.config;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.noear.solon.annotation.Configuration;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Inject("${file}")
|
||||||
|
@Data
|
||||||
|
public class LogFileConfig {
|
||||||
|
private String path;
|
||||||
|
private List<ServiceItem> services;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class ServiceItem {
|
||||||
|
private String name;
|
||||||
|
private int insCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.renkang.rkutils.service;
|
||||||
|
|
||||||
|
public interface LogFileService {
|
||||||
|
public void downloadLogFile();
|
||||||
|
}
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
package com.renkang.rkutils.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.renkang.rkutils.config.BaseConfig;
|
||||||
|
import com.renkang.rkutils.config.LogFileConfig;
|
||||||
|
import com.renkang.rkutils.service.LogFileService;
|
||||||
|
import com.renkang.rkutils.vo.FileInfo;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
import org.noear.solon.core.util.DateUtil;
|
||||||
|
import org.noear.solon.net.http.HttpResponse;
|
||||||
|
import org.noear.solon.net.http.HttpUtils;
|
||||||
|
import util.RSAUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class LogFileServiceImpl implements LogFileService {
|
||||||
|
@Inject
|
||||||
|
private LogFileConfig logFileConfig;
|
||||||
|
@Inject
|
||||||
|
private BaseConfig baseConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadLogFile() {
|
||||||
|
String path = logFileConfig.getPath();
|
||||||
|
log.info("开始下载日志文件: {}", path);
|
||||||
|
|
||||||
|
String username = baseConfig.getUsername();
|
||||||
|
String password = baseConfig.getPassword();
|
||||||
|
String gatewayUrl = baseConfig.getGatewayUrl();
|
||||||
|
String publicKey = baseConfig.getPublicKey();
|
||||||
|
try {
|
||||||
|
// 使用RSA公钥加密密码
|
||||||
|
String encryptedPassword = RSAUtil.encryptByPublicKey(password, publicKey);
|
||||||
|
|
||||||
|
log.info("密码已加密");
|
||||||
|
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("username", username);
|
||||||
|
jsonObject.put("password", encryptedPassword);
|
||||||
|
// 发送登录请求,使用加密后的密码
|
||||||
|
String response = HttpUtils.http(gatewayUrl + "/sys/mLogin")
|
||||||
|
.timeout(20)
|
||||||
|
.bodyOfJson(jsonObject.toJSONString())
|
||||||
|
.post();
|
||||||
|
JSONObject body = JSONObject.parseObject(response);
|
||||||
|
|
||||||
|
log.info("登录返回: {}", body);
|
||||||
|
boolean success = body.getBoolean("success");
|
||||||
|
if (!success) {
|
||||||
|
log.info("登录结果: {}", "失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String token = body.getJSONObject("result").getString("token");
|
||||||
|
log.info("登录成功,获取到的token为: {}", token);
|
||||||
|
|
||||||
|
for (LogFileConfig.ServiceItem serviceItem : logFileConfig.getServices()) {
|
||||||
|
String serviceName = serviceItem.getName();
|
||||||
|
int insCount = serviceItem.getInsCount();
|
||||||
|
log.info("开始下载服务: {}", serviceName);
|
||||||
|
String downloadUrl = gatewayUrl + "/" + serviceName + "/service/log/files?service=" + serviceName;
|
||||||
|
|
||||||
|
try (HttpResponse resp = HttpUtils.http(downloadUrl).timeout(30).header("x-access-token", token).exec("GET")) {
|
||||||
|
int code = resp.code();
|
||||||
|
if (code != 200) {
|
||||||
|
log.info("获取[{}]日志文件列表接口错误码: {}", serviceName, code);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String responseBody = resp.bodyAsString(); // 在流关闭前立即读取
|
||||||
|
log.info("返回接口数据: {}", responseBody);
|
||||||
|
JSONObject files = JSONObject.parseObject(responseBody);
|
||||||
|
|
||||||
|
// 修复:使用正确的对象检查success状态
|
||||||
|
boolean serSuccess = files.getBoolean("success");
|
||||||
|
if (!serSuccess) {
|
||||||
|
log.info("获取日志文件结果: {}", "失败");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray filesList = files.getJSONObject("result").getJSONArray("records");
|
||||||
|
log.info("获取到的日志文件为: {}", filesList);
|
||||||
|
|
||||||
|
// 开始下载日志文件
|
||||||
|
if (filesList != null && !filesList.isEmpty()) {
|
||||||
|
Set<String> instanceSet = new HashSet<>();
|
||||||
|
filesList.stream()
|
||||||
|
.filter(JSONObject.class::isInstance)
|
||||||
|
.map(JSONObject.class::cast)
|
||||||
|
.map(file -> file.getString("instance"))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(instance -> instance.replace(".", "_").replace(":", "_")) // 替换 . 和 : 为 _
|
||||||
|
.forEach(instanceSet::add);
|
||||||
|
for (int i = 0; i < filesList.size(); i++) {
|
||||||
|
JSONObject file = filesList.getJSONObject(i);
|
||||||
|
String fileName = file.getString("fileName");
|
||||||
|
String instance = file.getString("instance");
|
||||||
|
String lastModified = file.getString("lastModified");
|
||||||
|
|
||||||
|
String filePath = path + "/" + serviceName;
|
||||||
|
|
||||||
|
log.info("开始下载日志文件: {}/{}", filePath, fileName);
|
||||||
|
|
||||||
|
String downloadFileUrl = gatewayUrl + "/" + serviceName + "/service/log/file/down?filename="
|
||||||
|
+ fileName + "&instance=" + instance + "&service=" + serviceName;
|
||||||
|
|
||||||
|
|
||||||
|
String instanceStr = instance.replace(":", "_").replace(".", "_");
|
||||||
|
File downFile = new File(filePath + "/" + instanceStr + "_" + fileName);
|
||||||
|
|
||||||
|
|
||||||
|
if (fileName.endsWith(".gz")) {//如果是压缩文件,说明已经归档压缩了,不会再改变,先查询一下本地是否存在,如果存在就跳过
|
||||||
|
filePath = filePath + "/archive/";//压缩文件存放在archive目录下
|
||||||
|
downFile = new File(filePath + "/" + instanceStr + "_" + fileName);
|
||||||
|
if (downFile.exists()) {
|
||||||
|
log.info("文件已存在,跳过下载: {}", downFile.getAbsolutePath());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//判断一下所有log文件更新日期,将小于今天的全部删除
|
||||||
|
File[] logFiles = new File(filePath).listFiles((dir, name) ->
|
||||||
|
name.endsWith(".log") || name.matches(".*\\.log\\.\\d{3}")
|
||||||
|
);
|
||||||
|
if (logFiles != null) {
|
||||||
|
// 提前创建 /tmp/ 目录,避免重复创建
|
||||||
|
File tmpBaseDir = new File(filePath + "/tmp/");
|
||||||
|
if (!tmpBaseDir.exists()) {
|
||||||
|
tmpBaseDir.mkdirs();
|
||||||
|
}
|
||||||
|
for (File logFile : logFiles) {
|
||||||
|
try {
|
||||||
|
long lastModifiedTime = logFile.lastModified();
|
||||||
|
String lastDate = DateUtil.format(new Date(lastModifiedTime), "yyyy-MM-dd");
|
||||||
|
String haveFileName = logFile.getName();
|
||||||
|
// 判断是否需要移动文件(实例变化)
|
||||||
|
// 判断 haveFileName 是否以 instanceSet 中任意一个实例开头
|
||||||
|
boolean isMatched = instanceSet.stream().anyMatch(haveFileName::startsWith);
|
||||||
|
int prefixesCount = countUniquePrefixes(filePath);//查询文件夹下有多少个不同前缀
|
||||||
|
if (!isMatched && prefixesCount > insCount) {
|
||||||
|
File mvDir = new File(tmpBaseDir, lastDate);
|
||||||
|
if (!mvDir.exists()) {
|
||||||
|
mvDir.mkdirs();
|
||||||
|
}
|
||||||
|
Files.move(logFile.toPath(), mvDir.toPath().resolve(haveFileName), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
log.info("文件已移动至临时目录: {}", mvDir.toPath().resolve(haveFileName));
|
||||||
|
continue; // 已移动的文件无需进一步处理
|
||||||
|
}
|
||||||
|
if (lastModifiedTime < getTodayStartTime()) {
|
||||||
|
log.info("删除过期文件: {}", logFile.getAbsolutePath());
|
||||||
|
logFile.delete();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("处理文件时发生错误: {}", logFile.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在新的try-with-resources块中处理文件下载
|
||||||
|
try (HttpResponse downloadRes = HttpUtils.http(downloadFileUrl)
|
||||||
|
.header("x-access-token", token)
|
||||||
|
.header("Content-Type", "application/octet-stream")
|
||||||
|
.timeout(60).exec("GET")) {
|
||||||
|
|
||||||
|
int downloadCode = downloadRes.code();
|
||||||
|
if (downloadCode != 200) {
|
||||||
|
log.info("下载[{}]日志文件接口错误码: {}", serviceName, downloadCode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即读取字节数据,在流关闭前完成
|
||||||
|
byte[] bytes = downloadRes.bodyAsBytes();
|
||||||
|
log.info("下载文件大小: {}", bytes.length);
|
||||||
|
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
|
File downFileDir = new File(filePath);
|
||||||
|
if (!downFileDir.exists()) {
|
||||||
|
downFileDir.mkdirs(); // 修复:使用mkdirs()创建多级目录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保目录存在后再创建文件
|
||||||
|
if (!downFile.getParentFile().exists()) {
|
||||||
|
downFile.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
//当天的文件.log结尾,如果有存在的文件,文件名加序号
|
||||||
|
if (fileName.endsWith(".log")) {
|
||||||
|
// 获取基础文件名(不包含序号部分)
|
||||||
|
String baseFileName = getBaseFileNameWithoutSequence(downFile.getName());
|
||||||
|
// 查找目标目录中所有匹配的基础文件名
|
||||||
|
File parentDir = downFile.getParentFile();
|
||||||
|
|
||||||
|
FileInfo fileInfo = this.getFileInfo(parentDir, baseFileName);
|
||||||
|
|
||||||
|
//判断文件大小,如果本地文件大于线上文件,需要下载并重命名;如果本地文件小于线上文件,直接下载替换(本地文件以最大序号为准)
|
||||||
|
long localFileSize = fileInfo.getFileSize();
|
||||||
|
if (localFileSize == bytes.length) {
|
||||||
|
log.info("文件已存在,跳过下载: {}", downFile.getAbsolutePath());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (localFileSize > bytes.length) {
|
||||||
|
downFile = new File(parentDir, fileInfo.getNextFileName());
|
||||||
|
} else {
|
||||||
|
downFile = new File(parentDir, fileInfo.getCurrentMaxFileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(downFile)) {
|
||||||
|
fos.write(bytes);
|
||||||
|
log.info("成功下载文件: {}", downFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // 外层try-with-resources结束
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("下载日志文件过程中发生错误: ", e);
|
||||||
|
throw new RuntimeException("下载日志文件失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取基础文件名(去除序列号部分)
|
||||||
|
*/
|
||||||
|
private String getBaseFileNameWithoutSequence(String fileName) {
|
||||||
|
// 检查是否包含序号格式(如 .log.001)
|
||||||
|
if (fileName.contains(".")) {
|
||||||
|
String[] parts = fileName.split("\\.");
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
// 检查倒数第二个部分是否为数字
|
||||||
|
try {
|
||||||
|
Integer.parseInt(parts[parts.length - 1]);
|
||||||
|
// 返回去掉序号的部分
|
||||||
|
StringBuilder baseName = new StringBuilder();
|
||||||
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
|
if (i > 0) baseName.append(".");
|
||||||
|
baseName.append(parts[i]);
|
||||||
|
}
|
||||||
|
return baseName.toString();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 不是数字,说明没有序号,返回原始名称
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个可用的序号
|
||||||
|
*/
|
||||||
|
private int getNextAvailableSequence(File directory, String baseFileName) {
|
||||||
|
File[] existingFiles = directory.listFiles((dir, name) ->
|
||||||
|
name.startsWith(baseFileName) &&
|
||||||
|
(name.equals(baseFileName) || name.matches(baseFileName + "\\.\\d{3}")));
|
||||||
|
|
||||||
|
if (existingFiles == null) {
|
||||||
|
return 1; // 目录不存在或无法访问
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxSequence = 0;
|
||||||
|
|
||||||
|
for (File file : existingFiles) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
if (fileName.equals(baseFileName)) {
|
||||||
|
// 基础文件存在,从1开始
|
||||||
|
maxSequence = Math.max(maxSequence, 1);
|
||||||
|
} else if (fileName.matches(baseFileName + "\\.\\d{3}")) {
|
||||||
|
// 提取序号
|
||||||
|
String sequencePart = fileName.substring(baseFileName.length() + 1); // +1 for the dot
|
||||||
|
try {
|
||||||
|
int sequence = Integer.parseInt(sequencePart);
|
||||||
|
maxSequence = Math.max(maxSequence, sequence + 1);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 序号解析失败,跳过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private long getTodayStartTime() {
|
||||||
|
// 获取当前时间
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
// 计算今天0点的时间
|
||||||
|
java.util.Calendar calendar = java.util.Calendar.getInstance();
|
||||||
|
calendar.setTimeInMillis(currentTime);
|
||||||
|
calendar.set(java.util.Calendar.HOUR_OF_DAY, 0);
|
||||||
|
calendar.set(java.util.Calendar.MINUTE, 0);
|
||||||
|
calendar.set(java.util.Calendar.SECOND, 0);
|
||||||
|
calendar.set(java.util.Calendar.MILLISECOND, 0);
|
||||||
|
return calendar.getTimeInMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前最大序号的文件名、下一个序号的文件名以及最大序号文件的大小
|
||||||
|
*
|
||||||
|
* @param directory 目标目录
|
||||||
|
* @param baseFileName 基础文件名(不含序号)
|
||||||
|
* @return FileInfo 包含当前最大序号文件名、下一个序号文件名和文件大小的信息
|
||||||
|
*/
|
||||||
|
private FileInfo getFileInfo(File directory, String baseFileName) {
|
||||||
|
// 获取最大序号
|
||||||
|
int maxSequence = getNextAvailableSequence(directory, baseFileName) - 1;
|
||||||
|
|
||||||
|
// 构建当前最大序号文件名
|
||||||
|
String currentMaxFileName;
|
||||||
|
if (maxSequence <= 0) {
|
||||||
|
// 没有序号文件,直接使用基础文件名
|
||||||
|
currentMaxFileName = baseFileName;
|
||||||
|
} else {
|
||||||
|
// 存在序号文件,拼接序号
|
||||||
|
currentMaxFileName = baseFileName + "." + String.format("%03d", maxSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建下一个序号文件名
|
||||||
|
int nextSequence = maxSequence + 1;
|
||||||
|
String nextFileName = baseFileName + "." + String.format("%03d", nextSequence);
|
||||||
|
|
||||||
|
// 构造当前最大序号文件对象
|
||||||
|
File maxSequenceFile = new File(directory, currentMaxFileName);
|
||||||
|
|
||||||
|
// 检查文件是否存在并获取大小
|
||||||
|
long fileSize = 0;
|
||||||
|
if (maxSequenceFile.exists()) {
|
||||||
|
fileSize = maxSequenceFile.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回封装好的信息
|
||||||
|
return new FileInfo(currentMaxFileName, nextFileName, fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int countUniquePrefixes(String directoryPath) {
|
||||||
|
File directory = new File(directoryPath);
|
||||||
|
if (!directory.exists() || !directory.isDirectory()) {
|
||||||
|
log.info("目录不存在或不是一个有效目录: {}", directoryPath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> prefixSet = new HashSet<>();
|
||||||
|
File[] files = directory.listFiles();
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
// 提取最后一个 _ 前面的部分作为前缀
|
||||||
|
int lastUnderscoreIndex = fileName.lastIndexOf('_');
|
||||||
|
if (lastUnderscoreIndex != -1) {
|
||||||
|
String prefix = fileName.substring(0, lastUnderscoreIndex);
|
||||||
|
prefixSet.add(prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.info("唯一前缀数量: " + prefixSet.size());
|
||||||
|
return prefixSet.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.renkang.rkutils.task;
|
||||||
|
|
||||||
|
import com.renkang.rkutils.service.LogFileService;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
import org.noear.solon.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DownFileJob {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private LogFileService logFileService;
|
||||||
|
@Scheduled(name = "downFileJob")
|
||||||
|
public void downFile(){
|
||||||
|
logFileService.downloadLogFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.renkang.rkutils.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FileInfo {
|
||||||
|
private String currentMaxFileName; // 当前最大序号的文件名
|
||||||
|
private String nextFileName; // 下一个序号的文件名
|
||||||
|
private long fileSize; // 最大序号文件的大小
|
||||||
|
public FileInfo(String currentMaxFileName, String nextFileName, long fileSize) {
|
||||||
|
this.currentMaxFileName = currentMaxFileName;
|
||||||
|
this.nextFileName = nextFileName;
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class RSAUtil {
|
||||||
|
/**
|
||||||
|
* 使用公钥加密
|
||||||
|
*/
|
||||||
|
public static String encryptByPublicKey(String data, String publicKeyStr) throws Exception {
|
||||||
|
// 将公钥字符串转换为PublicKey对象
|
||||||
|
byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
|
||||||
|
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
||||||
|
java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
|
||||||
|
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
solon.config.load:
|
||||||
|
- "conf/app-xj.yml"
|
||||||
|
|
||||||
|
solon.logging.appender:
|
||||||
|
console:
|
||||||
|
pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(${PID:-}) --- %-15([%15.15thread]) %-56(%cyan(%-40.40logger{39}%L)) : %msg%n"
|
||||||
|
file:
|
||||||
|
pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level ${PID:-} --- %-15([%15.15thread]) %-56(%-40.40logger{39}%L) : %msg%n"
|
||||||
|
enable: true #是否启用
|
||||||
|
extension: ".log" #v2.2.18 后支持(例:.log, .log.gz, .log.zip)
|
||||||
|
maxFileSize: "10 MB"
|
||||||
|
maxHistory: "7" #单位:天
|
||||||
|
|
||||||
|
solon.scheduling.job:
|
||||||
|
downFileJob:
|
||||||
|
cron: "0 30 * * * ?" # 每小时的第30分钟执行
|
||||||
|
zone: "+08"
|
||||||
|
fixedRate: 0
|
||||||
|
# 任务间隔时间:1小时
|
||||||
|
fixedDelay: 0
|
||||||
|
initialDelay: 0
|
||||||
|
enable: true #用任务进行启停控制
|
||||||
|
|
||||||
|
base:
|
||||||
|
username: admin
|
||||||
|
password: "#fCjW%x2dv@a"
|
||||||
|
publicKey: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmZfR/bA9X3vp86y1aEpvwzXJYKRRF1fLau2+05/ZtaITLpV8bhkmSf3neSy/Q9gAdvG75Fr73E+GWE+K5b0BpvIS1jDGo319+PpZR39SaZTKZ27XFXrosmJTZutN79t819HS1VseleunHAFgMVufE9U5jP6LGzl/wbkSy01GhzwIDAQAB"
|
||||||
|
gatewayUrl: https://api-jkglpt.iosp.ydpt.tech
|
||||||
|
# gatewayUrl: https://xj-api.mcrm.vip:8888
|
||||||
|
file:
|
||||||
|
path: D:/xjlogs
|
||||||
|
services:
|
||||||
|
- name: health-system
|
||||||
|
insCount: 2
|
||||||
|
# - health-watch
|
||||||
Reference in New Issue
Block a user