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

5.7 KiB
Raw Blame History

UpsertUserAll 落库排查报告

背景:python upsert.py "<二维码>" --send 修复闸门后能真实发包, 服务端返回 returnCode=1,但数据未写入服务器。 本文档记录已完成的全部排查与结论。


一、结论速览

项 状态
闸门放行 已修复,returnCode=1
报文与真机对齐 非预期差异归零
数据落库 未落库(point/playCount 回查不变)
根因 未确定,已排除 6 类可能

二、已修复的 5 个 Bug

Bug 1:upsert.py 漏传 confirm(你遇到的直接原因)

upsert.py 原来只传 dry_run=not opts["send"],但 UpsertUserAll 的规则是 confirm=None 时取 config.UPSERT_CONFIRM(默认 False),所以照样被拦。

# 修复前
dry_run=not opts["send"],
# 修复后
dry_run=not opts["send"],
confirm=bool(opts["send"]),      # 关键

Bug 2:userExtend 缺 2 个字段

Get 侧 Upsert 侧
键数 14 16
差 — isPhotoAgree、isGotoCodeRead

这两个字段只在 Upsert 侧出现,必须补上。

Bug 3:userRating.udemae 有 7 个冗余大写字段

服务器 Get 响应里 npcWinNum 与 NpcWinNum 并存(23 键):

// Get 侧(source/2026-10-05.log:87)
{ "npcWinNum": 0, ..., "NpcWinNum": 0, "NpcLoseNum": 0, ... }   // 23 键
// Upsert 侧(source/2026-10-05.log:123)
{ "npcWinNum": 0, ... }                                          // 16 键

源码 Net.VO.Mai2/UserUdemae.cs 只声明了小写的 16 个,所以大写那批是 服务端冗余输出,回传时必须裁掉。修复前请求体比真机大 119 字节。

Bug 4:userCharacterList 排序错

顺序
charaSlot [395, 101, 400402, 400401, 200401]
真机 userCharacterList [101, 395, 200401, 400401, 400402](按 characterId 升序)

回传时按 characterId 升序,不是按 charaSlot 顺序。

Bug 5:扫码用的 chipID 形式错

用途 正确值 源码依据
aimedb 扫码 XXXX-XXXXXXXXXXX(完整) ChimeReaderManager.cs:250 KeychipId.Value
游戏服务器 TESTCLIENT1(短值) PacketGetGameSetting.cs:23 KeychipId.ShortValue

我原来扫码也用了短值。已按源码修正为完整形式。


三、报文对齐最终状态

顶层键        : 7/7
36 节点形态   : 36/36
userData      : 82/82 键
userPlaylog   : 110/110 键
userExtend    : 16/16 键
udemae        : 16/16 键
非预期差异    : 0
请求体大小    : 17158 vs 17161 字节(差 3 字节,来自数值位数)

深度递归对比(逐字段、逐元素)后,除「不同次登录必然不同」的值外,零差异。


四、已排除的可能(6 类)

# 假设 验证方式 结果
1 时序问题(写入有延迟) 发送后 T+0 / T+20s / T+45s 三次回查 都不变
2 差分节点为空导致「看不出变化」 直接改绝对值(point 4110→4111) 仍不写
3 字段缺失/多余 深度递归键集合 + 值对比 已归零
4 number 请求头缺失 补上 Packet.cs:170 要求的头 仍不写
5 playSpecial 值不对 查源码 CalcSpecialNum = 硬件标志位 非根因
6 缺少 GetUserNewItemList 前置 完整复刻真机 121→123 流程 仍不写

关于第 2 项的探测细节

这是最关键的一次探测 —— 直接 patch 把 userData.point 从 4110 改成 4111 发上去:

baseline point = 4110
上传成绩成功:returnCode=1
T+ 0s  point = 4110
T+20s  point = 4110
T+45s  point = 4110

连最简单的整数字段都不写,说明不是「数据不对」而是「整包没被接受」。


五、当前判断

服务端收下了请求(返回正确的 apiName),但没有应用。

可能的方向(未验证):

  1. 账号/机台绑定校验 —— token 可能绑定了特定 keychip,而 XXXX-XXXXXXXXXXX 只是文档里的示例值,不是你实际机台的 keychip。 若服务端校验「token 对应的机台 ≠ 请求里的 chipID」,就会静默拒收。

  2. 需要真实游玩产生的会话状态 —— 真机的 Upsert 发生在真实游玩之后, 服务端可能记录了「本次 loginId 下有一局真实游玩」才允许结算。 纯网络脚本没有这个前置状态。

  3. 服务端侧的风控/审计 —— 真机与脚本的 TLS 指纹、请求节奏等特征不同。


六、需要你提供的信息

要继续定位,最有价值的是:

# 信息 为什么关键
1 你机台的真实 Keychip ID 若 XXXX-XXXXXXXXXXX 不是你的机台,方向 1 就成立
2 该机台在 keychip.csv 里的对应行 可确认 keychip 与店铺/区域的绑定关系
3 真机跑一局后立刻导出 Upsert 报文 可拿同账号同机台的最新报文做 1:1 对比

source/keychip.csv 有 229KB 全量机台对照表,如果你能告诉我机台所在的 店铺 ID,我可以从中查出正确的 keychip。


七、本次排查的正面成果

即使尚未落库,这次排查也修掉了 5 个真实 Bug,其中 3 个(userExtend 缺字段、 udemae 冗余字段、userCharacterList 排序)是只有做深度值对比才能发现的:

  • 修复前:报文与真机有 11 处非预期差异
  • 修复后:0 处

自检 129/129 仍全绿。


八、复现方式

# 1) 真实发送 + 落库验证(会写服务器,不可撤销)
python upsert.py "<二维码>" --send

# 2) 零网络自检
python selftest.py