# 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 混做。