diff --git a/docs/technical/BARK_BATTLE_BACKEND_DDD_TECHNICAL_PLAN_2026-05-11.md b/docs/technical/BARK_BATTLE_BACKEND_DDD_TECHNICAL_PLAN_2026-05-11.md new file mode 100644 index 00000000..b1933036 --- /dev/null +++ b/docs/technical/BARK_BATTLE_BACKEND_DDD_TECHNICAL_PLAN_2026-05-11.md @@ -0,0 +1,799 @@ +# bark-battle 后端 DDD 技术方案(2026-05-11) + +## 1. 背景、范围与非目标 + +### 1.1 背景 + +`bark-battle` / “汪汪声浪大作战”是一个浏览器 2D 声控狗叫对战玩法。玩家通过麦克风发出狗叫声,浏览器 runtime 根据音量峰值、有效叫声次数与节奏推动顶部红蓝能量条;每局默认 30 秒;结束后按能量条偏向判定胜负或平局。 + +现有前端方案 `docs/technical/BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md` 已覆盖 Phaser / TypeScript / Vite / Web Audio / DOM HUD 的 runtime 落地方式,并明确不覆盖后端表结构、成绩持久化、作品发布、广场接入与实时多人协议。因此需要单独补充后端 DDD 技术方案,避免前端 runtime 在接入平台作品、正式游玩埋点、成绩、排行榜和发布闭环时承接不属于表现层的业务真相。 + +本方案遵循当前 Genarrative 后端路线:`server-rs` + Axum + SpacetimeDB。DDD 边界保持为: + +- `module-*`:纯领域规则与 use case 约束。 +- `spacetime-module`:SpacetimeDB 表、reducer、migration。 +- `spacetime-client`:SpacetimeDB 绑定调用 facade。 +- `api-server`:HTTP / SSE / BFF 门面与权限校验入口。 +- `shared-contracts`:前后端共享 DTO、请求、响应、错误码与 schema 约束。 +- `platform-*`:作品、发布、广场、埋点等平台能力的领域边界。 + +### 1.2 本文范围 + +本文覆盖后端方案,不实现代码: + +1. bark-battle 玩法接入级别建议。 +2. 后端 DDD 分层与职责边界。 +3. shared contracts 草案。 +4. SpacetimeDB 数据模型草案。 +5. HTTP API / facade 草案。 +6. SpacetimeDB migration 与绑定生成策略。 +7. 安全、隐私与反作弊约束。 +8. TDD / 验收顺序与可执行命令。 +9. 与现有前端方案和 BDD 文档的关系。 + +### 1.3 非目标 + +MVP 明确不做: + +- 不保存原始麦克风音频、音频片段、可还原语音内容的 waveform 或频谱明细。 +- 不做实时多人在线对战;首版只支持本地 runtime + 后端记录派生结果。 +- 不做复杂 AI 声纹识别、狗叫语义识别、身份声纹比对或真人/动物声纹分类。 +- 不由前端直接写正式成绩、排行榜或作品发布状态。 +- 不把 Phaser / Web Audio / DOM HUD 逻辑迁入后端。 +- 不在本方案中实现代码、建表或生成绑定。 + +## 2. 玩法接入级别建议 + +### 2.1 推荐首版闭环 + +建议先支持“本地 runtime + 可发布配置化作品 + 单局结果记录 / 可选排行榜”的闭环: + +1. 创作者创建 bark-battle 草稿,配置标题、描述、狗狗主题、背景、难度、单局时长、音量阈值、AI 对手参数和排行榜开关。 +2. 发布为稳定作品 ID,`playTypeId = "bark-battle"`。 +3. 玩家从作品页或广场进入 runtime,前端获取发布态 runtime config。 +4. 玩家授权麦克风后在本地完成 30 秒声控对战。 +5. 前端提交单局 finish 请求,只上传派生指标,例如峰值、有效叫声次数、节奏命中、最终能量、客户端结果摘要等。 +6. 后端校验 work、config version、run token、时长、分数范围和权限后,生成服务端认可的 run result / score summary。 +7. 若作品开启排行榜,则写入可投影的 leaderboard 记录。 +8. 正式作品级游玩埋点统一写 `work_play_start`,其中 `scope_kind=work`,`scope_id=稳定作品 ID`,metadata 包含 `playType`、`workId`、`sourceRoute`、`userId`。 + +### 2.2 后续增强路径 + +后续再考虑多人实时: + +- Phase 2:排行榜、挑战分享、个人历史成绩、作品统计面板。 +- Phase 3:异步影子对手 / ghost replay,但仍不保存原始音频,只保存低维派生曲线或聚合指标。 +- Phase 4:实时多人对战协议,需要独立同步模型、房间服务、延迟补偿、断线恢复与更严格反作弊;不应混入 MVP。 + +## 3. DDD 分层设计 + +### 3.1 总体分层 + +```text +frontend/runtime + -> api-server HTTP BFF + -> shared-contracts DTO + -> spacetime-client facade + -> generated SpacetimeDB bindings + -> spacetime-module reducers / tables / migration.rs + -> module-bark-battle pure domain + -> platform-work / platform-publish / platform-tracking / platform-leaderboard +``` + +### 3.2 `module-bark-battle` 职责 + +建议新增 `server-rs/crates/module-bark-battle/`,只放纯领域规则,不依赖 Axum、SpacetimeDB SDK、HTTP、数据库绑定或前端类型。 + +职责: + +- 校验 bark-battle 配置合法性。 +- 定义配置版本兼容规则。 +- 计算提交结果的派生分数区间与胜负判定是否自洽。 +- 计算 `ScoreSummary`、排行榜排序分数、统计指标。 +- 定义反作弊基础规则:时长范围、有效叫声次数上限、峰值范围、能量范围、提交窗口、run 状态机。 + +不职责: + +- 不访问数据库。 +- 不处理 HTTP 请求。 +- 不生成 SpacetimeDB 表。 +- 不处理麦克风音频采样。 + +### 3.3 `shared-contracts` 职责 + +建议新增 `server-rs/crates/shared-contracts/src/bark_battle.rs`,并在前端共享契约生成流程中对齐同名 DTO。 + +职责: + +- 定义草稿、发布态 runtime config、work summary、run start、run finish、run result、score summary、leaderboard DTO。 +- 定义 request / response / error code。 +- 保持字段命名、枚举值、时间单位和数值范围稳定。 + +约束: + +- DTO 可以表达业务数据,但不承载领域算法。 +- 前端不得手写与后端不一致的正式契约;如果存在 TypeScript mirror,需要由契约生成或测试保证一致。 + +### 3.4 `spacetime-module` 职责 + +建议在现有 SpacetimeDB 模块边界内新增 bark-battle 表与 reducer,或按当前仓库约定新增独立模块文件。职责: + +- 定义表:作品配置、发布版本、runtime run、score/stat、leaderboard entry。 +- 定义 reducer / procedure:保存草稿、发布版本、开始 run、结束 run、写排行榜、查询投影所需索引。 +- 维护 migration。 + +必须明确: + +- 所有表结构变更进入 `migration.rs`。 +- SpacetimeDB 绑定通过既有生成命令生成。 +- 不手改生成物,不手改 generated bindings。 + +### 3.5 `spacetime-client` 职责 + +职责: + +- 封装 generated bindings,向 `api-server` 提供稳定 facade。 +- 隐藏 reducer 名称、表订阅细节和绑定类型差异。 +- 将 SpacetimeDB 错误转换为后端内部错误模型。 + +不职责: + +- 不放业务规则主逻辑。 +- 不绕过 `module-bark-battle` 做胜负和分数裁决。 + +### 3.6 `api-server` 职责 + +Axum HTTP / BFF 门面职责: + +- 鉴权、用户上下文、work 权限、发布态读取权限。 +- 解析请求、调用 domain 校验、调用 spacetime-client facade。 +- 将内部错误映射成 HTTP status + shared error code。 +- 负责正式作品级游玩埋点入口,统一写 `work_play_start`。 +- 为前端 runtime 提供一次性 start token / run id,避免匿名 finish 直接刷榜。 + +### 3.7 `platform-*` 职责 + +- `platform-work`:稳定作品 ID、作品所有权、作品摘要、作品状态。 +- `platform-publish`:草稿到发布态版本、config version、发布可见性。 +- `platform-tracking`:统一埋点,尤其 `work_play_start`。 +- `platform-leaderboard`:若已有通用排行榜能力,bark-battle 只提供 score projection,不重复建设平台级排名系统。 + +### 3.8 `frontend/runtime` 职责边界 + +前端只做: + +- Phaser / DOM HUD 表现。 +- Web Audio 采样、环境噪声校准和本地即时反馈。 +- 本地临时 UI 状态:权限、倒计时、动画、结算展示。 +- 调用 start / finish / leaderboard API。 + +前端不得承接正式业务真相: + +- 不直接决定正式排行榜结果。 +- 不直接写作品发布状态。 +- 不绕过后端写成绩。 +- 不上传原始麦克风音频。 + +## 4. shared contracts 设计草案 + +以下为字段草案,具体 Rust / TypeScript 命名按仓库契约规范落地。 + +### 4.1 BarkBattleDraft + +```text +BarkBattleDraft { + draftId: string + ownerUserId: string + playTypeId: "bark-battle" + title: string + description?: string + theme: BarkBattleTheme + runtimeConfig: BarkBattleRuntimeConfigDraft + leaderboardEnabled: boolean + visibility: "private" | "unlisted" | "public" + createdAt: string + updatedAt: string +} +``` + +### 4.2 BarkBattleRuntimeConfig + +```text +BarkBattleRuntimeConfig { + workId: string + configVersion: number + playTypeId: "bark-battle" + durationMs: number // MVP 默认 30000 + energyMin: number // 默认 -100 + energyMax: number // 默认 100 + winEnergyThreshold: number // 可选:低于阈值可平局 + barkThreshold: number // 归一化 0..1 + peakWeight: number + barkCountWeight: number + rhythmWeight: number + opponent: { + difficulty: "easy" | "normal" | "hard" + basePower: number + variance: number + } + theme: { + playerDogSkin: string + opponentDogSkin: string + stageId: string + soundPackId?: string + } + leaderboardEnabled: boolean + publishedAt: string +} +``` + +### 4.3 WorkSummary + +```text +BarkBattleWorkSummary { + workId: string + playTypeId: "bark-battle" + title: string + description?: string + coverAssetId?: string + authorUserId: string + authorDisplayName?: string + configVersion: number + leaderboardEnabled: boolean + totalPlayCount: number + publishedAt: string + updatedAt: string +} +``` + +### 4.4 RunStart + +```text +BarkBattleRunStartRequest { + workId: string + configVersion: number + sourceRoute?: string + clientRuntimeVersion?: string +} + +BarkBattleRunStartResponse { + runId: string + runToken: string + workId: string + configVersion: number + serverStartedAt: string + expiresAt: string + runtimeConfig: BarkBattleRuntimeConfig +} +``` + +### 4.5 RunFinish + +```text +BarkBattleRunFinishRequest { + runId: string + runToken: string + workId: string + configVersion: number + clientStartedAt?: string + clientFinishedAt?: string + elapsedMs: number + finalEnergy: number + clientWinner: "player" | "opponent" | "draw" + metrics: BarkBattleDerivedMetrics + clientRuntimeVersion?: string +} + +BarkBattleDerivedMetrics { + peakVolumeMax: number // 0..1 + peakVolumeAvg: number // 0..1 + validBarkCount: number + rhythmHitCount: number + longestCombo: number + sampleWindowCount?: number + calibrationNoiseFloor?: number // 0..1 +} +``` + +### 4.6 RunResult + +```text +BarkBattleRunResult { + runId: string + workId: string + userId?: string + configVersion: number + accepted: boolean + serverWinner: "player" | "opponent" | "draw" + finalEnergy: number + score: number + scoreSummary: BarkBattleScoreSummary + leaderboardEntry?: BarkBattleLeaderboardEntry + antiCheatFlags: string[] + finishedAt: string +} +``` + +### 4.7 ScoreSummary + +```text +BarkBattleScoreSummary { + score: number + grade: "S" | "A" | "B" | "C" | "D" + finalEnergy: number + winMargin: number + validBarkCount: number + peakVolumeMax: number + rhythmHitCount: number + longestCombo: number + elapsedMs: number +} +``` + +### 4.8 Leaderboard 可选类型 + +```text +BarkBattleLeaderboardQuery { + workId: string + configVersion?: number + period: "all" | "daily" | "weekly" + limit: number + cursor?: string +} + +BarkBattleLeaderboardEntry { + rank?: number + runId: string + workId: string + userId?: string + displayName?: string + score: number + scoreSummary: BarkBattleScoreSummary + createdAt: string +} + +BarkBattleLeaderboardResponse { + workId: string + entries: BarkBattleLeaderboardEntry[] + viewerBest?: BarkBattleLeaderboardEntry + nextCursor?: string +} +``` + +## 5. 数据模型草案 + +### 5.1 作品配置表 + +建议表:`bark_battle_work_config`。 + +字段草案: + +- `work_id`:稳定作品 ID,关联平台作品。 +- `draft_id`:草稿 ID,可选。 +- `owner_user_id`:创作者。 +- `play_type_id`:固定 `bark-battle`。 +- `config_version`:发布配置版本。 +- `title`、`description`、`cover_asset_id`。 +- `runtime_config_json`:发布态 runtime 配置 JSON;字段需由 shared contracts 校验。 +- `leaderboard_enabled`。 +- `status`:`draft` / `published` / `archived`。 +- `created_at`、`updated_at`、`published_at`。 + +约束: + +- 同一 `work_id + config_version` 不可变;新发布生成新版本。 +- runtime 请求只读发布态配置。 + +### 5.2 runtime run 表 + +建议表:`bark_battle_runtime_run`。 + +字段草案: + +- `run_id`。 +- `run_token_hash`:保存 token hash,不保存明文 token。 +- `work_id`。 +- `config_version`。 +- `user_id`:匿名时可空或使用匿名会话 ID。 +- `source_route`。 +- `status`:`started` / `finished` / `rejected` / `expired`。 +- `server_started_at`、`server_finished_at`、`expires_at`。 +- `client_elapsed_ms`。 +- `final_energy`。 +- `client_winner`、`server_winner`。 +- `anti_cheat_flags_json`。 +- `client_runtime_version`。 + +### 5.3 score / stat 表 + +建议表:`bark_battle_score_record`。 + +字段草案: + +- `score_id`。 +- `run_id`。 +- `work_id`。 +- `config_version`。 +- `user_id`。 +- `score`。 +- `grade`。 +- `final_energy`。 +- `valid_bark_count`。 +- `peak_volume_max`。 +- `peak_volume_avg`。 +- `rhythm_hit_count`。 +- `longest_combo`。 +- `elapsed_ms`。 +- `metrics_json`:只保存派生聚合指标。 +- `created_at`。 + +明确禁止: + +- 不保存原始麦克风音频。 +- 不保存可还原语音的 PCM、Opus、MP3、WAV、base64 音频、逐帧 waveform。 +- 不保存高精度声纹向量。 + +### 5.4 leaderboard 表 + +若平台已有通用排行榜,优先复用平台 leaderboard 投影;否则可新增 `bark_battle_leaderboard_entry`: + +- `entry_id`。 +- `work_id`。 +- `config_version`。 +- `run_id`。 +- `user_id`。 +- `score`。 +- `tie_breaker_energy`。 +- `tie_breaker_elapsed_ms`。 +- `created_at`。 + +排序建议: + +1. `score` 降序。 +2. `final_energy` / `winMargin` 降序。 +3. `elapsed_ms` 更接近配置时长者优先,避免异常短局刷分。 +4. `created_at` 升序或按平台既有规则。 + +## 6. API 草案 + +路径仅为草案,落地时按 `api-server` 当前路由命名规范调整。 + +### 6.1 创建 / 保存草稿 + +```text +POST /api/bark-battle/drafts +PUT /api/bark-battle/drafts/{draftId} +GET /api/bark-battle/drafts/{draftId} +``` + +职责: + +- 仅创作者可创建和保存。 +- 校验 `playTypeId = bark-battle`。 +- 调用 `module-bark-battle` 校验 runtime config 范围。 + +### 6.2 发布 / 获取作品 + +```text +POST /api/bark-battle/drafts/{draftId}/publish +GET /api/works/{workId}/bark-battle +GET /api/bark-battle/works/{workId}/runtime-config +``` + +职责: + +- 发布生成稳定 `workId` 和递增 `configVersion`。 +- 获取作品只返回发布态配置与展示摘要。 +- 未发布或无权限作品返回明确错误。 + +### 6.3 start runtime + +```text +POST /api/bark-battle/runs/start +``` + +请求:`BarkBattleRunStartRequest`。响应:`BarkBattleRunStartResponse`。 + +职责: + +- 校验作品存在、发布态、可游玩。 +- 校验 config version,必要时返回最新版本。 +- 创建 `run_id` 与一次性 `run_token`。 +- 写正式作品级游玩埋点:`work_play_start`。 + +埋点要求: + +```text +event_key: work_play_start +scope_kind: work +scope_id: <稳定作品 ID> +metadata: { + playType: "bark-battle", + workId: "", + sourceRoute: "", + userId: "" +} +``` + +### 6.4 finish runtime + +```text +POST /api/bark-battle/runs/{runId}/finish +``` + +请求:`BarkBattleRunFinishRequest`。响应:`BarkBattleRunResult`。 + +职责: + +- 校验 run token。 +- 校验 run 仍处于 `started` 且未过期。 +- 校验 `work_id + config_version` 与 start 时一致。 +- 校验时长、finalEnergy、metrics 范围。 +- 使用 `module-bark-battle` 生成服务端认可的 `serverWinner`、`score`、`ScoreSummary`、`antiCheatFlags`。 +- 写 `bark_battle_runtime_run` finish 状态与 `bark_battle_score_record`。 +- 如开启 leaderboard 且结果 accepted,写排行榜。 + +### 6.5 作品级游玩埋点 + +`start runtime` 内部必须触发统一埋点;不建议前端单独调用一个 bark-battle 专用埋点 API。若平台已有通用 tracking API,则 api-server 内部调用 platform tracking facade: + +```text +track_event( + event_key = "work_play_start", + scope_kind = "work", + scope_id = workId, + metadata = { playType, workId, sourceRoute, userId } +) +``` + +### 6.6 可选排行榜 + +```text +GET /api/bark-battle/works/{workId}/leaderboard?period=all&limit=50&cursor=... +GET /api/bark-battle/works/{workId}/leaderboard/me +``` + +职责: + +- 只读已接受成绩。 +- 支持分页。 +- 支持匿名用户时隐藏或弱化身份展示。 +- 若作品关闭排行榜,返回空投影或明确 disabled 状态。 + +## 7. SpacetimeDB 与 migration 策略 + +### 7.1 表 / reducer / procedure 边界 + +SpacetimeDB 侧只承载持久化、索引、reducer 原子写入和可查询投影,不承载 HTTP 鉴权或前端表现。 + +建议 reducer / procedure: + +- `bark_battle_save_draft_config`:保存草稿配置。 +- `bark_battle_publish_work_config`:发布配置版本。 +- `bark_battle_start_run`:创建 runtime run。 +- `bark_battle_finish_run`:结束 run 并写 score。 +- `bark_battle_upsert_leaderboard_entry`:写排行榜投影。 +- `bark_battle_get_work_runtime_config`:读取发布态配置。 +- `bark_battle_get_leaderboard`:读取排行榜投影。 + +如当前架构要求 reducer 仅由 `spacetime-client` 调用,则 api-server 不直接操作 SpacetimeDB SDK。 + +### 7.2 migration.rs + +所有表结构变更必须进入 `migration.rs`: + +- 新增 bark-battle 表时写显式 migration。 +- 新增索引、唯一约束或版本字段时写 migration。 +- 从草稿 JSON 拆字段时写数据迁移说明。 +- 不允许只改 Rust struct 而不补 migration。 + +### 7.3 绑定生成 + +涉及 SpacetimeDB schema / reducer 变更后: + +1. 运行仓库既有 SpacetimeDB 绑定生成命令。 +2. 检查 generated bindings 变化。 +3. `spacetime-client` 只引用生成物,不手改生成物。 +4. shared contracts 与 generated bindings 的差异通过 facade 消化,不让前端直接依赖数据库绑定。 + +明确要求:不手改生成物,不手改 generated bindings,不用临时复制粘贴类型绕过生成流程。 + +### 7.4 api-server facade + +`api-server` 通过 `spacetime-client` facade 调用 SpacetimeDB: + +```text +BarkBattleService + -> BarkBattleDomainPolicy + -> BarkBattleSpacetimeClient + -> generated bindings +``` + +这样可以保证: + +- HTTP 层易测试。 +- domain 纯函数可独立测试。 +- SpacetimeDB 绑定变更不会扩散到 route handler。 + +## 8. 安全、隐私与反作弊 + +### 8.1 隐私 + +- 不上传原始音频。 +- 不保存原始音频。 +- 不保存可还原用户声音的高精度采样曲线。 +- 只保存派生指标:峰值、均值、有效叫声次数、节奏命中、最终能量、分数、耗时。 +- 前端权限文案必须说明麦克风只用于本地玩法输入,MVP 不上传原始声音。 + +### 8.2 不信任前端胜负 + +后端不能直接信任: + +- `clientWinner`。 +- `score`。 +- `finalEnergy`。 +- `elapsedMs`。 +- `validBarkCount`。 + +后端必须校验并重算服务端认可结果。MVP 因不上传音频,无法完全证明声音真实性,但仍需做边界反作弊。 + +### 8.3 校验规则 + +必须校验: + +- run token 是否匹配且未使用。 +- run 是否未过期。 +- `work_id + config_version` 是否与 start 时一致。 +- 用户是否有权限游玩该 work。 +- 提交时长是否接近配置时长,例如 30 秒局允许少量网络 / 页面调度误差。 +- `finalEnergy` 是否在配置范围。 +- `peakVolumeMax`、`peakVolumeAvg` 是否在 `0..1`。 +- `validBarkCount`、`rhythmHitCount`、`longestCombo` 是否在物理合理上限。 +- `clientStartedAt` / `clientFinishedAt` 与服务端时间窗口是否合理。 +- 同一用户 / 匿名会话的频率限制。 + +### 8.4 反作弊处理 + +建议结果状态: + +- `accepted`:写 score,可进入排行榜。 +- `accepted_with_flags`:写 score,但标记异常,默认不入榜或降低可信度。 +- `rejected`:不入榜,只记录 run 失败原因。 + +常见 flags: + +- `elapsed_too_short` +- `elapsed_too_long` +- `metric_out_of_range` +- `config_version_mismatch` +- `token_invalid` +- `duplicate_finish` +- `rate_limited` +- `impossible_bark_count` + +## 9. TDD / 验收顺序与命令 + +本任务只写方案,不执行代码实现。后续落地建议按以下顺序: + +### 9.1 domain 纯函数测试 + +先实现 `module-bark-battle`: + +```bash +cd server-rs +cargo test -p module-bark-battle +``` + +测试覆盖: + +- runtime config 校验。 +- finish metrics 范围校验。 +- 胜负判定。 +- score / grade / leaderboard score 计算。 +- 反作弊 flags。 + +### 9.2 contracts 测试 + +再实现 shared contracts: + +```bash +cd server-rs +cargo test -p shared-contracts bark_battle +cargo check -p shared-contracts +``` + +验收: + +- DTO 可序列化 / 反序列化。 +- 枚举值稳定。 +- 可选字段向后兼容。 +- TypeScript mirror 或契约生成产物与 Rust contract 对齐。 + +### 9.3 SpacetimeDB / api-server check + +实现表、reducer、migration 与 facade 后: + +```bash +cd server-rs +cargo check -p spacetime-module +cargo check -p spacetime-client +cargo check -p api-server +cargo test -p api-server bark_battle +``` + +如仓库有统一命令,以统一命令为准,例如: + +```bash +cd server-rs +cargo test --workspace +``` + +验收: + +- migration.rs 包含新增表与变更。 +- 绑定由生成命令产出,未手改生成物。 +- api-server route handler 只做编排,不内嵌复杂计分规则。 + +### 9.4 前端 contract 对齐 + +前端只在后端 contract 稳定后接入: + +```bash +npm run typecheck +npm test -- bark-battle +npm run build +``` + +验收: + +- runtime start / finish 请求字段与 shared contracts 一致。 +- 前端 result panel 展示后端 `RunResult`。 +- 前端本地结算只作为即时反馈,正式结果以后端返回为准。 + +### 9.5 手工验收清单 + +- 可以创建并保存 bark-battle 草稿。 +- 可以发布成稳定作品 ID,`playTypeId = bark-battle`。 +- runtime start 返回 config、runId、runToken。 +- start 写入 `work_play_start`,scope 与 metadata 符合要求。 +- finish 不上传音频,只上传派生指标。 +- finish 返回服务端认可的 result。 +- 异常时长、重复提交、config version mismatch 会被拒绝或打 flag。 +- 排行榜关闭时不写榜;开启时只写 accepted 结果。 + +## 10. 与现有前端方案和 BDD 文档的关系 + +### 10.1 依赖文档 + +- 前端 runtime 方案:`docs/technical/BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md`。 +- BDD / DDD / TDD 总计划:`.hermes/plans/2026-05-11_144229-bark-battle-2d-game-bdd-ddd-tdd-plan.md`。 +- 当前后端实现基线:`docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`。 +- SpacetimeDB 表结构变更约束:`docs/technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`。 + +### 10.2 与前端方案的对齐点 + +- 前端方案负责 Phaser / Web Audio / DOM HUD;本文负责作品、成绩、排行榜、发布和埋点。 +- 前端 `BarkBattleSnapshot` 可用于本地即时表现,但正式 `RunResult` 以后端返回为准。 +- 前端不上传原始音频,只上传 `BarkBattleDerivedMetrics`。 +- 前端本地 config 应来自后端发布态 `BarkBattleRuntimeConfig`,不能在生产游玩中使用未发布临时配置。 +- 前端 result panel 应能展示后端返回的 score、grade、antiCheatFlags 与 leaderboard entry。 + +### 10.3 与 BDD 的对齐点 + +后续 BDD 场景应覆盖: + +- 玩家从作品页进入 bark-battle runtime。 +- 玩家授权麦克风后开始 30 秒对战。 +- 玩家完成单局后看到后端确认结果。 +- 未授权麦克风时可以看到降级说明,但不写正式成绩。 +- 作品关闭排行榜时不展示排名入口。 +- 作品开启排行榜时展示当前作品排名。 +- 重复 finish / 过期 run / 配置版本不一致时返回可解释错误。 + +### 10.4 后端落地顺序建议 + +1. 先只做 contract + domain,固定 `playTypeId = bark-battle` 与配置 schema。 +2. 再做草稿 / 发布态作品配置读写。 +3. 再做 start / finish run 与 `work_play_start` 埋点。 +4. 最后做排行榜投影。 +5. 实时多人协议另起方案,不与 MVP 混做。 diff --git a/docs/technical/README.md b/docs/technical/README.md index fefb6a1f..d014e616 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -4,6 +4,7 @@ ## 文档列表 +- [BARK_BATTLE_BACKEND_DDD_TECHNICAL_PLAN_2026-05-11.md](./BARK_BATTLE_BACKEND_DDD_TECHNICAL_PLAN_2026-05-11.md):冻结“汪汪声浪大作战 / bark-battle”后端 DDD 技术方案,明确 `server-rs + Axum + SpacetimeDB` 分层边界、shared contracts、作品配置、runtime run、派生成绩、排行榜、`work_play_start` 埋点、migration/绑定生成策略,以及不保存原始麦克风音频的隐私与反作弊约束。 - [BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md](./BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md):冻结“汪汪声浪大作战 / bark-battle”2D 浏览器 runtime 技术方案,明确 Phaser + TypeScript + Vite 选型、纯 TS simulation 与 Phaser renderer/DOM HUD 边界、Web Audio 输入适配、移动端权限降级和后续测试验证命令。 - [CHILD_MOTION_DEMO_WARMUP_IMPLEMENTATION_SPEC_2026-05-09.md](./CHILD_MOTION_DEMO_WARMUP_IMPLEMENTATION_SPEC_2026-05-09.md):冻结儿童动作识别互动玩法 Demo 固定热身关的开发落地规格,覆盖横屏展示、摄像头背景虚化、角色剪影、绿色圆环 2 秒保持、动作教学、当前会话内空间边界记录和后续关卡安全暂停规则。 - [RUNTIME_INPUT_DEVICE_ABSTRACTION_2026-05-10.md](./RUNTIME_INPUT_DEVICE_ABSTRACTION_2026-05-10.md):记录运行态输入设备抽象层,明确鼠标、触控、mocap 等设备统一归一为通用拖拽语义,玩法组件只负责解释目标和落点。