## Context

### 现状

系统中代理钱包有两种类型（`tb_agent_wallet`）：
- `wallet_type=main`：预充值钱包，代理购买套餐时扣款
- `wallet_type=commission`：佣金钱包，订单完成后自动入账、可提现

当前 `/shops/commission-summary` 仅聚合佣金钱包数据，主钱包完全不可见。代理自己的操作路径依赖 `/my/` 前缀系列接口，与平台视角的 `/shops/:id/` 系列存在职责重叠，产生维护成本。

### 已有可复用能力

| 组件 | 方法 | 状态 |
|------|------|------|
| `AgentWalletStore` | `GetMainWallet(shopID)` | 已有，单条查询 |
| `AgentWalletStore` | `GetShopCommissionSummaryBatch(shopIDs)` | 已有，批量查佣金钱包 |
| `AgentWalletTransactionStore` | `ListByShopID / CountByShopID` | 已有，直接复用 |
| `ShopCommissionService` | `ListShopCommissionSummary` | 已有，需扩展 |
| `ShopCommissionService` | `ListShopCommissionRecords` | 已有，不动 |
| `ShopCommissionService` | `ListShopWithdrawalRequests` | 已有，不动 |
| `MyCommissionService` | `GetStats / GetDailyStats / CreateWithdrawalRequest` | 业务逻辑迁移到 ShopCommissionService，原 service 删除 |

---

## Goals / Non-Goals

**Goals:**
- 平台人员在一个列表（资金概况）中同时看到每个代理的预充值余额和佣金余额
- 代理账号通过相同接口看到自己的资金概况（GORM 多租户过滤）
- 提供预充值钱包流水接口，支持平台和代理两个视角
- 将 `/my/` 6 个接口的业务逻辑完整迁移至 `/shops/:id/`，删除冗余路径

**Non-Goals:**
- 不新增纯佣金钱包流水接口（`commission-records` 订单维度已满足需求）
- 不改动充值订单相关接口（`/agent-recharges` 系列不变）
- 不改动提现审批流程（`/commission/withdrawal-*` 不变）
- 不做向后兼容，直接删除 `/my/` 路由

---

## Decisions

### 决策 1：资金概况使用批量查询主钱包余额

**问题**：`/shops/fund-summary` 分页返回多条记录，每条需要主钱包余额。若逐条查询会产生 N+1。

**决策**：在 `AgentWalletStore` 新增 `GetShopMainWalletBatch(ctx, shopIDs) map[uint]*AgentWallet`，与现有 `GetShopCommissionSummaryBatch` 对称，一次 `WHERE shop_id IN (...)` 查完，在 Service 层合并。

**备选**：单次 SQL JOIN 查两个钱包类型 — 复杂度高，破坏 Store 层单一职责，放弃。

---

### 决策 2：主钱包流水接口不做独立 Handler，归入 ShopCommissionHandler

**问题**：主钱包流水是代理资金详情的一部分，是否需要独立 Handler。

**决策**：归入扩展后的 `ShopCommissionHandler`（或重命名为 `ShopFundHandler`），保持路由注册集中，避免 Handler 碎片化。

**注意**：`AgentWalletTransactionStore.ListByShopID` 已有，只需 Service 层加一个 `ListMainWalletTransactions(ctx, shopID, req)` 方法。

---

### 决策 3：`/my/` 路由的业务逻辑迁移方式

**问题**：`MyCommissionService` 中的 `GetStats`、`GetDailyStats`、`CreateWithdrawalRequest` 当前从 ctx 自动读取 shopID（隐式即"自己"），迁移后接口路径从 `:shop_id` 取，必须显式越权校验。

**决策**：
- 将三个方法迁移到 `ShopCommissionService`，签名改为接受 `shopID uint` 参数
- **Service 层入口**强制权限校验（不是 Handler 层），对齐 CLAUDE.md 的 Code Review 规范（参考 `internal/service/account/service.go`）
- Handler 只做参数解析，不写权限逻辑
- 删除 `MyCommissionService` 和 `MyCommissionHandler`

**两档校验策略**（根据业务严格度区分）：

| 方法 | 校验策略 | 理由 |
|---|---|---|
| `GetStats` / `GetDailyStats` | `middleware.CanManageShop(ctx, shopID)` | 查询类，平台和顶级代理可看下级数据 |
| `ListMainWalletTransactions` | `middleware.CanManageShop(ctx, shopID)` | 查询类，同上 |
| `ListShopWithdrawalRequests` / `ListShopCommissionRecords`（已有方法补齐） | `middleware.CanManageShop(ctx, shopID)` | 查询类，同上 |
| `CreateWithdrawalRequest` | **更严**：`userType==Agent` 且 `shopID==GetShopIDFromContext(ctx)` | 写操作，业务规定提现必须本人发起，平台和顶级代理均不允许代办 |

**越权校验覆盖范围**：不仅新增/迁移的方法要加，**已有的 `ListShopWithdrawalRequests` / `ListShopCommissionRecords` 也必须补齐** —— 现状它们只做了 `shopStore.GetByID` 的存在性检查，没有 `CanManageShop`，`/my/commission-records`、`/my/withdrawal-requests` 废弃后代理只要猜 shopID 就可读他人数据。是本变更必须修复的回归入口。

---

### 决策 4：Handler 不重命名，路由集中在 registerShopCommissionRoutes

原 `ShopCommissionHandler` 职责扩大（涵盖资金概况、主钱包流水、提现发起、佣金统计），保持原名 `ShopCommissionHandler` 不重命名，避免大范围文件改动。路由全部集中在扩展后的 `registerShopCommissionRoutes` 中，不再新建 `registerShopFundSummaryRoutes`，避免一个模块两处注册入口。

---

### 决策 5：`main_frozen_balance` 字段保留但标注预留

**现状**：`tb_agent_wallet.frozen_balance` 是通用字段，但主钱包当前业务无冻结场景（DB 验证 10 条主钱包记录 `frozen_balance` 全为 0）。

**决策**：`ShopFundSummaryItem` 保留 `main_frozen_balance`，作为未来扩展（如主钱包预授权/待扣款场景）预留位。Handler 直接返回 `AgentWallet.FrozenBalance`，无额外逻辑。前端展示可暂时不渲染，等业务触发时再暴露。

**备选**：不返回此字段 —— 将来接入时要改 DTO + 前端 + 文档，破坏性更大，放弃。

---

## 分层变更总览

```
Handler 层
  ShopCommissionHandler    新增方法: ListFundSummary, ListMainWalletTransactions,
                                     GetCommissionStats, GetCommissionDailyStats,
                                     CreateWithdrawal
                           删除方法: ListCommissionSummary（被 ListFundSummary 替代）
  MyCommissionHandler      整体删除

Service 层
  ShopCommissionService    构造函数补依赖: commissionWithdrawalSettingStore,
                                           agentWalletTransactionStore
                           改造方法: ListShopCommissionSummary → ListShopFundSummary
                                     （加批量主钱包查询）
                           新增方法: ListMainWalletTransactions, GetStats,
                                     GetDailyStats, CreateWithdrawalRequest
                                     （均在入口调 middleware.CanManageShop）
                           补权限校验: ListShopWithdrawalRequests,
                                       ListShopCommissionRecords
                                       （现状无越权校验，必须补齐）
  MyCommissionService      整体删除

Store 层
  AgentWalletStore              新增: GetShopMainWalletBatch(ctx, shopIDs)
  AgentWalletTransactionStore   新增: ListByWalletIDWithFilters, CountByWalletID
                                删除: ListByShopID, CountByShopID
                                      （全局无调用者，且会跨钱包类型返回
                                      数据，留着易被误用）

DTO 层
  ShopCommissionSummaryItem  → ShopFundSummaryItem（新增 main_balance, main_frozen_balance）
  ShopCommissionSummaryListReq / PageResult  → ShopFundSummaryListReq / PageResult
  新增: MainWalletTransactionItem, MainWalletTransactionListRequest/Response
  删除: MyCommissionSummaryResp, MyWithdrawalListReq, MyCommissionRecordListReq 等

路由层
  registerMyCommissionRoutes    删除
  registerShopCommissionRoutes  扩展：
                                  - GET  /commission-summary → GET /fund-summary
                                  - 新增 GET  /:shop_id/main-wallet/transactions
                                  - 新增 GET  /:shop_id/commission-stats
                                  - 新增 GET  /:shop_id/commission-daily-stats
                                  - 新增 POST /:shop_id/withdrawal-requests
```

---

## Risks / Trade-offs

| 风险 | 缓解措施 |
|------|---------|
| 批量查主钱包余额新增一次 DB 查询，影响 fund-summary 接口性能 | 数据量有限（代理数量通常 < 1000），IN 查询 < 5ms；如后期扩大可加 Redis 缓存 |
| 删除 `/my/` 路由是破坏性变更，前端需同步修改 | 开发阶段，不考虑向后兼容；前端在本次变更后同步切换 |
| `MyCommissionService` 删除后，若有其他地方引用会编译报错 | tasks 中包含全局 grep 检查，确保删干净 |
| commission-stats/daily-stats 迁移后，shopID 改为显式参数，逻辑路径变化 | 单独验证两个统计接口的查询结果与原 /my/ 接口一致 |
| **越权回归漏洞**：`/my/commission-records` / `/my/withdrawal-requests` 废弃后代理改走 `/shops/:shop_id/...`，而已有的 `ListShopCommissionRecords` / `ListShopWithdrawalRequests` 没做 `CanManageShop` 校验，代理只要猜到他人 shopID 就能读到敏感数据 | task 3.6 强制在这两个已有方法的入口补 `CanManageShop`；task 10.5 手动验证 403 |
| `CreateWithdrawalRequest` 若用 `CanManageShop` 会允许顶级代理替下级店铺提现 | 已确认产品不允许：`CreateWithdrawalRequest` 不使用 `CanManageShop`，改为更严格的双重校验（`userType==Agent` 且 `shopID==自己`），平台人员也禁止调用，与原 `/my/withdrawal-requests` 行为一致 |
| 删除 `AgentWalletTransactionStore.ListByShopID/CountByShopID` 可能导致外部/历史代码编译失败 | task 2.3 在删除前用 grep 确认无其他调用者（已在审查阶段验证过） |

---

## Migration Plan

1. DTO 层：重命名 `ShopCommissionSummaryItem → ShopFundSummaryItem` 并新增字段、新增主钱包流水 DTO
2. Store 层：
   - `AgentWalletStore` 新增 `GetShopMainWalletBatch`
   - `AgentWalletTransactionStore` 新增 `ListByWalletIDWithFilters` / `CountByWalletID`，删除 `ListByShopID` / `CountByShopID`
3. Service 层：
   - 补构造函数依赖
   - 改造 `ListShopFundSummary`
   - 迁移三个 `/my/` 方法（入口强制 `CanManageShop`）
   - 新增 `ListMainWalletTransactions`（入口强制 `CanManageShop`）
   - 给已有的 `ListShopWithdrawalRequests` / `ListShopCommissionRecords` 补 `CanManageShop`
4. Handler 层：`ShopCommissionHandler` 新增方法、删除 `ListCommissionSummary`
5. 路由层：扩展 `registerShopCommissionRoutes`、删除 `registerMyCommissionRoutes`
6. Bootstrap：删除 `MyCommissionHandler`/`myCommissionService`，更新 `shopCommissionService` 初始化
7. 文档生成器：`cmd/api/docs.go` 和 `cmd/gendocs/main.go`
8. 删除 `internal/handler/admin/my_commission.go`、`internal/service/my_commission/`、`internal/routes/my_commission.go`
9. `go build ./...` 全量编译 + grep 确认无残留引用
10. PostgreSQL MCP 手动验证

无数据库迁移，无需 rollback 策略。

## Open Questions

无，所有设计决策已在探索阶段与需求方确认。
