Compare commits

...

10 Commits

Author SHA1 Message Date
2d2c37f1bf 多配置文件 2025-06-04 20:07:38 +08:00
061a85f9a1 更新mac下执行文件为通用二进制 2025-05-23 16:04:52 +08:00
bc8427e6a0 增加阿里云推送和websocket 2025-04-24 19:10:55 +08:00
29db5ab157 add ip getter 2025-04-24 11:41:37 +08:00
884f80d3c0 优化获取ptach 2025-04-15 10:27:55 +08:00
a6b910ee31 增加获取patch接口,未测试 2025-04-14 20:00:46 +08:00
a878bf8c40 增加上传apk生成patch文件 2025-04-14 09:26:49 +08:00
02cf38acda linux 静态编译 2025-04-09 19:12:00 +08:00
TongTongStudio
c51c2416ef fix linux command 2025-04-09 01:19:55 +08:00
4fddefd78d add linux bsdiff 2025-04-09 00:26:32 +08:00
31 changed files with 963 additions and 274 deletions

9
DockerFile Normal file
View File

@@ -0,0 +1,9 @@
FROM eclipse-temurin:17-jdk-jammy
MAINTAINER TongTongStudio <tongtongstudios@gmail.com>
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak
VOLUME /tmp
RUN cd /tmp
ADD bsdiff .
ADD target/*.jar app.jar
EXPOSE map[65534/tcp:{}]
ENTRYPOINT ["java", "-jar", "/app.jar"]

1
README.md Normal file
View File

@@ -0,0 +1 @@
2025.05.12 android端需要增加获取Android/data目录下的所有文件计算所有hash

BIN
bsdiff/linux/bsdiff Executable file

Binary file not shown.

BIN
bsdiff/linux/bspatch Executable file

Binary file not shown.

BIN
bsdiff/mac/bsdiff Executable file

Binary file not shown.

BIN
bsdiff/mac/bspatch Executable file

Binary file not shown.

16
pom.xml
View File

@@ -84,6 +84,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
@@ -94,6 +98,18 @@
<artifactId>apk-parser</artifactId>
<version>2.6.10</version>
</dependency>
<!--异步-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-push20160801</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>push20160801</artifactId>
<version>1.0.17</version>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,10 @@
package com.mir4updater.backend.config;
public class FilePath {
public static final String UPLOAD_PATH = "uploadApk";
public static final String APK_FILE_PATH = "file";
public static final String ICON_PATH = "icon";
public static final String TEMP_PATH = "tmp";
public static final String PATCH_PATH = "patch";
}

View File

@@ -0,0 +1,13 @@
package com.mir4updater.backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@@ -1,6 +1,7 @@
package com.mir4updater.backend.controller;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
@@ -42,4 +43,10 @@ public class HelloController {
String result = stringRedisTemplate.opsForValue().get("username");
return result;
}
@PostMapping("/web_send")
public String webSendMessage(@RequestParam(value = "message") String message) {
WebSocketService.sendMessageAll(message);
return "sendMessageAll";
}
}

View File

@@ -0,0 +1,16 @@
package com.mir4updater.backend.controller;
import com.mir4updater.backend.utils.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IpController {
@GetMapping("/public-ip")
public String getPublicIp(HttpServletRequest request) {
String clientIp = IpUtils.getClientIpAddress(request);
return "Your public IP address is: " + clientIp;
}
}

View File

@@ -0,0 +1,29 @@
package com.mir4updater.backend.controller.apk;
import com.mir4updater.backend.entity.PatchFileInfo;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.DiffPathService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CheckPatchController {
public static Logger logger = LogManager.getLogger(CheckPatchController.class);
@Autowired
DiffPathService diffPathService;
@GetMapping("/android/check_patch")
public Result checkPatch(@RequestParam("pkg") String pkg, @RequestParam(value = "version_code") Long versionCode, @RequestParam(value = "md5") String oldMd5) {
PatchFileInfo patchFileInfo = diffPathService.getPatchInfo(pkg, versionCode, oldMd5);
if (patchFileInfo == null) {
return Result.notFound();
} else {
return Result.success(patchFileInfo);
}
}
}

View File

@@ -1,97 +0,0 @@
package com.mir4updater.backend.controller.apk;
import com.mir4updater.backend.entity.ApkInfo;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.ApkInfoService;
import com.mir4updater.backend.utils.HashUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@RestController
public class CreateDiffPatchFile {
public static Logger logger = LogManager.getLogger(CreateDiffPatchFile.class);
private static final String UPLOAD_PATH = "uploadApk";
public static final String PATCH_PATH = "patch";
@Autowired
private ApkInfoService apkInfoService;
@PostMapping("/android/create_patch")
public Result uploadApk(@RequestParam("old_id") int oldId, @RequestParam("new_id") int newId) throws Exception {
String projectPath = System.getProperty("user.dir");
String patchPath = projectPath + File.separator + UPLOAD_PATH + File.separator + PATCH_PATH + File.separator;
File patchFileDir = new File(patchPath);
if (!patchFileDir.exists()) {
patchFileDir.mkdirs();
}
ApkInfo oldApkInfo = apkInfoService.getApkInfo(oldId);
ApkInfo newApkInfo = apkInfoService.getApkInfo(newId);
if (oldApkInfo == null || newApkInfo == null) {
return Result.notFound();
}
if (!Objects.equals(oldApkInfo.getPackageName(), newApkInfo.getPackageName())) {
return Result.error("包名不一致");
}
if (oldApkInfo.getVersionCode() >= newApkInfo.getVersionCode()) {
return Result.error("旧版本号不能大于等于新版本号");
}
File patchFile = new File(patchPath + oldApkInfo.getPackageName() + "_" + oldApkInfo.getVersionCode() + "_" + newApkInfo.getVersionCode() + ".patch");
// 根据操作系统选择命令
String os = System.getProperty("os.name").toLowerCase();
String command;
if (os.contains("win")) {
command = "bsdiff" + File.separator + "win" + File.separator + "bsdiff.exe";
} else if (os.contains("nix") || os.contains("mac")) {
command = "bsdiff" + File.separator + "linux" + File.separator + "bsdiff";
} else {
throw new UnsupportedOperationException("Unsupported OS");
}
File file = new File(command);
logger.info("bsdiff file exists = " + file.exists());
// 将 command 和 arg 合并成一个数组command 作为第一个参数
List<String> cmdArgs = new ArrayList<>();
cmdArgs.add(command); // 第一个参数是命令本身
cmdArgs.add(oldApkInfo.getFilePath());// 后续参数
cmdArgs.add(newApkInfo.getFilePath());
cmdArgs.add(patchFile.getAbsolutePath());
logger.info("bsdiff args = " + cmdArgs);
ProcessBuilder pb = new ProcessBuilder(cmdArgs); // 传入合并后的参数列表
pb.redirectErrorStream(true); // 合并错误流和输入流
Process process = pb.start();
int exitCode = process.waitFor();
logger.info("退出码: " + exitCode);
// 读取输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = reader.lines().collect(Collectors.joining("\n"));
if (exitCode == 0) {
String md5 = HashUtils.getFileMD5(patchFile);
logger.info("patchFile md5 = " + md5);
return Result.success(md5);
} else {
return Result.error(result);
}
}
}

View File

@@ -0,0 +1,232 @@
package com.mir4updater.backend.controller.apk;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mir4updater.backend.config.FilePath;
import com.mir4updater.backend.entity.ApkInfo;
import com.mir4updater.backend.entity.PatchFileInfo;
import com.mir4updater.backend.entity.XapkManifest;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.ApkInfoService;
import com.mir4updater.backend.service.DiffPathService;
import com.mir4updater.backend.utils.ApkUtils;
import com.mir4updater.backend.utils.HashUtils;
import com.mir4updater.backend.utils.PatchUtils;
import net.dongliu.apk.parser.ApkFile;
import net.dongliu.apk.parser.bean.ApkMeta;
import net.dongliu.apk.parser.bean.UseFeature;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@RestController
public class CreateDiffPatchFileController {
public static Logger logger = LogManager.getLogger(CreateDiffPatchFileController.class);
@Autowired
private ApkInfoService apkInfoService;
@Autowired
DiffPathService diffPathService;
@PostMapping("/android/create_patch")
public Result uploadApk(@RequestParam("old_id") int oldId, @RequestParam("new_id") int newId) throws Exception {
String projectPath = System.getProperty("user.dir");
String patchPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.PATCH_PATH + File.separator;
File patchFileDir = new File(patchPath);
if (!patchFileDir.exists()) {
patchFileDir.mkdirs();
}
ApkInfo oldApkInfo = apkInfoService.getApkInfo(oldId);
ApkInfo newApkInfo = apkInfoService.getApkInfo(newId);
if (oldApkInfo == null || newApkInfo == null) {
return Result.notFound();
}
if (!Objects.equals(oldApkInfo.getPackageName(), newApkInfo.getPackageName())) {
return Result.error("包名不一致");
}
if (oldApkInfo.getVersionCode() >= newApkInfo.getVersionCode()) {
return Result.error("旧版本号不能大于等于新版本号");
}
File patchFile = new File(patchPath + oldApkInfo.getPackageName() + "_" + oldApkInfo.getVersionCode() + "_" + newApkInfo.getVersionCode() + ".patch");
long startTime = System.currentTimeMillis();
logger.info("started at " + startTime);
PatchUtils.BsdiffResult bsdiffResult = PatchUtils.createDiffPatch(oldApkInfo.getFilePath(), newApkInfo.getFilePath(), patchFile.getPath());
long endTime = System.currentTimeMillis() - startTime;
int exitCode = bsdiffResult.getExitCode();
String result = bsdiffResult.getResult();
if (exitCode == 0) {
String md5 = HashUtils.getFileMD5(patchFile);
logger.info("patchFile md5 = " + md5 + " 用时:" + endTime + "ms");
return Result.success(md5);
} else {
return Result.error(result);
}
}
/**
* 通过上传文件查询最新版本并创建最新补丁文件
*
* @param multipartFile
* @return
* @throws Exception
*/
@PostMapping("/android/create_latest_patch")
public Result createLatestPatch(@RequestParam("file") MultipartFile multipartFile) throws Exception {
if (multipartFile != null && !multipartFile.isEmpty()) {
String projectPath = System.getProperty("user.dir");
logger.info("当前项目路径为:" + projectPath);
String tempPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.TEMP_PATH + File.separator;
File tempFileDir = new File(tempPath);
if (!tempFileDir.exists()) {
tempFileDir.mkdirs();
}
//1.接收上传的应用,写入到硬盘
String originalFilename = multipartFile.getOriginalFilename();
File tempFile = new File(tempPath + File.separator + originalFilename);
logger.info("tempFile path = " + tempFile.getAbsolutePath());
try {
multipartFile.transferTo(tempFile);
} catch (FileUploadException e) {
logger.error(e.getMessage());
}
if (!ApkUtils.isAPK(tempFile) && !ApkUtils.isXAPK(tempFile)) {
return Result.error().setMessage("请上传apk或xapk文件");
}
String md5 = HashUtils.getFileMD5(tempFile);
logger.info("tempFile md5 = " + md5);
String sha1 = HashUtils.calculateSHA1(tempFile);
logger.info("tempFile sha1 = " + sha1);
String sha256 = HashUtils.calculateSHA256(tempFile);
logger.info("tempFile sha256 = " + sha256);
if (ApkUtils.isAPK(tempFile)) {
logger.info("tempFile type is apk");
ApkFile apkFile = new ApkFile(tempFile);
ApkMeta apkMeta = apkFile.getApkMeta();
apkFile.close();
logger.info(apkMeta.getLabel());
logger.info(apkMeta.getPackageName());
logger.info(apkMeta.getVersionCode());
logger.info(apkMeta.getVersionName());
for (UseFeature feature : apkMeta.getUsesFeatures()) {
logger.info(feature.getName());
}
String packageName = apkMeta.getPackageName();
long versionCode = apkMeta.getVersionCode();
PatchFileInfo patchFileInfo = diffPathService.getPatchInfo(packageName, versionCode, md5);
if (patchFileInfo != null) {
return Result.error("patch文件存在请直接下载");
}
List<ApkInfo> apkInfoList = apkInfoService.getApkInfo(packageName);
Optional<ApkInfo> apkInfoOptional = apkInfoList.stream().max(new Comparator<ApkInfo>() {
@Override
public int compare(ApkInfo o1, ApkInfo o2) {
return Long.compare(o1.getVersionCode(), o2.getVersionCode());
}
});
if (apkInfoOptional.isPresent()) {
ApkInfo apkInfo = apkInfoOptional.get();
logger.info("latest apkinfo = " + apkInfo);
if (versionCode >= apkInfo.getVersionCode()) {
return Result.success().setMessage(packageName + " 已是最新版本");
} else {
String apkPath = apkInfo.getFilePath();
String patchPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.PATCH_PATH + File.separator;
File patchFile = new File(patchPath + packageName + "_" + versionCode + "_" + apkInfo.getVersionCode() + ".patch");
long startTime = System.currentTimeMillis();
logger.info("started at " + startTime);
PatchUtils.BsdiffResult bsdiffResult = PatchUtils.createDiffPatch(tempFile.getAbsolutePath(), apkPath, patchFile.getPath());
long endTime = System.currentTimeMillis() - startTime;
int exitCode = bsdiffResult.getExitCode();
String result = bsdiffResult.getResult();
if (exitCode == 0) {
String patchMd5 = HashUtils.getFileMD5(patchFile);
logger.info("patchFile md5 = " + patchMd5);
logger.info("tempFile delete = " + tempFile.delete());
Map<String, Object> map = new HashMap<>();
map.put("patchMd5", patchMd5);
map.put("path", patchFile.getAbsolutePath());
map.put("time", endTime + "ms");
return Result.mapResult(map).setSuccess(true).setCode(Result.CODE_SUCCESS).setMessage("创建成功");
} else {
logger.info("tempFile delete = " + tempFile.delete());
return Result.error(result);
}
}
} else {
logger.info("tempFile delete = " + tempFile.delete());
return Result.error().setMessage(packageName + " 没有上传其他版本");
}
} else if (ApkUtils.isXAPK(tempFile)) {
//一般不会出现这种情况
logger.info("tempFile type is xapk");
try (ZipFile zipFile = new ZipFile(tempFile)) {
// 遍历ZIP文件中的所有条目
zipFile.stream().forEach(zipEntry -> {
logger.info(zipEntry.getName());
});
Optional<? extends ZipEntry> manifestOptional = zipFile.stream().filter(zipEntry -> "manifest.json".equals(zipEntry.getName())).findFirst();
if (manifestOptional.isPresent()) {
ZipEntry manifestZipEntry = manifestOptional.get();
try {
InputStream manifestInputStream = zipFile.getInputStream(manifestZipEntry);
// 读取文件内容
String content = new String(manifestInputStream.readAllBytes(), StandardCharsets.UTF_8);
// 创建ObjectMapper实例
ObjectMapper objectMapper = new ObjectMapper();
// 将Java对象转换为JSON字符串
String jsonString = objectMapper.writeValueAsString(content);
XapkManifest xapkManifest = objectMapper.readValue(content, XapkManifest.class);
// logger.info(xapkManifest);
logger.info(xapkManifest.getName());
logger.info(xapkManifest.getPackage_name());
logger.info(xapkManifest.getVersion_code());
logger.info(xapkManifest.getVersion_name());
} catch (IOException e) {
e.printStackTrace();
System.err.println("读取文件失败: " + manifestZipEntry.getName());
return Result.error().setMessage("读取文件失败");
}
} else {
return Result.error().setMessage("读取xapk配置失败");
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("打开ZIP文件失败: " + e.getMessage());
return Result.error().setMessage("打开ZIP文件失败");
}
}
} else {
return Result.error().setMessage("文件为空");
}
return Result.error().setMessage("未知错误");
}
}

View File

@@ -1,10 +1,12 @@
package com.mir4updater.backend.controller.apk;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mir4updater.backend.config.FilePath;
import com.mir4updater.backend.entity.ApkInfo;
import com.mir4updater.backend.entity.XapkManifest;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.ApkInfoService;
import com.mir4updater.backend.utils.ApkUtils;
import com.mir4updater.backend.utils.FileUtils;
import com.mir4updater.backend.utils.HashUtils;
import net.dongliu.apk.parser.ApkFile;
@@ -33,10 +35,7 @@ import java.util.zip.ZipFile;
@RestController
public class UploadApkController {
private static final String UPLOAD_PATH = "uploadApk";
private static final String APK_FILE_PATH = "file";
private static final String ICON_PATH = "icon";
private static final String TEMP_PATH = "tmp";
public static Logger logger = LogManager.getLogger(UploadApkController.class);
@@ -50,9 +49,9 @@ public class UploadApkController {
String projectPath = System.getProperty("user.dir");
logger.info("当前项目路径为:" + projectPath);
String apkPath = projectPath + File.separator + UPLOAD_PATH + File.separator + APK_FILE_PATH;
String iconPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ICON_PATH + File.separator;
String tempPath = projectPath + File.separator + UPLOAD_PATH + File.separator + TEMP_PATH + File.separator;
String apkPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.APK_FILE_PATH;
String iconPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.ICON_PATH + File.separator;
String tempPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.TEMP_PATH + File.separator;
File apkFileDir = new File(apkPath);
if (!apkFileDir.exists()) {
@@ -78,7 +77,7 @@ public class UploadApkController {
logger.error(e.getMessage());
}
if (!isAPK(file) && !isXAPK(file)) {
if (!ApkUtils.isAPK(file) && !ApkUtils.isXAPK(file)) {
return Result.error().setMessage("请上传apk或xapk文件");
}
String md5 = HashUtils.getFileMD5(file);
@@ -90,7 +89,7 @@ public class UploadApkController {
File iconFile = new File(iconPath + md5 + ".png");
if (isAPK(file)) {
if (ApkUtils.isAPK(file)) {
logger.info("file type is apk");
//调用工具类解析apk
//analysisAPK(apkFileDir.getAbsolutePath(), file.getAbsolutePath());
@@ -135,7 +134,7 @@ public class UploadApkController {
}
return Result.success().setMessage("上传成功");
} else if (isXAPK(file)) {
} else if (ApkUtils.isXAPK(file)) {
logger.info("file type is xapk");
try (ZipFile zipFile = new ZipFile(file)) {
// 遍历ZIP文件中的所有条目
@@ -210,54 +209,6 @@ public class UploadApkController {
return Result.error().setMessage("未知错误");
}
// 判断是否为APK
public static boolean isAPK(File file) throws IOException {
// 检查扩展名是否为.apk
if (file.getName().toLowerCase().endsWith(".apk")) {
// 进一步验证是否为有效的APK文件
ZipFile zipFile = new ZipFile(file);
boolean apkFile = zipFile.getEntry("AndroidManifest.xml") != null;
zipFile.close();
return apkFile;
}
return false;
}
// 判断是否为XAPK
public static boolean isXAPK(File file) {
// 检查扩展名是否为.xapk
if (file.getName().toLowerCase().endsWith(".xapk")) {
return true; // 扩展名为.xapk通常直接视为XAPK
}
// 若扩展名不明确,检查内容结构
try (ZipFile zipFile = new ZipFile(file)) {
boolean hasJson = zipFile.getEntry("manifest.json") != null;
boolean hasAPK = false;
// boolean hasOBB = false;
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
// 检查是否包含APK文件
if (name.endsWith(".apk")) {
hasAPK = true;
}
// 检查是否包含OBB目录
// if (name.startsWith("Android/obb/")) {
// hasOBB = true;
// }
}
zipFile.close();
// XAPK通常包含至少一个APK或OBB数据
return hasAPK || hasJson;
} catch (IOException e) {
return false;
}
}
private void analysisAPK(String dirPath, String filePath) throws Exception {

View File

@@ -0,0 +1,78 @@
package com.mir4updater.backend.controller.push;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.push20160801.AsyncClient;
import com.aliyun.sdk.service.push20160801.models.BindAliasRequest;
import com.aliyun.sdk.service.push20160801.models.BindAliasResponse;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;
import java.util.concurrent.CompletableFuture;
public class AsyncPush {
public static void main(String[] args) throws Exception {
// HttpClient Configuration
/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
.maxConnections(128) // Set the connection pool size
.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
// Configure the proxy
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001))
.setCredentials("<your-proxy-username>", "<your-proxy-password>"))
// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
.x509TrustManagers(new X509TrustManager[]{})
.keyManagers(new KeyManager[]{})
.ignoreSSL(false)
.build();*/
// Configure Credentials authentication information, including ak, secret, token
StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
// Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
.accessKeyId("335514186")
.accessKeySecret("dc39560b76c54c408ece7dce2f21464f")
//.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token
.build());
// Configure the Client
AsyncClient client = AsyncClient.builder()
//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
.credentialsProvider(provider)
//.serviceConfiguration(Configuration.create()) // Service-level configuration
// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
.overrideConfiguration(
ClientOverrideConfiguration.create()
// Endpoint 请参考 https://api.aliyun.com/product/Push
.setEndpointOverride("cloudpush.aliyuncs.com")
//.setConnectTimeout(Duration.ofSeconds(30))
)
.build();
// Parameter settings for API request
BindAliasRequest bindAliasRequest = BindAliasRequest.builder()
.appKey(1L)
.deviceId("e0b4fb8ebeb44de5be3617f4a901b8f5")
// Request-level configuration rewrite, can set Http request parameters, etc.
// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
.build();
// Asynchronously get the return value of the API request
CompletableFuture<BindAliasResponse> response = client.bindAlias(bindAliasRequest);
// Synchronously get the return value of the API request
BindAliasResponse resp = response.get();
System.out.println(new Gson().toJson(resp));
// Asynchronous processing of return values
/*response.thenAccept(resp -> {
System.out.println(new Gson().toJson(resp));
}).exceptionally(throwable -> { // Handling exceptions
System.out.println(throwable.getMessage());
return null;
});*/
// Finally, close the client
client.close();
}
}

View File

@@ -0,0 +1,40 @@
package com.mir4updater.backend.controller.push;
import com.aliyun.push20160801.models.PushRequest;
import com.aliyun.push20160801.models.PushResponse;
import com.aliyun.teaopenapi.models.Config;
public class SyncPush {
public static com.aliyun.push20160801.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
Config config = new Config();
// 您的AccessKey ID
config.accessKeyId = accessKeyId;
// 您的AccessKey Secret
config.accessKeySecret = accessKeySecret;
config.regionId = "cn-hangzhou";
return new com.aliyun.push20160801.Client(config);
}
public static void main(String[] args_) throws Exception {
com.aliyun.push20160801.Client client = createClient("LTAI5tBWFbkuKgcobdcqpFus",
"bYvEk0lgfvUzuA8NSlcSTKPkKy5Uow");
PushRequest request = new PushRequest()
.setAppKey(335514186L)
.setPushType("MESSAGE")
.setDeviceType("ANDROID")
.setStoreOffline(true)
.setIOSRemind(true)
.setAndroidRemind(true)
.setTarget("ALIAS")
.setTargetValue("e0b4fb8ebeb44de5be3617f4a901b8f5")
.setTitle("test")
.setBody("2")
.setIOSRemindBody("3")
.setAndroidPopupTitle("4")
.setAndroidPopupBody("5");
PushResponse response = client.push(request);
System.out.println(response.getStatusCode());
}
}

View File

@@ -3,6 +3,8 @@ package com.mir4updater.backend.entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@@ -10,8 +12,9 @@ import java.io.Serializable;
@Entity
@Table(name = "apk_info")
@EqualsAndHashCode
public class ApkInfo implements Serializable {
public ApkInfo() {
}
@Embedded
private ApkInfoPK id; // 复合主键
@@ -51,86 +54,6 @@ public class ApkInfo implements Serializable {
@Column(name = "xapk_version", length = 11)
int xapk_version;
public ApkInfo() {
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public long getVersionCode() {
return versionCode;
}
public void setVersionCode(long versionCode) {
this.versionCode = versionCode;
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public String getSha1() {
return sha1;
}
public void setSha1(String sha1) {
this.sha1 = sha1;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getIconPath() {
return iconPath;
}
public void setIconPath(String iconPath) {
this.iconPath = iconPath;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public boolean isXapk() {
return xapk;
}
public void setXapk(boolean xapk) {
this.xapk = xapk;
}
public int getXapk_version() {
return xapk_version;
}
public void setXapk_version(int xapk_version) {
this.xapk_version = xapk_version;
}
}

View File

@@ -0,0 +1,63 @@
package com.mir4updater.backend.entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@Data
@Entity
@Table(name = "patch_file_info")
@EqualsAndHashCode
public class PatchFileInfo implements Serializable {
@Id
@Column(name = "patch_id", nullable = false, length = 20)
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
int appId;
@Column(name = "package_name", nullable = false)
String packageName;
@Column(name = "old_version", nullable = false)
long oldVersion;
@Column(name = "old_md5", nullable = false)
String oldMd5;
@Column(name = "old_sha1", nullable = false)
String oldSha1;
@Column(name = "old_sha256", nullable = false)
String oldSha256;
@Column(name = "new_version", nullable = false)
long newVersion;
@Column(name = "new_md5", nullable = false)
String newMd5;
@Column(name = "new_sha1", nullable = false)
String newSha1;
@Column(name = "new_sha256", nullable = false)
String newSha256;
@Column(name = "patch_file_path", nullable = false)
String patch_file_path;
@Column(name = "patch_file_md5", nullable = false)
String patch_file_md5;
@Column(name = "patch_file_sha1", nullable = false)
String patch_file_sha1;
@Column(name = "patch_file_sha256", nullable = false)
String patch_file_sha256;
@Column(name = "new_file_name", nullable = false)
String new_file_name;
@Column(name = "is_xapk", columnDefinition = "bit(1) default 0")
boolean xapk = false;
}

View File

@@ -0,0 +1,9 @@
package com.mir4updater.backend.repository;
import com.mir4updater.backend.entity.PatchFileInfo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PatchInfoRepository extends JpaRepository<PatchFileInfo, Long> {
public PatchFileInfo findPatchFileInfoByPackageNameAndOldVersionAndOldMd5(String packageName, Long oldVersion, String oldMd5);
}

View File

@@ -0,0 +1,17 @@
package com.mir4updater.backend.service;
import com.mir4updater.backend.entity.PatchFileInfo;
import com.mir4updater.backend.repository.PatchInfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DiffPathService {
@Autowired
PatchInfoRepository patchInfoRepository;
public PatchFileInfo getPatchInfo(String packageName, Long oldVersion, String oldMd5) {
return patchInfoRepository.findPatchFileInfoByPackageNameAndOldVersionAndOldMd5(packageName, oldVersion, oldMd5);
}
}

View File

@@ -0,0 +1,46 @@
package com.mir4updater.backend.service;
import com.aliyun.push20160801.models.PushRequest;
import com.aliyun.push20160801.models.PushResponse;
import com.aliyun.teaopenapi.models.Config;
import org.springframework.stereotype.Service;
@Service
public class PushService {
public static com.aliyun.push20160801.Client createClient() throws Exception {
Config config = new Config();
// 您的AccessKey ID
config.accessKeyId = "LTAI5tBWFbkuKgcobdcqpFus";
// 您的AccessKey Secret
config.accessKeySecret = "bYvEk0lgfvUzuA8NSlcSTKPkKy5Uow";
config.regionId = "cn-shenzhen";
return new com.aliyun.push20160801.Client(config);
}
public PushResponse pushUpdate(String uuid, String message) throws Exception {
PushRequest request = new PushRequest()
.setAppKey(335514186L)
.setPushType("MESSAGE")
.setDeviceType("ANDROID")
.setTarget("ALIAS")
.setTargetValue(uuid)
.setTitle("test")
.setBody("2");
PushResponse response = createClient().push(request);
return response;
}
public PushResponse pushAllDevices(String message) throws Exception {
PushRequest request = new PushRequest()
.setAppKey(335514186L)
.setPushType("MESSAGE")
.setDeviceType("ANDROID")
.setTarget("ALL")
.setTargetValue("ALL")
.setTitle("test")
.setBody("2");
PushResponse response = createClient().push(request);
return response;
}
}

View File

@@ -0,0 +1,67 @@
package com.mir4updater.backend.service;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ServerEndpoint("/websocket/{terminalId}")
public class WebSocketService {
private static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();
public static Logger logger = LogManager.getLogger(WebSocketService.class);
@OnOpen
public void onOpen(@PathParam("terminalId") String terminalId, Session session) {
if (CLIENTS.containsKey(terminalId)) {
try {
CLIENTS.get(terminalId).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
CLIENTS.put(terminalId, session);
logger.info(session.getId());
logger.info("终端 {} 已连接,当前在线数:{}", terminalId, CLIENTS.size());
}
@OnClose
public void onClose(@PathParam("terminalId") String terminalId, Session session) {
CLIENTS.remove(terminalId);
logger.info(session.getId());
logger.info("终端 {} 已断开", terminalId);
}
@OnMessage
public void onMessage(String message, Session session) {
logger.info(session.getId());
logger.info("收到消息:{}", message);
}
// 推送消息方法
public static void sendMessage(String terminalId, String message) {
Session session = CLIENTS.get(terminalId);
if (session != null && session.isOpen()) {
session.getAsyncRemote().sendText(message); // 异步发送避免阻塞
}
}
public static void sendMessageAll(String message) {
for (Map.Entry<String, Session> entry : CLIENTS.entrySet()) {
Session session = entry.getValue();
if (session != null && session.isOpen()) {
session.getAsyncRemote().sendText(message); // 异步发送避免阻塞
}
}
}
}

View File

@@ -0,0 +1,60 @@
package com.mir4updater.backend.utils;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ApkUtils {
// 判断是否为APK
public static boolean isAPK(File file) throws IOException {
// 检查扩展名是否为.apk
if (file.getName().toLowerCase().endsWith(".apk")) {
// 进一步验证是否为有效的APK文件
ZipFile zipFile = new ZipFile(file);
boolean apkFile = zipFile.getEntry("AndroidManifest.xml") != null;
zipFile.close();
return apkFile;
}
return false;
}
// 判断是否为XAPK
public static boolean isXAPK(File file) {
// 检查扩展名是否为.xapk
if (file.getName().toLowerCase().endsWith(".xapk")) {
return true; // 扩展名为.xapk通常直接视为XAPK
}
// 若扩展名不明确,检查内容结构
try (ZipFile zipFile = new ZipFile(file)) {
boolean hasJson = zipFile.getEntry("manifest.json") != null;
boolean hasAPK = false;
// boolean hasOBB = false;
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
// 检查是否包含APK文件
if (name.endsWith(".apk")) {
hasAPK = true;
}
// 检查是否包含OBB目录
// if (name.startsWith("Android/obb/")) {
// hasOBB = true;
// }
}
zipFile.close();
// XAPK通常包含至少一个APK或OBB数据
return hasAPK || hasJson;
} catch (IOException e) {
return false;
}
}
}

View File

@@ -0,0 +1,44 @@
package com.mir4updater.backend.utils;
import jakarta.servlet.http.HttpServletRequest;
public class IpUtils {
public static String getClientIpAddress(HttpServletRequest request) {
String[] headersToCheck = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};
String ip = null;
for (String header : headersToCheck) {
ip = request.getHeader(header);
if (isValidIp(ip)) {
break;
}
}
// 处理多IP情况
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
// 回退到默认方法
if (!isValidIp(ip)) {
ip = request.getRemoteAddr();
}
// 处理IPv6本地地址
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
return ip;
}
private static boolean isValidIp(String ip) {
return ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip);
}
}

View File

@@ -0,0 +1,82 @@
package com.mir4updater.backend.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class PatchUtils {
public static Logger logger = LogManager.getLogger(PatchUtils.class);
public static BsdiffResult createDiffPatch(String oldApkPath, String newApkPath, String patchFilePath) throws Exception {
// 根据操作系统选择命令
String os = System.getProperty("os.name").toLowerCase();
String command;
if (os.contains("win")) {
command = "bsdiff" + File.separator + "win" + File.separator + "bsdiff.exe";
} else if (os.contains("linux")) {
command = "bsdiff" + File.separator + "linux" + File.separator + "bsdiff";
} else if (os.contains("mac")) {
command = "bsdiff" + File.separator + "mac" + File.separator + "bsdiff";
} else {
throw new UnsupportedOperationException("Unsupported OS");
}
File file = new File(command);
logger.info("bsdiff file exists = " + file.exists());
// 将 command 和 arg 合并成一个数组command 作为第一个参数
List<String> cmdArgs = new ArrayList<>();
cmdArgs.add(command); // 第一个参数是命令本身
cmdArgs.add(oldApkPath);// 后续参数
cmdArgs.add(newApkPath);
cmdArgs.add(patchFilePath);
logger.info("bsdiff args = " + cmdArgs);
ProcessBuilder pb = new ProcessBuilder(cmdArgs); // 传入合并后的参数列表
pb.redirectErrorStream(true); // 合并错误流和输入流
Process process = pb.start();
int exitCode = process.waitFor();
logger.info("退出码: " + exitCode);
// 读取输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = reader.lines().collect(Collectors.joining("\n"));
BsdiffResult bsdiffResult = new BsdiffResult(exitCode, result);
return bsdiffResult;
}
public static class BsdiffResult implements Serializable {
int exitCode;
String result;
public BsdiffResult(int exitCode, String result) {
this.exitCode = exitCode;
this.result = result;
}
public int getExitCode() {
return exitCode;
}
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
}

View File

@@ -0,0 +1,36 @@
spring.application.name=Mir4Updater
server.port=65533
server.address=0.0.0.0
## \u6570\u636E\u8FDE\u63A5\u4FE1\u606F
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3305/spring_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=tt
spring.datasource.password=fanhuitong
# Hibernate\u914D\u7F6E
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# \u542F\u7528 SQL \u65E5\u5FD7
spring.jpa.show-sql=true
# \u683C\u5F0F\u5316 SQL
#spring.jpa.properties.hibernate.format_sql=true
# \u663E\u793A\u7ED1\u5B9A\u53C2\u6570\u7684\u5177\u4F53\u503C\uFF08\u5173\u952E\u914D\u7F6E\uFF09
logging.level.org.hibernate.type.descriptor.sql=TRACE
# \u6587\u4EF6\u5927\u5C0F
spring.servlet.multipart.max-file-size=2GB
spring.servlet.multipart.max-request-size=2GB
mybatis.type-aliases-package=com.mir4updater.backend.entity
mybatis.mapperLocations=classpath:mapper/*.xml
# redis\u57FA\u7840\u914D\u7F6E
# 0\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A\u4F60\u8981\u64CD\u63A7\u7684 Redis \u4E0A\u7684\u54EA\u4E2A\u6570\u636E\u5E93
spring.data.redis.database=0
# 6379\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A Redis \u7AEF\u53E3
spring.data.redis.port=6379
# \u8FD9\u91CC\u586B\u5199\u4F60\u7684\u670D\u52A1\u5668\u5730\u5740
spring.data.redis.host=127.0.0.1
spring.data.redis.password=fanhuitong
# \u53EF\u7701\u7565
spring.data.redis.lettuce.pool.min-idle=5
spring.data.redis.lettuce.pool.max-idle=10
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=1ms
spring.data.redis.lettuce.shutdown-timeout=100ms

View File

@@ -0,0 +1,36 @@
spring.application.name=Mir4Updater
server.port=65534
server.address=0.0.0.0
## \u6570\u636E\u8FDE\u63A5\u4FE1\u606F
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3305/spring_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=tt
spring.datasource.password=fanhuitong
# Hibernate\u914D\u7F6E
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# \u542F\u7528 SQL \u65E5\u5FD7
spring.jpa.show-sql=true
# \u683C\u5F0F\u5316 SQL
#spring.jpa.properties.hibernate.format_sql=true
# \u663E\u793A\u7ED1\u5B9A\u53C2\u6570\u7684\u5177\u4F53\u503C\uFF08\u5173\u952E\u914D\u7F6E\uFF09
logging.level.org.hibernate.type.descriptor.sql=TRACE
# \u6587\u4EF6\u5927\u5C0F
spring.servlet.multipart.max-file-size=2GB
spring.servlet.multipart.max-request-size=2GB
mybatis.type-aliases-package=com.mir4updater.backend.entity
mybatis.mapperLocations=classpath:mapper/*.xml
# redis\u57FA\u7840\u914D\u7F6E
# 0\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A\u4F60\u8981\u64CD\u63A7\u7684 Redis \u4E0A\u7684\u54EA\u4E2A\u6570\u636E\u5E93
spring.data.redis.database=0
# 6379\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A Redis \u7AEF\u53E3
spring.data.redis.port=6379
# \u8FD9\u91CC\u586B\u5199\u4F60\u7684\u670D\u52A1\u5668\u5730\u5740
spring.data.redis.host=127.0.0.1
spring.data.redis.password=fanhuitong
# \u53EF\u7701\u7565
spring.data.redis.lettuce.pool.min-idle=5
spring.data.redis.lettuce.pool.max-idle=10
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=1ms
spring.data.redis.lettuce.shutdown-timeout=100ms

View File

@@ -0,0 +1,36 @@
spring.application.name=Mir4Updater
server.port=65532
server.address=0.0.0.0
## \u6570\u636E\u8FDE\u63A5\u4FE1\u606F
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3305/spring_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=tt
spring.datasource.password=fanhuitong
# Hibernate\u914D\u7F6E
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# \u542F\u7528 SQL \u65E5\u5FD7
spring.jpa.show-sql=true
# \u683C\u5F0F\u5316 SQL
#spring.jpa.properties.hibernate.format_sql=true
# \u663E\u793A\u7ED1\u5B9A\u53C2\u6570\u7684\u5177\u4F53\u503C\uFF08\u5173\u952E\u914D\u7F6E\uFF09
logging.level.org.hibernate.type.descriptor.sql=TRACE
# \u6587\u4EF6\u5927\u5C0F
spring.servlet.multipart.max-file-size=2GB
spring.servlet.multipart.max-request-size=2GB
mybatis.type-aliases-package=com.mir4updater.backend.entity
mybatis.mapperLocations=classpath:mapper/*.xml
# redis\u57FA\u7840\u914D\u7F6E
# 0\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A\u4F60\u8981\u64CD\u63A7\u7684 Redis \u4E0A\u7684\u54EA\u4E2A\u6570\u636E\u5E93
spring.data.redis.database=0
# 6379\u4E5F\u662F\u9ED8\u8BA4\u503C\uFF0C\u8868\u793A Redis \u7AEF\u53E3
spring.data.redis.port=6379
# \u8FD9\u91CC\u586B\u5199\u4F60\u7684\u670D\u52A1\u5668\u5730\u5740
spring.data.redis.host=127.0.0.1
spring.data.redis.password=fanhuitong
# \u53EF\u7701\u7565
spring.data.redis.lettuce.pool.min-idle=5
spring.data.redis.lettuce.pool.max-idle=10
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=1ms
spring.data.redis.lettuce.shutdown-timeout=100ms

View File

@@ -1,38 +1,3 @@
spring.application.name=Mir4Updater
server.port=65534
## 数据连接信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3305/spring_boot?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=tt
spring.datasource.password=fanhuitong
# Hibernate配置
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# 启用 SQL 日志
spring.jpa.show-sql=true
# 格式化 SQL
#spring.jpa.properties.hibernate.format_sql=true
# 显示绑定参数的具体值(关键配置)
logging.level.org.hibernate.type.descriptor.sql=TRACE
# 文件大小
spring.servlet.multipart.max-file-size=2GB
spring.servlet.multipart.max-request-size=2GB
mybatis.type-aliases-package=com.mir4updater.backend.entity
mybatis.mapperLocations=classpath:mapper/*.xml
# redis基础配置
# 0也是默认值表示你要操控的 Redis 上的哪个数据库
spring.data.redis.database=0
# 6379也是默认值表示 Redis 端口
spring.data.redis.port=6379
# 这里填写你的服务器地址
spring.data.redis.host=127.0.0.1
spring.data.redis.password=fanhuitong
# 可省略
spring.data.redis.lettuce.pool.min-idle=5
spring.data.redis.lettuce.pool.max-idle=10
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=1ms
spring.data.redis.lettuce.shutdown-timeout=100ms
# application.properties
# \u9ED8\u8BA4\u6FC0\u6D3B\u751F\u4EA7\u73AF\u5883
spring.profiles.active=prod

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB