## Context

当前 `tb_asset_recharge_record` 混合了两个职责：业务订单（用户关心：状态、金额、套餐）和支付凭证（系统关心：交易号、支付渠道、回调）。强充流程创建的是充值记录而非订单，导致用户在 `/api/c/v1/orders` 看不到强充产生的记录。幂等键命中时直接报错而非引导用户恢复支付，造成"列表为空 + 无法重新下单"的死锁体验。

**现有数据流**：
```
强充发起  → 写 tb_asset_recharge_record (单号前缀 CRCH)
微信回调  → 更新 tb_asset_recharge_record + 增加钱包余额
auto_purchase → 读 tb_asset_recharge_record.linked_package_ids → 创建 tb_order (套餐) + 扣款
```

**目标数据流**：
```
强充发起  → 写 tb_recharge_order + tb_payment (CRCH 前缀保持兼容)
微信回调  → 更新 tb_payment + 更新 tb_recharge_order + 增加钱包余额
auto_purchase → 读 tb_recharge_order.linked_package_ids → 创建 tb_order (套餐) + 创建 tb_payment (钱包) + 扣款
```

## Goals / Non-Goals

**Goals:**

- 彻底替换 `tb_asset_recharge_record`，用 `tb_recharge_order` + `tb_payment` 完整承接其所有功能
- 充值订单通过独立接口 `/api/c/v1/recharge-orders` 展示；套餐订单接口 `/api/c/v1/orders` 保持不变
- 幂等键命中时返回已有待支付充值订单，引导前端拉起支付而非报错
- 普通钱包充值（非强充）同样写 `tb_recharge_order` + `tb_payment`
- 全部功能验证通过后，作为最后一步 DROP `tb_asset_recharge_record`

**Non-Goals:**

- 不合并 `tb_order` 和 `tb_recharge_order`（各自独立，不引入多态 order_type）
- 不做 UNION 查询（充值订单和套餐订单各有独立入口，不混合展示）
- 不引入父子订单关联关系
- 不考虑回退（一次性完整替换，无过渡期）
- 不迁移历史数据（历史充值记录业务已完成，DROP 旧表即可）

## Decisions

### 决策 1：新建 `tb_recharge_order` 而非扩展 `tb_order`

**选择**：独立的 `tb_recharge_order` 表，不将 `wallet_recharge` 作为 `order_type` 注入 `tb_order`。

**原因**：充值订单与套餐订单领域边界清晰——充值订单无佣金、无卖家、无套餐明细、有自动购包状态；合并进 `tb_order` 会使该表充满特定类型才有意义的 NULL 列，且所有查询都需要加 `order_type` 判断。独立表让每张表的字段 100% 有效，业务逻辑无需条件分支。

**放弃的替代方案**：多态 `order_type` 方案——混合 NULL 列问题和业务逻辑污染问题无法解决。

### 决策 2：充值订单和套餐订单各自独立接口

**选择**：`/api/c/v1/orders` 保持不变，仅返回套餐购买订单（`tb_order`）。新增 `/api/c/v1/recharge-orders` 独立返回充值订单（`tb_recharge_order`）。

**原因**：两类订单业务语义不同——充值订单有 auto_purchase_status，套餐订单有佣金状态；强行混合展示需要 UNION 和统一 DTO，引入不必要的复杂度和长期维护负担。前端各看各的，接口职责清晰，DTO 字段 100% 有意义。

### 决策 3：`tb_payment` 统一承接所有支付凭证

**选择**：新建 `tb_payment` 表，`order_id` + `order_type`（`package_order` / `recharge_order`）多态关联到对应业务表。

**原因**：支付是基础设施，与业务订单解耦后，未来新增支付方式（富途、积分等）只需修改支付层，不触碰业务订单表。支付回调路由从"按单号前缀分发到不同 Service"简化为"查 `tb_payment` → 关联业务订单"。

**字段设计**：
```sql
CREATE TABLE tb_payment (
    id                   BIGSERIAL PRIMARY KEY,
    payment_no           VARCHAR(40)  NOT NULL UNIQUE,    -- 保持 CRCH/PAY 前缀兼容
    order_id             BIGINT       NOT NULL,
    order_type           VARCHAR(30)  NOT NULL,           -- 'package_order' | 'recharge_order'
    payment_method       VARCHAR(20)  NOT NULL,           -- wallet | wechat | alipay | offline
    amount               BIGINT       NOT NULL,           -- 分
    status               SMALLINT     NOT NULL DEFAULT 1, -- 1待支付 2已支付 3已失败 4已退款
    third_party_trade_no VARCHAR(100),
    payment_config_id    BIGINT,
    payment_voucher_key  VARCHAR(500),
    paid_at              TIMESTAMPTZ,
    created_at           TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at           TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    deleted_at           TIMESTAMPTZ
);
CREATE INDEX idx_payment_order    ON tb_payment(order_id, order_type);
CREATE INDEX idx_payment_no       ON tb_payment(payment_no) WHERE deleted_at IS NULL;
CREATE INDEX idx_payment_status   ON tb_payment(status) WHERE deleted_at IS NULL;
```

### 决策 4：`tb_recharge_order` 字段设计

承接 `tb_asset_recharge_record` 的业务字段 + 增加与资产的直接关联：

```sql
CREATE TABLE tb_recharge_order (
    id                   BIGSERIAL PRIMARY KEY,
    recharge_order_no    VARCHAR(40)  NOT NULL UNIQUE,   -- 沿用 CRCH 前缀
    user_id              BIGINT       NOT NULL,           -- 发起用户（个人客户 ID）
    asset_wallet_id      BIGINT       NOT NULL,
    resource_type        VARCHAR(20)  NOT NULL,           -- iot_card | device
    resource_id          BIGINT       NOT NULL,
    iot_card_id          BIGINT,                          -- 单卡充值时有值（用于 ListOrders 查询）
    device_id            BIGINT,                          -- 设备充值时有值
    amount               BIGINT       NOT NULL,           -- 充值金额（分）
    status               SMALLINT     NOT NULL DEFAULT 1, -- 1待支付 2已支付 3已关闭 4已退款
    paid_at              TIMESTAMPTZ,
    shop_id_tag          BIGINT       NOT NULL DEFAULT 0,
    enterprise_id_tag    BIGINT,
    operator_type        VARCHAR(30)  NOT NULL,           -- personal_customer | admin_user
    generation           INT          NOT NULL DEFAULT 1,
    -- 强充专有字段（普通充值为 NULL）
    linked_package_ids   JSONB,
    linked_order_type    VARCHAR(20),
    linked_carrier_type  VARCHAR(20),
    linked_carrier_id    BIGINT,
    auto_purchase_status VARCHAR(20),                     -- pending | success | failed
    -- 累计充值（用于强充阈值判断）
    accumulated_recharge_series BIGINT NOT NULL DEFAULT 0,
    created_at           TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at           TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    deleted_at           TIMESTAMPTZ
);
CREATE INDEX idx_recharge_order_card      ON tb_recharge_order(iot_card_id, generation) WHERE deleted_at IS NULL;
CREATE INDEX idx_recharge_order_device    ON tb_recharge_order(device_id, generation) WHERE deleted_at IS NULL;
CREATE INDEX idx_recharge_order_user      ON tb_recharge_order(user_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_recharge_order_no        ON tb_recharge_order(recharge_order_no) WHERE deleted_at IS NULL;
CREATE INDEX idx_recharge_order_status    ON tb_recharge_order(status) WHERE deleted_at IS NULL;
```

### 决策 5：旧表在所有验证通过后作为最后一步 DROP

**选择**：迁移过程中保留 `tb_asset_recharge_record`（只读，不再写入）；全部功能验证通过后，最后一个任务执行 `DROP TABLE tb_asset_recharge_record`。

**原因**：保留旧表到最后是唯一的安全网——若验证发现遗漏的调用路径，还能对照旧表数据排查。一旦验证完整通过，立即 DROP，不拖延，不留历史包袱。不做重命名归档，不设 3-6 个月观察期。

### 决策 6：幂等键修改

**现有问题**：`SetNX(redisKey, "processing")` 命中时直接返回 "订单正在创建中"，不区分"真正在处理"和"已创建待支付"。

**修改方案**：`!claimed` 时 `GET` 当前值：
- 值为 `"processing"` → 并发请求，返回原有报错
- 值为 `recharge_order_no` → 查 `tb_recharge_order`，若 `status=1`（待支付）则返回已有充值订单，前端拉起支付

## Risks / Trade-offs

- **[历史充值记录不可见]** → 接受，`/api/c/v1/recharge-orders` 仅展示新表数据，用户在新表建立前的历史充值不展示；历史充值已到账，业务无影响
- **[`/api/c/v1/wallet/recharges` 数据断层]** → 接受，旧接口后端改为查 `tb_recharge_order`，历史数据不补填

## Open Questions

- `auto_purchase_status` 触发失败时的重试策略是否调整（当前 Asynq 最多重试 3 次）？（不在本次范围，单独讨论）
