## ADDED Requirements

### Requirement: 迁移接口的越权校验（通用要求）

本规范下所有 `/shops/:shop_id/...` 迁移/新增接口，Service 层方法入口 SHALL 调用 `middleware.CanManageShop(ctx, shopID)` 做显式越权校验。Handler 层只做参数解析，不承担权限逻辑。该要求同时追溯适用于已有但缺少校验的 `ListShopWithdrawalRequests`、`ListShopCommissionRecords` 两个方法（回归漏洞修复）。

统一错误返回：`errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")`。

#### Scenario: 代理传非自己管辖店铺 ID 被 Service 层拦截

- **WHEN** 代理 A（shopID=10）请求本规范任意一个 `/shops/:shop_id/...` 接口，传入代理 B（shopID=20）的 shopID
- **THEN** Service 层入口的 `middleware.CanManageShop(ctx, 20)` 返回 error，接口返回 403，消息 "无权限操作该资源或资源不存在"

#### Scenario: 平台人员不受 CanManageShop 限制

- **WHEN** 平台人员请求任意 `/shops/:shop_id/...` 接口
- **THEN** `middleware.CanManageShop` 直接放行，接口正常返回数据

#### Scenario: 已有方法 ListShopCommissionRecords / ListShopWithdrawalRequests 补齐校验

- **WHEN** 代理账号请求 `/shops/{非自己管辖shopID}/commission-records` 或 `/withdrawal-requests`
- **THEN** 实现后返回 403（回归漏洞修复前这两个方法会返回数据）

---

### Requirement: 通过店铺 ID 查询佣金统计

系统 SHALL 提供 `GET /api/admin/shops/:shop_id/commission-stats` 接口，返回指定代理店铺在给定时间范围内的佣金汇总统计。

**路径参数**：`shop_id`（必填）
**查询参数**：`start_time`、`end_time`（可选，ISO8601 格式）
**响应**：与原 `/my/commission-stats` 一致（`CommissionStatsResponse`）
**权限**：Service 层入口调用 `middleware.CanManageShop(ctx, shopID)`

#### Scenario: 平台人员查询某代理的佣金统计

- **WHEN** 平台人员请求 `GET /shops/123/commission-stats`
- **THEN** 系统返回店铺 123 的佣金汇总统计

#### Scenario: 代理查询自己的佣金统计

- **WHEN** 代理账号请求 `GET /shops/自己ID/commission-stats`
- **THEN** 系统返回该代理自己的佣金统计数据

#### Scenario: 代理尝试查询他人统计被拦截

- **WHEN** 代理账号请求 `GET /shops/他人ID/commission-stats`
- **THEN** 系统返回 403 错误

---

### Requirement: 通过店铺 ID 查询每日佣金统计

系统 SHALL 提供 `GET /api/admin/shops/:shop_id/commission-daily-stats` 接口，返回指定代理店铺的每日佣金趋势数据。

**路径参数**：`shop_id`（必填）
**查询参数**：`start_date`、`end_date`（可选，默认最近 30 天）
**响应**：与原 `/my/commission-daily-stats` 一致（`[]DailyCommissionStatsResponse`）
**权限**：Service 层入口调用 `middleware.CanManageShop(ctx, shopID)`

#### Scenario: 平台人员查询某代理的每日佣金

- **WHEN** 平台人员请求 `GET /shops/123/commission-daily-stats`
- **THEN** 系统返回店铺 123 的每日佣金趋势

#### Scenario: 代理查询自己的每日佣金

- **WHEN** 代理账号请求 `GET /shops/自己ID/commission-daily-stats`
- **THEN** 系统返回该代理自己的每日佣金数据，默认最近 30 天

#### Scenario: 代理尝试查询他人数据被拦截

- **WHEN** 代理账号请求 `GET /shops/他人ID/commission-daily-stats`
- **THEN** 系统返回 403 错误

---

### Requirement: 通过店铺 ID 发起提现申请

系统 SHALL 提供 `POST /api/admin/shops/:shop_id/withdrawal-requests` 接口，允许代理为自己的店铺发起佣金提现申请。

**路径参数**：`shop_id`（必填）
**请求体**：与原 `POST /my/withdrawal-requests` 一致（`CreateMyWithdrawalReq`）
**响应**：与原 `POST /my/withdrawal-requests` 一致（`CreateMyWithdrawalResp`）

**权限规则**（严于其他迁移接口，**不使用** `CanManageShop`）：
- Service 层入口必须满足两个条件：
  1. `userType == UserTypeAgent`（仅代理商用户）
  2. `shopID == middleware.GetShopIDFromContext(ctx)`（必须本人店铺，禁止顶级代理替下级提现）
- 平台人员/超管/企业账号一律 403：业务上提现必须由代理本人发起，平台不代办
- 顶级代理替下级提现也禁止：代理只能给自己的店铺发起提现申请

#### Scenario: 代理为自己发起提现

- **WHEN** 代理账号请求 `POST /shops/{自己shopID}/withdrawal-requests`，传入合法金额和收款信息
- **THEN** 系统创建提现申请，返回申请 ID、提现单号、手续费信息

#### Scenario: 代理尝试为他人（非下级）发起提现被拦截

- **WHEN** 代理账号 A 请求 `POST /shops/{无关代理B的shopID}/withdrawal-requests`
- **THEN** 系统返回 403 错误，消息"仅可为本人店铺发起提现"

#### Scenario: 顶级代理尝试替下级店铺发起提现被拦截

- **WHEN** 顶级代理 P 请求 `POST /shops/{P的下级shop_id}/withdrawal-requests`（即使 `CanManageShop` 会放行）
- **THEN** 系统返回 403 错误，消息"仅可为本人店铺发起提现"。理由：业务上提现必须本人发起

#### Scenario: 平台人员尝试替代理发起提现被拦截

- **WHEN** 平台人员或超管请求 `POST /shops/:shop_id/withdrawal-requests`
- **THEN** 系统返回 403 错误，消息"仅代理商用户可发起提现"

#### Scenario: 可提现余额不足

- **WHEN** 代理发起提现金额超过可提现余额
- **THEN** 系统返回业务错误，消息为"可提现余额不足"

---

## REMOVED Requirements

### Requirement: 通过 /my/ 路径查询佣金统计

**Reason**: 路径统一到 `/shops/:id/` 前缀，`/my/` 系列接口职责与平台视角重叠，增加维护成本
**Migration**: 使用 `GET /api/admin/shops/:shop_id/commission-stats` 替代 `GET /api/admin/my/commission-stats`

### Requirement: 通过 /my/ 路径查询每日佣金统计

**Reason**: 同上，路径统一
**Migration**: 使用 `GET /api/admin/shops/:shop_id/commission-daily-stats` 替代 `GET /api/admin/my/commission-daily-stats`

### Requirement: 通过 /my/ 路径发起提现申请

**Reason**: 同上，路径统一
**Migration**: 使用 `POST /api/admin/shops/:shop_id/withdrawal-requests` 替代 `POST /api/admin/my/withdrawal-requests`

### Requirement: 通过 /my/ 路径查询佣金概览

**Reason**: 由 `/shops/fund-summary` 替代，新接口同时包含预充值钱包余额
**Migration**: 使用 `GET /api/admin/shops/fund-summary` 替代 `GET /api/admin/my/commission-summary`

### Requirement: 通过 /my/ 路径查询提现记录

**Reason**: 路径统一
**Migration**: 使用 `GET /api/admin/shops/:shop_id/withdrawal-requests` 替代 `GET /api/admin/my/withdrawal-requests`

### Requirement: 通过 /my/ 路径查询佣金明细

**Reason**: 路径统一
**Migration**: 使用 `GET /api/admin/shops/:shop_id/commission-records` 替代 `GET /api/admin/my/commission-records`
