11 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Provider Callback Log | 在 slot-pwa 按分库分表(对齐 GameResultLogModel)落地 game_provider_callback_log_{NN},并实现 POP 回调全链路(收包落库 → 验签/解析/处理状态)。其余厂商后续 PR 扩展。 |
|
false |
Provider 原始回调日志(02)实现计划
范围(已确认)
- 本期:分库分表 DDL + Shard Model/Service + POP 两条路由接入 + PHPUnit
- 后续 PR:gasea / quick / jsgame / bbgt / jdb
明确不做(属 03_provider_tx.md):game_provider_tx_01、wallet 改造、Round 聚合。
文档与 DDL:已与 02 §4.1、slot-pwa/db/game_provider_callback_log_shard.sql 对齐(无 gateway_version;含分库分表说明)。
现状与约束
| 项 | 现状 |
|---|---|
| POP 路由 | config/route.php /pop/Cash/Get、/pop/Cash/TransferInOut |
| POP 验签 | POPAuthMiddleware 验签在 Controller 前 |
| 分表参考(本期主参考) | app/model/shard/GameResultLogModel.php + CreateSharedTable |
| 非分表参考 | GameLaunchSessionModel 仅用于 Launch Session,不用于 callback_log |
| ID 生成 | IdGenerator::nextGameResultId() 同款 Redis 序号 + 毫秒时间戳 |
需求文档表名 game_provider_callback_log_01 表示逻辑表前缀;物理表为 user_transaction_{dbNN}.game_provider_callback_log_{tableNN}(与 game_result_log_{NN} 同库族)。
分库分表设计
路由规则(对齐 GameResultLog)
// GameResultLogModel
getPreDatabase() => 'user_transaction'
getPreTable() => 'game_provider_callback_log' // 物理表 game_provider_callback_log_01..NN
getShardKey() => 'uid'
- 构造:
new GameProviderCallbackLogModel($shardUid),由ShardServicecrc32($shardUid) % (库数×表数)定位库表。 - 写入:
insertData()(同TransactionLog::insertGameResult)。 - 更新:
updateByWhere(),条件必须含id+uid(分片键),例如['id' => $id, 'uid' => $shardUid]。
分片键 uid 的选取(收包时)
| 场景 | shardUid |
|---|---|
POP 已带 userid |
(int) post.userid |
| 尚未解析 / 验签失败但 body 无 uid | 0(需求允许 uid=0,仍落同一分片桶,保留审计) |
| 解析成功后 uid 与收包时不一致 | 不迁移分片;仅 UPDATE 修正行内 uid 等业务字段(行已在收包分片,避免跨库搬家) |
Request 上下文(Middleware 写入,Logic/Middleware 读取):
providerCallbackLogId— 雪花主键providerCallbackShardUid— 分片键,后续所有 UPDATE 必带
与 GameResultLog 的差异
| 项 | GameResultLog | callback_log |
|---|---|---|
汇总库 s_all.* |
有 app/model/all/GameResultLogModel + MQ 同步 |
本期不做(无跨分片报表需求;后续若要全局检索再加 DBSync) |
| 字段 | 精简结果字段 | 需求全文:raw_body/headers、验签/解析/处理状态等 |
处理顺序
sequenceDiagram
participant Provider
participant LogMW as ProviderCallbackLogMiddleware
participant PopAuth as POPAuthMiddleware
participant Logic as CashLogic
participant Shard as user_transaction_XX.game_provider_callback_log_YY
Provider->>LogMW: POST
LogMW->>LogMW: resolveShardUid from post
LogMW->>Shard: INSERT id=nextProviderCallbackLogId
LogMW->>PopAuth: request + logId + shardUid
PopAuth->>Shard: UPDATE verify_status
PopAuth->>Logic: handler
Logic->>Shard: UPDATE parse/process
1. 数据库(分表 DDL)
修改 CreateSharedTable
在 createShareTable() 循环内、createGameResultTable() 之后增加 createGameProviderCallbackLogTable($db, $num):
- 库:
user_transaction_01…user_transaction_{share_database_num} - 表:
game_provider_callback_log_01…game_provider_callback_log_{share_table_num} - 字段/索引以 02 §4.1 为准,除外:不建
gateway_version、不建idx_gateway_version;其余 KEY(idx_provider_tx、idx_round、idx_uid_received、idx_body_hash、idx_process_status)保留 - 主键
id非自增,应用写入IdGenerator::nextProviderCallbackLogId()
参考 SQL(文档用,非单库执行)
新增 slot-pwa/db/game_provider_callback_log_shard.sql:单分片模板 + 注释说明需通过 php webman createSharedTable(或项目既有命令)批量建表。
部署:docker exec ... php webman createSharedTable(与现有 user_transaction_log / game_result_log 相同流程)。
2. 配置
| 配置项 | 用途 |
|---|---|
callback_request_id_prefix |
如 creq_ |
route_callback_map |
路径 → 默认 callback_type |
provider_shard_uid_field |
厂商 → POST 分片字段(POP:userid) |
RedisKeyManagerService:PROVIDER_CALLBACK_LOG_NEXT_ID
IdGenerator:nextProviderCallbackLogId()(对齐 nextGameResultId)
3. 领域层
Shard Model — app/model/shard/GameProviderCallbackLogModel.php
继承 BaseShardModel:
protected function getPreDatabase(): string { return 'user_transaction'; }
protected function getPreTable(): string { return 'game_provider_callback_log'; }
protected function getShardKey(): string { return 'uid'; }
- 状态常量:
VERIFY_*/PARSE_*/PROCESS_* - 数据访问(均需构造时传入
$shardUid):insertReceived(GameProviderCallbackLogReceiveParams $params): void(内部insertData)updateVerifyStatus(int $id, ...)updateParseResult(int $id, GameProviderCallbackLogParseParams $params)updateProcessStatus(int $id, ...)findById(int $id): ?array(findOne(['id' => $id, 'uid' => $this->_shardVal]))
Value Objects(可放 app/model/game/ 或 app/entity/game/)
GameProviderCallbackLogReceiveParams— 含shardUid、落库字段GameProviderCallbackLogParseParams— 解析后业务字段
Service — app/service/game/ProviderCallbackLogService.php
recordReceivedFromRequest(Request $request): void— 解析shardUid、生成id/request_id、同步new GameProviderCallbackLogModel($shardUid)->insertReceived(...),设置$request->providerCallbackLogId/providerCallbackShardUidmarkVerify*(int $shardUid, int $id, ...)markParse*(int $shardUid, int $id, ...)markProcess*(int $shardUid, int $id, ...)
所有 mark 方法内部统一:new GameProviderCallbackLogModel($shardUid)->updateByWhere(['id' => $id, 'uid' => $shardUid], ...)
4. Middleware(POP 本期)
- 新增
ProviderCallbackLogMiddleware— 最先执行 config/middleware.php'pop' => [ProviderCallbackLogMiddleware, POPAuthMiddleware]POPAuthMiddleware:验签结果写回 callback_log;失败VERIFY_FAILED+PROCESS_REJECTED/pop/admin/跳过落库
5. POP Logic
- 从
$request读取providerCallbackLogId+providerCallbackShardUid balance/modifyTransferInOut:parse/process 状态;重复 tid →PROCESS_DUPLICATE- Validate 失败:
PARSE_FAILED+PROCESS_REJECTED(保留已落库的 raw 证据)
6. 测试
参照分表写入方式(非 LaunchSession 单表):
tests/Support/ProviderCallbackLogTestCase.php— 固定测试shardUid,insertReceived+ teardowndeletetests/Feature/GameProviderCallbackLogShardModelTest.php- 初始
verify_status=0、process_status=0 updateByWhere后终态、processed_at写入- 同
body_hash、同/不同shardUid可各插一条
- 初始
docker exec -w /app/www/slot/slot-pwa php82 ./vendor/bin/phpunit
7. 验收对照(POP + 分表)
| 验收项 | 实现 |
|---|---|
| 原始请求可追溯 | raw_body / raw_headers 写入对应分片表 |
| 验签/解析失败仍有行 | Middleware 先 INSERT(shardUid 可为 0) |
| BALANCE 仅 callback_log | 无 provider_tx |
| 重复回调多条 log | 每次 INSERT;tid 重复 → process_status=3 |
| 分库分表 | 与 GameResultLog 相同 user_transaction_* 族 |
8. 后续 PR
- 其它厂商 Middleware +
provider_shard_uid_field - 可选:
app/model/all/GameProviderCallbackLogModel+ DBSync(仅当需要s_all汇总检索时)
关键文件
| 操作 | 路径 |
|---|---|
| 修改 | app/command/CreateSharedTable.php |
| 新增 | db/game_provider_callback_log_shard.sql(模板) |
| 新增 | app/model/shard/GameProviderCallbackLogModel.php |
| 新增 | app/model/game/GameProviderCallbackLog*Params.php |
| 新增 | app/service/game/ProviderCallbackLogService.php |
| 新增 | app/middleware/ProviderCallbackLogMiddleware.php |
| 修改 | IdGenerator.php, RedisKeyManagerService.php, middleware.php, POPAuthMiddleware.php |
| 修改 | app/pop/logic/CashLogic.php, app/pop/controller/CashController.php |
| 新增 | tests/Feature/GameProviderCallbackLogShardModelTest.php |
完成编码后执行 ~/.cursor/hooks/verify-slot-backend.sh 并粘贴输出。