# 执行规则

## Token 规则

- 优先从 `config.json` 读取；**不判断 token 有效期、不主动清除 token**
- 仅当接口返回会话失效时，停止当前步骤并向用户索取对应 token
- 新 token 默认只用于当前会话和当前失败步骤的重试；写入 `config.json` 是独立的凭据持久化操作，必须另行获得用户明确同意
- 不在消息、命令输出、日志或报告中展示 token；持久化时保持 `config.json` 权限为 `600`
- 小游戏后台 token（Step 1/2a）与阿波罗 token（Step 2b）**分开管理**

---

## ID 路由规则

只按用户明确给出的字段名路由，不按数字位数、大小或排列顺序猜测：

| 字段名 | 含义 | 用途 |
|--------|------|------|
| `uid` | 用户 UID | Step 1、Step 3、Step 4 |
| `activity_center_id` | 活动中心活动 ID | Step 2a 请求的 `id` |
| `apollo_activity_id` / `act_id` | Apollo/转盘活动 ID | Apollo module 后缀，以及 Step 3/4 的 `act_id` |
| `activity_entry_id` | 活动入口 ID | 仅记录 Step 2a `group_list[0].id`，不得当作上述任一 ID |

- `uid` 必须明确提供。活动 ID 至少明确提供 `activity_center_id` 或 `apollo_activity_id` / `act_id` 之一。
- 用户只给出未标注的数字，或多个数字但未说明映射时，先询问每个数字对应的字段；**在消除歧义前不得调用任何接口**。
- 从 `activity_center_id` 开始：Step 2a 精确查询，从 `group_list[0].page_link` 的 `id` 参数派生 Apollo ID，再执行 Step 2b。
- 从 `apollo_activity_id` / `act_id` 开始：直接执行 Step 2b，从配置读取 `draw_card_id`，再执行 Step 2a 补充校验。
- 两种活动 ID 都由用户提供时，仍按上述读接口交叉校验；任何不一致都停止在 Step 3 之前并询问用户。
- Step 3 前必须完成 UID 有效性、活动中心精确匹配、Apollo 配置存在及两类活动 ID 一致性校验。

---

## Step 2b — Apollo 配置说明

- `module` = `guaranteed_turntable_` + 已验证的 **Apollo 活动 ID**。从活动中心开始时，该 ID 取自 `group_list[0].page_link` 的 `id` 参数；它不是 `activity_center_id`，也不是 `group_list[0].id`
- **活动时间以 Apollo 返回的 `start_time` / `end_time` 为准**（与 Step 2a/3 返回的时间可能不同）
- 校验：当前时间 < `start_time` → 中断；当前时间 > `end_time` → 中断
- 记录总抽奖次数（`do_cost` 数组长度）
- 写入报告的字段：
  - 代币道具：`lottery.coin.item_id`、`lottery.coin.type`
  - 苹果兑换比例：`lottery.exchange_rate`（N苹果 = 1代币）

---

## Step 2a 补充校验（以 Apollo ID 开始时执行）

- 取 Apollo 配置中的 `draw_card_id`，将其作为 `activity_center_id` 走 Step 2a 查询活动基本信息
- 取返回的 `group_list[0].page_link` 中的 `id` 参数值，与已查询的 Apollo `act_id` 对比：
  - **一致** → 活动配置正常，分别记录活动中心 ID、Apollo 活动 ID 和活动入口 ID（`group_list[0].id`），继续执行
  - **不一致** → 停止流程，向用户说明差异并要求修正 ID；不得进入 Step 3

---

## Step 3 — 确认项

向用户一次性展示以下信息，等待确认后才执行抽奖；这是调用抽奖接口的唯一业务副作用确认门，不包含凭据持久化或无关文件删除授权：

| 信息 | 来源 |
|------|------|
| 活动名称 / act_id / 活动时间 | Apollo config |
| 用户 uid / market / 当前道具数量 | Step 1 + info |
| 已抽次数 / 总次数 | `info.reward_got` 数 / `do_cost` 数组长度 |

同时展示：
- 剩余抽奖次数 = 总次数 − 已抽次数
- 预计最短等待时间 = `max(剩余次数 - 1, 0) × 10 秒`

确认后连续执行 Step 4，不逐次询问。确认前不得调用抽奖接口。

大奖保底（big_reward）**优先取 Step 2b Apollo 配置**；若 Apollo 未返回则降级取 Step 3 返回的 `config.lottery.big_reward`

---

## Step 4 — 循环抽奖记录项

每次记录：
- 抽奖时间（格式 `yyyy-MM-dd HH:mm:ss`，请求发出时取当前时间）
- 花费代币数（`do_cost[N-1].price`）
- 奖品信息（name / type / item_id / num / use_limit_time）

每次抽奖之间等待 **10 秒**（最后一次无需等待）

---

## Step 5 — 报告保存规则

- 在 skill 目录下创建 `tmp/`（若不存在则新建）
- 文件名：`{大奖名称}_{活动配置id}_{yyyyMMddHHmm}.md`
- 首行写入：`<!-- expires: {写入时间 + 24小时} -->`
- 每次执行时扫描 `tmp/` 删除所有已过期文件
