diff --git a/Dockerfile b/Dockerfile index 42bbb8a..cab9937 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /workspace COPY pom.xml ./pom.xml COPY common ./common +COPY im-sdk ./im-sdk COPY tenant-service ./tenant-service COPY im-service ./im-service COPY push-service ./push-service diff --git a/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java b/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java index 43404ed..489c3b6 100644 --- a/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java +++ b/file-service/src/main/java/com/xuqm/file/service/FileStorageService.java @@ -76,9 +76,15 @@ public class FileStorageService { Optional existing = fileRepository.findByHash(hash); if (existing.isPresent()) { FileEntity entity = existing.get(); - entity.setLastAccessedAt(Instant.now()); - fileRepository.save(entity); - return toUploadResult(entity); + // 检查磁盘文件是否仍然存在,若不存在则重新写入 + if (Files.exists(Paths.get(entity.getStoragePath()))) { + entity.setLastAccessedAt(Instant.now()); + fileRepository.save(entity); + return toUploadResult(entity); + } + // 文件记录存在但磁盘文件已丢失,删除旧记录后重新上传 + fileRepository.delete(entity); + fileRepository.flush(); } String ext = resolveExtension(originalName, mimeType); diff --git a/im-sdk/src/main/java/com/xuqm/im/sdk/XuqmImServerSdk.java b/im-sdk/src/main/java/com/xuqm/im/sdk/XuqmImServerSdk.java index 1c1d02c..ff50f50 100644 --- a/im-sdk/src/main/java/com/xuqm/im/sdk/XuqmImServerSdk.java +++ b/im-sdk/src/main/java/com/xuqm/im/sdk/XuqmImServerSdk.java @@ -50,7 +50,7 @@ public final class XuqmImServerSdk { this.objectMapper = new ObjectMapper() .registerModule(new JavaTimeModule()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - this.baseUrl = trimTrailingSlash(Objects.requireNonNull(builder.baseUrl, "baseUrl")); + this.baseUrl = trimTrailingSlash(builder.baseUrl); this.pushBaseUrl = trimTrailingSlash(builder.pushBaseUrl == null ? builder.baseUrl : builder.pushBaseUrl); this.updateBaseUrl = trimTrailingSlash(builder.updateBaseUrl == null ? builder.baseUrl : builder.updateBaseUrl); this.appId = Objects.requireNonNull(builder.appId, "appId"); @@ -1735,8 +1735,10 @@ public final class XuqmImServerSdk { return value.endsWith("/") ? value.substring(0, value.length() - 1) : value; } + private static final String DEFAULT_BASE_URL = "https://dev.xuqinmin.com"; + public static final class Builder { - private String baseUrl; + private String baseUrl = DEFAULT_BASE_URL; private String pushBaseUrl; private String updateBaseUrl; private String appId; @@ -1776,6 +1778,8 @@ public final class XuqmImServerSdk { } public XuqmImServerSdk build() { + Objects.requireNonNull(this.appId, "appId is required"); + Objects.requireNonNull(this.appSecret, "appSecret is required"); return new XuqmImServerSdk(this); } } diff --git a/update-service/src/main/java/com/xuqm/update/service/UpdateAssetService.java b/update-service/src/main/java/com/xuqm/update/service/UpdateAssetService.java index db4f28e..0fea549 100644 --- a/update-service/src/main/java/com/xuqm/update/service/UpdateAssetService.java +++ b/update-service/src/main/java/com/xuqm/update/service/UpdateAssetService.java @@ -50,6 +50,9 @@ public class UpdateAssetService { @Value("${update.base-url:https://update.dev.xuqinmin.com}") private String baseUrl; + @Value("${file.service.internal-url:http://127.0.0.1:8086}") + private String fileServiceInternalUrl; + public UpdateAssetService(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @@ -180,9 +183,31 @@ public class UpdateAssetService { if (normalized.endsWith(".ipa")) { return inspectIpa(file, fileName); } + // 文件名无后缀时,按文件魔数尝试解析(APK/IPA 本质都是 ZIP) + if (isZipFile(file)) { + try { + return inspectApk(file, fileName); + } catch (Exception ignored) { + } + try { + return inspectIpa(file, fileName); + } catch (Exception ignored) { + } + } return new AppPackageInspectResult(platformFromFileName(fileName), null, null, null, fileName, false); } + private boolean isZipFile(Path file) throws IOException { + if (!Files.exists(file) || Files.size(file) < 4) { + return false; + } + try (InputStream in = Files.newInputStream(file)) { + byte[] magic = new byte[4]; + int read = in.read(magic); + return read == 4 && magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x03 && magic[3] == 0x04; + } + } + private AppPackageInspectResult inspectIpa(Path file, String fileName) throws Exception { try (ZipFile zipFile = new ZipFile(file.toFile())) { ZipEntry entry = zipFile.stream() @@ -338,9 +363,14 @@ public class UpdateAssetService { } private RemotePackage downloadRemotePackage(String packageUrl, boolean tempFile) throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(packageUrl).openConnection(); + // 同一台服务器上的 file-service 使用内部地址,避免 Nginx HTTPS 代理的性能损耗 + String internalUrl = packageUrl; + if (packageUrl != null && packageUrl.contains("file.dev.xuqinmin.com")) { + internalUrl = packageUrl.replace("https://file.dev.xuqinmin.com", fileServiceInternalUrl); + } + HttpURLConnection connection = (HttpURLConnection) new URL(internalUrl).openConnection(); connection.setConnectTimeout(15_000); - connection.setReadTimeout(30_000); + connection.setReadTimeout(300_000); connection.setInstanceFollowRedirects(true); int status = connection.getResponseCode();