Files
Astral/docs/实跑验证记录.md
T
Txiaochen 41909b9169 docs: WaHappy 开发文档
孤儿分支,只含 docs/。内容包括:
- 项目总览 / 主脚本编写指南 / 日志指南
- 上传成绩开发文档 / UpsertUserAll 踩坑清单(28 条)
- 真机 Hook 对照报告 / 实跑验证记录
- 购买功能票流程(含被服务端拒绝的排查过程)
  · 扫码登录协议技术规范
2026-10-06 14:42:32 +08:00

134 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实跑验证记录(真实发包 + 落库确认)
> 本文记录本工程**第一次真实跑通**「扫码登录 → 取数 → 上传成绩 → 落库回查 → 登出」的完整证据。
> 执行方式:`python upsert.py <二维码> --send`(等价于下面的驱动方式)。
---
## 一、结论
| 环节 | 结果 |
|---|---|
| 扫码登录 | ✅ user_id=12345678,token 获取成功 |
| 用户预览 | ✅ errorId=0、banState=0、isLogin=False |
| 正式登录 | ✅ returnCode=1,loginId=7693236821794227665 |
| 抓基线快照 | ✅ 19 个节点全部成功,失败 0 个 |
| 组包 + 发包前自检 | ✅ 0 问题 |
| 冷却 | ✅ 等待 60.0 秒(模拟真机节奏) |
| **上传成绩** | ✅ **returnCode=1** |
| **落库回查** | ✅ **userData.playCount 1260 → 1261** |
| 登出 | ✅ returnCode=1 |
**这是关键突破**:之前的失败模式是「returnCode=1 但没落库」。
本次是**回码成功 + 重新拉服务器数据确认数值真的变了**,两者同时成立。
---
## 二、时间线(2026-10-06 01:28)
```
01:28:01 扫码登录成功 user_id=12345678
01:28:01 用户登录成功 loginId=7693236821794227665
loginDateTime=1791250081 dateTime=1791221281
01:28:03 结算快照抓取完成 成功 19 个节点,失败 0 个
01:28:04 角色快照取自服务器 395:3803 / 101:4458 / 400402:137 / 400401:137 / 200401:137
01:28:04 发包前自检通过
01:28:04 上传成绩冷却 等待 60.0 秒后再发送(模拟真机节奏)
01:29:04 冷却结束 共等待 60.0 秒
01:29:04 上传成功 returnCode=1
01:29:04 落库回查:
playCount 1260 -> 1261
currentPlayCount 188 -> 189
lastPlayDate 2026-10-05 22:32:21 -> 2026-10-06 01:28:04
musicId=143 level=4 playCount -> 11
01:29:05 【落库成功】userData.playCount 已 +1
01:29:05 用户登出成功 returnCode=1
```
---
## 三、落库证据(上传后重新拉服务器数据)
上传成功后**不登出、立刻**调 `GetUserDataApi` + `GetUserMusicApi` 回读,服务器返回:
```json
{
"userData": {
"playCount": 1261,
"currentPlayCount": 189,
"lastPlayDate": "2026-10-06 01:28:04",
"lastLoginDate": "2026-10-06 01:28:01",
"eventWatchedDate": "2026-10-06 01:28:01",
"lastPlaceId": 0,
"point": 4460,
"charaSlot": [395, 101, 400402, 400401, 200401]
}
}
```
```
musicId=143 level=4 -> playCount = 11
```
对照**上传前**的基线:`playCount=1260`、`currentPlayCount=188`、`(143,4).playCount=10`。
三项全部 +1,**证明服务端真的把这份报文写进去了**。
同时确认:
- `point` 保持 4460 不变 —— 与「`userGetPointList` / `userLoginBonusList` 留空」的预期一致
- `lastLoginDate` = 登录时刻(01:28:01),`lastPlayDate` = 上报时刻(01:28:04),两者分离正确
- `charaSlot` 未被破坏
---
## 四、这次跑通依赖的关键实现点
1. **`number` 请求头**:先跑 `Ping()` 取得往返毫秒,之后所有请求带上(本次 47 ms)
2. **Cookie 复用**:全程同一个 `MaimaiNetClient`,`UserLoginApi` 的 `Set-Cookie: JSESSIONID` 自动带入后续请求
3. **userData 整表覆盖**:先抓 19 节点快照作基线,再按本局演算 +1 后整表回传
4. **差分节点只回传变化项**:`userMusicDetailList` 只回传 (143,4) 那条、`userMapList` 只回传 550003、
`userCharacterList` 只回传 5 只组队角色
5. **11 条位串与数组长度严格对齐**:`isNewFavoriteList="22212"` 等全部复刻真机
6. **`playSpecial` 用 `CalcSpecialNum=1024`**:与真机位模式一致
7. **`characterLevelN` 用游玩前等级**:playlog 3803/4458/137,userCharacterList 才是 3804/4459/138
---
## 五、复现方式
```bash
# 1) 预检(不发包,零副作用)
python upsert.py "<二维码>" --debug
# 2) 真实上传(★ 会把成绩写进服务器,不可撤销)
python upsert.py "<二维码>" --send
# 想跳过 60 秒冷却
python upsert.py "<二维码>" --send --cooldown 0
# 想跳过上传后的落库回查
python upsert.py "<二维码>" --send --no-verify
```
`--send` 会自动做落库回查:重新拉 `GetUserDataApi` + `GetUserMusicApi`,
对比 `playCount` 有没有 +1,并打印 `【落库成功】` 或 `【疑似未落库】`。
> ★ 建议先用 `--debug` 跑一次预检:DEBUG 日志会完整打印请求头、请求体、响应体,
> 便于逐字段核对。代价是日志文件会很大(本次 DEBUG 日志约 200 万行)。
---
## 六、账号状态变化(本次真实写入的内容)
| 项 | 上传前 | 上传后 |
|---|---|---|
| `userData.playCount` | 1260 | **1261** |
| `userData.currentPlayCount` | 188 | **189** |
| `userData.lastPlayDate` | 2026-10-05 22:32:21 | **2026-10-06 01:28:04** |
| `userMusicDetailList` (143, Re:Master) playCount | 10 | **11** |
| `userMapList` 550003 distance | 1457000 | **1468000**(+11000) |
| `userCharacterList` 5 只组队角色 | 3803/4458/137/137/137 | **3804/4459/138/138/138**(+1) |
| `userData.point` | 4460 | 4460(不变) |
写入的 `userPlaylogList[0]` 是 `musicId=143`(Re:Master)全 Miss 的失败局,
判定数 441/19/192/0/3,与 `config.PLAY_*` 一致。