hejianhao
2026-09-01 f4e8dfd7f8f11966f4b5dc8c6661cbc3feccc4cf
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/system/service/AppVersionSettingsAppService.java
@@ -42,7 +42,7 @@
        return toDetailVO(requireItem(id));
    }
    @Transactional
    @Transactional(rollbackFor = Exception.class)
    public AppVersionDetailVO create(AppVersionCreateRequest request) {
        AppVersionConfig latest = latest();
        String androidFileUrl = normalizeOptionalText(request.getAndroidFileUrl());
@@ -65,6 +65,10 @@
    }
    public AppVersionCheckVO checkUpdate(String platform, Integer currentVersionCode) {
        return checkUpdate(platform, currentVersionCode, null);
    }
    public AppVersionCheckVO checkUpdate(String platform, Integer currentVersionCode, String currentVersionNo) {
        if (currentVersionCode == null) {
            throw new IllegalArgumentException("当前版本号不能为空");
        }
@@ -82,7 +86,7 @@
        String downloadUrl = AppVersionConfig.PLATFORM_ANDROID.equals(normalizedPlatform)
                ? normalizeOptionalText(latest.getAndroidFileUrl())
                : normalizeOptionalText(latest.getIosFileUrl());
        boolean hasUpdate = downloadUrl != null && defaultNumber(latest.getVersionCode()) > currentVersionCode;
        boolean hasUpdate = downloadUrl != null && isNewerVersion(latest, currentVersionCode, currentVersionNo);
        vo.setHasUpdate(hasUpdate);
        if (!hasUpdate) {
            return vo;
@@ -95,6 +99,86 @@
        return vo;
    }
    public AppVersionCheckVO checkUpdateNew(String platform) {
        String normalizedPlatform = normalizePlatform(platform);
        AppVersionConfig latest = latest();
        AppVersionCheckVO vo = new AppVersionCheckVO();
        if (latest == null) {
            vo.setHasUpdate(Boolean.FALSE);
            return vo;
        }
        String downloadUrl = AppVersionConfig.PLATFORM_ANDROID.equals(normalizedPlatform)
                ? normalizeOptionalText(latest.getAndroidFileUrl())
                : normalizeOptionalText(latest.getIosFileUrl());
        boolean hasUpdate = downloadUrl != null;
        vo.setHasUpdate(hasUpdate);
        if (!hasUpdate) {
            return vo;
        }
        vo.setLatestVersionNo(latest.getVersionNo());
        vo.setLatestVersionCode(defaultNumber(latest.getVersionCode()));
        vo.setForceUpdate(integerToBoolean(latest.getForceUpdate()));
        vo.setUpdateNotice(latest.getUpdateNotice());
        vo.setDownloadUrl(downloadUrl);
        return vo;
    }
    /**
     * 判断后台最新版本是否高于当前 APP,兼容原生 versionCode 与展示 versionNo 不一致的历史包。
     */
    private boolean isNewerVersion(AppVersionConfig latest, Integer currentVersionCode, String currentVersionNo) {
        if (defaultNumber(latest.getVersionCode()) > currentVersionCode) {
            return true;
        }
        return compareVersionNo(latest.getVersionNo(), currentVersionNo) > 0;
    }
    /**
     * 比较数字点号版本号,无法解析的版本号不参与兜底判断。
     */
    private int compareVersionNo(String latestVersionNo, String currentVersionNo) {
        String latest = normalizeOptionalText(latestVersionNo);
        String current = normalizeOptionalText(currentVersionNo);
        if (latest == null || current == null) {
            return 0;
        }
        String[] latestParts = latest.split("\\.");
        String[] currentParts = current.split("\\.");
        int maxLength = Math.max(latestParts.length, currentParts.length);
        for (int i = 0; i < maxLength; i++) {
            Integer latestNumber = parseVersionNumber(latestParts, i);
            Integer currentNumber = parseVersionNumber(currentParts, i);
            if (latestNumber == null || currentNumber == null) {
                return 0;
            }
            int compareResult = latestNumber.compareTo(currentNumber);
            if (compareResult != 0) {
                return compareResult;
            }
        }
        return 0;
    }
    /**
     * 解析指定版本号片段,缺失片段按 0 处理。
     */
    private Integer parseVersionNumber(String[] parts, int index) {
        if (index >= parts.length) {
            return 0;
        }
        String value = parts[index] == null ? "" : parts[index].trim();
        if (!value.matches("\\d+")) {
            return null;
        }
        try {
            return Integer.valueOf(value);
        } catch (NumberFormatException ex) {
            return null;
        }
    }
    private AppVersionConfig latest() {
        return appVersionConfigMapper.selectOne(new LambdaQueryWrapper<AppVersionConfig>()
                .eq(AppVersionConfig::getDeleted, 0)