Files
cursor/plans/实现_launch_session_a28d3479.plan.md
ray zhou 1bcb6120dd ok
2026-05-29 17:23:17 +08:00

8.3 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
实现 Launch Session 在 slot-pwa 中实现子需求 01基于现有 GameLogic.launch 链路增加 game_launch_session 落库、Redis 短窗口防重复、Session 关闭接口与过期定时任务;本期跳过 game_gateway_control / 风险画像检查。
id content status
ddl-launch-session 新增 slot-pwa/db/game_launch_session.sqlDDL 与需求一致) completed
id content status
model-launch-session 新增 GameLaunchSessionModel + 状态常量与查询/更新方法 completed
id content status
logic-launch-session 新增 GameLaunchSessionLogicRedis 去重、创建/关闭 Session、编排 Provider launch completed
id content status
api-routes 改造 GameController.launch 响应;新增 close 接口与 route/Validate/DTO completed
id content status
expire-command 新增 game:launch-session:expire 命令 + game.php TTL 配置 completed
id content status
refactor-game-logic GameLogic 委托新 LogicIdGenerator/RedisKey 扩展;跑 verify 脚本 completed
false

实现子需求 01Launch Sessionslot-pwa

现状

  • 需求文档:01_launch_session.md
  • 服务仓库:slot-pwaPRD 中的 slot-pwa
  • 已有GameController::launchGameLogic::launch → 查 g_game / g_platform → 调 Provider launch() 返回 URL
  • 缺失game_launch_session 表、Model、Session 创建/关闭/过期、Redis 去重、POST /slot-game/session/close 路由
  • 本期不做(你已确认):game_gateway_controlgame_user_risk_profile_01 准入检查
sequenceDiagram
  participant Client
  participant GameController
  participant GameLaunchSessionLogic
  participant Redis
  participant DB as game_launch_session
  participant Provider

  Client->>GameController: launch(gameCode)
  GameController->>GameLaunchSessionLogic: launchWithSession
  GameLaunchSessionLogic->>Redis: 短窗口去重
  alt 命中有效 Session
    GameLaunchSessionLogic-->>Client: 复用 launch_url 或重新拉 URL 策略见下
  else 新启动
    GameLaunchSessionLogic->>DB: INSERT status=1
    GameLaunchSessionLogic->>Provider: launch
    GameLaunchSessionLogic->>DB: 更新 launch_url_hash
    GameLaunchSessionLogic-->>Client: url + session_id
  end

  Client->>GameController: session/close(session_id)
  GameController->>GameLaunchSessionLogic: closeSession
  GameLaunchSessionLogic->>DB: status=2, closed_at

实现策略

1. 数据库迁移

新增 SQLslot-pwa/db/game_launch_session.sql

  • DDL 与需求文档 逐字一致game_launch_session 全字段 + 索引)
  • 部署:在 Docker MySQL 中执行(goMysql / 项目 mysql 连接)

2. Model 层

新建 slot-pwa/app/model/game/GameLaunchSessionModel.php

  • connection = mysqltable = game_launch_session
  • 状态常量:STATUS_ACTIVE=1STATUS_RISK_BLOCKED=6(与 PRD 一致)
  • 查询方法(业务语义命名):
    • findActiveBySessionId(string $sessionId)
    • findLatestActiveByUidProviderGame(int $uid, string $providerCode, string $gameCode)Redis 未命中时兜底)
    • markClosed(int $id): void / markExpiredBatch(): int(供定时任务)

3. Logic 层(核心)

新建 slot-pwa/app/api/logic/GameLaunchSessionLogic.php,从 GameLogic 抽离并编排:

步骤 说明
解析游戏 复用现有 GGameModel / GGamePlatformMappingModel / GPlatformModel 校验;provider_code = GPlatformModel.code
Redis 去重 Keygame:launch:dedup:{uid}:{provider_code}:{game_code}TTL 8s(需求 5~10s 取中值Valuesession_id
命中去重 查 DB 中 status=1 且未过期的 Session若存在且 Provider 允许,直接返回已有 session_id + 不再请求 Provider避免重复 LaunchURL若 Session 无效则走新启动
创建 Session 生成 session_idlaunch_request_idbin2hex(random_bytes(16)) 或类似 UUID 风格);id 复用 IdGenerator 模式新增 nextLaunchSessionId()
调 Provider 调用现有 BaseGameService::getGameService()->launch()
写回 launch_url_hash = hash('sha256', $url);失败时 status=4 + remark
关闭 closeSession(uid, session_id):校验归属 → status=2closed_at=now调 wallet / round / 盈利统计

重构 GameLogic::launch

  • 薄封装:委托 GameLaunchSessionLogic::launchWithSession(GameLaunchDTO),保持 game:launch 命令兼容

4. API 层

改造 Launch 响应GameController::launch

// 返回扩展示例
['url' => $url, 'session_id' => $sessionId, 'launch_request_id' => $launchRequestId]

新增关闭接口

内容
路由 POST /slot-game/session/close(写入 config/route.php
Controller 新建 GameLaunchSessionController 或扩展现有 GameController
Validate session_id requireuid 从 Auth 中间件 / 请求体(与现有 launch 一致用 body uid
DTO GameLaunchSessionCloseDTO

错误处理:将 RuntimeException 逐步改为 Webman\Exception\BusinessException + 明确错误码(与项目 exception handler 对齐);关键日志带 uidgame_codesession_id

5. Redis Key 管理

ShareRedisKeyManagerServiceRedisKeyManagerService 增加:

getGameLaunchDedupKey(int $uid, string $providerCode, string $gameCode): string

6. Session 过期任务

新建 Console 命令 slot-pwa/app/command/GameLaunchSessionExpire.php

  • 名称:game:launch-session:expire
  • SQL 逻辑同需求文档:status=1 AND expired_at < NOW(3)status=3
  • 可由 crontab / 运维定时 docker exec ... php webman game:launch-session:expire

配置项(config/game.phpparams.php

  • launch_session_ttl_seconds(默认如 7200写入 expired_at
  • launch_dedup_ttl_seconds(默认 8

7. 验收对照(子需求 §6

验收项 实现方式
session_id / launch_request_id 唯一 DB UNIQUE + 生成逻辑
Redis 短窗口不重复 LaunchURL dedup key + 复用 active session
退出 status=2无资金副作用 close 仅 UPDATE session
过期 status=3 定时命令

文件清单(新增/修改)

操作 路径
新增 slot-pwa/db/game_launch_session.sql
新增 slot-pwa/app/model/game/GameLaunchSessionModel.php
新增 slot-pwa/app/api/logic/GameLaunchSessionLogic.php
新增 slot-pwa/app/api/controller/GameLaunchSessionController.php(或扩展 GameController
新增 slot-pwa/app/api/dto/GameLaunchSessionCloseDTO.php
新增 slot-pwa/app/api/validate/GameLaunchSessionValidate.php
新增 slot-pwa/app/command/GameLaunchSessionExpire.php
修改 slot-pwa/app/api/logic/GameLogic.php
修改 slot-pwa/app/api/controller/GameController.php
修改 slot-pwa/config/route.php
修改 slot-pwa/app/service/IdGenerator.php+ RedisKey 常量)
修改 slot-pwa/config/game.phpTTL 配置)

测试建议(实现后)

# 建表
docker exec -i goMysql mysql -uroot -p<pwd> <db> < slot-pwa/db/game_launch_session.sql

# Launch
docker exec -w /app/www/slot/slot-pwa php82 php webman game:launch <uid> -c <gameCode>

# 过期
docker exec -w /app/www/slot/slot-pwa php82 php webman game:launch-session:expire

手工:连续两次 launch8s 内)应复用 sessionPOST /slot-game/session/close 后 DB status=2

完成门禁

实现 PHP 后执行 ~/.cursor/hooks/verify-slot-backend.sh,最终回复粘贴完整输出。