# 设计：修复一番赏次数卡支付与退款逻辑

## 1. 整体架构与流程
### 支付流程 (Modified)
```mermaid
sequenceDiagram
    participant User
    participant Frontend (Vue)
    participant Backend (API)
    participant DB

    User->>Frontend: 选择一番赏格位，点击支付 (使用次数卡)
    Frontend->>Backend: API: /api/app/lottery/join (use_game_pass=true)
    Backend->>DB: Check Game Pass Balance
    Backend->>DB: Deduct Game Pass (Remaining - N)
    Backend->>DB: Create Order (Amount=0, Status=2, Remark="gp_use:ID:N")
    Backend-->>Frontend: Response (Success, Amount=0, Status=2)
    
    alt Amount == 0
        Frontend->>Frontend: Skip WeChat Pay
        Frontend->>Frontend: Show Result / Flip Cards
    else Amount > 0
        Frontend->>Backend: Create WeChat Order
        Frontend->>WeChat: Request Payment
    end
```

### 退款流程 (Modified)
```mermaid
sequenceDiagram
    participant Admin
    participant Backend (Refund API)
    participant DB

    Admin->>Backend: API: /api/admin/refund (OrderNo)
    Backend->>DB: Get Order & Remark
    Backend->>Backend: Parse Remark for "gp_use:ID:Count" pairs
    loop For Each Pass
        Backend->>DB: Update user_game_passes SET remaining+=Count
    end
    Backend->>DB: Update Order Status = Refunded
    Backend-->>Admin: Success
```

## 2. 核心组件设计
### 2.1 Lottery App (`lottery_app.go`)
- **Logic**: In `JoinLottery` handler, inside the transaction where `useGamePass` is true.
- **Change**:
  ```go
  // Before
  deducted += canDeduct
  // After
  deducted += canDeduct
  gamePassUsage = append(gamePassUsage, fmt.Sprintf("gp_use:%d:%d", p.ID, canDeduct))
  ...
  order.Remark += "|" + strings.Join(gamePassUsage, "|")
  ```

### 2.2 Refund Admin (`pay_refund_admin.go`)
- **Logic**: In `CreateRefund`.
- **Change**:
  - Remove simple regex `game_pass:(\d+)`.
  - Add loop to find all `gp_use:(\d+):(\d+)`.
  - Execute restoration for each match.

### 2.3 Frontend (`index.vue`)
- **Logic**: `onPaymentConfirm`.
- **Change**:
  ```javascript
  if (joinResult.actual_amount === 0 || joinResult.status === 2 || joinResult.status === 'paid') {
      // Direct Success
      onPaymentSuccess({ result: lotteryResult })
      return
  }
  ```

## 3. 接口契约
- 无新增接口。
- `JoinLottery` 响应保持不变，前端需利用现有的 `actual_amount` 和 `status` 字段。

## 4. 异常处理
- **Refund Partial Failure**: Cannot easily happen in transaction. If one update fails, whole refund fails.
- **Legacy Orders**: Old orders have `use_game_pass` but no `gp_use:ID`. Refund logic should fallback to old behavior (try to find 1 pass or just warn/skip).
  - *Fallback Strategy*: If no `gp_use` found but `use_game_pass` is present, log warning or try to restore 1 count to *any* valid pass of user?
  - *Decision*: Since user specifically asked for this fix, we assume it's for FUTURE/NEW orders or current testing. For legacy orders, we can leave as is or try best effort. Given "Strict" requirement, we will implement the new logic. Legacy fallback: Scan `game_pass:ID` (old format if any?) - wait, old code didn't write ID at all. So legacy orders cannot be automatically restored safely. This is acceptable for a "Fix".
