# 实跑验证记录(真实发包 + 落库确认) > 本文记录本工程**第一次真实跑通**「扫码登录 → 取数 → 上传成绩 → 落库回查 → 登出」的完整证据。 > 执行方式:`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_*` 一致。