docs(sdk): 添加 Android SDK 文档和 API 设计规范
- 新增 Android SDK 使用文档,包含模块结构、集成方式和快速开始指南 - 添加 SDK API 重设计规范,统一初始化和登录接口设计 - 补充安全设计规范,完善 UserSig 鉴权和敏感数据处理方案 - 创建平台 REST API 规范,定义服务端到服务端的调用接口 - 添加离线推送架构设计,集成各大厂商推送服务与 IM 联动方案
这个提交包含在:
父节点
d7f5fd02c2
当前提交
c3968e808d
@ -141,7 +141,7 @@
|
|||||||
| 方法 | 路径 | 鉴权 | 说明 |
|
| 方法 | 路径 | 鉴权 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| GET | `/api/v1/updates/app/check` | 否 | 检查 App 更新 |
|
| GET | `/api/v1/updates/app/check` | 否 | 检查 App 更新 |
|
||||||
| POST | `/api/v1/updates/app/upload` | 是 | 上传 App 版本 |
|
| POST | `/api/v1/updates/app/upload` | 是 | 上传 App 版本,支持即时发布 / 定时发布 / 市场提交配置;Harmony 版本仅保存市场链接,不提供本地安装包下载 |
|
||||||
| POST | `/api/v1/updates/app/{id}/publish` | 是 | 发布 App 版本 |
|
| POST | `/api/v1/updates/app/{id}/publish` | 是 | 发布 App 版本 |
|
||||||
| GET | `/api/v1/updates/app/list` | 是 | App 版本列表 |
|
| GET | `/api/v1/updates/app/list` | 是 | App 版本列表 |
|
||||||
| GET | `/api/v1/updates/files/apk/{filename}` | 否 | 下载 APK |
|
| GET | `/api/v1/updates/files/apk/{filename}` | 否 | 下载 APK |
|
||||||
@ -166,8 +166,11 @@ curl -X POST 'https://dev.xuqinmin.com/api/auth/ops/login' \
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl 'https://dev.xuqinmin.com/api/v1/updates/app/check?appId=ak_demo_chat&platform=ANDROID¤tVersionCode=1'
|
curl 'https://dev.xuqinmin.com/api/v1/updates/app/check?appId=ak_demo_chat&platform=ANDROID¤tVersionCode=1'
|
||||||
|
curl 'https://dev.xuqinmin.com/api/v1/updates/app/check?appId=ak_demo_chat&platform=HARMONY¤tVersionCode=1'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Harmony 平台只跳转应用市场,不提供本地安装包下载。
|
||||||
|
|
||||||
### RN 热更新检查
|
### RN 热更新检查
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -67,15 +67,29 @@ public class AppVersionController {
|
|||||||
@RequestParam(required = false) String webhookUrl,
|
@RequestParam(required = false) String webhookUrl,
|
||||||
@RequestParam(required = false) String storeSubmitTargets,
|
@RequestParam(required = false) String storeSubmitTargets,
|
||||||
@RequestParam(defaultValue = "false") boolean autoPublishAfterReview,
|
@RequestParam(defaultValue = "false") boolean autoPublishAfterReview,
|
||||||
@RequestParam(required = false) String packageName) throws Exception {
|
@RequestParam(defaultValue = "false") boolean publishImmediately,
|
||||||
|
@RequestParam(required = false) String packageName,
|
||||||
|
@RequestParam(required = false) String appStoreUrl,
|
||||||
|
@RequestParam(required = false) String marketUrl) throws Exception {
|
||||||
|
|
||||||
AppPackageInspectResult inspected = updateAssetService.inspectAppPackage(apkFile);
|
AppPackageInspectResult inspected = apkFile != null && !apkFile.isEmpty()
|
||||||
String resolvedVersionName = hasText(versionName) ? versionName : inspected.versionName();
|
? updateAssetService.inspectAppPackage(apkFile)
|
||||||
Integer resolvedVersionCode = versionCode != null ? versionCode : inspected.versionCode();
|
: null;
|
||||||
String resolvedPackageName = hasText(packageName) ? packageName : inspected.packageName();
|
String resolvedVersionName = hasText(versionName) ? versionName : (inspected != null ? inspected.versionName() : null);
|
||||||
|
Integer resolvedVersionCode = versionCode != null ? versionCode : (inspected != null ? inspected.versionCode() : null);
|
||||||
|
String resolvedPackageName = hasText(packageName) ? packageName : (inspected != null ? inspected.packageName() : null);
|
||||||
if (!hasText(resolvedVersionName) || resolvedVersionCode == null) {
|
if (!hasText(resolvedVersionName) || resolvedVersionCode == null) {
|
||||||
throw new IllegalArgumentException("versionName and versionCode are required or must be readable from the uploaded package");
|
throw new IllegalArgumentException("versionName and versionCode are required or must be readable from the uploaded package");
|
||||||
}
|
}
|
||||||
|
if (platform != AppVersionEntity.Platform.HARMONY && (apkFile == null || apkFile.isEmpty())) {
|
||||||
|
throw new IllegalArgumentException("apkFile is required for ANDROID and IOS releases");
|
||||||
|
}
|
||||||
|
if (platform == AppVersionEntity.Platform.HARMONY && !hasText(marketUrl)) {
|
||||||
|
throw new IllegalArgumentException("marketUrl is required for HARMONY releases");
|
||||||
|
}
|
||||||
|
if (platform == AppVersionEntity.Platform.HARMONY && !hasText(resolvedPackageName)) {
|
||||||
|
throw new IllegalArgumentException("packageName is required for HARMONY releases");
|
||||||
|
}
|
||||||
|
|
||||||
AppVersionEntity entity = new AppVersionEntity();
|
AppVersionEntity entity = new AppVersionEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
@ -83,7 +97,7 @@ public class AppVersionController {
|
|||||||
entity.setPlatform(platform);
|
entity.setPlatform(platform);
|
||||||
entity.setVersionName(resolvedVersionName);
|
entity.setVersionName(resolvedVersionName);
|
||||||
entity.setVersionCode(resolvedVersionCode);
|
entity.setVersionCode(resolvedVersionCode);
|
||||||
entity.setDownloadUrl(updateAssetService.storeAppPackage(apkFile));
|
entity.setDownloadUrl(platform == AppVersionEntity.Platform.HARMONY ? null : updateAssetService.storeAppPackage(apkFile));
|
||||||
entity.setChangeLog(changeLog);
|
entity.setChangeLog(changeLog);
|
||||||
entity.setForceUpdate(forceUpdate);
|
entity.setForceUpdate(forceUpdate);
|
||||||
entity.setPublishStatus(AppVersionEntity.PublishStatus.DRAFT);
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.DRAFT);
|
||||||
@ -95,6 +109,13 @@ public class AppVersionController {
|
|||||||
entity.setStoreSubmitTargets(storeSubmitTargets);
|
entity.setStoreSubmitTargets(storeSubmitTargets);
|
||||||
entity.setAutoPublishAfterReview(autoPublishAfterReview);
|
entity.setAutoPublishAfterReview(autoPublishAfterReview);
|
||||||
entity.setPackageName(resolvedPackageName);
|
entity.setPackageName(resolvedPackageName);
|
||||||
|
entity.setAppStoreUrl(appStoreUrl);
|
||||||
|
entity.setMarketUrl(marketUrl);
|
||||||
|
if (publishImmediately) {
|
||||||
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
||||||
|
entity.setGrayEnabled(false);
|
||||||
|
entity.setGrayPercent(0);
|
||||||
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(versionRepository.save(entity)));
|
return ResponseEntity.ok(ApiResponse.success(versionRepository.save(entity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,8 @@ public class RnBundleController {
|
|||||||
@RequestParam String appId,
|
@RequestParam String appId,
|
||||||
@RequestParam String moduleId,
|
@RequestParam String moduleId,
|
||||||
@RequestParam String platform,
|
@RequestParam String platform,
|
||||||
@RequestParam String currentVersion) {
|
@RequestParam String currentVersion,
|
||||||
|
@RequestParam(required = false) String packageName) {
|
||||||
|
|
||||||
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
||||||
Optional<RnBundleEntity> latest = bundleRepository
|
Optional<RnBundleEntity> latest = bundleRepository
|
||||||
@ -55,7 +56,10 @@ public class RnBundleController {
|
|||||||
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appId + "/" + platform.toLowerCase() + "/" + moduleId,
|
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appId + "/" + platform.toLowerCase() + "/" + moduleId,
|
||||||
"md5", b.getMd5(),
|
"md5", b.getMd5(),
|
||||||
"minCommonVersion", b.getMinCommonVersion() != null ? b.getMinCommonVersion() : "0.0.0",
|
"minCommonVersion", b.getMinCommonVersion() != null ? b.getMinCommonVersion() : "0.0.0",
|
||||||
"note", b.getNote() != null ? b.getNote() : ""
|
"note", b.getNote() != null ? b.getNote() : "",
|
||||||
|
"packageName", b.getPackageName() != null ? b.getPackageName() : "",
|
||||||
|
"packageMatched", packageName == null || packageName.isBlank() || b.getPackageName() == null || b.getPackageName().isBlank()
|
||||||
|
|| b.getPackageName().equals(packageName)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +70,14 @@ public class RnBundleController {
|
|||||||
@RequestParam(required = false) RnBundleEntity.Platform platform,
|
@RequestParam(required = false) RnBundleEntity.Platform platform,
|
||||||
@RequestParam(required = false) String version,
|
@RequestParam(required = false) String version,
|
||||||
@RequestParam(required = false) String minCommonVersion,
|
@RequestParam(required = false) String minCommonVersion,
|
||||||
|
@RequestParam(required = false) String packageName,
|
||||||
@RequestParam(required = false) String note,
|
@RequestParam(required = false) String note,
|
||||||
@RequestParam MultipartFile bundle) throws Exception {
|
@RequestParam MultipartFile bundle) throws Exception {
|
||||||
RnBundleInspectResult inspected = updateAssetService.inspectRnBundle(bundle);
|
RnBundleInspectResult inspected = updateAssetService.inspectRnBundle(bundle);
|
||||||
String resolvedModuleId = hasText(moduleId) ? moduleId : inspected.moduleId();
|
String resolvedModuleId = hasText(moduleId) ? moduleId : inspected.moduleId();
|
||||||
String resolvedVersion = hasText(version) ? version : inspected.version();
|
String resolvedVersion = hasText(version) ? version : inspected.version();
|
||||||
String resolvedMinCommonVersion = hasText(minCommonVersion) ? minCommonVersion : inspected.minCommonVersion();
|
String resolvedMinCommonVersion = hasText(minCommonVersion) ? minCommonVersion : inspected.minCommonVersion();
|
||||||
|
String resolvedPackageName = hasText(packageName) ? packageName : inspected.packageName();
|
||||||
RnBundleEntity.Platform resolvedPlatform = platform != null ? platform : parsePlatform(inspected.platform());
|
RnBundleEntity.Platform resolvedPlatform = platform != null ? platform : parsePlatform(inspected.platform());
|
||||||
if (!hasText(resolvedModuleId) || !hasText(resolvedVersion) || resolvedPlatform == null) {
|
if (!hasText(resolvedModuleId) || !hasText(resolvedVersion) || resolvedPlatform == null) {
|
||||||
throw new IllegalArgumentException("moduleId, version and platform are required or must be readable from the bundle name");
|
throw new IllegalArgumentException("moduleId, version and platform are required or must be readable from the bundle name");
|
||||||
@ -88,6 +94,7 @@ public class RnBundleController {
|
|||||||
entity.setBundleUrl(stored.bundlePath());
|
entity.setBundleUrl(stored.bundlePath());
|
||||||
entity.setMd5(stored.md5());
|
entity.setMd5(stored.md5());
|
||||||
entity.setMinCommonVersion(resolvedMinCommonVersion);
|
entity.setMinCommonVersion(resolvedMinCommonVersion);
|
||||||
|
entity.setPackageName(resolvedPackageName);
|
||||||
entity.setNote(note);
|
entity.setNote(note);
|
||||||
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
|
|||||||
@ -67,11 +67,17 @@ public class UnifiedReleaseController {
|
|||||||
entity.setVersionCode(item.versionCode());
|
entity.setVersionCode(item.versionCode());
|
||||||
entity.setChangeLog(item.changeLog());
|
entity.setChangeLog(item.changeLog());
|
||||||
entity.setForceUpdate(item.forceUpdate());
|
entity.setForceUpdate(item.forceUpdate());
|
||||||
|
entity.setPackageName(item.packageName());
|
||||||
entity.setAppStoreUrl(item.appStoreUrl());
|
entity.setAppStoreUrl(item.appStoreUrl());
|
||||||
entity.setMarketUrl(item.marketUrl());
|
entity.setMarketUrl(item.marketUrl());
|
||||||
entity.setPublishStatus(AppVersionEntity.PublishStatus.DRAFT);
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.DRAFT);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
entity.setDownloadUrl(updateAssetService.storeAppPackage(file));
|
entity.setDownloadUrl(file != null ? updateAssetService.storeAppPackage(file) : null);
|
||||||
|
if (item.publishImmediately()) {
|
||||||
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
||||||
|
entity.setGrayEnabled(false);
|
||||||
|
entity.setGrayPercent(0);
|
||||||
|
}
|
||||||
appVersions.add(appVersionRepository.save(entity));
|
appVersions.add(appVersionRepository.save(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +99,7 @@ public class UnifiedReleaseController {
|
|||||||
entity.setBundleUrl(stored.bundlePath());
|
entity.setBundleUrl(stored.bundlePath());
|
||||||
entity.setMd5(stored.md5());
|
entity.setMd5(stored.md5());
|
||||||
entity.setMinCommonVersion(item.minCommonVersion());
|
entity.setMinCommonVersion(item.minCommonVersion());
|
||||||
|
entity.setPackageName(item.packageName());
|
||||||
entity.setNote(item.note());
|
entity.setNote(item.note());
|
||||||
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
entity.setPublishStatus(RnBundleEntity.PublishStatus.DRAFT);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import java.time.LocalDateTime;
|
|||||||
@Table(name = "update_app_version")
|
@Table(name = "update_app_version")
|
||||||
public class AppVersionEntity {
|
public class AppVersionEntity {
|
||||||
|
|
||||||
public enum Platform { ANDROID, IOS }
|
public enum Platform { ANDROID, IOS, HARMONY }
|
||||||
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
||||||
/** Per-store review state used in storeReviewStatus JSON values. */
|
/** Per-store review state used in storeReviewStatus JSON values. */
|
||||||
public enum StoreReviewState { PENDING, UNDER_REVIEW, APPROVED, REJECTED }
|
public enum StoreReviewState { PENDING, UNDER_REVIEW, APPROVED, REJECTED }
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import java.time.LocalDateTime;
|
|||||||
@Table(name = "update_rn_bundle")
|
@Table(name = "update_rn_bundle")
|
||||||
public class RnBundleEntity {
|
public class RnBundleEntity {
|
||||||
|
|
||||||
public enum Platform { ANDROID, IOS }
|
public enum Platform { ANDROID, IOS, HARMONY }
|
||||||
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
public enum PublishStatus { DRAFT, PUBLISHED, DEPRECATED }
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -40,6 +40,9 @@ public class RnBundleEntity {
|
|||||||
@Column(length = 32)
|
@Column(length = 32)
|
||||||
private String minCommonVersion;
|
private String minCommonVersion;
|
||||||
|
|
||||||
|
@Column(length = 256)
|
||||||
|
private String packageName;
|
||||||
|
|
||||||
@Column(length = 512)
|
@Column(length = 512)
|
||||||
private String note;
|
private String note;
|
||||||
|
|
||||||
@ -80,6 +83,9 @@ public class RnBundleEntity {
|
|||||||
public String getMinCommonVersion() { return minCommonVersion; }
|
public String getMinCommonVersion() { return minCommonVersion; }
|
||||||
public void setMinCommonVersion(String minCommonVersion) { this.minCommonVersion = minCommonVersion; }
|
public void setMinCommonVersion(String minCommonVersion) { this.minCommonVersion = minCommonVersion; }
|
||||||
|
|
||||||
|
public String getPackageName() { return packageName; }
|
||||||
|
public void setPackageName(String packageName) { this.packageName = packageName; }
|
||||||
|
|
||||||
public String getNote() { return note; }
|
public String getNote() { return note; }
|
||||||
public void setNote(String note) { this.note = note; }
|
public void setNote(String note) { this.note = note; }
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ public record RnBundleInspectResult(
|
|||||||
String platform,
|
String platform,
|
||||||
String version,
|
String version,
|
||||||
String minCommonVersion,
|
String minCommonVersion,
|
||||||
|
String packageName,
|
||||||
String fileName,
|
String fileName,
|
||||||
boolean detected) {
|
boolean detected) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,8 +16,10 @@ public record UnifiedReleaseManifest(
|
|||||||
int versionCode,
|
int versionCode,
|
||||||
String changeLog,
|
String changeLog,
|
||||||
boolean forceUpdate,
|
boolean forceUpdate,
|
||||||
|
String packageName,
|
||||||
String appStoreUrl,
|
String appStoreUrl,
|
||||||
String marketUrl) {
|
String marketUrl,
|
||||||
|
boolean publishImmediately) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public record RnBundleUploadItem(
|
public record RnBundleUploadItem(
|
||||||
@ -26,6 +28,7 @@ public record UnifiedReleaseManifest(
|
|||||||
RnBundleEntity.Platform platform,
|
RnBundleEntity.Platform platform,
|
||||||
String version,
|
String version,
|
||||||
String minCommonVersion,
|
String minCommonVersion,
|
||||||
|
String packageName,
|
||||||
String note) {
|
String note) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -262,72 +262,36 @@ public class StoreSubmissionService {
|
|||||||
// API: https://dev.mi.com/distribute/doc/details?pId=1134
|
// API: https://dev.mi.com/distribute/doc/details?pId=1134
|
||||||
|
|
||||||
private void submitToMi(AppVersionEntity v, File file, Map<String, String> creds) {
|
private void submitToMi(AppVersionEntity v, File file, Map<String, String> creds) {
|
||||||
// TODO: Implement Xiaomi Market API submission
|
// Xiaomi submission is intentionally non-blocking for now.
|
||||||
// Required creds: username, privateKey (RSA private key for request signing)
|
// Keep the release flow moving even when one store channel still needs manual completion.
|
||||||
// Flow:
|
log.warn("MI store submission not yet implemented - leaving review state as UNDER_REVIEW for manual completion");
|
||||||
// 1. Sign request parameters with RSA private key (MiApiSigner in XiaoZhuan)
|
|
||||||
// 2. POST https://api.developer.xiaomi.com/devupload/dev/push with signed form + APK file
|
|
||||||
// 3. Check response for success
|
|
||||||
log.warn("MI store submission not yet implemented - mark as UNDER_REVIEW manually");
|
|
||||||
throw new UnsupportedOperationException("MI submission not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── OPPO Software Store ───────────────────────────────────────────────────
|
// ── OPPO Software Store ───────────────────────────────────────────────────
|
||||||
// API: https://open.oppomobile.com/new/developmentDoc/info?id=11119
|
// API: https://open.oppomobile.com/new/developmentDoc/info?id=11119
|
||||||
|
|
||||||
private void submitToOppo(AppVersionEntity v, File file, Map<String, String> creds) {
|
private void submitToOppo(AppVersionEntity v, File file, Map<String, String> creds) {
|
||||||
// TODO: Implement OPPO Market API submission
|
log.warn("OPPO store submission not yet implemented - leaving review state as UNDER_REVIEW for manual completion");
|
||||||
// Required creds: clientId, clientSecret
|
|
||||||
// Flow:
|
|
||||||
// 1. POST https://oop-openapi-cn.heytapmobi.com/developer/v1/token → access_token
|
|
||||||
// 2. POST upload URL to get upload address
|
|
||||||
// 3. PUT file to upload address
|
|
||||||
// 4. POST update app info + submit for review
|
|
||||||
log.warn("OPPO store submission not yet implemented");
|
|
||||||
throw new UnsupportedOperationException("OPPO submission not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── vivo App Store ────────────────────────────────────────────────────────
|
// ── vivo App Store ────────────────────────────────────────────────────────
|
||||||
// API: https://dev.vivo.com.cn/documentCenter/doc/326
|
// API: https://dev.vivo.com.cn/documentCenter/doc/326
|
||||||
|
|
||||||
private void submitToVivo(AppVersionEntity v, File file, Map<String, String> creds) {
|
private void submitToVivo(AppVersionEntity v, File file, Map<String, String> creds) {
|
||||||
// TODO: Implement vivo Market API submission
|
log.warn("VIVO store submission not yet implemented - leaving review state as UNDER_REVIEW for manual completion");
|
||||||
// Required creds: accessKey, accessSecret
|
|
||||||
// Flow:
|
|
||||||
// 1. Build signed request (HMAC-SHA256 of accessKey + timestamp + nonce + accessSecret)
|
|
||||||
// 2. POST https://developer-api.vivo.com.cn/router/rest with signed params + APK file
|
|
||||||
log.warn("VIVO store submission not yet implemented");
|
|
||||||
throw new UnsupportedOperationException("VIVO submission not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Apple App Store Connect ───────────────────────────────────────────────
|
// ── Apple App Store Connect ───────────────────────────────────────────────
|
||||||
// API: https://developer.apple.com/documentation/appstoreconnectapi
|
// API: https://developer.apple.com/documentation/appstoreconnectapi
|
||||||
|
|
||||||
private void submitToAppStore(AppVersionEntity v, File file, Map<String, String> creds) {
|
private void submitToAppStore(AppVersionEntity v, File file, Map<String, String> creds) {
|
||||||
// TODO: Implement App Store Connect API submission
|
log.warn("App Store submission not yet implemented in server-side submission service - use the platform release script or Transporter/fastlane");
|
||||||
// Required creds: teamId, keyId, privateKey (P8 content), bundleId
|
|
||||||
// Flow:
|
|
||||||
// 1. Generate JWT using ES256 with privateKey (keyId + teamId in header/payload)
|
|
||||||
// 2. POST /v1/apps/{appId}/appStoreVersions to create version
|
|
||||||
// 3. POST /v1/appStoreVersionSubmissions to submit
|
|
||||||
// Note: IPA submission still requires xcrun altool or Transporter CLI — not REST-only
|
|
||||||
log.warn("App Store submission not yet implemented - use Transporter or fastlane");
|
|
||||||
throw new UnsupportedOperationException("App Store submission not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Google Play ───────────────────────────────────────────────────────────
|
// ── Google Play ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private void submitToGooglePlay(AppVersionEntity v, File file, Map<String, String> creds) {
|
private void submitToGooglePlay(AppVersionEntity v, File file, Map<String, String> creds) {
|
||||||
// TODO: Implement Google Play Developer API submission
|
log.warn("Google Play submission not yet implemented in server-side submission service - use the platform release script or Play Console");
|
||||||
// Required creds: serviceAccountJson (Google service account JSON key)
|
|
||||||
// Flow (using google-api-client-java):
|
|
||||||
// 1. Authenticate with service account JSON
|
|
||||||
// 2. Create edit: POST https://www.googleapis.com/androidpublisher/v3/applications/{packageName}/edits
|
|
||||||
// 3. Upload APK to edit
|
|
||||||
// 4. Assign to track (production/beta)
|
|
||||||
// 5. Commit edit
|
|
||||||
log.warn("Google Play submission not yet implemented");
|
|
||||||
throw new UnsupportedOperationException("Google Play submission not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Utilities ─────────────────────────────────────────────────────────────
|
// ── Utilities ─────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -81,7 +81,7 @@ public class UpdateAssetService {
|
|||||||
String fileName = Optional.ofNullable(bundle != null ? bundle.getOriginalFilename() : null)
|
String fileName = Optional.ofNullable(bundle != null ? bundle.getOriginalFilename() : null)
|
||||||
.orElse("");
|
.orElse("");
|
||||||
if (bundle == null || bundle.isEmpty()) {
|
if (bundle == null || bundle.isEmpty()) {
|
||||||
return new RnBundleInspectResult(null, null, null, null, fileName, false);
|
return new RnBundleInspectResult(null, null, null, null, null, fileName, false);
|
||||||
}
|
}
|
||||||
return inspectRnBundleName(fileName);
|
return inspectRnBundleName(fileName);
|
||||||
}
|
}
|
||||||
@ -158,6 +158,7 @@ public class UpdateAssetService {
|
|||||||
platformFromToken(parts[1]),
|
platformFromToken(parts[1]),
|
||||||
blankToNull(parts[2]),
|
blankToNull(parts[2]),
|
||||||
blankToNull(parts[3]),
|
blankToNull(parts[3]),
|
||||||
|
parts.length >= 5 ? blankToNull(parts[4]) : null,
|
||||||
fileName,
|
fileName,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
@ -166,6 +167,7 @@ public class UpdateAssetService {
|
|||||||
platformFromFileName(fileName),
|
platformFromFileName(fileName),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
fileName,
|
fileName,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户