返回架构笔记
Arch Day 110

Arch Day 110: 系统设计面试(1) — 设计支付系统

Arch Day 110: 系统设计面试(1) — 设计支付系统

2026-03-29
第四阶段 - 高阶融合
系统设计支付系统幂等一致性对账通道路由

日期: 2026-03-29 (Day 110) 阶段: 第四阶段 - 高阶融合 标签: #系统设计 #支付系统 #幂等 #一致性 #对账 #通道路由


面试模拟 — 45分钟

面试场景

面试官: "请设计一个支付系统,支持用户在线购物付款。我们需要支持多种支付方式(信用卡、借记卡、第三方支付),日均交易量从百万级起步,未来要支撑到十亿级。"


第一步:需求澄清(5分钟)

我的提问

功能需求确认:
Q1: 支持哪些支付场景?只有在线支付,还是也包括线下POS?
A: 主要在线支付,后续扩展线下。

Q2: 支付方式包括哪些?
A: 信用卡/借记卡、支付宝/微信支付、银行转账。

Q3: 是否需要退款功能?
A: 是的,需要支持全额和部分退款。

Q4: 是否需要国际支付/多币种?
A: 第一阶段国内,后续支持跨境。

Q5: 对账需求?
A: 需要与支付服务商(PSP)对账,日终结算。

非功能需求确认:
Q6: 延迟要求?
A: 支付发起到结果返回 <3秒。

Q7: 可用性要求?
A: 99.99%(支付系统不能挂)。

Q8: 日均交易量?
A: 当前100万笔/天,峰值500万笔/天。目标支撑10亿笔/天。

Q9: 安全合规要求?
A: PCI-DSS合规,敏感数据加密存储。

需求总结

核心功能:
├── 收单(Acquire): 用户发起支付→选择支付方式→完成支付
├── 退款(Refund): 全额/部分退款
├── 查询(Query): 支付状态查询
├── 对账(Reconcile): 与PSP日终对账
└── 通道路由(Route): 智能选择最优支付通道

非功能要求:
├── 延迟: <3秒(端到端)
├── 可用性: 99.99%
├── 一致性: 资金操作必须强一致
├── 幂等: 网络超时重试不能重复扣款
├── 安全: PCI-DSS合规
└── 扩展: 100万→10亿 笔/天

第二步:高层设计(10分钟)

2.1 架构总览

支付系统高层架构:

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  ┌──────────┐    ┌──────────┐    ┌──────────────────────┐   │
│  │ 商户/App │───→│ API      │───→│  Payment Service     │   │
│  │          │    │ Gateway  │    │  (支付核心服务)       │   │
│  └──────────┘    └──────────┘    └──────────┬───────────┘   │
│                                              │               │
│                    ┌─────────────────────────┼──────┐        │
│                    │                         │      │        │
│              ┌─────┴─────┐  ┌────────┐  ┌───┴───┐  │        │
│              │ Route     │  │ Risk   │  │ Ledger│  │        │
│              │ Engine    │  │ Engine │  │ (账本) │  │        │
│              │ (通道路由) │  │ (风控) │  │       │  │        │
│              └─────┬─────┘  └────────┘  └───────┘  │        │
│                    │                                 │        │
│              ┌─────┴──────────────────────────────┐ │        │
│              │    Channel Adapter Layer            │ │        │
│              │  ┌──────┐ ┌──────┐ ┌──────┐       │ │        │
│              │  │银联   │ │支付宝│ │微信  │ ...   │ │        │
│              │  │Adapter│ │Adapter│ │Adapter│      │ │        │
│              │  └──────┘ └──────┘ └──────┘       │ │        │
│              └────────────────────────────────────┘ │        │
│                                                     │        │
│              ┌──────────────────────────────────────┘        │
│              │                                               │
│        ┌─────┴─────┐  ┌──────────┐  ┌──────────────────┐   │
│        │ Reconcile │  │ Settle   │  │ Notification     │   │
│        │ Service   │  │ Service  │  │ Service          │   │
│        │ (对账)    │  │ (结算)   │  │ (通知)           │   │
│        └───────────┘  └──────────┘  └──────────────────┘   │
│                                                              │
└──────────────────────────────────────────────────────────────┘

2.2 核心流程

支付核心流程(Happy Path):

用户    商户App    API GW    Payment    Risk    Route    Channel    PSP
 │        │         │        Service   Engine  Engine   Adapter     │
 │─下单──→│         │          │         │       │        │         │
 │        │──支付──→│          │         │       │        │         │
 │        │         │──创建───→│         │       │        │         │
 │        │         │          │──风控──→│       │        │         │
 │        │         │          │←─通过──│       │        │         │
 │        │         │          │──路由─────────→│        │         │
 │        │         │          │←─选择通道────│        │         │
 │        │         │          │──调用通道──────────────→│         │
 │        │         │          │                        │──请求──→│
 │        │         │          │                        │←─结果──│
 │        │         │          │←──通道结果─────────────│         │
 │        │         │          │──更新状态             │         │
 │        │         │          │──记账────→Ledger      │         │
 │        │         │←─结果───│                       │         │
 │        │←─结果──│          │                       │         │
 │←─结果─│         │          │                       │         │

状态机:
  CREATED → PROCESSING → SUCCESS / FAILED / TIMEOUT
                ↓
            REFUNDING → REFUNDED / REFUND_FAILED

第三步:核心组件设计(15分钟)

3.1 数据模型

-- 支付订单表(核心)
CREATE TABLE payment_order (
    id              BIGINT PRIMARY KEY,
    order_no        VARCHAR(64) UNIQUE NOT NULL,   -- 业务订单号
    pay_no          VARCHAR(64) UNIQUE NOT NULL,    -- 支付流水号
    merchant_id     BIGINT NOT NULL,
    amount          BIGINT NOT NULL,                -- 金额(分)
    currency        VARCHAR(3) DEFAULT 'CNY',
    status          VARCHAR(20) NOT NULL,           -- CREATED/PROCESSING/SUCCESS/FAILED
    pay_method      VARCHAR(20),                    -- CARD/ALIPAY/WECHAT
    channel_code    VARCHAR(20),                    -- 实际使用的通道
    channel_txn_no  VARCHAR(128),                   -- 通道交易号
    idempotency_key VARCHAR(128) UNIQUE,            -- 幂等键
    expire_time     TIMESTAMP,                      -- 支付超时时间
    success_time    TIMESTAMP,
    created_at      TIMESTAMP DEFAULT NOW(),
    updated_at      TIMESTAMP DEFAULT NOW(),
    version         INT DEFAULT 0                   -- 乐观锁
);

-- 账本表(复式记账)
CREATE TABLE ledger_entry (
    id              BIGINT PRIMARY KEY,
    pay_no          VARCHAR(64) NOT NULL,
    account_no      VARCHAR(64) NOT NULL,           -- 账户号
    direction       VARCHAR(1) NOT NULL,            -- D(借)/C(贷)
    amount          BIGINT NOT NULL,                -- 金额(分)
    balance_after   BIGINT NOT NULL,                -- 记账后余额
    entry_type      VARCHAR(20) NOT NULL,           -- PAYMENT/REFUND/FEE
    created_at      TIMESTAMP DEFAULT NOW()
);
-- 约束:每笔交易的借方总额 = 贷方总额(复式记账平衡)

-- 通道配置表
CREATE TABLE channel_config (
    id              BIGINT PRIMARY KEY,
    channel_code    VARCHAR(20) UNIQUE NOT NULL,
    channel_name    VARCHAR(64),
    status          VARCHAR(10),                    -- ACTIVE/INACTIVE
    fee_rate        DECIMAL(6,4),                   -- 手续费率
    daily_limit     BIGINT,                         -- 日限额
    priority        INT,                            -- 优先级
    weight          INT DEFAULT 100,                -- 路由权重
    success_rate    DECIMAL(5,2),                   -- 历史成功率
    avg_latency_ms  INT,                            -- 平均延迟
    updated_at      TIMESTAMP
);

-- 对账记录表
CREATE TABLE reconcile_record (
    id              BIGINT PRIMARY KEY,
    recon_date      DATE NOT NULL,
    channel_code    VARCHAR(20) NOT NULL,
    our_count       INT,                            -- 我方交易数
    our_amount      BIGINT,                         -- 我方金额
    their_count     INT,                            -- 对方交易数
    their_amount    BIGINT,                         -- 对方金额
    diff_count      INT,                            -- 差异数
    diff_amount     BIGINT,                         -- 差异金额
    status          VARCHAR(20),                    -- MATCHED/DIFF/PENDING
    created_at      TIMESTAMP DEFAULT NOW()
);

3.2 API设计

核心API:

1. 创建支付
POST /api/v1/payments
Headers:
  Idempotency-Key: {unique-key}    ★ 幂等键
  X-Merchant-Id: {merchant-id}
  Authorization: Bearer {token}
Body:
{
  "order_no": "ORD-2026032900001",
  "amount": 9900,                   // 分
  "currency": "CNY",
  "pay_method": "ALIPAY",
  "return_url": "https://...",
  "notify_url": "https://...",      // 异步通知地址
  "description": "iPhone 16 Pro",
  "expire_seconds": 900             // 15分钟超时
}
Response:
{
  "pay_no": "PAY-20260329-XXXXX",
  "status": "CREATED",
  "pay_url": "https://...",         // 跳转支付页面URL
  "expire_time": "2026-03-29T15:15:00Z"
}

2. 查询支付
GET /api/v1/payments/{pay_no}
Response:
{
  "pay_no": "PAY-20260329-XXXXX",
  "status": "SUCCESS",
  "amount": 9900,
  "success_time": "2026-03-29T15:01:23Z",
  "channel_txn_no": "CH-20260329-YYYYY"
}

3. 退款
POST /api/v1/payments/{pay_no}/refunds
Headers:
  Idempotency-Key: {unique-key}
Body:
{
  "refund_amount": 9900,
  "reason": "用户申请退款"
}

4. 异步通知(Webhook)
POST {merchant_notify_url}
Body:
{
  "pay_no": "PAY-20260329-XXXXX",
  "order_no": "ORD-2026032900001",
  "status": "SUCCESS",
  "amount": 9900,
  "sign": "SHA256签名"              // 防篡改
}
// 商户返回 "SUCCESS" 表示收到
// 未收到SUCCESS → 重试(1s, 5s, 30s, 5m, 30m, 1h, 6h, 24h)

3.3 幂等性设计 ★ 面试重点

幂等性实现方案:

┌──────────────────────────────────────────────────────────┐
│                 幂等性处理流程                             │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  请求进入                                                │
│    │                                                     │
│    ├── 1. 提取 Idempotency-Key                          │
│    │   (由调用方生成,全局唯一)                           │
│    │                                                     │
│    ├── 2. 查询幂等表                                    │
│    │   SELECT * FROM idempotent_record                  │
│    │   WHERE idempotency_key = ?                        │
│    │                                                     │
│    ├── 3a. 记录不存在 → 新请求                           │
│    │   INSERT INTO idempotent_record                    │
│    │   (key, request_hash, status='PROCESSING')         │
│    │   → 执行业务逻辑                                   │
│    │   → 更新 status='DONE', response=结果              │
│    │                                                     │
│    ├── 3b. 记录存在 + status='DONE' → 重复请求          │
│    │   → 直接返回上次的 response(不重复执行)           │
│    │                                                     │
│    └── 3c. 记录存在 + status='PROCESSING' → 并发请求    │
│        → 等待或返回"处理中"                              │
│                                                          │
│  关键设计点:                                             │
│  ├── Idempotency-Key 由调用方生成(通常是 订单号+操作类型)│
│  ├── 幂等记录和业务操作在同一个事务中                     │
│  ├── 幂等记录设置TTL(例如24小时),自动清理               │
│  └── 请求Hash校验:相同Key但不同请求参数 → 返回错误      │
└──────────────────────────────────────────────────────────┘

3.4 通道路由设计

通道路由引擎:

┌──────────────────────────────────────────────────────────┐
│                 Route Engine                               │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  输入:支付请求(金额/支付方式/商户/用户)                  │
│                                                          │
│  Step 1: 可用通道筛选                                    │
│  ├── 通道状态 = ACTIVE                                   │
│  ├── 支持该支付方式                                      │
│  ├── 日限额未满                                          │
│  └── 金额在通道限额范围内                                │
│                                                          │
│  Step 2: 通道评分                                        │
│  Score = w1 × 成功率                                     │
│        + w2 × (1/延迟)                                   │
│        + w3 × (1/费率)                                   │
│        + w4 × 优先级                                     │
│                                                          │
│  权重示例:                                               │
│  w1=0.4(成功率最重要)                                    │
│  w2=0.2(延迟)                                            │
│  w3=0.3(费率)                                            │
│  w4=0.1(优先级)                                          │
│                                                          │
│  Step 3: 选择通道                                        │
│  ├── 正常模式: 加权随机选择(按评分加权)                  │
│  └── 降级模式: 按优先级顺序选择                          │
│                                                          │
│  Step 4: 失败重试                                        │
│  ├── 通道A失败 → 自动切换通道B                           │
│  ├── 最多重试2次                                         │
│  └── 每次切换选择下一个最优通道                          │
│                                                          │
│  通道健康度实时更新:                                     │
│  ├── 滑动窗口统计(最近5分钟成功率)                       │
│  ├── 成功率<80% → 降权                                   │
│  ├── 成功率<50% → 熔断(暂停使用该通道)                   │
│  └── 恢复检测: 每30秒发一笔探测交易                      │
└──────────────────────────────────────────────────────────┘

3.5 对账系统设计

对账流程(每日凌晨执行):

┌──────────────────────────────────────────────────────────┐
│                  Daily Reconciliation                      │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  Step 1: 获取数据                                        │
│  ├── 我方: 从payment_order表查询昨日所有交易              │
│  └── 对方: 下载PSP提供的对账文件(通常是CSV/Excel)        │
│                                                          │
│  Step 2: 逐笔比对                                        │
│  ├── 以我方流水号和对方交易号进行匹配                     │
│  ├── 匹配成功 → 比对金额/状态                            │
│  │   ├── 完全一致 → 标记 MATCHED                         │
│  │   └── 不一致 → 标记 DIFF + 记录差异详情               │
│  └── 未匹配:                                             │
│      ├── 我方有对方无(长款) → 可能对方延迟结算           │
│      └── 对方有我方无(短款) → 可能我方掉单 ⚠️           │
│                                                          │
│  Step 3: 差异处理                                        │
│  ├── 自动处理:                                          │
│  │   ├── 金额差≤1分 → 自动调平(四舍五入导致)            │
│  │   └── 状态差异+我方pending → 主动查询PSP同步状态      │
│  └── 人工处理:                                          │
│      ├── 金额差>1分 → 人工审核                          │
│      ├── 短款 → 紧急排查                                │
│      └── 连续3天未匹配 → 升级处理                       │
│                                                          │
│  Step 4: 生成报告                                        │
│  ├── 对账总览(匹配率/差异笔数/差异金额)                  │
│  ├── 差异明细(每笔差异的详情)                            │
│  └── 告警通知(差异率>0.1%告警)                          │
└──────────────────────────────────────────────────────────┘

第四步:深入讨论(10分钟)

4.1 一致性保证

支付系统的一致性挑战:

问题: 内部扣款成功 但 调用PSP超时 → 不知道是否真的扣款了

解决方案:

方案1: 本地消息表(Transactional Outbox)
┌──────────────────────────────────────────┐
│  Transaction:                             │
│  BEGIN                                    │
│    UPDATE account SET balance -= 100      │
│    INSERT INTO outbox (msg_type='PAY',    │
│      payload={...}, status='PENDING')     │
│  COMMIT                                   │
│                                           │
│  异步:                                    │
│    定时任务扫描 outbox 表                  │
│    → 发送到PSP                            │
│    → 成功: status='SENT'                  │
│    → 失败: 重试(最多N次)                  │
│    → 超时: 主动查询PSP状态                 │
└──────────────────────────────────────────┘

方案2: 状态机 + 补偿
┌──────────────────────────────────────────┐
│  正向流程:                                │
│  CREATED → PROCESSING → SUCCESS          │
│                                           │
│  超时处理:                                │
│  PROCESSING超过30秒 → 主动查询PSP        │
│  ├── PSP说成功 → 更新为SUCCESS           │
│  ├── PSP说失败 → 更新为FAILED + 退款     │
│  └── PSP也不知道 → 标记UNCERTAIN         │
│      → 等对账时确认                       │
│                                           │
│  补偿流程(最终一致):                      │
│  UNCERTAIN → 对账确认 → SUCCESS/退款      │
└──────────────────────────────────────────┘

关键原则:
├── "宁可多查不可少查" — 超时一定要主动查询PSP
├── "宁可退款不可多扣" — 不确定时倾向于退款给用户
└── "对账是安全网" — 所有不一致最终通过对账发现

4.2 安全设计

支付安全要点:

1. PCI-DSS合规
├── 卡号不落库 → 使用Token化服务(替代卡号)
├── 传输加密 → 全链路TLS 1.3
├── 存储加密 → AES-256加密敏感字段
└── 访问控制 → 最小权限 + 审计日志

2. 防重放攻击
├── Idempotency-Key(已有)
├── 请求时间戳校验(±5分钟)
├── 签名验证(HMAC-SHA256)
└── Nonce(一次性随机数)

3. 防篡改
├── 请求签名: 商户用私钥签名,系统用公钥验证
├── 通知签名: 系统用私钥签名,商户用公钥验证
└── 关键字段(金额/收款方)参与签名

4. 限流与反欺诈
├── IP级限流: 单IP 100次/分钟
├── 商户级限流: 根据合同配置
├── 异常检测: 短时间大量小额交易→告警
└── 黑名单: 已知欺诈IP/商户/卡号

第五步:扩展性设计(5分钟)

5.1 从百万到十亿级

扩展策略:

━━━━━ 阶段1: 百万级(100万笔/天) ━━━━━
├── 单机房部署
├── 主从数据库(MySQL)
├── Redis缓存热点数据
├── 单Redis做分布式锁
└── 日处理量: ~12 TPS均值, ~100 TPS峰值

━━━━━ 阶段2: 千万级(1000万笔/天) ━━━━━
├── 数据库分库分表
│   ├── 按商户ID分库(8库)
│   └── 按时间分表(月表)
├── 读写分离
├── 消息队列(Kafka)异步化
│   ├── 通知异步发送
│   └── 对账数据异步写入
├── 多活部署(同城双中心)
└── 日处理量: ~120 TPS均值, ~1000 TPS峰值

━━━━━ 阶段3: 亿级(1亿笔/天) ━━━━━
├── 微服务拆分(支付/风控/路由/对账/结算独立服务)
├── 数据库分片(64+分片)
├── 热点账户分桶
│   └── 热门商户账户拆成N个子账户,汇总时合并
├── 异地多活(双城三中心)
├── 去中心化ID生成(Snowflake)
└── 日处理量: ~1200 TPS均值, ~10000 TPS峰值

━━━━━ 阶段4: 十亿级(10亿笔/天) ━━━━━
├── 全球化多Region部署
├── 流式计算(Flink)替代批处理对账
├── 时序数据库存储交易日志
├── CQRS(读写分离架构)
│   ├── 写: 事件溯源(Event Sourcing)
│   └── 读: 物化视图 + ES搜索
├── 自动化扩缩容(K8s HPA)
└── 日处理量: ~12000 TPS均值, ~100000 TPS峰值

5.2 高可用设计

高可用方案:

┌──────────────────────────────────────────────────────────┐
│              高可用设计要点                                │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  1. 服务层高可用                                         │
│  ├── 无状态服务: 多实例+负载均衡                         │
│  ├── 优雅关闭: 处理完当前请求再关闭                      │
│  ├── 健康检查: /health + /ready                         │
│  └── 超时控制: 每个外部调用设置超时                      │
│                                                          │
│  2. 数据层高可用                                         │
│  ├── 主从复制: 异步复制(0.5-1秒延迟)                    │
│  ├── 自动故障转移: MHA/Orchestrator                      │
│  ├── 跨机房备份: 每日全量+实时Binlog同步                 │
│  └── 分库分表: 单库故障只影响部分商户                    │
│                                                          │
│  3. 通道层高可用                                         │
│  ├── 多通道冗余: 同一支付方式至少2个通道                 │
│  ├── 自动切换: 通道A故障→自动切换通道B                   │
│  ├── 熔断器: 成功率<50%→暂停该通道                      │
│  └── 降级策略: 所有通道不可用→引导用户使用其他支付方式   │
│                                                          │
│  4. 容灾策略                                             │
│  ├── 同城双中心: 两个机房Active-Active                   │
│  ├── 异地灾备: RPO<30秒, RTO<5分钟                      │
│  └── 定期演练: 每季度一次故障切换演练                    │
│                                                          │
└──────────────────────────────────────────────────────────┘

面试官追问及应对

追问1: "如果PSP返回超时,你怎么处理?"

PSP超时处理策略:

1. 不能假设失败→立即退款(PSP可能已经扣款成功)
2. 不能假设成功→给用户确认(可能真的失败了)

正确做法:
├── Step 1: 标记为UNCERTAIN状态
├── Step 2: 立即(1-3秒后)主动查询PSP状态
│   ├── PSP返回成功 → 更新为SUCCESS
│   ├── PSP返回失败 → 更新为FAILED
│   └── PSP也不确定 → 继续Step 3
├── Step 3: 启动定时重试查询(5s, 30s, 5m, 30m)
│   └── 最多查询6次(约1小时)
├── Step 4: 仍不确定 → 等待日终对账确认
│   ├── 对账发现已扣款 → 补更新为SUCCESS
│   └── 对账发现未扣款 → 更新为FAILED
└── Step 5: 对用户展示
    ├── 立即返回"支付处理中"
    ├── 确认成功后通知用户
    └── 确认失败后引导重新支付

追问2: "如何保证不重复扣款?"

三道防线:

第一道: Idempotency-Key
├── 相同Key的请求只处理一次
└── Key = 订单号 + 操作类型(如 ORD-001-PAY)

第二道: 状态机控制
├── CREATED → PROCESSING(只允许一次状态转移)
├── 使用乐观锁(version字段)防止并发
└── UPDATE ... WHERE status='CREATED' AND version=N

第三道: PSP侧幂等
├── 传递相同的商户订单号给PSP
├── PSP自身会做去重
└── 同一商户订单号不会扣两次款

三道防线任意一道起作用就能防止重复扣款。

追问3: "如何处理跨境支付?"

跨境支付扩展:

新增组件:
├── 汇率服务: 实时获取+定期刷新+锁定机制
├── 合规服务: 制裁名单检查/大额申报/税务
├── 多币种账本: 支持多币种记账+汇兑损益
└── 国际通道: VISA/MasterCard/SWIFT

关键挑战:
├── 汇率波动: 从用户下单到实际扣款可能有汇率变动
│   → 汇率锁定(15分钟内有效)
├── 结算时间: 跨境结算T+2到T+5
│   → 对账周期相应延长
├── 合规要求: 不同国家不同要求
│   → 合规规则引擎(可配置)
└── 时区差异: 日切时间不同
    → 统一使用UTC时间

架构决策记录(ADR)

ADR-1: 选择本地消息表而非分布式事务

决策: 使用本地消息表(Transactional Outbox)保证一致性
替代方案: TCC分布式事务 / Saga
原因:
├── 本地消息表实现简单,可靠性高
├── TCC对PSP有侵入——PSP不支持Try/Confirm/Cancel
├── Saga复杂度高,回滚逻辑容易出错
└── 支付系统的最终一致性可以接受(有对账兜底)

ADR-2: 选择幂等键由调用方生成

决策: Idempotency-Key由商户/调用方生成
替代方案: 服务端生成
原因:
├── 调用方知道"什么是重复"——同一笔订单支付就是重复
├── 服务端生成需要额外Round Trip
├── 行业惯例(Stripe/PayPal都是调用方生成)
└── 简化服务端逻辑

今日总结

支付系统设计核心要点

  1. 幂等性是支付系统第一原则——网络超时必须可安全重试
  2. 状态机+补偿解决分布式一致性——最终一致而非强一致
  3. 对账是安全网——所有不一致最终通过对账发现和修复
  4. 通道路由是成本优化的关键——按成功率/费率/延迟动态选择
  5. 安全是底线——PCI-DSS、签名验证、防重放、Token化
  6. 扩展性分阶段规划——不要过早优化,但要预留扩展点

面试答题框架

45分钟分配:
5min  需求澄清(问清楚再动手)
10min 高层设计(先画全景图)
15min 核心组件(数据模型+API+关键流程)
10min 深入讨论(一致性/安全/幂等)
5min  扩展性(容量规划+高可用)

参考资源