基于 Spring-Retry 的智能重试系统

71 阅读25分钟

银行接口调用复杂场景处理技术分析

1. 概述

在支付系统中,银行接口调用是最核心也是最复杂的环节。由于网络不稳定、银行系统特性差异、业务规则复杂等因素,银行接口调用经常面临各种异常场景。本文深入分析四种最典型的复杂场景及其处理方案。

1.1 核心挑战

  • 网络不确定性:超时不等于失败,成功不等于到账
  • 银行系统差异:不同银行的响应格式、错误码、处理时间差异巨大
  • 状态不一致:本地状态与银行状态可能不同步
  • 资金安全:任何处理不当都可能导致资金损失

1.2 处理原则

  • 安全第一:宁可多查询,不可漏处理
  • 幂等保证:相同请求多次执行结果一致
  • 主动确认:不依赖银行主动通知
  • 分类处理:技术问题和业务问题区别对待

2. 场景一:超时后返回"订单已存在"

2.1 问题描述

这是支付系统中最危险的场景之一:

时间线:
T1: 发起银行支付请求
T2: 网络超时,但银行可能已处理
T3: 系统重试支付
T4: 银行返回"订单已存在"

核心风险:不知道第一次调用的实际结果,可能导致重复扣款或漏扣款。

2.2 完整处理方案

@Service
public class BankTimeoutRetryHandler {

    /**
     * 超时重试处理流程
     */
    public BankResponse handleTimeoutRetry(PaymentOrder order, BankTimeoutException timeoutException) {

        // 1. 记录超时事件
        recordTimeoutEvent(order, timeoutException);

        // 2. 智能等待策略
        waitForBankProcessing(order);

        // 3. 主动查询银行状态
        BankQueryResponse queryResponse = queryBankOrderStatus(order);

        if (queryResponse.isSuccess()) {
            return handleQueryResult(order, queryResponse);
        } else {
            return handleRetryPayment(order);
        }
    }

    /**
     * 智能等待策略
     */
    private void waitForBankProcessing(PaymentOrder order) {

        // 根据银行类型和金额确定等待时间
        long waitTime = calculateWaitTime(order.getBankCode(), order.getAmount());

        // 大额订单:10秒,小额订单:5秒
        if (order.getAmount().compareTo(new BigDecimal("10000")) > 0) {
            waitTime = 10000;
        } else {
            waitTime = 5000;
        }

        try {
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 处理查询结果
     */
    private BankResponse handleQueryResult(PaymentOrder order, BankQueryResponse queryResponse) {

        switch (queryResponse.getOrderStatus()) {
            case SUCCESS:
                // 银行已成功,更新本地状态
                updateOrderToSuccess(order, queryResponse);
                return BankResponse.success(queryResponse);

            case FAILED:
                // 银行已失败,可以重新发起支付
                return retryWithNewOrderId(order);

            case PROCESSING:
                // 仍在处理中,继续等待
                return scheduleDelayedQuery(order);

            default:
                throw new UnknownBankStatusException("未知银行状态: " + queryResponse.getOrderStatus());
        }
    }

    /**
     * 使用新订单号重试
     */
    private BankResponse retryWithNewOrderId(PaymentOrder order) {

        // 生成新的银行订单号
        String newBankOrderId = generateNewBankOrderId(order);
        order.setBankOrderId(newBankOrderId);

        try {
            return bankClient.payment(buildBankRequest(order));
        } catch (BankOrderExistsException e) {
            // 新订单号仍然冲突,说明银行系统异常
            throw new BankSystemException("银行系统异常,订单号生成冲突");
        }
    }
}

2.3 关键技术点

等待时间策略
  • 小额支付:5秒等待,银行处理较快
  • 大额支付:10秒等待,可能需要额外审核
  • 特殊银行:根据历史数据调整等待时间
查询重试机制
  • 指数退避:1s, 2s, 4s, 8s...
  • 最大重试:3次查询失败后转人工处理
  • 超时控制:总查询时间不超过5分钟

3. 场景二:响应解析失败的重试判断

3.1 问题分析

银行响应解析失败的原因多样:

  • 格式错误:JSON/XML格式不正确
  • 编码问题:字符编码不匹配
  • 内容截断:网络传输中数据丢失
  • 格式变更:银行接口升级导致格式变化

3.2 响应解析失败的深度分析

3.2.1 解析失败的根本原因
/**
 * 响应解析失败的常见原因分析:
 * 1. 格式问题:JSON/XML格式不正确、字段缺失
 * 2. 编码问题:UTF-8/GBK编码不匹配
 * 3. 内容截断:网络传输中数据丢失或截断
 * 4. 接口升级:银行接口版本升级导致格式变化
 * 5. 异常响应:银行返回HTML错误页面而非JSON/XML
 * 6. 压缩问题:GZIP压缩/解压缩失败
 * 7. 特殊字符:包含不可见字符或控制字符
 */
3.2.2 多层次解析策略
@Component
public class EnhancedBankResponseParser {

    private static final Logger log = LoggerFactory.getLogger(EnhancedBankResponseParser.class);

    /**
     * 增强的响应解析器
     */
    public BankResponse parseBankResponse(String rawResponse, PaymentOrder order) {

        // 1. 预处理检查
        ParsePreCheckResult preCheckResult = preCheckResponse(rawResponse, order);
        if (!preCheckResult.isValid()) {
            return handlePreCheckFailure(preCheckResult, order);
        }

        // 2. 多格式尝试解析
        for (ResponseFormat format : ResponseFormat.values()) {
            try {
                BankResponse response = tryParseWithFormat(rawResponse, format, order);
                if (response != null) {
                    log.info("解析成功: orderId={}, format={}", order.getOrderId(), format);
                    return response;
                }
            } catch (Exception e) {
                log.debug("格式{}解析失败: orderId={}, error={}", format, order.getOrderId(), e.getMessage());
            }
        }

        // 3. 智能内容分析
        return performIntelligentAnalysis(rawResponse, order);
    }

    /**
     * 响应预检查
     */
    private ParsePreCheckResult preCheckResponse(String rawResponse, PaymentOrder order) {

        ParsePreCheckResult result = new ParsePreCheckResult();

        // 检查响应是否为空
        if (StringUtils.isBlank(rawResponse)) {
            result.setValid(false);
            result.setFailureReason("响应内容为空");
            result.setRecommendedAction(RecommendedAction.RETRY_AFTER_DELAY);
            return result;
        }

        // 检查响应长度
        if (rawResponse.length() < 10) {
            result.setValid(false);
            result.setFailureReason("响应内容过短,疑似网络问题");
            result.setRecommendedAction(RecommendedAction.RETRY_IMMEDIATELY);
            return result;
        }

        if (rawResponse.length() > 1024 * 1024) { // 1MB
            result.setValid(false);
            result.setFailureReason("响应内容过大,疑似异常");
            result.setRecommendedAction(RecommendedAction.QUERY_BANK_STATUS);
            return result;
        }

        // 检查是否为HTML错误页面
        if (isHtmlErrorPage(rawResponse)) {
            result.setValid(false);
            result.setFailureReason("银行返回HTML错误页面");
            result.setRecommendedAction(RecommendedAction.QUERY_BANK_STATUS);
            return result;
        }

        // 检查编码问题
        if (hasEncodingIssues(rawResponse)) {
            result.setValid(false);
            result.setFailureReason("响应存在编码问题");
            result.setRecommendedAction(RecommendedAction.RETRY_WITH_ENCODING_FIX);
            return result;
        }

        result.setValid(true);
        return result;
    }

    /**
     * 多格式解析尝试
     */
    private BankResponse tryParseWithFormat(String rawResponse, ResponseFormat format, PaymentOrder order) {

        switch (format) {
            case JSON:
                return tryJsonParse(rawResponse, order);

            case XML:
                return tryXmlParse(rawResponse, order);

            case FORM_ENCODED:
                return tryFormEncodedParse(rawResponse, order);

            case PLAIN_TEXT:
                return tryPlainTextParse(rawResponse, order);

            case CUSTOM_DELIMITED:
                return tryCustomDelimitedParse(rawResponse, order);

            default:
                return null;
        }
    }

    /**
     * JSON解析增强版
     */
    private BankResponse tryJsonParse(String rawResponse, PaymentOrder order) {

        try {
            // 1. 标准JSON解析
            return parseStandardJson(rawResponse);

        } catch (JsonParseException e) {

            // 2. 尝试修复常见JSON问题
            String fixedJson = fixCommonJsonIssues(rawResponse);
            if (!fixedJson.equals(rawResponse)) {
                try {
                    log.info("JSON修复后重新解析: orderId={}", order.getOrderId());
                    return parseStandardJson(fixedJson);
                } catch (Exception ex) {
                    log.debug("修复后仍解析失败: orderId={}", order.getOrderId());
                }
            }

            // 3. 提取关键信息
            return extractKeyInfoFromBrokenJson(rawResponse, order);
        }
    }

    /**
     * 修复常见JSON问题
     */
    private String fixCommonJsonIssues(String jsonString) {

        String fixed = jsonString;

        // 移除BOM标记
        if (fixed.startsWith("\uFEFF")) {
            fixed = fixed.substring(1);
        }

        // 移除前后空白字符
        fixed = fixed.trim();

        // 修复单引号问题
        fixed = fixed.replaceAll("'([^']*)':", "\"$1\":");
        fixed = fixed.replaceAll(":[ ]*'([^']*)'", ": \"$1\"");

        // 修复尾部逗号问题
        fixed = fixed.replaceAll(",[ ]*}", "}");
        fixed = fixed.replaceAll(",[ ]*]", "]");

        // 修复缺失引号的字段名
        fixed = fixed.replaceAll("([{,][ ]*)([a-zA-Z_][a-zA-Z0-9_]*)[ ]*:", "$1\"$2\":");

        // 移除注释
        fixed = fixed.replaceAll("//.*", "");
        fixed = fixed.replaceAll("/\\*.*?\\*/", "");

        return fixed;
    }

    /**
     * 从损坏的JSON中提取关键信息
     */
    private BankResponse extractKeyInfoFromBrokenJson(String brokenJson, PaymentOrder order) {

        log.warn("JSON解析失败,尝试提取关键信息: orderId={}", order.getOrderId());

        BankResponse response = new BankResponse();

        // 提取状态码
        String statusCode = extractFieldValue(brokenJson, "status", "code", "result_code");
        if (statusCode != null) {
            response.setErrorCode(statusCode);
        }

        // 提取消息
        String message = extractFieldValue(brokenJson, "message", "msg", "error_msg", "desc");
        if (message != null) {
            response.setErrorMessage(message);
        }

        // 提取银行订单号
        String bankOrderId = extractFieldValue(brokenJson, "bank_order_id", "transaction_id", "ref_no");
        if (bankOrderId != null) {
            response.setBankOrderId(bankOrderId);
        }

        // 根据提取的信息判断结果
        if (isSuccessIndicator(statusCode, message)) {
            response.setSuccess(true);
            log.info("从损坏JSON中识别为成功: orderId={}, status={}", order.getOrderId(), statusCode);
        } else if (isFailureIndicator(statusCode, message)) {
            response.setSuccess(false);
            log.info("从损坏JSON中识别为失败: orderId={}, status={}", order.getOrderId(), statusCode);
        } else {
            // 无法确定,需要查询
            response.setNeedQuery(true);
            log.warn("从损坏JSON中无法确定结果: orderId={}", order.getOrderId());
        }

        return response;
    }

    /**
     * 提取字段值(支持多个可能的字段名)
     */
    private String extractFieldValue(String content, String... fieldNames) {

        for (String fieldName : fieldNames) {

            // 尝试不同的模式
            String[] patterns = {
                "\"" + fieldName + "\"[ ]*:[ ]*\"([^\"]*)",      // "field":"value"
                "'" + fieldName + "'[ ]*:[ ]*'([^']*)",          // 'field':'value'
                fieldName + "[ ]*=[ ]*\"([^\"]*)",               // field="value"
                fieldName + "[ ]*:[ ]*([^,}\\]\\s]*)"            // field:value
            };

            for (String pattern : patterns) {
                Pattern regex = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
                Matcher matcher = regex.matcher(content);
                if (matcher.find()) {
                    return matcher.group(1).trim();
                }
            }
        }

        return null;
    }

    /**
     * 智能内容分析
     */
    private BankResponse performIntelligentAnalysis(String rawResponse, PaymentOrder order) {

        log.warn("所有格式解析失败,执行智能分析: orderId={}", order.getOrderId());

        // 1. 内容特征分析
        ContentAnalysisResult analysisResult = analyzeResponseContent(rawResponse);

        // 2. 根据分析结果决策
        switch (analysisResult.getContentType()) {
            case SUCCESS_INDICATOR:
                return createSuccessResponse(analysisResult, order);

            case FAILURE_INDICATOR:
                return createFailureResponse(analysisResult, order);

            case PROCESSING_INDICATOR:
                return createProcessingResponse(analysisResult, order);

            case NETWORK_ERROR:
                return createNetworkErrorResponse(analysisResult, order);

            case UNKNOWN_CONTENT:
            default:
                return createUnknownResponse(analysisResult, order);
        }
    }

    /**
     * 响应内容分析
     */
    private ContentAnalysisResult analyzeResponseContent(String content) {

        ContentAnalysisResult result = new ContentAnalysisResult();

        // 转换为小写便于匹配
        String lowerContent = content.toLowerCase();

        // 成功指示器检查
        String[] successIndicators = {
            "success", "成功", "ok", "accepted", "approved", "completed",
            "\"status\":\"00\"", "\"code\":\"0000\"", "\"result\":\"success\"",
            "交易成功", "支付成功", "处理成功"
        };

        for (String indicator : successIndicators) {
            if (lowerContent.contains(indicator.toLowerCase())) {
                result.setContentType(ContentType.SUCCESS_INDICATOR);
                result.addEvidence("发现成功指示器: " + indicator);
                result.setConfidence(calculateConfidence(content, indicator));
                return result;
            }
        }

        // 失败指示器检查
        String[] failureIndicators = {
            "fail", "error", "失败", "错误", "rejected", "declined", "denied",
            "\"status\":\"01\"", "\"code\":\"9999\"", "\"result\":\"fail\"",
            "余额不足", "账户异常", "密码错误", "交易失败"
        };

        for (String indicator : failureIndicators) {
            if (lowerContent.contains(indicator.toLowerCase())) {
                result.setContentType(ContentType.FAILURE_INDICATOR);
                result.addEvidence("发现失败指示器: " + indicator);
                result.setConfidence(calculateConfidence(content, indicator));
                return result;
            }
        }

        // 处理中指示器检查
        String[] processingIndicators = {
            "processing", "pending", "处理中", "等待", "审核中",
            "\"status\":\"02\"", "\"result\":\"processing\""
        };

        for (String indicator : processingIndicators) {
            if (lowerContent.contains(indicator.toLowerCase())) {
                result.setContentType(ContentType.PROCESSING_INDICATOR);
                result.addEvidence("发现处理中指示器: " + indicator);
                result.setConfidence(calculateConfidence(content, indicator));
                return result;
            }
        }

        // 网络错误检查
        if (isNetworkErrorContent(content)) {
            result.setContentType(ContentType.NETWORK_ERROR);
            result.addEvidence("识别为网络错误内容");
            result.setConfidence(0.8);
            return result;
        }

        // 未知内容
        result.setContentType(ContentType.UNKNOWN_CONTENT);
        result.addEvidence("无法识别内容类型");
        result.setConfidence(0.0);

        return result;
    }

    /**
     * 计算置信度
     */
    private double calculateConfidence(String content, String indicator) {

        double baseConfidence = 0.6;

        // 如果指示器出现在JSON/XML结构中,置信度更高
        if (content.contains("\"" + indicator + "\"") || content.contains("<" + indicator + ">")) {
            baseConfidence += 0.3;
        }

        // 如果有多个相关指示器,置信度更高
        String lowerContent = content.toLowerCase();
        String lowerIndicator = indicator.toLowerCase();

        if (lowerIndicator.contains("success") || lowerIndicator.contains("成功")) {
            if (lowerContent.contains("00") || lowerContent.contains("0000")) {
                baseConfidence += 0.2;
            }
        }

        if (lowerIndicator.contains("fail") || lowerIndicator.contains("失败")) {
            if (lowerContent.contains("01") || lowerContent.contains("9999")) {
                baseConfidence += 0.2;
            }
        }

        return Math.min(baseConfidence, 1.0);
    }

    /**
     * 检查是否为网络错误内容
     */
    private boolean isNetworkErrorContent(String content) {

        String[] networkErrorIndicators = {
            "connection timeout", "read timeout", "connection refused",
            "network error", "socket timeout", "connection reset",
            "502 bad gateway", "503 service unavailable", "504 gateway timeout",
            "网络超时", "连接超时", "网络错误"
        };

        String lowerContent = content.toLowerCase();

        for (String indicator : networkErrorIndicators) {
            if (lowerContent.contains(indicator)) {
                return true;
            }
        }

        return false;
    }
}

enum ResponseFormat {
    JSON,               // JSON格式
    XML,                // XML格式
    FORM_ENCODED,       // 表单编码格式
    PLAIN_TEXT,         // 纯文本格式
    CUSTOM_DELIMITED    // 自定义分隔符格式
}

enum ContentType {
    SUCCESS_INDICATOR,      // 成功指示
    FAILURE_INDICATOR,      // 失败指示
    PROCESSING_INDICATOR,   // 处理中指示
    NETWORK_ERROR,          // 网络错误
    UNKNOWN_CONTENT         // 未知内容
}

enum RecommendedAction {
    RETRY_IMMEDIATELY,          // 立即重试
    RETRY_AFTER_DELAY,          // 延迟重试
    QUERY_BANK_STATUS,          // 查询银行状态
    RETRY_WITH_ENCODING_FIX,    // 修复编码后重试
    NO_RETRY                    // 不重试
}

@Data
class ParsePreCheckResult {
    private boolean valid;
    private String failureReason;
    private RecommendedAction recommendedAction;
}

@Data
class ContentAnalysisResult {
    private ContentType contentType;
    private List<String> evidences = new ArrayList<>();
    private double confidence;

    public void addEvidence(String evidence) {
        this.evidences.add(evidence);
    }
}
3.2.3 高级重试决策引擎
@Component
public class AdvancedRetryDecisionEngine {

    /**
     * 高级重试决策
     */
    public RetryDecision makeRetryDecision(ParseFailureContext context) {

        // 1. 基础检查
        if (context.getRetryCount() >= getMaxRetryCount(context.getOrder())) {
            return RetryDecision.NO_RETRY("超过最大重试次数");
        }

        // 2. 响应内容分析
        ResponseAnalysis analysis = analyzeResponseContent(context);

        // 3. 历史成功率分析
        double historicalSuccessRate = getHistoricalSuccessRate(context.getOrder().getBankCode());

        // 4. 网络状况分析
        NetworkCondition networkCondition = analyzeNetworkCondition(context);

        // 5. 综合决策
        return makeComprehensiveDecision(analysis, historicalSuccessRate, networkCondition, context);
    }

    /**
     * 综合决策逻辑
     */
    private RetryDecision makeComprehensiveDecision(ResponseAnalysis analysis,
                                                   double historicalSuccessRate,
                                                   NetworkCondition networkCondition,
                                                   ParseFailureContext context) {

        // 决策权重计算
        DecisionWeights weights = calculateDecisionWeights(analysis, historicalSuccessRate, networkCondition);

        // 如果响应包含明确的成功/失败指示,优先查询确认
        if (analysis.hasDefinitiveIndicator()) {
            if (analysis.getConfidence() > 0.8) {
                return RetryDecision.QUERY_INSTEAD("高置信度指示器,查询确认");
            }
        }

        // 网络条件很差,延长重试间隔
        if (networkCondition == NetworkCondition.POOR) {
            long delay = calculateAdaptiveDelay(context.getRetryCount(), networkCondition);
            return RetryDecision.RETRY_AFTER_DELAY(delay);
        }

        // 银行历史成功率很低,优先查询
        if (historicalSuccessRate < 0.7) {
            return RetryDecision.QUERY_INSTEAD("银行成功率较低,查询确认");
        }

        // 根据解析失败类型决策
        switch (analysis.getFailureType()) {
            case FORMAT_ERROR:
                return handleFormatError(analysis, context);

            case ENCODING_ERROR:
                return handleEncodingError(analysis, context);

            case TRUNCATION_ERROR:
                return handleTruncationError(analysis, context);

            case NETWORK_ERROR:
                return handleNetworkError(analysis, context);

            default:
                return RetryDecision.QUERY_INSTEAD("未知错误类型,查询确认");
        }
    }

    /**
     * 处理格式错误
     */
    private RetryDecision handleFormatError(ResponseAnalysis analysis, ParseFailureContext context) {

        // 如果是JSON格式错误,尝试修复后重试
        if (analysis.getOriginalFormat() == ResponseFormat.JSON) {
            if (context.getRetryCount() == 0) {
                return RetryDecision.RETRY_WITH_FIX("尝试修复JSON格式");
            }
        }

        // 如果是XML格式错误,可能是编码问题
        if (analysis.getOriginalFormat() == ResponseFormat.XML) {
            return RetryDecision.RETRY_WITH_ENCODING_CHANGE("尝试不同编码解析XML");
        }

        // 其他格式错误,查询确认
        return RetryDecision.QUERY_INSTEAD("格式错误无法修复");
    }

    /**
     * 处理编码错误
     */
    private RetryDecision handleEncodingError(ResponseAnalysis analysis, ParseFailureContext context) {

        // 尝试不同的编码方式
        String[] encodings = {"UTF-8", "GBK", "GB2312", "ISO-8859-1"};

        if (context.getTriedEncodings().size() < encodings.length) {
            String nextEncoding = getNextUntried Encoding(encodings, context.getTriedEncodings());
            return RetryDecision.RETRY_WITH_ENCODING(nextEncoding);
        }

        // 所有编码都尝试过,查询确认
        return RetryDecision.QUERY_INSTEAD("所有编码方式都已尝试");
    }

    /**
     * 处理截断错误
     */
    private RetryDecision handleTruncationError(ResponseAnalysis analysis, ParseFailureContext context) {

        // 截断错误通常是网络问题,可以重试
        if (context.getRetryCount() < 2) {
            long delay = 2000 * (context.getRetryCount() + 1); // 2s, 4s
            return RetryDecision.RETRY_AFTER_DELAY(delay);
        }

        // 多次重试仍截断,可能是银行系统问题,查询确认
        return RetryDecision.QUERY_INSTEAD("多次重试仍出现截断");
    }

    /**
     * 自适应延迟计算
     */
    private long calculateAdaptiveDelay(int retryCount, NetworkCondition networkCondition) {

        long baseDelay = 1000; // 1秒基础延迟

        // 根据网络状况调整
        switch (networkCondition) {
            case EXCELLENT:
                baseDelay = 500;
                break;
            case GOOD:
                baseDelay = 1000;
                break;
            case FAIR:
                baseDelay = 2000;
                break;
            case POOR:
                baseDelay = 5000;
                break;
        }

        // 指数退避
        long delay = baseDelay * (1L << retryCount);

        // 最大延迟限制
        return Math.min(delay, 30000); // 最大30秒
    }
}

@Data
class DecisionWeights {
    private double responseAnalysisWeight = 0.4;
    private double historicalDataWeight = 0.3;
    private double networkConditionWeight = 0.2;
    private double businessContextWeight = 0.1;
}

enum NetworkCondition {
    EXCELLENT,  // 优秀
    GOOD,       // 良好
    FAIR,       // 一般
    POOR        // 较差
}

enum ParseFailureType {
    FORMAT_ERROR,       // 格式错误
    ENCODING_ERROR,     // 编码错误
    TRUNCATION_ERROR,   // 截断错误
    NETWORK_ERROR,      // 网络错误
    UNKNOWN_ERROR       // 未知错误
}
3.2.4 响应修复和重构机制
@Component
public class ResponseRepairService {

    /**
     * 尝试修复损坏的响应
     */
    public String repairBrokenResponse(String brokenResponse, ResponseFormat expectedFormat) {

        switch (expectedFormat) {
            case JSON:
                return repairBrokenJson(brokenResponse);

            case XML:
                return repairBrokenXml(brokenResponse);

            default:
                return brokenResponse;
        }
    }

    /**
     * 修复损坏的JSON
     */
    private String repairBrokenJson(String brokenJson) {

        String repaired = brokenJson;

        // 1. 基础清理
        repaired = basicJsonCleanup(repaired);

        // 2. 结构修复
        repaired = repairJsonStructure(repaired);

        // 3. 字段修复
        repaired = repairJsonFields(repaired);

        // 4. 验证修复结果
        if (isValidJson(repaired)) {
            log.info("JSON修复成功");
            return repaired;
        }

        // 5. 如果修复失败,尝试提取关键部分
        return extractJsonCore(brokenJson);
    }

    /**
     * JSON结构修复
     */
    private String repairJsonStructure(String json) {

        String repaired = json.trim();

        // 确保以{开始,}结束
        if (!repaired.startsWith("{") && !repaired.startsWith("[")) {
            // 查找第一个{或[
            int startIndex = Math.max(repaired.indexOf('{'), repaired.indexOf('['));
            if (startIndex > 0) {
                repaired = repaired.substring(startIndex);
            }
        }

        // 确保正确结束
        if (repaired.startsWith("{") && !repaired.endsWith("}")) {
            // 查找最后一个}
            int lastBrace = repaired.lastIndexOf('}');
            if (lastBrace > 0) {
                repaired = repaired.substring(0, lastBrace + 1);
            } else {
                repaired += "}";
            }
        }

        if (repaired.startsWith("[") && !repaired.endsWith("]")) {
            // 查找最后一个]
            int lastBracket = repaired.lastIndexOf(']');
            if (lastBracket > 0) {
                repaired = repaired.substring(0, lastBracket + 1);
            } else {
                repaired += "]";
            }
        }

        return repaired;
    }

    /**
     * 提取JSON核心内容
     */
    private String extractJsonCore(String brokenJson) {

        // 尝试提取关键字段构建新的JSON
        Map<String, String> extractedFields = new HashMap<>();

        // 提取常见字段
        String[] commonFields = {"status", "code", "message", "result", "data", "error"};

        for (String field : commonFields) {
            String value = extractFieldValue(brokenJson, field);
            if (value != null) {
                extractedFields.put(field, value);
            }
        }

        // 构建新的JSON
        if (!extractedFields.isEmpty()) {
            StringBuilder jsonBuilder = new StringBuilder("{");
            boolean first = true;

            for (Map.Entry<String, String> entry : extractedFields.entrySet()) {
                if (!first) {
                    jsonBuilder.append(",");
                }
                jsonBuilder.append("\"").append(entry.getKey()).append("\":\"")
                          .append(entry.getValue()).append("\"");
                first = false;
            }

            jsonBuilder.append("}");
            return jsonBuilder.toString();
        }

        return brokenJson;
    }
}

6. 基于Spring Retry的重试实现

6.1 Spring Retry配置

@Configuration
@EnableRetry
public class BankRetryConfiguration {

    /**
     * 银行接口重试模板配置
     */
    @Bean
    public RetryTemplate bankRetryTemplate() {

        RetryTemplate retryTemplate = new RetryTemplate();

        // 重试策略配置
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(2000); // 2秒间隔
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // 重试条件配置
        CompositeRetryPolicy retryPolicy = new CompositeRetryPolicy();
        retryPolicy.setPolicies(new RetryPolicy[]{
            createTimeoutRetryPolicy(),
            createParseErrorRetryPolicy(),
            createNetworkErrorRetryPolicy()
        });

        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }

    /**
     * 超时重试策略
     */
    private RetryPolicy createTimeoutRetryPolicy() {

        SimpleRetryPolicy timeoutPolicy = new SimpleRetryPolicy();
        timeoutPolicy.setMaxAttempts(3);

        Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
        retryableExceptions.put(SocketTimeoutException.class, true);
        retryableExceptions.put(ConnectTimeoutException.class, true);
        retryableExceptions.put(ReadTimeoutException.class, true);

        timeoutPolicy.setRetryableExceptions(retryableExceptions);

        return timeoutPolicy;
    }

    /**
     * 解析错误重试策略
     */
    private RetryPolicy createParseErrorRetryPolicy() {

        SimpleRetryPolicy parsePolicy = new SimpleRetryPolicy();
        parsePolicy.setMaxAttempts(2); // 解析错误最多重试2次

        Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
        retryableExceptions.put(JsonParseException.class, true);
        retryableExceptions.put(XmlParseException.class, true);
        retryableExceptions.put(ResponseParseException.class, true);

        parsePolicy.setRetryableExceptions(retryableExceptions);

        return parsePolicy;
    }

    /**
     * 网络错误重试策略
     */
    private RetryPolicy createNetworkErrorRetryPolicy() {

        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000);  // 1秒
        backOffPolicy.setMultiplier(2.0);        // 指数倍数
        backOffPolicy.setMaxInterval(30000);     // 最大30秒

        SimpleRetryPolicy networkPolicy = new SimpleRetryPolicy();
        networkPolicy.setMaxAttempts(4);

        Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
        retryableExceptions.put(ConnectException.class, true);
        retryableExceptions.put(NoRouteToHostException.class, true);
        retryableExceptions.put(SocketException.class, true);

        networkPolicy.setRetryableExceptions(retryableExceptions);

        return networkPolicy;
    }
}

6.2 银行接口重试服务

@Service
public class BankInterfaceRetryService {

    @Autowired
    private RetryTemplate bankRetryTemplate;

    @Autowired
    private BankClient bankClient;

    @Autowired
    private BankQueryService bankQueryService;

    /**
     * 带重试的银行支付调用
     */
    @Retryable(
        value = {BankTimeoutException.class, BankNetworkException.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 2000, multiplier = 2, maxDelay = 10000)
    )
    public BankResponse callBankPaymentWithRetry(PaymentOrder order) {

        try {
            log.info("调用银行支付接口: orderId={}, attempt={}",
                    order.getOrderId(), getCurrentAttempt());

            BankResponse response = bankClient.payment(buildBankRequest(order));

            // 验证响应有效性
            validateBankResponse(response, order);

            return response;

        } catch (BankTimeoutException e) {
            log.warn("银行接口超时: orderId={}, timeout={}ms",
                    order.getOrderId(), e.getTimeoutDuration());
            throw e; // 重新抛出,触发重试

        } catch (BankResponseParseException e) {
            log.warn("银行响应解析失败: orderId={}, error={}",
                    order.getOrderId(), e.getMessage());

            // 尝试智能解析
            BankResponse recoveredResponse = attemptIntelligentParse(e.getRawResponse(), order);
            if (recoveredResponse != null) {
                return recoveredResponse;
            }

            throw e; // 重新抛出,触发重试

        } catch (BankBusinessException e) {
            log.info("银行业务失败: orderId={}, errorCode={}",
                    order.getOrderId(), e.getErrorCode());

            // 业务异常不重试,直接返回
            return BankResponse.failed(e.getErrorCode(), e.getMessage());
        }
    }

    /**
     * 重试恢复方法
     */
    @Recover
    public BankResponse recoverFromBankFailure(BankTimeoutException ex, PaymentOrder order) {

        log.error("银行接口重试失败,执行恢复逻辑: orderId={}", order.getOrderId());

        // 1. 主动查询银行状态
        try {
            BankQueryResponse queryResponse = queryBankStatusWithRetry(order);

            if (queryResponse.isSuccess()) {
                return convertQueryToPaymentResponse(queryResponse);
            }

        } catch (Exception e) {
            log.error("查询银行状态也失败: orderId={}", order.getOrderId(), e);
        }

        // 2. 如果查询也失败,标记为需要人工处理
        return BankResponse.needManualProcess("重试和查询都失败,需要人工处理");
    }

    @Recover
    public BankResponse recoverFromParseFailure(BankResponseParseException ex, PaymentOrder order) {

        log.error("响应解析重试失败: orderId={}, rawResponse={}",
                order.getOrderId(), ex.getRawResponse());

        // 1. 尝试从原始响应中提取关键信息
        BankResponse extractedResponse = extractCriticalInfo(ex.getRawResponse(), order);
        if (extractedResponse != null && extractedResponse.hasDefinitiveResult()) {
            return extractedResponse;
        }

        // 2. 主动查询确认
        try {
            BankQueryResponse queryResponse = queryBankStatusWithRetry(order);
            return convertQueryToPaymentResponse(queryResponse);

        } catch (Exception e) {
            log.error("解析失败后查询也失败: orderId={}", order.getOrderId(), e);
            return BankResponse.needManualProcess("解析失败且查询失败");
        }
    }

    /**
     * 带重试的银行查询
     */
    @Retryable(
        value = {BankTimeoutException.class, BankNetworkException.class},
        maxAttempts = 5,
        backoff = @Backoff(delay = 3000, multiplier = 1.5, maxDelay = 15000)
    )
    public BankQueryResponse queryBankStatusWithRetry(PaymentOrder order) {

        log.info("查询银行订单状态: orderId={}, bankOrderId={}",
                order.getOrderId(), order.getBankOrderId());

        return bankQueryService.queryOrderStatus(order);
    }

    @Recover
    public BankQueryResponse recoverFromQueryFailure(Exception ex, PaymentOrder order) {

        log.error("银行查询重试失败: orderId={}", order.getOrderId(), ex);

        // 查询失败,返回未知状态
        BankQueryResponse response = new BankQueryResponse();
        response.setSuccess(false);
        response.setErrorMessage("查询重试失败: " + ex.getMessage());

        return response;
    }
}

6.3 自定义重试策略

@Component
public class BankSpecificRetryPolicy implements RetryPolicy {

    private final Map<String, BankRetryConfig> bankRetryConfigs;

    public BankSpecificRetryPolicy() {
        this.bankRetryConfigs = initializeBankConfigs();
    }

    /**
     * 初始化银行特定配置
     */
    private Map<String, BankRetryConfig> initializeBankConfigs() {

        Map<String, BankRetryConfig> configs = new HashMap<>();

        // 工商银行配置
        configs.put("ICBC", BankRetryConfig.builder()
            .maxAttempts(3)
            .initialDelay(2000)
            .maxDelay(10000)
            .multiplier(2.0)
            .timeoutThreshold(30000)
            .build());

        // 建设银行配置
        configs.put("CCB", BankRetryConfig.builder()
            .maxAttempts(4)
            .initialDelay(3000)
            .maxDelay(15000)
            .multiplier(1.5)
            .timeoutThreshold(45000)
            .build());

        // 农业银行配置
        configs.put("ABC", BankRetryConfig.builder()
            .maxAttempts(2)
            .initialDelay(5000)
            .maxDelay(20000)
            .multiplier(2.5)
            .timeoutThreshold(60000)
            .build());

        return configs;
    }

    @Override
    public boolean canRetry(RetryContext context) {

        // 获取当前订单信息
        PaymentOrder order = (PaymentOrder) context.getAttribute("paymentOrder");
        if (order == null) {
            return false;
        }

        // 获取银行特定配置
        BankRetryConfig config = bankRetryConfigs.get(order.getBankCode());
        if (config == null) {
            config = getDefaultConfig();
        }

        // 检查重试次数
        if (context.getRetryCount() >= config.getMaxAttempts()) {
            return false;
        }

        // 检查异常类型
        Throwable lastThrowable = context.getLastThrowable();
        if (lastThrowable == null) {
            return true;
        }

        // 业务异常不重试
        if (lastThrowable instanceof BankBusinessException) {
            return false;
        }

        // 技术异常可以重试
        return isTechnicalException(lastThrowable);
    }

    @Override
    public RetryContext open(RetryPolicy parent) {
        return new SimpleRetryContext(parent);
    }

    @Override
    public void close(RetryContext context) {
        // 清理资源
    }

    @Override
    public void registerThrowable(RetryContext context, Throwable throwable) {
        context.registerThrowable(throwable);

        // 记录重试统计
        recordRetryMetrics(context, throwable);
    }

    /**
     * 判断是否为技术异常
     */
    private boolean isTechnicalException(Throwable throwable) {

        return throwable instanceof SocketTimeoutException ||
               throwable instanceof ConnectTimeoutException ||
               throwable instanceof ConnectException ||
               throwable instanceof BankTimeoutException ||
               throwable instanceof BankNetworkException ||
               throwable instanceof BankResponseParseException;
    }

    /**
     * 记录重试指标
     */
    private void recordRetryMetrics(RetryContext context, Throwable throwable) {

        PaymentOrder order = (PaymentOrder) context.getAttribute("paymentOrder");
        if (order != null) {

            meterRegistry.counter("bank.retry.attempt",
                "bank", order.getBankCode(),
                "exception", throwable.getClass().getSimpleName(),
                "attempt", String.valueOf(context.getRetryCount())
            ).increment();
        }
    }
}

@Data
@Builder
class BankRetryConfig {
    private int maxAttempts;        // 最大重试次数
    private long initialDelay;      // 初始延迟
    private long maxDelay;          // 最大延迟
    private double multiplier;      // 延迟倍数
    private long timeoutThreshold;  // 超时阈值
}

6.4 重试监听器

@Component
public class BankRetryListener implements RetryListener {

    private static final Logger log = LoggerFactory.getLogger(BankRetryListener.class);

    @Autowired
    private MeterRegistry meterRegistry;

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {

        PaymentOrder order = extractPaymentOrder(context);
        if (order != null) {
            log.info("开始重试: orderId={}, method={}",
                    order.getOrderId(), callback.getClass().getSimpleName());

            // 记录重试开始
            context.setAttribute("retryStartTime", System.currentTimeMillis());
        }

        return true;
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        PaymentOrder order = extractPaymentOrder(context);
        if (order != null) {

            long startTime = (Long) context.getAttribute("retryStartTime");
            long duration = System.currentTimeMillis() - startTime;

            if (throwable == null) {
                // 重试成功
                log.info("重试成功: orderId={}, totalAttempts={}, duration={}ms",
                        order.getOrderId(), context.getRetryCount() + 1, duration);

                meterRegistry.counter("bank.retry.success",
                    "bank", order.getBankCode(),
                    "attempts", String.valueOf(context.getRetryCount() + 1)
                ).increment();

            } else {
                // 重试失败
                log.error("重试失败: orderId={}, totalAttempts={}, duration={}ms, finalError={}",
                        order.getOrderId(), context.getRetryCount() + 1, duration, throwable.getMessage());

                meterRegistry.counter("bank.retry.failure",
                    "bank", order.getBankCode(),
                    "attempts", String.valueOf(context.getRetryCount() + 1),
                    "finalError", throwable.getClass().getSimpleName()
                ).increment();
            }

            // 记录重试耗时
            meterRegistry.timer("bank.retry.duration",
                "bank", order.getBankCode()
            ).record(duration, TimeUnit.MILLISECONDS);
        }
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {

        PaymentOrder order = extractPaymentOrder(context);
        if (order != null) {

            log.warn("重试失败: orderId={}, attempt={}, error={}",
                    order.getOrderId(), context.getRetryCount(), throwable.getMessage());

            // 记录每次重试失败
            meterRegistry.counter("bank.retry.attempt.failure",
                "bank", order.getBankCode(),
                "attempt", String.valueOf(context.getRetryCount()),
                "error", throwable.getClass().getSimpleName()
            ).increment();

            // 检查是否需要告警
            checkRetryAlert(order, context, throwable);
        }
    }

    /**
     * 检查重试告警
     */
    private void checkRetryAlert(PaymentOrder order, RetryContext context, Throwable throwable) {

        // 如果重试次数过多,发送告警
        if (context.getRetryCount() >= 2) {

            alertService.sendAlert("银行接口重试次数过多",
                String.format("订单: %s, 银行: %s, 重试次数: %d, 错误: %s",
                    order.getOrderId(), order.getBankCode(),
                    context.getRetryCount(), throwable.getMessage()));
        }

        // 如果是特定银行频繁失败,发送告警
        double recentFailureRate = calculateRecentFailureRate(order.getBankCode());
        if (recentFailureRate > 0.3) { // 30%

            alertService.sendAlert("银行接口失败率过高",
                String.format("银行: %s, 最近失败率: %.2f%%",
                    order.getBankCode(), recentFailureRate * 100));
        }
    }

    private PaymentOrder extractPaymentOrder(RetryContext context) {
        return (PaymentOrder) context.getAttribute("paymentOrder");
    }

    private double calculateRecentFailureRate(String bankCode) {
        // 计算最近1小时的失败率
        // 这里简化实现,实际应该从监控系统获取
        return 0.0;
    }
}

6.5 重试使用示例

@Service
public class PaymentService {

    @Autowired
    private BankInterfaceRetryService bankRetryService;

    /**
     * 处理支付请求
     */
    public PaymentResult processPayment(PaymentRequest request) {

        PaymentOrder order = createPaymentOrder(request);

        try {
            // 使用Spring Retry调用银行接口
            BankResponse bankResponse = bankRetryService.callBankPaymentWithRetry(order);

            // 处理银行响应
            return handleBankResponse(order, bankResponse);

        } catch (Exception e) {
            log.error("支付处理失败: orderId={}", order.getOrderId(), e);
            return PaymentResult.failed("支付处理失败: " + e.getMessage());
        }
    }

    /**
     * 使用RetryTemplate的编程式重试
     */
    public BankResponse callBankWithTemplate(PaymentOrder order) {

        return bankRetryTemplate.execute(context -> {

            // 设置上下文信息
            context.setAttribute("paymentOrder", order);

            // 调用银行接口
            return bankClient.payment(buildBankRequest(order));

        }, context -> {

            // 重试失败后的恢复逻辑
            log.error("银行调用重试失败: orderId={}", order.getOrderId());

            // 尝试查询确认
            try {
                BankQueryResponse queryResponse = bankQueryService.queryOrderStatus(order);
                return convertQueryToPaymentResponse(queryResponse);
            } catch (Exception e) {
                return BankResponse.needManualProcess("调用和查询都失败");
            }
        });
    }
}

6.6 重试配置优化

# application.yml
spring:
  retry:
    # 全局重试配置
    max-attempts: 3
    backoff:
      delay: 1000
      max-delay: 10000
      multiplier: 2.0

# 银行特定配置
bank:
  retry:
    configs:
      ICBC:
        max-attempts: 3
        initial-delay: 2000
        max-delay: 10000
        multiplier: 2.0
        timeout-threshold: 30000
      CCB:
        max-attempts: 4
        initial-delay: 3000
        max-delay: 15000
        multiplier: 1.5
        timeout-threshold: 45000
      ABC:
        max-attempts: 2
        initial-delay: 5000
        max-delay: 20000
        multiplier: 2.5
        timeout-threshold: 60000

7. Spring Retry的优势

7.1 声明式重试

  • 简洁性:通过注解即可实现重试逻辑
  • 可读性:重试逻辑与业务逻辑分离
  • 可维护性:统一的重试配置管理

7.2 灵活的重试策略

  • 多种退避策略:固定间隔、指数退避、随机间隔
  • 条件重试:基于异常类型的精确控制
  • 恢复机制:重试失败后的优雅降级

7.3 完善的监控

  • 重试监听器:全面的重试过程监控
  • 指标收集:重试次数、成功率、耗时统计
  • 告警机制:异常情况的及时通知

这种基于Spring Retry的实现方案既保持了代码的简洁性,又提供了强大的重试能力和完善的监控机制,是生产环境中银行接口调用的最佳实践。

/**
 * JSON解析错误处理
 */
private BankResponse handleJsonParseError(String rawResponse, PaymentOrder order, JsonParseException e) {

    // 1. 关键字检测
    if (containsSuccessIndicator(rawResponse)) {
        // 包含成功标识,主动查询确认
        return triggerActiveQuery(order, "JSON_PARSE_ERROR_BUT_SUCCESS_INDICATOR");
    }

    if (containsFailureIndicator(rawResponse)) {
        // 包含失败标识,判定为失败
        return BankResponse.failed("响应格式错误但包含失败标识");
    }

    // 2. 响应长度检查
    if (rawResponse.length() < 50) {
        // 响应过短,可能是网络问题,可以重试
        return BankResponse.needRetry("响应内容过短,疑似网络问题");
    }

    // 3. 无法判断,主动查询
    return triggerActiveQuery(order, "JSON_PARSE_ERROR_UNKNOWN");
}

/**
 * 关键字检测
 */
private boolean containsSuccessIndicator(String response) {
    String[] successKeywords = {
        "success", "成功", "00", "SUCCESS", "ACCEPTED",
        "\"status\":\"00\"", "\"code\":\"0000\"", "\"result\":\"success\""
    };

    String lowerResponse = response.toLowerCase();
    return Arrays.stream(successKeywords)
            .anyMatch(keyword -> lowerResponse.contains(keyword.toLowerCase()));
}

private boolean containsFailureIndicator(String response) {
    String[] failureKeywords = {
        "fail", "error", "失败", "错误", "FAILED", "REJECTED",
        "\"status\":\"01\"", "\"code\":\"9999\"", "\"result\":\"fail\""
    };

    String lowerResponse = response.toLowerCase();
    return Arrays.stream(failureKeywords)
            .anyMatch(keyword -> lowerResponse.contains(keyword.toLowerCase()));
}

}


### 3.3 重试决策矩阵

```java
@Component
public class RetryDecisionMatrix {

    /**
     * 重试决策表
     */
    public RetryDecision shouldRetry(ParseFailureContext context) {

        // 检查重试次数
        if (context.getRetryCount() >= 3) {
            return RetryDecision.NO_RETRY("超过最大重试次数");
        }

        // 分析响应内容
        ResponseAnalysis analysis = analyzeResponse(context.getRawResponse());

        switch (analysis.getType()) {
            case PARTIAL_SUCCESS:
                // 包含成功信息,查询而不重试
                return RetryDecision.QUERY_INSTEAD("响应包含成功信息");

            case PARTIAL_FAILURE:
                // 包含失败信息,不重试
                return RetryDecision.NO_RETRY("响应包含失败信息");

            case NETWORK_ERROR:
                // 网络错误,延迟重试
                return RetryDecision.RETRY_AFTER_DELAY(5000);

            case FORMAT_ERROR:
                // 格式错误,查询确认
                return RetryDecision.QUERY_INSTEAD("响应格式错误");

            case EMPTY_RESPONSE:
                // 空响应,可能是超时,重试
                return RetryDecision.RETRY_AFTER_DELAY(3000);

            default:
                return RetryDecision.NO_RETRY("无法分析响应类型");
        }
    }

    /**
     * 响应内容分析
     */
    private ResponseAnalysis analyzeResponse(String response) {

        if (StringUtils.isBlank(response)) {
            return new ResponseAnalysis(ResponseType.EMPTY_RESPONSE);
        }

        if (response.length() < 20) {
            return new ResponseAnalysis(ResponseType.NETWORK_ERROR);
        }

        if (containsSuccessKeywords(response)) {
            return new ResponseAnalysis(ResponseType.PARTIAL_SUCCESS);
        }

        if (containsFailureKeywords(response)) {
            return new ResponseAnalysis(ResponseType.PARTIAL_FAILURE);
        }

        if (!isValidFormat(response)) {
            return new ResponseAnalysis(ResponseType.FORMAT_ERROR);
        }

        return new ResponseAnalysis(ResponseType.UNKNOWN);
    }
}

4. 场景三:"处理中"状态的轮询策略

4.1 处理中状态管理

银行返回"处理中"状态时,需要建立完善的轮询机制:

@Component
public class BankProcessingStatusHandler {

    // 不同银行的处理配置
    private final Map<String, ProcessingConfig> bankConfigs = Map.of(
        "ICBC", new ProcessingConfig(30000, 300000, 5),  // 工行:30s首查,5分钟超时,最多5次
        "CCB",  new ProcessingConfig(60000, 600000, 3),  // 建行:60s首查,10分钟超时,最多3次
        "ABC",  new ProcessingConfig(45000, 450000, 4)   // 农行:45s首查,7.5分钟超时,最多4次
    );

    /**
     * 处理"处理中"状态
     */
    public BankResponse handleProcessingStatus(PaymentOrder order, BankResponse processingResponse) {

        // 1. 获取银行配置
        ProcessingConfig config = getProcessingConfig(order.getBankCode());

        // 2. 记录处理中状态
        recordProcessingStatus(order, processingResponse, config);

        // 3. 启动异步轮询
        scheduleAsyncPolling(order, config);

        // 4. 返回处理中响应
        return BankResponse.processing(
            processingResponse.getBankOrderId(),
            "银行处理中,预计" + (config.getFirstQueryDelay() / 1000) + "秒后可查询结果"
        );
    }

    /**
     * 异步轮询任务
     */
    @Async("bankQueryExecutor")
    public void scheduleAsyncPolling(PaymentOrder order, ProcessingConfig config) {

        int queryCount = 0;
        long startTime = System.currentTimeMillis();

        while (queryCount < config.getMaxQueryTimes()) {

            try {
                // 计算等待时间(指数退避)
                long delay = calculateQueryDelay(queryCount, config);
                Thread.sleep(delay);

                // 执行查询
                BankQueryResponse queryResponse = bankQueryService.queryOrderStatus(order);
                queryCount++;

                if (queryResponse.isSuccess()) {
                    switch (queryResponse.getOrderStatus()) {
                        case SUCCESS:
                            handleAsyncQuerySuccess(order, queryResponse);
                            return;

                        case FAILED:
                            handleAsyncQueryFailure(order, queryResponse);
                            return;

                        case PROCESSING:
                            // 继续等待
                            log.debug("银行订单仍在处理: orderId={}, queryCount={}",
                                    order.getOrderId(), queryCount);
                            break;
                    }
                }

                // 检查总超时
                if (System.currentTimeMillis() - startTime > config.getMaxWaitTime()) {
                    handleQueryTimeout(order, queryCount);
                    return;
                }

            } catch (Exception e) {
                log.error("轮询查询异常: orderId={}, queryCount={}", order.getOrderId(), queryCount, e);

                if (queryCount >= config.getMaxQueryTimes() - 1) {
                    handleQueryException(order, e);
                    return;
                }
            }
        }

        // 达到最大查询次数
        handleMaxQueryReached(order, queryCount);
    }

    /**
     * 计算查询延迟(指数退避 + 银行特性)
     */
    private long calculateQueryDelay(int queryCount, ProcessingConfig config) {

        if (queryCount == 0) {
            return config.getFirstQueryDelay();
        }

        // 指数退避:30s, 60s, 120s, 240s...
        long exponentialDelay = config.getFirstQueryDelay() * (1L << queryCount);

        // 最大不超过5分钟
        return Math.min(exponentialDelay, 300000);
    }
}

@Data
@AllArgsConstructor
class ProcessingConfig {
    private final long firstQueryDelay;  // 首次查询延迟
    private final long maxWaitTime;      // 最大等待时间
    private final int maxQueryTimes;     // 最大查询次数
}

4.2 轮询策略优化

智能间隔调整
  • 首次查询:根据银行特性设定(30-60秒)
  • 后续查询:指数退避,但不超过5分钟
  • 最大次数:根据银行SLA设定(3-5次)
超时处理
  • 总超时:10分钟后强制结束轮询
  • 单次超时:30秒查询超时
  • 异常处理:连续3次查询失败转人工处理

5. 场景四:业务失败与技术失败的区分

5.1 失败类型分类

准确区分业务失败和技术失败是支付系统的关键能力:

@Component
public class BankFailureClassifier {

    /**
     * 失败类型分类
     */
    public FailureType classifyFailure(BankResponse response, Exception exception) {

        // 1. 技术失败检查
        if (isTechnicalFailure(response, exception)) {
            return FailureType.TECHNICAL_FAILURE;
        }

        // 2. 业务失败检查
        if (isBusinessFailure(response)) {
            return FailureType.BUSINESS_FAILURE;
        }

        // 3. 未知失败
        return FailureType.UNKNOWN_FAILURE;
    }

    /**
     * 技术失败判断
     */
    private boolean isTechnicalFailure(BankResponse response, Exception exception) {

        // 网络层异常
        if (exception instanceof ConnectTimeoutException ||
            exception instanceof SocketTimeoutException ||
            exception instanceof ConnectException ||
            exception instanceof UnknownHostException) {
            return true;
        }

        // HTTP状态码异常
        if (response != null && isTechnicalHttpStatus(response.getHttpStatus())) {
            return true;
        }

        // 银行系统错误码
        if (response != null && isTechnicalErrorCode(response.getErrorCode())) {
            return true;
        }

        return false;
    }

    /**
     * 业务失败判断
     */
    private boolean isBusinessFailure(BankResponse response) {

        if (response == null) {
            return false;
        }

        return isBusinessErrorCode(response.getErrorCode());
    }

    /**
     * 技术错误码映射表
     */
    private boolean isTechnicalErrorCode(String errorCode) {

        Map<String, String> technicalErrorCodes = Map.of(
            "9999", "系统异常",
            "8888", "网络异常",
            "7777", "数据库异常",
            "6666", "服务不可用",
            "5555", "系统维护中",
            "TIMEOUT", "请求超时",
            "NETWORK_ERROR", "网络错误",
            "SYSTEM_ERROR", "系统错误",
            "SERVICE_UNAVAILABLE", "服务不可用"
        );

        return technicalErrorCodes.containsKey(errorCode);
    }

    /**
     * 业务错误码映射表
     */
    private boolean isBusinessErrorCode(String errorCode) {

        Map<String, String> businessErrorCodes = Map.of(
            "1001", "余额不足",
            "1002", "账户状态异常",
            "1003", "密码错误",
            "1004", "卡片状态异常",
            "1005", "超过限额",
            "1006", "账户冻结",
            "1007", "交易时间限制",
            "INSUFFICIENT_BALANCE", "余额不足",
            "INVALID_ACCOUNT", "无效账户",
            "EXCEED_LIMIT", "超过限额",
            "ACCOUNT_FROZEN", "账户冻结"
        );

        return businessErrorCodes.containsKey(errorCode);
    }

    /**
     * HTTP状态码判断
     */
    private boolean isTechnicalHttpStatus(int httpStatus) {

        // 5xx服务器错误 - 技术问题
        if (httpStatus >= 500 && httpStatus < 600) {
            return true;
        }

        // 特定的4xx错误 - 技术问题
        Set<Integer> technicalClientErrors = Set.of(
            408, // Request Timeout
            429, // Too Many Requests
            502, // Bad Gateway
            503, // Service Unavailable
            504  // Gateway Timeout
        );

        return technicalClientErrors.contains(httpStatus);
    }
}

enum FailureType {
    TECHNICAL_FAILURE,  // 技术失败
    BUSINESS_FAILURE,   // 业务失败
    UNKNOWN_FAILURE     // 未知失败
}

5.2 不同失败类型的处理策略

@Service
public class BankFailureHandler {

    /**
     * 根据失败类型处理
     */
    public PaymentResult handleBankFailure(PaymentOrder order, BankResponse response, Exception exception) {

        FailureType failureType = failureClassifier.classifyFailure(response, exception);

        switch (failureType) {
            case TECHNICAL_FAILURE:
                return handleTechnicalFailure(order, response, exception);

            case BUSINESS_FAILURE:
                return handleBusinessFailure(order, response);

            case UNKNOWN_FAILURE:
                return handleUnknownFailure(order, response, exception);

            default:
                throw new IllegalStateException("未知失败类型: " + failureType);
        }
    }

    /**
     * 技术失败处理策略
     */
    private PaymentResult handleTechnicalFailure(PaymentOrder order, BankResponse response, Exception exception) {

        log.warn("银行技术失败: orderId={}, error={}", order.getOrderId(), exception.getMessage());

        // 1. 记录技术失败事件
        recordTechnicalFailure(order, response, exception);

        // 2. 判断重试策略
        RetryStrategy retryStrategy = determineRetryStrategy(order, exception);

        switch (retryStrategy) {
            case IMMEDIATE_RETRY:
                return retryImmediately(order);

            case DELAYED_RETRY:
                return scheduleDelayedRetry(order, calculateRetryDelay(order.getRetryCount()));

            case QUERY_FIRST:
                return queryBeforeRetry(order);

            case NO_RETRY:
                return markAsNeedManualProcess(order, "技术失败无法重试");

            default:
                throw new IllegalStateException("未知重试策略: " + retryStrategy);
        }
    }

    /**
     * 业务失败处理策略
     */
    private PaymentResult handleBusinessFailure(PaymentOrder order, BankResponse response) {

        log.info("银行业务失败: orderId={}, errorCode={}, errorMsg={}",
                order.getOrderId(), response.getErrorCode(), response.getErrorMessage());

        // 1. 记录业务失败
        recordBusinessFailure(order, response);

        // 2. 分析业务失败类型
        BusinessFailureType businessFailureType = analyzeBusinessFailure(response);

        switch (businessFailureType) {
            case INSUFFICIENT_BALANCE:
                // 余额不足 - 直接失败,不重试
                return PaymentResult.failed("余额不足", response.getErrorCode());

            case ACCOUNT_FROZEN:
                // 账户冻结 - 直接失败,不重试
                return PaymentResult.failed("账户冻结", response.getErrorCode());

            case EXCEED_LIMIT:
                // 超过限额 - 可能是临时限制,可以稍后重试
                return scheduleDelayedRetry(order, 300000); // 5分钟后重试

            case INVALID_PASSWORD:
                // 密码错误 - 直接失败,不重试
                return PaymentResult.failed("密码错误", response.getErrorCode());

            case TIME_RESTRICTION:
                // 时间限制 - 等待到允许时间后重试
                return scheduleTimeBasedRetry(order);

            default:
                // 其他业务失败 - 直接失败
                return PaymentResult.failed(response.getErrorMessage(), response.getErrorCode());
        }
    }

    /**
     * 未知失败处理策略
     */
    private PaymentResult handleUnknownFailure(PaymentOrder order, BankResponse response, Exception exception) {

        log.error("银行未知失败: orderId={}, response={}, exception={}",
                order.getOrderId(), response, exception.getMessage());

        // 1. 记录未知失败
        recordUnknownFailure(order, response, exception);

        // 2. 主动查询确认状态
        try {
            BankQueryResponse queryResponse = bankQueryService.queryOrderStatus(order);

            if (queryResponse.isSuccess()) {
                return convertQueryToPaymentResult(queryResponse);
            } else {
                // 查询也失败,转人工处理
                return markAsNeedManualProcess(order, "未知失败且查询失败");
            }

        } catch (Exception e) {
            log.error("查询银行状态失败: orderId={}", order.getOrderId(), e);
            return markAsNeedManualProcess(order, "未知失败且查询异常");
        }
    }

    /**
     * 重试策略决策
     */
    private RetryStrategy determineRetryStrategy(PaymentOrder order, Exception exception) {

        // 检查重试次数
        if (order.getRetryCount() >= 3) {
            return RetryStrategy.NO_RETRY;
        }

        // 根据异常类型决策
        if (exception instanceof ConnectTimeoutException) {
            return RetryStrategy.QUERY_FIRST; // 连接超时,先查询
        }

        if (exception instanceof SocketTimeoutException) {
            return RetryStrategy.QUERY_FIRST; // 读取超时,先查询
        }

        if (exception instanceof ConnectException) {
            return RetryStrategy.DELAYED_RETRY; // 连接失败,延迟重试
        }

        if (exception instanceof UnknownHostException) {
            return RetryStrategy.NO_RETRY; // DNS解析失败,不重试
        }

        return RetryStrategy.DELAYED_RETRY; // 默认延迟重试
    }
}

enum RetryStrategy {
    IMMEDIATE_RETRY,    // 立即重试
    DELAYED_RETRY,      // 延迟重试
    QUERY_FIRST,        // 先查询再决定
    NO_RETRY            // 不重试
}

enum BusinessFailureType {
    INSUFFICIENT_BALANCE,   // 余额不足
    ACCOUNT_FROZEN,         // 账户冻结
    EXCEED_LIMIT,           // 超过限额
    INVALID_PASSWORD,       // 密码错误
    TIME_RESTRICTION,       // 时间限制
    OTHER                   // 其他
}

6. 综合处理框架

6.1 统一异常处理器

@Component
public class BankInterfaceExceptionHandler {

    /**
     * 统一异常处理入口
     */
    public PaymentResult handleBankException(PaymentOrder order, Exception exception, BankResponse response) {

        try {
            // 1. 记录异常事件
            recordExceptionEvent(order, exception, response);

            // 2. 分类处理
            if (exception instanceof BankTimeoutException) {
                return timeoutRetryHandler.handleTimeoutRetry(order, (BankTimeoutException) exception);
            }

            if (exception instanceof BankResponseParseException) {
                return responseParseHandler.handleParseFailure(order, (BankResponseParseException) exception);
            }

            if (response != null && "PROCESSING".equals(response.getStatus())) {
                return processingStatusHandler.handleProcessingStatus(order, response);
            }

            // 3. 通用失败处理
            return bankFailureHandler.handleBankFailure(order, response, exception);

        } catch (Exception e) {
            log.error("异常处理器本身异常: orderId={}", order.getOrderId(), e);
            return PaymentResult.needManualProcess("异常处理失败,需要人工介入");
        }
    }
}

6.2 监控和告警

@Component
public class BankInterfaceMonitor {

    /**
     * 记录银行接口指标
     */
    public void recordBankInterfaceMetrics(PaymentOrder order, BankResponse response, Exception exception, long duration) {

        String bankCode = order.getBankCode();
        String result = determineResult(response, exception);

        // 1. 记录调用次数和耗时
        meterRegistry.timer("bank.interface.call",
            "bank", bankCode,
            "result", result
        ).record(duration, TimeUnit.MILLISECONDS);

        // 2. 记录成功率
        meterRegistry.counter("bank.interface.result",
            "bank", bankCode,
            "result", result
        ).increment();

        // 3. 记录异常类型
        if (exception != null) {
            meterRegistry.counter("bank.interface.exception",
                "bank", bankCode,
                "exception_type", exception.getClass().getSimpleName()
            ).increment();
        }

        // 4. 检查告警条件
        checkAlertConditions(bankCode, result, duration);
    }

    /**
     * 告警检查
     */
    private void checkAlertConditions(String bankCode, String result, long duration) {

        // 单次调用耗时过长
        if (duration > 30000) { // 30秒
            alertService.sendAlert("银行接口调用耗时过长",
                String.format("银行: %s, 耗时: %d ms", bankCode, duration));
        }

        // 失败率过高检查(需要结合历史数据)
        double failureRate = calculateRecentFailureRate(bankCode);
        if (failureRate > 0.1) { // 10%
            alertService.sendAlert("银行接口失败率过高",
                String.format("银行: %s, 失败率: %.2f%%", bankCode, failureRate * 100));
        }
    }
}

7. 最佳实践总结

7.1 设计原则

  1. 安全优先:宁可多查询确认,不可遗漏处理
  2. 幂等保证:所有操作都要保证幂等性
  3. 主动确认:不依赖银行的主动通知
  4. 分类处理:技术问题和业务问题区别对待
  5. 监控完善:全面的监控和告警机制

7.2 关键技术点

  1. 超时处理:智能等待 + 主动查询 + 新订单号重试
  2. 解析失败:关键字检测 + 重试决策 + 主动查询
  3. 处理中状态:异步轮询 + 指数退避 + 超时控制
  4. 失败分类:错误码映射 + 异常类型判断 + 差异化处理

7.3 运维要点

  1. 监控指标:成功率、耗时、异常类型、重试次数
  2. 告警规则:失败率阈值、耗时阈值、异常频率
  3. 日志记录:完整的调用链路和状态变更
  4. 人工介入:复杂场景的人工处理机制

8. 总结

银行接口调用的复杂场景处理是支付系统的核心技术挑战。通过建立完善的分类处理机制、智能重试策略、主动查询确认和全面监控告警,可以有效应对各种异常情况,确保支付系统的稳定性和资金安全。

在实际应用中,需要根据具体的银行特性、业务需求和系统架构进行适当调整,持续优化处理策略,提升系统的健壮性和用户体验。