diff --git a/update-service/src/main/java/com/xuqm/update/service/AppStoreService.java b/update-service/src/main/java/com/xuqm/update/service/AppStoreService.java index 16c9eb3..eb6f24c 100644 --- a/update-service/src/main/java/com/xuqm/update/service/AppStoreService.java +++ b/update-service/src/main/java/com/xuqm/update/service/AppStoreService.java @@ -71,7 +71,7 @@ public class AppStoreService { boolean enabled) { boolean isCreate = configRepo.findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(appKey, storeType).isEmpty(); AppStoreConfigEntity entity = configRepo - .findByAppKeyAndStoreType(appKey, storeType) + .findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(appKey, storeType) .orElseGet(AppStoreConfigEntity::new); if (entity.getId() == null) { diff --git a/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java b/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java index 907520f..ee81ef7 100644 --- a/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java +++ b/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java @@ -200,7 +200,7 @@ public class StoreSubmissionService { // no stale webhooks or poll cycles can trigger auto-publish from the old review. if ("WITHDRAWN".equals(activeState)) { AppStoreConfigEntity withdrawCfg = configRepo - .findByAppKeyAndStoreType(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) + .findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) .orElse(null); if (withdrawCfg != null && withdrawCfg.isEnabled()) { try { @@ -220,7 +220,7 @@ public class StoreSubmissionService { } AppStoreConfigEntity cfg = configRepo - .findByAppKeyAndStoreType(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) + .findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) .orElse(null); if (cfg == null || !cfg.isEnabled()) { skippedCount.incrementAndGet(); @@ -432,8 +432,16 @@ public class StoreSubmissionService { state.setCanSubmit(false); state.setBlockReason("查询失败,不能确认状态"); } else if (!state.getOnlineVersionCode().isBlank() && !submittedCode.equals(state.getOnlineVersionCode())) { - state.setNonCurrentRelease(true); - state.setCanSubmit(true); + int cmp = compareVersionCodes(state.getOnlineVersionCode(), submittedCode); + if (cmp > 0) { + state.setNonCurrentRelease(true); + state.setCanSubmit(false); + state.setBlockReason("应用商店已有更高版本上线,禁止提交更低版本"); + } else { + // onlineVersionCode < submittedCode: normal new release, allow submit + state.setNonCurrentRelease(false); + state.setCanSubmit(true); + } } else { state.setCanSubmit(true); } @@ -504,7 +512,7 @@ public class StoreSubmissionService { reviewState, onShelfName, onShelfCode, versionName, versionCode, - isLive, !isLive && !onShelfCode.isBlank(), + isLive, !isLive && !onShelfCode.isBlank() && compareVersionCodes(onShelfCode, submittedCode) > 0, true, "", sanitizeJson(resp.getBody())); } @@ -523,7 +531,7 @@ public class StoreSubmissionService { // Only treat as live for the CURRENT submission when onlineVersionCode matches submittedCode. boolean currentSubmissionLive = submittedCode.equals(onlineVersionCode) && !submittedCode.isBlank(); boolean hasOnlineVersion = updateVersion || !onlineVersionCode.isBlank(); - boolean nonCurrentRelease = hasOnlineVersion && !currentSubmissionLive; + boolean nonCurrentRelease = hasOnlineVersion && !currentSubmissionLive && compareVersionCodes(onlineVersionCode, submittedCode) > 0; StoreRemoteState.ReviewState reviewState; if (appStatus == 5 && currentSubmissionLive) { reviewState = StoreRemoteState.ReviewState.REJECTED; @@ -567,13 +575,15 @@ public class StoreSubmissionService { } log.info("OPPO remote state: versionId={} auditInt={} onlineCode={} submittedCode={} reviewState={}", v.getId(), auditInt, onlineVersionCode, submittedCode, reviewState); + boolean currentSubmissionLive = isLive && submittedCode.equals(onlineVersionCode); + boolean nonCurrentRelease = isLive && !submittedCode.equals(onlineVersionCode) && compareVersionCodes(onlineVersionCode, submittedCode) > 0; return StoreRemoteState.ok( AppStoreConfigEntity.StoreType.OPPO, reviewState, onlineVersionName, onlineVersionCode, "", "", - isLive && submittedCode.equals(onlineVersionCode), - isLive && !submittedCode.equals(onlineVersionCode), + currentSubmissionLive, + nonCurrentRelease, true, "", sanitizeJson(appData)); } @@ -611,7 +621,7 @@ public class StoreSubmissionService { reviewState = StoreRemoteState.ReviewState.UNKNOWN; } boolean currentSubmissionLive = isLive && submittedCode.equals(onlineVersionCode); - boolean nonCurrentRelease = isLive && !submittedCode.equals(onlineVersionCode); + boolean nonCurrentRelease = isLive && !submittedCode.equals(onlineVersionCode) && compareVersionCodes(onlineVersionCode, submittedCode) > 0; log.info("VIVO remote state queried: versionId={} submittedCode={} onlineVersionCode={} onlineVersionName={} status={} reviewState={} currentSubmissionLive={} nonCurrentRelease={}", v.getId(), submittedCode, onlineVersionCode, onlineVersionName, status, reviewState, currentSubmissionLive, nonCurrentRelease); StoreRemoteState state = StoreRemoteState.ok( @@ -654,13 +664,15 @@ public class StoreSubmissionService { } else { reviewState = StoreRemoteState.ReviewState.UNKNOWN; } + boolean currentSubmissionLive = isLive && submittedCode.equals(onlineVersionCode); + boolean nonCurrentRelease = isLive && !submittedCode.equals(onlineVersionCode) && compareVersionCodes(onlineVersionCode, submittedCode) > 0; return StoreRemoteState.ok( AppStoreConfigEntity.StoreType.HONOR, reviewState, onlineVersionName, onlineVersionCode, "", "", - isLive && submittedCode.equals(onlineVersionCode), - isLive && !submittedCode.equals(onlineVersionCode), + currentSubmissionLive, + nonCurrentRelease, true, "", sanitizeJson(body)); } @@ -710,8 +722,14 @@ public class StoreSubmissionService { if (mappedState != AppVersionEntity.StoreReviewState.UNDER_REVIEW) { log.info("Store review poll: {}/{} status changed to {}", v.getId(), storeType, mappedState); if (mappedState == AppVersionEntity.StoreReviewState.APPROVED) { - storeService.updateStoreReviewLive(v.getId(), storeType, !polled.isCurrentSubmissionLive(), - buildLiveReason(polled), buildExtra(polled)); + int cmp = compareVersionCodes(polled.getOnlineVersionCode(), String.valueOf(v.getVersionCode())); + if (polled.isCurrentSubmissionLive() || cmp >= 0) { + storeService.updateStoreReviewLive(v.getId(), storeType, !polled.isCurrentSubmissionLive(), + buildLiveReason(polled), buildExtra(polled)); + } else { + log.debug("Store review poll: {}/{} online {} < submitted {} — keeping current state", + v.getId(), storeType, polled.getOnlineVersionCode(), v.getVersionCode()); + } } else { storeService.updateStoreReview(v.getId(), storeType, mappedState, "厂商审核状态轮询检测"); } @@ -720,11 +738,17 @@ public class StoreSubmissionService { } } else { if (polled.getReviewState() == StoreRemoteState.ReviewState.ONLINE) { - log.info("Store review poll: {}/{} was REJECTED but store has live version currentSubmissionLive={} nonCurrentRelease={} liveVersionName={} liveVersionCode={}", - v.getId(), storeType, polled.isCurrentSubmissionLive(), polled.isNonCurrentRelease(), - polled.getOnlineVersionName(), polled.getOnlineVersionCode()); - storeService.updateStoreReviewLive(v.getId(), storeType, !polled.isCurrentSubmissionLive(), - buildLiveReason(polled), buildExtra(polled)); + int cmp = compareVersionCodes(polled.getOnlineVersionCode(), String.valueOf(v.getVersionCode())); + if (cmp >= 0) { + log.info("Store review poll: {}/{} was REJECTED but store has live version currentSubmissionLive={} nonCurrentRelease={} liveVersionName={} liveVersionCode={}", + v.getId(), storeType, polled.isCurrentSubmissionLive(), polled.isNonCurrentRelease(), + polled.getOnlineVersionName(), polled.getOnlineVersionCode()); + storeService.updateStoreReviewLive(v.getId(), storeType, !polled.isCurrentSubmissionLive(), + buildLiveReason(polled), buildExtra(polled)); + } else { + log.debug("Store review poll: {}/{} was REJECTED but online {} < submitted {} — not marking pre-existing", + v.getId(), storeType, polled.getOnlineVersionCode(), v.getVersionCode()); + } } else if ("MI".equals(storeType) && polled.getReviewState() == StoreRemoteState.ReviewState.UNDER_REVIEW_XIAOMI) { log.info("Store review poll: {}/{} was REJECTED but Xiaomi has no current-version reject signal — restoring UNDER_REVIEW", @@ -825,11 +849,19 @@ public class StoreSubmissionService { buildLiveReason(polled), buildExtra(polled)); } else if (!isApproved) { // Store has a different live version (preExisting) and we didn't have APPROVED yet. - // Mark as pre-existing live so UI shows which version is actually on the store. - log.info("Manual refresh: {}/{} pre-existing live detected currentSubmissionLive={} liveVersionCode={}", - v.getId(), storeType, false, polled.getOnlineVersionCode()); - storeService.updateStoreReviewLive(v.getId(), storeType, true, - buildLiveReason(polled), buildExtra(polled)); + // Only mark as pre-existing when the online version is >= submitted version. + // If online < submitted, this is a normal new-release scenario — do not write + // APPROVED+nonCurrentRelease which would block the submission UI. + int cmp = compareVersionCodes(polled.getOnlineVersionCode(), String.valueOf(v.getVersionCode())); + if (cmp >= 0) { + log.info("Manual refresh: {}/{} pre-existing live detected currentSubmissionLive={} liveVersionCode={}", + v.getId(), storeType, false, polled.getOnlineVersionCode()); + storeService.updateStoreReviewLive(v.getId(), storeType, true, + buildLiveReason(polled), buildExtra(polled)); + } else { + log.info("Manual refresh: {}/{} online version {} < submitted {} — normal new release, skipping pre-existing mark", + v.getId(), storeType, polled.getOnlineVersionCode(), v.getVersionCode()); + } } else { // Already APPROVED (from webhook): version approved but pending distribution. // Do NOT overwrite with nonCurrentRelease=true — that would show a misleading @@ -1552,7 +1584,7 @@ public class StoreSubmissionService { for (String storeType : targets) { try { AppStoreConfigEntity cfg = configRepo - .findByAppKeyAndStoreType(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) + .findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(v.getAppKey(), AppStoreConfigEntity.StoreType.valueOf(storeType)) .orElse(null); if (cfg != null && cfg.isEnabled()) { try { @@ -1755,7 +1787,7 @@ public class StoreSubmissionService { // Try to call HUAWEI API to update release plan if token available try { AppStoreConfigEntity cfg = configRepo - .findByAppKeyAndStoreType(v.getAppKey(), AppStoreConfigEntity.StoreType.HUAWEI) + .findTopByAppKeyAndStoreTypeOrderByUpdatedAtDesc(v.getAppKey(), AppStoreConfigEntity.StoreType.HUAWEI) .orElse(null); if (cfg != null && cfg.isEnabled()) { Map creds = parseConfig(cfg.getConfigJson()); @@ -2639,4 +2671,18 @@ public class StoreSubmissionService { String full = root.getClass().getSimpleName() + ": " + message; return full.length() > 900 ? full.substring(0, 900) + "..." : full; } + + /** + * Compare two version-code strings numerically. + * @return positive if online > submitted, negative if online < submitted, 0 if equal or unparseable. + */ + private static int compareVersionCodes(String onlineCode, String submittedCode) { + try { + int online = Integer.parseInt(onlineCode.trim()); + int submitted = Integer.parseInt(submittedCode.trim()); + return Integer.compare(online, submitted); + } catch (NumberFormatException | NullPointerException e) { + return 0; + } + } }