docs: WaHappy 开发文档
孤儿分支,只含 docs/。内容包括: - 项目总览 / 主脚本编写指南 / 日志指南 - 上传成绩开发文档 / UpsertUserAll 踩坑清单(28 条) - 真机 Hook 对照报告 / 实跑验证记录 - 购买功能票流程(含被服务端拒绝的排查过程) · 扫码登录协议技术规范
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
# 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`),所以照样被拦。
|
||||
|
||||
```python
|
||||
# 修复前
|
||||
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 键):
|
||||
|
||||
```json
|
||||
// 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 仍全绿。
|
||||
|
||||
---
|
||||
|
||||
## 八、复现方式
|
||||
|
||||
```powershell
|
||||
# 1) 真实发送 + 落库验证(会写服务器,不可撤销)
|
||||
python upsert.py "<二维码>" --send
|
||||
|
||||
# 2) 零网络自检
|
||||
python selftest.py
|
||||
```
|
||||
Reference in New Issue
Block a user