## Why

认证系统就绪后（提案 1），客户端需要完整的业务接口来支撑核心使用场景：查看资产信息、购买套餐、钱包充值、查看订单、实名认证跳转、设备操作。本提案覆盖客户端**全部 15 个业务接口**，是 C 端体验的核心支撑。

其中**套餐购买含强充两阶段处理**是最复杂的接口，涉及强充判断 → 微信支付 → 充值回调 → 异步套餐购买的完整链路，需要特别严谨的设计。

**前置依赖**：提案 0（数据模型修复）、提案 1（认证系统）。

## What Changes

### 模块 B：资产信息（4 个接口）

- **B1 资产基本信息** `GET /api/c/v1/asset/info?identifier=xxx`：复用 `asset.Service.Resolve()`，个人客户调用不走 shop_id 数据权限过滤
- **B2 可购买套餐列表** `GET /api/c/v1/asset/packages?identifier=xxx`：按渠道区分价格（代理→`allocation.retail_price`，平台→`SuggestedRetailPrice`）；过滤条件包含 `Package.status`、`shelf_status`、加油包前置校验；按价格升序排序
- **B3 历史套餐列表** `GET /api/c/v1/asset/package-history?identifier=xxx`：按资产当前 `generation` 过滤，复用 `dto.AssetPackageResponse`
- **B4 手动刷新** `POST /api/c/v1/asset/refresh`：卡类型调 Gateway 刷新；设备类型有 Redis 冷却时间

### 模块 C：钱包与充值（5 个接口）

- **C1 钱包详情** `GET /api/c/v1/wallet/detail?identifier=xxx`：不存在则自动创建空钱包
- **C2 钱包流水列表** `GET /api/c/v1/wallet/transactions?identifier=xxx`：通过 wallet_id 天然隔离（不需 generation 过滤），支持 transaction_type / 时间范围筛选
- **C3 充值预检** `GET /api/c/v1/wallet/recharge-check?identifier=xxx`：复用 `recharge.Service.GetRechargeCheck()`，返回是否需要强充、强充金额、触发类型
- **C4 创建充值订单** `POST /api/c/v1/wallet/recharge`：客户端仅支持微信支付；OpenID 由后端查表获取（安全规范）；`operator_type=personal_customer`；`generation` 写时快照；拉起 JSAPI 支付
- **C5 充值订单列表** `GET /api/c/v1/wallet/recharges?identifier=xxx`：按 `generation` 过滤

### 模块 D：套餐购买（3 个接口，含核心强充流程）

- **D1 创建套餐购买订单** `POST /api/c/v1/orders/create`：**核心接口**。含资产归属校验、套餐校验、实名校验、强充两阶段处理、幂等性保证
- **D2 套餐订单列表** `GET /api/c/v1/orders?identifier=xxx`：按 `generation` 过滤
- **D3 套餐订单详情** `GET /api/c/v1/orders/:id`：归属校验（通过资产虚拟号匹配 PersonalCustomerDevice）

### 模块 E：实名认证（1 个接口）

- **E1 获取实名跳转链接** `GET /api/c/v1/realname/link?identifier=xxx&iccid=xxx`：两个入口（购买拦截 / 设备卡列表主动选择）；三种模式（none/template/gateway）

### 模块 F：设备能力（5 个接口）

- **F1 设备卡列表** `GET /api/c/v1/device/cards?identifier=xxx`：从 CMP 数据库查，不调 Gateway
- **F2 设备重启** `POST /api/c/v1/device/reboot`
- **F3 恢复出厂** `POST /api/c/v1/device/factory-reset`
- **F4 设置 WiFi** `POST /api/c/v1/device/wifi`：注意 Gateway WiFiReq 的 cardNo 字段实际传入设备 IMEI
- **F5 切卡** `POST /api/c/v1/device/switch-card`

### D1 套餐购买核心流程（含强充两阶段）

```
客户端发起 POST /api/c/v1/orders/create
    │
    ▼
① 解析标识符 → card/device + asset_type + asset_id
    │
    ▼
② 资产归属校验
   查 PersonalCustomerDevice WHERE customer_id=? AND virtual_no=?
   未绑定 → 403 "无权操作该资产"
    │
    ▼
③ 套餐购买校验
   调 purchaseValidationService.ValidateCardPurchase/ValidateDevicePurchase
   检查: series_id、Package.status、shelf_status、加油包前置条件
    │
    ▼
④ 实名校验
   套餐 enable_realname_activation=true 且卡 real_name_status=0
   → 返回 { code: NEED_REALNAME, need_realname: true }
    │
    ▼
⑤ OpenID 查询
   根据 customer_id + app_type 从 PersonalCustomerOpenID 查询 openid
   找不到 → 返回 { code: OPENID_NOT_FOUND }
    │
    ▼
⑥ 幂等性检查（Redis 业务键 + 分布式锁）
    │
    ▼
⑦ 强充检查
   调 checkForceRechargeRequirement()
    │
    ├─── 不需要强充 ──────────────────────────────┐
    │                                              │
    │    ⑧A 创建 Order                            │
    │    (source="client", generation=当前)         │
    │    拉起微信 JSAPI 支付                        │
    │    → 返回 { order_type: "package",            │
    │             order, pay_config }               │
    │                                              │
    └─── 需要强充 ────────────────────────┐        │
         │                                │        │
         ▼                                │        │
    pay_amount = max(force_amount,        │        │
                     package_total_price)  │        │
         │                                │        │
         ▼                                │        │
    ⑧B 创建 AssetRechargeRecord           │        │
    (linked_package_ids=package_ids,       │        │
     generation=当前)                      │        │
    拉起微信 JSAPI 支付                    │        │
    → 返回 { order_type: "recharge",       │        │
             recharge, pay_config,         │        │
             linked_package_info }         │        │
                                          │        │
═══════════════════════════════════════════╧════════╧═══

强充支付成功后的两阶段回调处理：

微信支付成功 → 充值回调
    │
    ▼
第一阶段（同步，事务内）:
├── 1. 钱入钱包（余额增加）
├── 2. 更新充值单状态为已完成
├── 3. 更新累计/首充状态
└── 4. 检查一次性佣金触发
    │
    ▼
第二阶段（异步，Asynq 任务）:
├── 5. 入队 AutoPurchaseAfterRecharge(recharge_record_id)
├── 6. 异步执行:
│   ├── a. 从钱包扣款（payment_method=wallet）
│   ├── b. 创建 Order（source="client", generation=当前）
│   └── c. 激活套餐
└── 7. 失败处理:
    ├── a. Asynq 自动重试（最多 3 次）
    ├── b. 全部失败 → 标记 auto_purchase_status="failed"
    └── c. 钱已在钱包中，用户可手动操作
```

### 实名跳转流程

```
前端调用 GET /api/c/v1/realname/link?identifier=xxx[&iccid=yyy]
    │
    ▼
解析标识符 → 确定目标卡:
├── 直接是卡 → 用该卡
├── 是设备 + 传了 iccid → 查该 iccid 对应的卡
└── 是设备 + 没传 iccid → 查 DeviceSimBinding 中 isActive=1 的卡
    │
    ▼
检查 card.real_name_status == 1?
├── YES → "该卡已完成实名"
└── NO
    │
    ▼
查 Carrier WHERE id=card.carrier_id → 获取 realname_link_type:
├── 'none'     → "该运营商暂不支持在线实名"
├── 'template' → 替换占位符 {iccid}/{msisdn}/{virtual_no} → 返回 URL
└── 'gateway'  → 调 gateway.GetRealnameLink(card.ICCID) → 返回 URL
```

## Capabilities

### New Capabilities

- `client-asset-info`：客户端资产信息查询（B1）、可购买套餐列表（B2，含渠道价格、加油包校验、上下架过滤）、历史套餐列表（B3，含 generation 过滤）、手动刷新（B4）
- `client-wallet-recharge`：客户端钱包详情（C1）、流水列表（C2）、充值预检（C3）、创建充值订单（C4，含 OpenID 安全规范、operator_type、generation 快照）、充值订单列表（C5）
- `client-order-purchase`：套餐购买订单创建（D1，含归属校验、实名校验、强充两阶段、幂等性）、订单列表（D2）、订单详情（D3）、强充回调异步购买（AutoPurchaseAfterRecharge Asynq 任务）
- `client-realname-link`：实名跳转链接（E1），三种模式、两个入口、设备多卡选择
- `client-device-capability`：设备卡列表（F1）、重启（F2）、恢复出厂（F3）、WiFi 设置（F4）、切卡（F5）

### Modified Capabilities

- `asset-resolve`：Resolve 方法增加"无数据权限过滤"的客户端调用入口
- `wallet-recharge`：充值回调增加两阶段处理——同步入账 + 异步自动购买；AssetRechargeRecord 新增 `auto_purchase_status` 字段跟踪异步购买状态
- `package-purchase-validation`：增加客户端场景的归属校验（PersonalCustomerDevice）和实名校验拦截
- `iot-order`：Order 新增客户端创建路径（source="client"），支持 generation 写入
- `force-recharge-check`：强充检查结果输出给客户端，支持前端提示强充金额和套餐价格拆分

## Impact

- **新增文件**：`internal/handler/app/client_asset.go`、`client_wallet.go`、`client_order.go`、`client_realname.go`、`client_device.go`（5 个 Handler）；`internal/service/client_order/service.go`（客户端订单 Service）；新增 Asynq 任务 `AutoPurchaseAfterRecharge`；新增 DTO 文件；常量和错误码
- **修改文件**：`internal/service/order/service.go`（提取强充逻辑供客户端复用）；`internal/service/recharge/service.go`（充值回调增加两阶段处理）；`internal/service/asset/service.go`（增加无数据权限调用方式）；`internal/routes/personal.go`（新增客户端业务路由）；`internal/bootstrap/`（注册新模块）；`cmd/api/docs.go` + `cmd/gendocs/main.go`（文档生成器）
- **新增 API 路由**：`/api/c/v1/` 下 18 个端点
- **数据库变更**：AssetRechargeRecord 新增 `auto_purchase_status` 字段
- **新增 Asynq 任务类型**：`task:auto_purchase_after_recharge`
