diff --git a/.gitignore b/.gitignore
index 9154f4c..65cfb52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,32 @@
hs_err_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
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..fe06dff
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,99 @@
+
+
+ 4.0.0
+
+
+ org.noear
+ solon-parent
+ 3.9.0
+
+
+
+ com.renkang
+ rkutils
+ 1.0
+
+ jar
+
+ rkutils project for Solon
+
+
+ 17
+
+
+
+
+ org.noear
+ solon-lib
+
+
+
+ org.noear
+ solon-scheduling-simple
+
+
+
+ org.noear
+ solon-logging-logback-jakarta
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.60
+ compile
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.noear
+ solon-net-httputils
+
+
+
+ org.noear
+ solon-test
+ test
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ org.noear
+ solon-maven-plugin
+
+
+
+
+
+
+ tencent
+ https://mirrors.cloud.tencent.com/nexus/repository/maven-public/
+
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/renkang/rkutils/App.java b/src/main/java/com/renkang/rkutils/App.java
new file mode 100644
index 0000000..3c66f15
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/App.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/renkang/rkutils/config/BaseConfig.java b/src/main/java/com/renkang/rkutils/config/BaseConfig.java
new file mode 100644
index 0000000..9eb2ddf
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/config/BaseConfig.java
@@ -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;
+
+}
diff --git a/src/main/java/com/renkang/rkutils/config/LogFileConfig.java b/src/main/java/com/renkang/rkutils/config/LogFileConfig.java
new file mode 100644
index 0000000..7207f3b
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/config/LogFileConfig.java
@@ -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 services;
+
+ @Data
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class ServiceItem {
+ private String name;
+ private int insCount;
+ }
+}
diff --git a/src/main/java/com/renkang/rkutils/service/LogFileService.java b/src/main/java/com/renkang/rkutils/service/LogFileService.java
new file mode 100644
index 0000000..fae72a3
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/service/LogFileService.java
@@ -0,0 +1,5 @@
+package com.renkang.rkutils.service;
+
+public interface LogFileService {
+ public void downloadLogFile();
+}
diff --git a/src/main/java/com/renkang/rkutils/service/impl/LogFileServiceImpl.java b/src/main/java/com/renkang/rkutils/service/impl/LogFileServiceImpl.java
new file mode 100644
index 0000000..ab112a9
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/service/impl/LogFileServiceImpl.java
@@ -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 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 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();
+ }
+}
diff --git a/src/main/java/com/renkang/rkutils/task/DownFileJob.java b/src/main/java/com/renkang/rkutils/task/DownFileJob.java
new file mode 100644
index 0000000..3e8f3b4
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/task/DownFileJob.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/renkang/rkutils/vo/FileInfo.java b/src/main/java/com/renkang/rkutils/vo/FileInfo.java
new file mode 100644
index 0000000..3d49a40
--- /dev/null
+++ b/src/main/java/com/renkang/rkutils/vo/FileInfo.java
@@ -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;
+ }
+}
diff --git a/src/main/java/util/RSAUtil.java b/src/main/java/util/RSAUtil.java
new file mode 100644
index 0000000..1cc7dce
--- /dev/null
+++ b/src/main/java/util/RSAUtil.java
@@ -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);
+ }
+}
diff --git a/src/main/resources/app.yml b/src/main/resources/app.yml
new file mode 100644
index 0000000..7fffe94
--- /dev/null
+++ b/src/main/resources/app.yml
@@ -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
\ No newline at end of file