8.3 KiB
8.3 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 实现 Launch Session | 在 slot-pwa 中实现子需求 01:基于现有 GameLogic.launch 链路增加 game_launch_session 落库、Redis 短窗口防重复、Session 关闭接口与过期定时任务;本期跳过 game_gateway_control / 风险画像检查。 |
|
false |
实现子需求 01:Launch Session(slot-pwa)
现状
- 需求文档:01_launch_session.md
- 服务仓库:
slot-pwa(PRD 中的 slot-pwa) - 已有:
GameController::launch→GameLogic::launch→ 查g_game/g_platform→ 调 Providerlaunch()返回 URL - 缺失:
game_launch_session表、Model、Session 创建/关闭/过期、Redis 去重、POST /slot-game/session/close路由 - 本期不做(你已确认):
game_gateway_control、game_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. 数据库迁移
新增 SQL:slot-pwa/db/game_launch_session.sql
- DDL 与需求文档 逐字一致(
game_launch_session全字段 + 索引) - 部署:在 Docker MySQL 中执行(
goMysql/ 项目mysql连接)
2. Model 层
新建 slot-pwa/app/model/game/GameLaunchSessionModel.php
connection = mysql,table = game_launch_session- 状态常量:
STATUS_ACTIVE=1…STATUS_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 去重 | Key:game:launch:dedup:{uid}:{provider_code}:{game_code},TTL 8s(需求 5~10s 取中值);Value:session_id |
| 命中去重 | 查 DB 中 status=1 且未过期的 Session;若存在且 Provider 允许,直接返回已有 session_id + 不再请求 Provider(避免重复 LaunchURL);若 Session 无效则走新启动 |
| 创建 Session | 生成 session_id、launch_request_id(bin2hex(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=2,closed_at=now;不调 wallet / round / 盈利统计 |
- 薄封装:委托
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 require;uid 从 Auth 中间件 / 请求体(与现有 launch 一致用 body uid) |
| DTO | GameLaunchSessionCloseDTO |
错误处理:将 RuntimeException 逐步改为 Webman\Exception\BusinessException + 明确错误码(与项目 exception handler 对齐);关键日志带 uid、game_code、session_id。
5. Redis Key 管理
在 ShareRedisKeyManagerService 或 RedisKeyManagerService 增加:
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.php 或 params.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.php(TTL 配置) |
测试建议(实现后)
# 建表
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
手工:连续两次 launch(8s 内)应复用 session;POST /slot-game/session/close 后 DB status=2。
完成门禁
实现 PHP 后执行 ~/.cursor/hooks/verify-slot-backend.sh,最终回复粘贴完整输出。