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

5.2 KiB
Raw Blame History

实跑验证记录(真实发包 + 落库确认)

本文记录本工程第一次真实跑通「扫码登录 → 取数 → 上传成绩 → 落库回查 → 登出」的完整证据。 执行方式: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 回读,服务器返回:

{
  "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

五、复现方式

# 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_* 一致。