diff --git a/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/admin_walk.py b/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/admin_walk.py new file mode 100644 index 00000000..0a8b56fa --- /dev/null +++ b/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/admin_walk.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +admin 审核台 链路② UI 走查 —— 最小 CDP 客户端(websocket-client 直驱)。 +在 mini-desktop 本地驱动 headless chrome(:9222)走查 game-admin(:4174): +登录 → 项目审核队列 → 点「通过」approve 真实待审游戏 → 取证。 +普通 DOM 页面,evaluate 用于受控点击(非游戏试玩,不受 player_cdp 只读纪律约束)。 +阶段:recon(看登录页) / login(登录+进队列+dump) / approve(登录+进队列+点通过+确认+验证)。 +""" +import json, base64, time, sys, urllib.request +import websocket # websocket-client 1.7.0 + +CDP_HTTP = "http://localhost:9222" +ADMIN = "http://localhost:4174" +TARGET_GAME = sys.argv[2] if len(sys.argv) > 2 else "" # approve 阶段指定 gameId + + +def new_target(url="about:blank"): + """Chrome 111+ 必须用 PUT /json/new 建 target。""" + req = urllib.request.Request(f"{CDP_HTTP}/json/new?{url}", method="PUT") + d = json.load(urllib.request.urlopen(req, timeout=10)) + return d["webSocketDebuggerUrl"], d["id"] + + +class CDP: + def __init__(self, ws_url): + self.ws = websocket.create_connection(ws_url, max_size=None, timeout=40, + suppress_origin=True) + self._id = 0 + + def send(self, method, params=None, timeout=40): + self._id += 1 + mid = self._id + self.ws.send(json.dumps({"id": mid, "method": method, "params": params or {}})) + end = time.time() + timeout + while time.time() < end: + self.ws.settimeout(max(0.1, end - time.time())) + try: + msg = json.loads(self.ws.recv()) + except Exception: + continue + if msg.get("id") == mid: + if "error" in msg: + raise RuntimeError(f"{method}: {msg['error']}") + return msg.get("result", {}) + raise TimeoutError(method) + + def evaljs(self, expr, timeout=40): + r = self.send("Runtime.evaluate", + {"expression": expr, "returnByValue": True, "awaitPromise": True}, + timeout) + if "exceptionDetails" in r: + return {"__js_error__": json.dumps(r.get("exceptionDetails"), ensure_ascii=False)[:300]} + return r.get("result", {}).get("value") + + def nav(self, url, settle=6): + self.send("Page.navigate", {"url": url}) + time.sleep(settle) + + def shot(self, path): + r = self.send("Page.captureScreenshot", {"format": "png"}) + with open(path, "wb") as f: + f.write(base64.b64decode(r["data"])) + + def close(self): + try: + self.ws.close() + except Exception: + pass + + +# ---- JS 片段 ---- +JS_DUMP_LOGIN = r"""(()=>{ + const inp=[...document.querySelectorAll('input')].map(e=>({type:e.type,ph:e.placeholder||'',val:(e.value||'').slice(0,20)})); + const btn=[...document.querySelectorAll('button')].map(e=>(e.innerText||'').replace(/\s+/g,'')).filter(Boolean); + return {href:location.href, title:document.title, inputs:inp, buttons:btn}; +})()""" + +JS_CLICK_LOGIN = r"""(()=>{ + const btns=[...document.querySelectorAll('button')]; + const b=btns.find(e=>(e.innerText||'').replace(/\s+/g,'')==='登录'); + if(b){b.click(); return 'clicked登录';} + return 'notfound登录|avail:'+btns.map(e=>(e.innerText||'').replace(/\s+/g,'')).filter(Boolean).join(','); +})()""" + +JS_DUMP_QUEUE = r"""(()=>{ + const rows=[...document.querySelectorAll('.el-table__row')].map(r=>{ + const cells=[...r.querySelectorAll('td')].map(td=>(td.innerText||'').trim().replace(/\s+/g,' ').slice(0,40)); + const btns=[...r.querySelectorAll('button')].map(b=>(b.innerText||'').replace(/\s+/g,'')); + return {cells, btns}; + }); + return {href:location.href, rowCount:rows.length, rows:rows.slice(0,8), + bodyText:(document.body.innerText||'').replace(/\s+/g,' ').slice(0,260)}; +})()""" + +def js_click_pass(game): + return r"""(()=>{ + const rows=[...document.querySelectorAll('.el-table__row')]; + for(const r of rows){ + const txt=r.innerText||''; + if(txt.includes('%s')){ + const pass=[...r.querySelectorAll('button')].find(b=>(b.innerText||'').replace(/\s+/g,'')==='通过'); + if(pass){pass.click(); return 'clicked通过 for %s';} + return 'row matched but no通过btn'; + } + } + return 'gameid %s not found in rows'; + })()""" % (game, game, game) + +JS_CONFIRM = r"""(()=>{ + const btns=[...document.querySelectorAll('.el-message-box button, .el-overlay button, .el-dialog button')]; + const ok=btns.find(b=>{const t=(b.innerText||'').replace(/\s+/g,'');return t==='确定'||t==='确认';}); + if(ok){ok.click(); return 'confirmed';} + return 'no-confirm-dialog|btns:'+btns.map(b=>(b.innerText||'').replace(/\s+/g,'')).join(','); +})()""" + + +def do_login(c): + """导航到 / → 被弹去登录 → 点登录(表单已预填 admin/admin123/芋道源码)。""" + c.nav(f"{ADMIN}/", settle=7) + c.shot("/tmp/aw_login.png") + print("LOGIN_PAGE:", json.dumps(c.evaljs(JS_DUMP_LOGIN), ensure_ascii=False)) + print("CLICK_LOGIN:", c.evaljs(JS_CLICK_LOGIN)) + time.sleep(6) # 等鉴权 + 跳转 + print("AFTER_LOGIN_HREF:", c.evaljs("location.href")) + + +def main(): + stage = sys.argv[1] if len(sys.argv) > 1 else "recon" + ws_url, tid = new_target("about:blank") + c = CDP(ws_url) + c.send("Page.enable") + c.send("Runtime.enable") + + if stage == "recon": + c.nav(f"{ADMIN}/", settle=7) + c.shot("/tmp/aw_recon.png") + print(json.dumps(c.evaljs(JS_DUMP_LOGIN), ensure_ascii=False, indent=1)) + + elif stage == "login": + do_login(c) + c.nav(f"{ADMIN}/wanxiang/review", settle=6) + c.shot("/tmp/aw_queue.png") + print("QUEUE:", json.dumps(c.evaljs(JS_DUMP_QUEUE), ensure_ascii=False, indent=1)) + + elif stage == "approve": + do_login(c) + c.nav(f"{ADMIN}/wanxiang/review", settle=6) + c.shot("/tmp/aw_before.png") + before = c.evaljs(JS_DUMP_QUEUE) + print("BEFORE:", json.dumps(before, ensure_ascii=False)) + print("CLICK_PASS:", c.evaljs(js_click_pass(TARGET_GAME))) + time.sleep(2) + c.shot("/tmp/aw_dialog.png") + print("CONFIRM:", c.evaljs(JS_CONFIRM)) + time.sleep(4) # 等 API 回 + 刷新队列 + c.shot("/tmp/aw_after.png") + after = c.evaljs(JS_DUMP_QUEUE) + print("AFTER:", json.dumps(after, ensure_ascii=False)) + + c.close() + + +if __name__ == "__main__": + main() diff --git a/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/run_walk.sh b/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/run_walk.sh new file mode 100644 index 00000000..6c333d2f --- /dev/null +++ b/docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/run_walk.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# admin 走查 runner:整个流程输出重定向到文件,chrome 全 fd 脱离 ssh 管道。 +# 用法:ssh mini 'nohup bash /tmp/run_walk.sh >/dev/null 2>&1 & ';再轮询 /tmp/walk_out.txt +STAGE="${1:-recon}" +exec > /tmp/walk_out.txt 2>&1 +echo "=== run_walk stage=$STAGE @ $(date +%H:%M:%S) ===" +pkill -f "chrome.*9222" 2>/dev/null; sleep 1; rm -rf /tmp/chrome-aw +setsid google-chrome --headless=new --no-sandbox --no-zygote --disable-gpu \ + --disable-dev-shm-usage --remote-debugging-port=9222 --remote-allow-origins=* \ + --user-data-dir=/tmp/chrome-aw /tmp/chrome-aw.log 2>&1 & +sleep 6 +echo "VER:"; curl -s http://localhost:9222/json/version 2>/dev/null | head -c 110; echo +echo "WALK:"; python3 /tmp/admin_walk.py "$STAGE" "$2" 2>&1 +pkill -f "chrome.*9222" 2>/dev/null +echo "=== DONE @ $(date +%H:%M:%S) ===" diff --git a/docs/agent-specs/2026-06-11-Wave4-community-biz-execution.md b/docs/agent-specs/2026-06-11-Wave4-community-biz-execution.md new file mode 100644 index 00000000..1fd05588 --- /dev/null +++ b/docs/agent-specs/2026-06-11-Wave4-community-biz-execution.md @@ -0,0 +1,749 @@ +# Wave4(community + biz)· execution 版 + +- **文档编号**:HJ-WAVE4-EXEC-001 +- **日期**:2026-06-11 +- **状态**:待实现 agent 认领 + 主 agent 验收(评审版三项战略决策 D-A/D-B/D-C 已创始人拍板,见 §0) +- **唯一设计依据**:`docs/agent-specs/2026-06-11-Wave4-community-biz-review.md`(编号 HJ-WAVE4-001,已两轮对抗评审定稿)+ `docs/agent-specs/2026-06-11-Wave4-community-biz-review-评审纪要.md`(R1 24 条 + R2 6 条逐条处置)。**本文不得与评审版矛盾**;执行中发现矛盾,停工上报,以评审版为准。 +- **读者**:实现 agent 分工认领(建议 community 先、biz 后,单序列资源由 §0 单 agent 收口);主 agent 验收。 +- **环境铁律**(继承项目记忆 `internal-build-infra-servers` / `m1-runtime-bringup-state` / `frontend-spine-built`): + - 重型构建/测试一律上 **mini-desktop**(`ssh mini-desktop`,15G 内存,有 java17/mvn/docker);**本机严禁 mvn/npm build**(小内存会 OOM);源码同步走 `git push`(阿里云 Gitea `ssh://git@101.200.34.71:2222`)→ mini-desktop `git pull`(scp 源码树被分类器拦)。 + - staging app 在 mini-desktop 隔离容器 `http://100.64.0.7:48080`(对内 `http://localhost:48080`)。 + - 含中文 SQL 在 mini-desktop 执行务必 `--default-character-set=utf8mb4`(防乱码,记忆铁律)。 + - **「骨架编译过 ≠ 真实可验收」**(评审版 R10 铁律 + 记忆 `golden-loop-b1-done` / `frontend-link-ui-walk-publish-fix`):完成条件强制要求真实写链路 staging 实测(§9.3 两条系统身份写链路)+ biz/community UI 入口真人走查(§9.4),不以批跑/API 全绿替代。 + +--- + +## 0. 拍板前提(评审版 §0.1 / §7,本文全按拍板结果起草) + +| 决策 | 拍板结果(本文落点) | +|---|---| +| **D-A** biz 形态 | **轻量 lead form 先行**——只交付「询单→报价→进度→demo 确认」信息流闭环,资金流(收款/分账/在线签章)留 M4(§7 biz 建设 + §10 M4 留债) | +| **D-B** community 功能优先级 | **通知底座最小集**——T-CMU-10 编排 + T-CMU-06 站内信 + T-CMU-11 等级引擎(+可选短信 T-CMU-09),一次覆盖 5/5 P0;SOC/GRW 域仅留接口桩、不实现(§6 community 建设) | +| **D-C** 是否本波接 ip seam | **不接、维持 D5 推迟**——本波 P-BIZ-02 直接复用 aigc 4 模板 + runtime 沙箱(绕开 T-IP-09 模板市场),无需 ip seam | +| 建设次序(execution 单 agent 定) | **先 community 后 biz**(community 触达机制最独立、复用面集中、风险最低;biz 外部对接多、受 pay 桩制约,适合后段) | +| P-INC-01 计数权威源(execution §0 单 agent 拍) | **(a) project 发布成功后同进程 notify community 计数**(与三上游同范式;实查 ProjectApi **无** `countPublishedByCreator` 方法,方案 b 需 project 新增端点,故取 a 上游单点改动)。详见 §6.4 | + +--- + +## 1. 目标与范围边界 + +### 1.1 目标 + +交付 **Wave4 总 11 P0 = community 5 + biz 6 可验证**,不破坏已建 11 模块(错误码段/Flyway 序号/前缀/契约文件名零冲突)。 + +- **community(5 P0 = P-NTF-01/02/03/05 + P-INC-01)**:通知底座(编排 + 站内信 + 等级引擎)。触达机制 = **同进程 `-api` notify-push**(评审版 §3.3 定型):aigc 生成完成 / compliance·project 审核出结果 / trade 收益变动后,由各自 service 在本地事务**提交后**同进程调用 community 暴露的 `CommunityNotifyApi`(复用 `@Primary` 本地实现),写站内信。**不新建 MQ、不动 `events.schema.json`**。 +- **biz(6 P0 = P-BIZ-01/02/03/04/08/12)**:轻量 lead form 信息流闭环(询单→报价→进度看板→demo 试玩确认),复用 aigc 4 模板 / runtime 取包 / admin 队列 / project 轻量状态机范式。**资金流(收款/分账/在线签章)留 M4**。 + +### 1.2 明确不做(继承评审版 §1 非目标,实现 agent 不得擅自扩界) + +- biz **不收款结算**(T-BIZ-08 收费、T-BIZ-03 在线电子签章)→ 本波只到「状态机走通 + 线下签署后台标记兜底」,资金流并入 M4。 +- community **不全量铺开 T-CMU 全家桶**——SOC/GRW 域(关系图谱/排行/成就/扇出/邮件,全 P1/P2)只留接口桩、不实现。 +- **不引入 Flowable / yudao-bpm**(当前注释未装配)——复用 project 已落地的轻量状态机范式(`ProjectStatusEnum` + `ProjectServiceImpl` 写前流转校验,**无独立 `ProjectStateService.java`**)。 +- **community「记入钱包/流量包」本波不跨模块写**——实查 `feed.yaml` 仅 `boost`/`pinned`(无流量包发放端点)、`trade.yaml` 无 grant/incentive 端点,两个跨模块写 seam **当前均不存在**;本波只写 community **自有 reward 触发记录表**,真实记账/到账留 M4。 +- **P-BIZ-11 学生防沉迷不在 biz**——经 Doc C 已划归 compliance(`T-CMP-36/37`),由 compliance 承接(不计入 biz)。 +- **本波两类留债分开记账**(评审版 R1,勿混):**①资金流债**(biz 签署/收款、community reward 真实记账)→ 统一并入 **M4**;**②前端 UI 入口债**(biz 运营代客发起=WS4 / 客户进度看板=WS3)→ 跨工位协调项,可与本波后端**并行**推进,不与 M4 资金流债捆绑延期(§10)。 +- **`events.schema.json` 本波零改**(community 走 notify-push、不新增遥测事件)——主动规避「只改契约不改 `TelemetryEventEnum` = 事件静默 rejected」双处同步坑(评审版 §3.3 保留口径)。 + +--- + +## 2. 涉及模块与文件路径(克隆 + 接线全清单) + +> 路径前缀统一 `/root/games-development-ai/`;包名固定 `cn.wanxiang.game.module.{community|biz}.{层}`(克隆黄金模块 `game-module-project` 范式,**禁** `cn.iocoder` 业务包名)。 + +### 2.1 新建模块(克隆黄金模块) + +| 模块 | 子模块 | 关键文件(举黄金模块 project 实例为样板,file:line) | +|---|---|---| +| **community** | `game-module-community-api` | `pom.xml`(artifactId→`game-module-community-api`)/ `enums/ApiConstants.java`(NAME→`community-server`、PREFIX→`RPC_API_PREFIX+"/community"`)/ `enums/ErrorCodeConstants.java`(段 `1_107_000_000` 起)/ `api/CommunityNotifyApi.java`(Feign 接口,照 `ProjectApi.java:23` `@FeignClient(name=ApiConstants.NAME)`)/ `dto/`(跨模块 DTO) | +| | `game-module-community-server` | `pom.xml`(artifactId→`game-module-community-server`)/ `controller/app/message/AppCommunityMessageController.java`(`@RequestMapping("/community")`,前缀 `/app-api` 框架自动加,照 `AppProjectController.java:33-37`)/ `controller/admin/notification/AdminCommunityNotificationController.java`(mock-trigger,照 `AdminProjectController.java:31-35`)/ `service/{message,level,notify}/`(业务规则 + `@Transactional` 只加此层)/ `dal/dataobject/community/{MessageDO,LevelDO,RewardDO}.java`(`extends TenantBaseDO`,照 `ProjectDO.java:18-83`)/ `dal/mysql/community/`(`@Mapper extends BaseMapperX`,显式列禁裸 `select*`,照 `ProjectMapper.java:16-87`)/ `convert/`(`BeanUtils`+手写 static,**非 MapStruct**,照 `ProjectConvert.java`)/ `api/CommunityNotifyApiImpl.java`(`@RestController @Validated @Primary implements CommunityNotifyApi`,照 `ProjectApiImpl.java:22-25`)/ `src/test/java/.../service/.../*ServiceImplTest.java`(`extends BaseMockitoUnitTest`) | +| **biz** | `game-module-biz-api` | 同 community-api 结构(artifactId→`-biz-api`、NAME→`biz-server`、段 `1_110_000_000` 起)+ `enums/BizLeadStatusEnum.java`(照 `ProjectStatusEnum.java:23-68`) | +| | `game-module-biz-server` | 同 community-server 结构(artifactId→`-biz-server`);controller 偏 `controller/admin/biz/`(运营为主)+ 少量 `controller/app/biz/`(客户提单 + 我的订单);`service/lead/BizLeadServiceImpl.java`(流转校验内联,照 `ProjectServiceImpl.java:111-116/350-370`) | + +### 2.2 公共装配点(⚠️ **两处**,缺任一处克隆即卡 compile,实现 agent **不得以旧 skill 为唯一依据**) + +> **评审版 §2.4 + R2-2 硬提示**:`add-business-module.md` 步骤表当前**仅列「步骤5=yudao-server/pom.xml 加依赖」,未把 root `game-cloud/pom.xml` 的 `` 注册列为独立步**(开头还明写「无需改 Application/yaml,只需在 yudao-server/pom.xml 加依赖」)。该 root pom 注册步**计划本波收口后才回写进 skill**。故本波克隆**以本节「装配点=两处」为准**——只读 skill 会漏掉 root pom 的 `` 注册,触发「缺此步父 pom 找不到聚合定义 → `mvn -pl … compile` 失败」。 + +| # | 文件(亲核 2026-06-11 dev/2.0.0) | 改动 | 缺此步的后果 | +|---|---|---|---| +| **①** | `game-cloud/pom.xml`(root 聚合 pom)`` 块(实查 `:10` `` … `:34` 末为 `game-module-studio` … `:35` ``) | 在 **line34 之后 / line35 `` 之前**追加 `game-module-community` + `game-module-biz` | `mvn -pl game-module-community compile` 找不到父 pom 聚合定义 → **compile 失败** | +| **②** | `game-cloud/yudao-server/pom.xml` `` 块(实查 `:23` `` … `:80` 末 game-module 依赖为 `game-module-studio-server`) | 在 **studio-server `` 块之后**追加两个 ``(groupId=`cn.iocoder.cloud` / artifactId=`game-module-community-server`、`game-module-biz-server` / version=`${revision}`) | 单体不把模块编进 JAR → 启动后无此模块、Swagger 无分组 | +| ③ | `YudaoServerApplication.java` / `application.yaml` | **零改**(`YudaoServerApplication` 组件扫描 + `@MapperScan` + `application.yaml` type-aliases 已通配 `cn.wanxiang.game.module`;端前缀按 `controller.app`/`controller.admin` 包名自动加) | —(明确写入本文降认知负荷) | + +### 2.3 契约文件(新建,独占无冲突) + +- `contracts/db-schemas/V12.0.0__create_game_community.sql` + `game-cloud/yudao-server/src/main/resources/db/migration/V12.0.0__create_game_community.sql`(两处**字节一致**,见 §0/§5) +- `contracts/db-schemas/V13.0.0__create_game_biz.sql` + `game-cloud/yudao-server/src/main/resources/db/migration/V13.0.0__create_game_biz.sql`(两处字节一致) +- `contracts/api-schemas/community.yaml`、`contracts/api-schemas/biz.yaml`(独占新文件,实查均不存在) +- `contracts/README.md §四`:community=107 / biz=110 **已在册**(`:79`,只读确认,**不改**) + +### 2.4 上游 notify 接线点(本波在上游各新增 1 处同进程调用 + 上游 `-server/pom.xml` 加 `game-module-community-api` 依赖) + +| 挂点 | 文件:line(亲核 2026-06-11) | 接线说明(详见 §6.5) | +|---|---|---| +| 挂点1 P-NTF-02 生成完成 | `game-module-aigc/.../service/callback/DifyCallbackServiceImpl.java:~52`(`return txService.handleCallbackTx(reqVO);` 内层 `@Transactional` 三表写入链于此提交) | 改为先接 `Boolean result`,**提交后经 `aigcTaskMapper.selectByTraceId(reqVO.getTraceId())` 回查任务**,仅 `task.status==SUCCEEDED 且 versionId!=null`(真实成功终态,排除 config_invalid/failed/幂等无新版本)才调 `notifyGenerateDone(task.getCreatorUserId(), task.getGameId(), task.getVersionId())`,再 return(实参三字段均取自回查任务,**不走 `projectApi.getCreatorUserId`**)| +| 挂点2 P-NTF-03 审核结果 + P-INC-01 发布计数 | **外层 caller** `game-module-project/.../controller/admin/project/AdminProjectController.java:55-57`(`projectService.reviewProject(reviewReqVO, reviewerUserId)`;service 本身 `@Transactional` 于 `ProjectServiceImpl.java:207`) | service 返回后调 `notifyReviewResult(...)`;`isApprove` 时多调一次 `notifyProjectPublished(...)`(合并在同一 caller) | +| 挂点3 P-NTF-05 收益变动 | `game-module-trade/.../service/settle/SettlementServiceImpl.java:~78`(settle 循环内 `firstRecord` 分支 / `newlyRecorded++`;`recordIncome` 为 `@Transactional` 单条已提交,循环体非事务) | `if (firstRecord) {...}` 分支内调 `notifyIncomeChanged(...)`(仅首次真实入账才通知,幂等复跑不重发) | + +> ⚠️ **行号漂移声明**:以上 file:line 基于 dev/2.0.0 工作区快照(亲核日 2026-06-11)。落地时若上游模块有改动,**以语义定位为准**(aigc=`handleCallbackTx` 返回处、project=`AdminProjectController.reviewProject` 调用返回后、trade=settle 循环 `firstRecord` 分支)。 + +--- + +## 3. 数据流与依赖(一图看清边界) + +```mermaid +graph TB + subgraph UP["上游(本波各加 1 处提交后同进程 notify)"] + AGC["aigc DifyCallbackServiceImpl
handleCallbackTx 提交后"] + PRJ["project AdminProjectController
reviewProject 返回后"] + TRD["trade SettlementServiceImpl
settle firstRecord 分支"] + end + subgraph CMU["community(V12 / 段 1-107 / CommunityNotifyApi @Primary)"] + NAPI["CommunityNotifyApi 入口
(系统身份注入 LoginUser(id=0)+finally clearContext)"] + MSG["game_community_message 站内信"] + LVL["game_community_level 等级
(published_count 原子自增)"] + RWD["game_community_reward 触发记录
(uk 幂等;发放留 M4)"] + end + subgraph BIZ["biz(V13 / 段 1-110)"] + LEAD["game_biz_lead 询单(状态机)"] + QUOTE["game_biz_quote 报价"] + PROG["game_biz_progress 进度工单"] + ACC["game_biz_acceptance 验收"] + end + subgraph REUSE["只读复用(biz 不反写)"] + RTPKG["runtime 取包双路由
/app-api/runtime/package/{versionId}(+/manifest)"] + TPL["aigc 4 模板 clicker/merge/idle/tycoon"] + end + subgraph M4["M4 留债(明确推迟)"] + PAY["pay 收款 / 在线签章 / trade IncentiveCreditApi / feed 流量包-api / 现金到账"] + end + + AGC -.notifyGenerateDone.-> NAPI + PRJ -.notifyReviewResult + notifyProjectPublished.-> NAPI + TRD -.notifyIncomeChanged.-> NAPI + NAPI --> MSG + NAPI --> LVL --> RWD + LEAD --> QUOTE + LEAD --> PROG + LEAD --> ACC + RTPKG -.demo 可预览判定(只读).-> BIZ + TPL -.模板复用(只读).-> BIZ + RWD -.真实记账/到账.-> M4 + ACC -.签署/收款.-> M4 + + classDef deb fill:#fee,stroke:#c33; + class PAY deb; + classDef new fill:#eef,stroke:#36c; + class AGC,PRJ,TRD new; +``` + +**事务边界铁律(评审版 §3.3/§4 红线)**:三上游写方法本身均 `@Transactional`,notify 必须在**本地事务提交后**调用(=外层 caller 或循环内已提交分支),避免「通知失败回滚已提交业务」。三个 notify 调用点统一**`try-catch` 吞异常 + 仅记 error log**,不得让通知失败中断业务/结算循环(详见 §6.5、§8.3)。 + +--- + +## 4. 接口与数据契约(DDL + 端点 + CommunityNotifyApi) + +> DDL 套黄金模块公共列模板(recon `errorcode-flyway-ddlconv` 实证范式):InnoDB + utf8mb4;显式列 + 列级中文 COMMENT;状态机用 `TINYINT`,**非法流转由 Service 校验、DO 层不承载**;每表强制 6 审计列(`creator`/`create_time`/`updater`/`update_time`/`deleted BIT(1) DEFAULT b'0'`/`tenant_id BIGINT DEFAULT 0`,全 NOT NULL);唯一键含 `(业务键, deleted, tenant_id)`;业务归属列 `creator_user_id`/`user_id`(**归属隔离边界**)**区别于** Yudao 审计列 `creator`(VARCHAR64)。 + +> **⚠️ 归属隔离口径钉实(R-medium PII 红线,实查黄金模块未用 @DataPermission)**:实查 `game-module-project` 的 DAL/Mapper **零 `@DataPermission` 注解**(全 game-module grep 无命中),其归属隔离实为 **Mapper 查询显式以 `creator_user_id`/`user_id` 作 WHERE 谓词、且该 userId 取自 `SecurityFrameworkUtils.getLoginUserId()`(token 解析、非前端入参)**——范本=`ProjectMapper.selectMyPage`「强制按当前用户过滤」、`TradeController` 各 app 端点 `getLoginUserId()`。**故 community/biz 客户侧端点(`/app-api/community/messages`、`/app-api/biz/leads/my` 等)必须在 Mapper 查询里强制以登录态 user_id 作谓词 enforce 隔离,不得依赖「黄金模块并未启用的 @DataPermission 框架机制」**(照搬「靠 DataPermission」会既不加注解、又误以为框架已隔离 → 用户可读他人站内信/收益数额/审核拒绝原因等 PII)。隐私隔离必须在后端查询层 enforce(可信边界),前端不够。 + +### 4.1 community DDL — `V12.0.0__create_game_community.sql`(三表) + +> 文件头注释块须含:契约#2 DB 迁移标识 + 模块名 community + owner WS5 / 文件名 + Flyway 纪律(只新增、已合入禁改、回滚写补偿迁移 V12.0.1)/ 表清单 + 职责(引架构 Doc B community 章节)/ 错误码段 `community = 1-107-***-***` / 约定(InnoDB+utf8mb4、显式列、Yudao **6 审计列**(`creator`/`create_time`/`updater`/`update_time`/`deleted`/`tenant_id`,与 §4 表头及黄金模块 V1/V9 头注对齐,勿误作「七」去找幽灵第 7 列)、状态机 tinyint、中文注释、禁裸 select*)。 + +```sql +-- 表1:game_community_message 站内消息中心(P-NTF-01/02/03/05 投递落点) +CREATE TABLE `game_community_message` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '消息 ID', + `user_id` BIGINT NOT NULL COMMENT '收件人用户 ID(归属隔离:Mapper 强制以 getLoginUserId() 作 WHERE 谓词,用户只见自己消息)', + `type` TINYINT NOT NULL COMMENT '消息类型:1系统 2公告 3审核 4收益', + `biz_ref` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '业务关联键(如 gameId/taskId/settleId,供前端跳转)', + `title` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '消息标题', + `content` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '消息正文(审核拒绝含原因)', + `read_status` TINYINT NOT NULL DEFAULT 0 COMMENT '已读状态:0未读 1已读', + `read_time` DATETIME NULL COMMENT '已读时间', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列;系统身份投递=0)', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者(系统身份写=0,防 NOT NULL 拒绝)', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除:0未删 1已删', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID(MVP 单租户=0)', + PRIMARY KEY (`id`), + KEY `idx_user_read` (`user_id`, `read_status`, `create_time`) COMMENT '我的消息中心列表(按已读态+时间倒序)', + KEY `idx_user_type` (`user_id`, `type`, `create_time`) COMMENT '按四类聚合查询' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '站内消息中心(四类聚合,T-CMU-06)'; + +-- 表2:game_community_level 创作者等级(P-INC-01 计数权威落点 published_count) +CREATE TABLE `game_community_level` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '等级记录 ID', + `creator_user_id` BIGINT NOT NULL COMMENT '创作者用户 ID', + `level` TINYINT NOT NULL DEFAULT 1 COMMENT '创作者等级:1小白(MVP 仅维护计数+新人里程碑,分层留 P1)', + `published_count` INT NOT NULL DEFAULT 0 COMMENT '已发布作品数(计数权威源=project 发布成功 notify 原子累加)', + `last_milestone` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '末次已触发里程碑键(如 publish_3,幂等防重复触发)', + `last_milestone_time` DATETIME NULL COMMENT '末次里程碑触发时间', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_creator` (`creator_user_id`, `deleted`, `tenant_id`) COMMENT '一创作者一条等级记录(幂等初始化)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '创作者等级(T-CMU-11 等级引擎)'; + +-- 表3:game_community_reward 新人奖励触发记录(P-INC-01;本波只写触发记录,发放留 M4) +CREATE TABLE `game_community_reward` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '奖励触发记录 ID', + `creator_user_id` BIGINT NOT NULL COMMENT '创作者用户 ID', + `milestone` VARCHAR(32) NOT NULL COMMENT '里程碑键(如 publish_3 发满3作品)', + `reward_type` TINYINT NOT NULL COMMENT '奖励类型:1流量包 2现金', + `reward_amount` BIGINT NOT NULL DEFAULT 0 COMMENT '奖励额度(流量包=曝光量;现金=分,BIGINT 防浮点;与 trade 同口径)', + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '发放状态:0已触发待发放(本波终态) 1已发放(M4 回写)', + `granted_ref` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'M4 发放回执(trade 流水 id / feed 流量包 id),本波空', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(系统身份触发=0)', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者(系统身份写=0)', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_creator_milestone` (`creator_user_id`, `milestone`, `deleted`, `tenant_id`) COMMENT '同创作者同里程碑只触发一次(幂等防重复发钱)', + KEY `idx_status` (`status`) COMMENT 'M4 待发放扫描' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '新人奖励触发记录(P-INC-01,发放留 M4)'; +``` + +### 4.2 biz DDL — `V13.0.0__create_game_biz.sql`(四表) + +> 文件头注释块同 §4.1 范式(模块名 biz / owner WS5 / 错误码段 `biz = 1-110-***-***` / 资金流边界:本波只写自有表,不写 pay/trade,签署/收款/分账留 M4)。 + +```sql +-- 表1:game_biz_lead B/G 端定制询单主单(含轻量状态机;CRM=工单) +-- 状态机 status:0待跟进 →1已报价 →2制作中 →3待验收 →4已交付(单线性五态,BizLeadService 写前校验) +CREATE TABLE `game_biz_lead` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '定制询单 ID', + `customer_user_id` BIGINT NULL COMMENT '提单客户用户 ID(B 端自助提单时落;运营代客发起可空)', + `owner_user_id` BIGINT NULL COMMENT '跟进负责人用户 ID(运营,admin 队列认领)', + `biz_type` TINYINT NOT NULL DEFAULT 3 COMMENT '定制场景:1品牌营销 2文旅 3通用定制', + `contact_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '联系人', + `contact_phone` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '联系电话', + `company` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '企业/机构名称', + `title` VARCHAR(120) NOT NULL DEFAULT '' COMMENT '需求标题', + `scene_desc` VARCHAR(2000) NOT NULL DEFAULT '' COMMENT '场景需求描述(B/G 端在线提交的定制需求正文)', + `template_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '选定的玩法模板 ID(复用 aigc 4 模板 clicker/merge/idle/tycoon)', + `demo_version_id` BIGINT NULL COMMENT 'demo 预览版本 ID(runtime game_version.id;取包走 /app-api/runtime/package/{versionId})', + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态机:0待跟进 1已报价 2制作中 3待验收 4已交付', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列)', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者(系统/异步写须注入 LoginUser(id=0),否则 NOT NULL 拒绝)', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除:0未删 1已删', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID(MVP 单租户=0)', + PRIMARY KEY (`id`), + KEY `idx_owner_status` (`owner_user_id`, `status`) COMMENT 'admin 处理队列:按负责人+状态', + KEY `idx_customer` (`customer_user_id`, `create_time`) COMMENT '客户侧我的订单看板(/app-api/biz/leads/my)', + KEY `idx_status_update` (`status`, `update_time`) COMMENT '运营队列按状态+时间' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'B端定制询单主单(轻量状态机)'; + +-- 表2:game_biz_quote 报价表(报价≠可支付订单,参数化估价输出;收款留 M4) +CREATE TABLE `game_biz_quote` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '报价 ID', + `lead_id` BIGINT NOT NULL COMMENT '所属询单 ID(game_biz_lead.id)', + `quote_no` INT NOT NULL DEFAULT 1 COMMENT '报价版本号(同询单内可多轮报价,递增;唯一性由 service 以 max(quote_no)+1 串行生成保证、DB 不强约束——与黄金模块 game_version.version_no 同范式,非唯一键。若后续需 DB 级唯一可加 uk_lead_quote(lead_id,quote_no,deleted,tenant_id))', + `amount_fen` BIGINT NOT NULL DEFAULT 0 COMMENT '报价金额(单位:分,与 trade 同口径;本波仅展示不收款)', + `service_items` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT '服务项明细(编排内容,参数化估价表单产出)', + `valid_days` INT NOT NULL DEFAULT 30 COMMENT '报价有效天数', + `remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', + PRIMARY KEY (`id`), + KEY `idx_lead` (`lead_id`, `quote_no`) COMMENT '按询单查报价历史' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'B端定制报价表(报价≠下单,收款留 M4)'; + +-- 表3:game_biz_progress 进度工单表(状态机各节点流转记录,驱动定制进度看板) +CREATE TABLE `game_biz_progress` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '进度工单 ID', + `lead_id` BIGINT NOT NULL COMMENT '所属询单 ID', + `from_status` TINYINT NULL COMMENT '流转前状态(首条为 null)', + `to_status` TINYINT NOT NULL COMMENT '流转后状态(0-4)', + `operator_user_id` BIGINT NULL COMMENT '操作人用户 ID(运营推进;系统推进可空)', + `note` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '进度说明(看板时间线展示)', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', + PRIMARY KEY (`id`), + KEY `idx_lead_time` (`lead_id`, `create_time`) COMMENT '按询单取进度时间线(看板)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'B端定制进度工单表(看板时间线)'; + +-- 表4:game_biz_acceptance 交付验收表(试玩→反馈→确认三态;签署/收款留 M4) +CREATE TABLE `game_biz_acceptance` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '验收记录 ID', + `lead_id` BIGINT NOT NULL COMMENT '所属询单 ID', + `demo_version_id` BIGINT NOT NULL COMMENT '交付 demo 版本 ID(runtime game_version.id,试玩取包用)', + `feedback` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT '客户试玩反馈', + `confirm_status` TINYINT NOT NULL DEFAULT 0 COMMENT '确认态:0待确认 1已确认', + `confirmed_time` DATETIME NULL COMMENT '客户确认时间', + `signed_offline` BIT(1) NOT NULL DEFAULT b'0' COMMENT '线下签署后台标记兜底(M4 接在线签章前过渡:0未签 1已线下签)', + `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者', + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_lead_version` (`lead_id`, `demo_version_id`, `deleted`, `tenant_id`) COMMENT '同询单同 demo 版本一条验收(含 deleted/tenant 维度)', + KEY `idx_lead` (`lead_id`) COMMENT '按询单查验收' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'B端定制交付验收表(试玩/反馈/确认;签署收款留 M4)'; +``` + +### 4.3 CommunityNotifyApi 方法签名(`@Primary` 本地实现) + +> 路径:`game-module-community-api/.../api/CommunityNotifyApi.java`(接口,`@FeignClient(name=ApiConstants.NAME)`、`ApiConstants.NAME="community-server"`、`PREFIX=RpcConstants.RPC_API_PREFIX+"/community"`)+ `community-server/.../api/CommunityNotifyApiImpl.java`(`@RestController @Validated @Primary implements CommunityNotifyApi`,照 `ProjectApiImpl.java:22-25`)。实现入口**注入系统身份 `LoginUser(id=0)`+finally `clearContext`**(§8.2)。统一 `CommonResult` 信封;幂等以 `(userId,type,bizRef)` 去重(重复投递不重复落库)。 + +```java +public interface CommunityNotifyApi { + // P-NTF-02 生成完成通知:aigc 在 DifyCallbackServiceImpl.handleCallback 提交后调 + // 触发判定 = 提交后回查任务为 SUCCEEDED 终态且 versionId 已回填(非 result==TRUE,见 §6.5 挂点1); + // 三实参均取自回查的 AigcTaskDO(creatorUserId/gameId/versionId) + CommonResult notifyGenerateDone(@RequestParam("creatorUserId") Long creatorUserId, + @RequestParam("gameId") Long gameId, + @RequestParam("versionId") Long versionId); + // P-NTF-03 审核结果通知:project 在 reviewProject 提交后调; + // decision 三态(1通过/2拒绝/3下架,对齐 ReviewDecisionEnum)——本波仅对 1/2 发通知, + // decision=3 下架(含封禁联动)本波不发(caller 侧以 decision∈{1,2} 守门,见 §6.5 挂点2);reject(2) 须带 reason + CommonResult notifyReviewResult(@RequestParam("creatorUserId") Long creatorUserId, + @RequestParam("gameId") Long gameId, + @RequestParam("decision") Integer decision, + @RequestParam(value = "reason", required = false) String reason); + // P-NTF-05 收益变动通知:trade 在 SettlementServiceImpl.settle 入账成功后调 + // netAmount=创作者实得净额(分);settle 作用域无现成 net(recordIncome 内部算、不返回), + // 须在 settle 内现算 BigDecimal.valueOf(gross).multiply(creatorShare).setScale(0,DOWN)(见 §6.5 挂点3) + CommonResult notifyIncomeChanged(@RequestParam("creatorUserId") Long creatorUserId, + @RequestParam("source") Integer source, + @RequestParam("sourceRef") String sourceRef, + @RequestParam("netAmount") Long netAmount); + // P-INC-01 发布计数(方案a):project 发布成功后同进程 notify,累加 published_count 并触发里程碑 + CommonResult notifyProjectPublished(@RequestParam("creatorUserId") Long creatorUserId, + @RequestParam("gameId") Long gameId); +} +``` + +### 4.4 community.yaml 端点清单(`contracts/api-schemas/community.yaml`,段注释 1-107) + +| 方法 路由 | P0 | 说明 | +|---|---|---| +| GET `/app-api/community/messages` | P-NTF-01 | 站内信分页列表(query: `type` 1系统/2公告/3审核/4收益 可选, `readStatus` 可选, `cursor`/`size`) | +| POST `/app-api/community/messages/{id}/read` | P-NTF-01 | 标记单条已读(落 `read_status=1`+`read_time`) | +| GET `/app-api/community/messages/unread-count` | P-NTF-01 | 未读数角标(按 type 分组计数) | +| GET `/app-api/community/level` | P-INC-01 | 当前创作者等级 + `published_count` + 下一里程碑进度(如 2/3 距新人奖励) | +| GET `/app-api/community/rewards` | P-INC-01 | 我的奖励触发记录列表(status 触发态/发放态可见) | +| [RPC] `/rpc-api/community/*` | — | `CommunityNotifyApi` 四方法,供上游同进程 `@Primary` 调用,非对外业务路由 | +| [staging only] POST `/admin-api/community/notifications/{type}/mock-trigger` | — | 上游未接通前独立验收四类投递,**仅 dev/staging 生效**(`@Profile({"dev","staging"})` 或网关层关闭,生产关闭)。⚠️ 实查全 game-module **无 `@Profile` 先例**(现有 dev/staging 差异化手段是 `@ConditionalOnProperty` 灰度开关,如 aigc `AIGC_EXECUTOR_ENABLED`)——若用 `@Profile` 须知是本仓首例、在类或方法级标注、并验证 staging profile 下 Bean 注册生效;亦可改用 `@ConditionalOnProperty(name="community.mock-trigger.enabled", havingValue="true")` 与现有灰度开关风格一致。二选一钉死 | + +### 4.5 biz.yaml 端点清单(`contracts/api-schemas/biz.yaml`,段注释 1-110;端前缀框架按包名自动加,`@RequestMapping` 只写 `/biz`) + +**客户侧 `/app-api/biz/*`(game-studio,用户 Token + Mapper 强制 `getLoginUserId()` 归属谓词,WS3)** + +| 方法 路由 | P0 | 说明 | +|---|---|---| +| POST `/app-api/biz/leads` | P-BIZ-01 | 提交定制需求询单(落库即开状态机=待跟进0 + 落 progress 首条 to_status=0);req=`BizLeadCreateReqVO`(bizType/title/sceneDesc/contactName/contactPhone/company);resp=`CommonResult` | +| GET `/app-api/biz/leads/my` | P-BIZ-03 | 我的定制订单列表(`customer_user_id` 过滤,客户只见自己) | +| GET `/app-api/biz/leads/{id}` | — | 询单详情(含当前状态/最新报价/进度时间线/demo 可预览判定) | +| GET `/app-api/biz/leads/{id}/progress` | P-BIZ-03 | 进度看板时间线(`game_biz_progress` 按时间) | +| GET `/app-api/biz/templates` | P-BIZ-02 | 按场景检索可选模板(复用 aigc 4 模板);query=`bizType` | +| POST `/app-api/biz/acceptances/{leadId}/feedback` | P-BIZ-04 | 客户试玩后提交反馈;req=`BizFeedbackReqVO`(demoVersionId/feedback) | +| POST `/app-api/biz/acceptances/{leadId}/confirm` | P-BIZ-04 | 客户确认验收(`confirm_status=1`,状态机 3→4 已交付) | + +> demo 试玩取包**不在 biz.yaml 自建端点**——直接复用 runtime 真实双路由:`GET /app-api/runtime/package/{versionId}`(清单 meta,`CommonResult`,biz 看板「是否可预览」判定走此条)+ `GET /app-api/runtime/package/{versionId}/manifest`(原始 JSON,宿主 sha256 比对后注入)。**两条须写全**(评审版 R2-3)。 + +**admin `/admin-api/biz/*`(game-admin,RBAC `@PreAuthorize`,WS4)** + +| 方法 路由 | P0 | 权限点 | 说明 | +|---|---|---|---| +| GET `/admin-api/biz/leads/page` | — | `biz:lead:query` | 处理队列(按 owner/status/bizType 筛选) | +| POST `/admin-api/biz/leads` | P-BIZ-08/12 | `biz:lead:create` | 运营代客发起定制单(`customer_user_id` 可空) | +| PUT `/admin-api/biz/leads/{id}/assign` | — | `biz:lead:assign` | 认领/指派负责人(`owner_user_id`) | +| POST `/admin-api/biz/leads/{id}/quotes` | P-BIZ-08 | `biz:quote:create` | 报价输出(参数化估价,报价≠可支付订单;状态机 0→1) | +| GET `/admin-api/biz/leads/{id}/quotes` | — | `biz:quote:query` | 报价历史 | +| POST `/admin-api/biz/leads/{id}/advance` | P-BIZ-03 | `biz:lead:advance` | 推进状态机(→2制作中/→3待验收,→3 守 demo 可预览);req=`BizAdvanceReqVO`(toStatus/note/demoVersionId) | +| PUT `/admin-api/biz/acceptances/{id}/sign-offline` | P-BIZ-04 | `biz:acceptance:sign` | 线下签署后台标记兜底(`signed_offline=1`) | + +> **权限点登记(§7.4 必做)**:`biz:lead:query/create/assign/advance`、`biz:quote:create/query`、`biz:acceptance:sign` 需在 yudao `system_menu` 登记后 RBAC 才放行(范本=`project:review:query` 登记方式)。 + +--- + +## §0. 全局单序列资源预占登记(⚠️ 开篇第一节,单 agent 一次性收口提交) + +> **目的**(评审版 §8 §0 / R1-5 / R6):防 V12/V13 双占、文件名碰撞、错误码撞段。这是**一个可勾的中央登记动作,由单 agent 收口提交**(建 community 的 agent 在动任何代码前先做完本节并提交,biz agent 从该提交 pull)。 + +### §0.1 预占前置校验(落地时实跑,证据入提交说明) + +```bash +cd /root/games-development-ai +# ① Flyway 文件名占用校验(按文件名,非内容;应全空、exit≠0=未占用) +ls contracts/db-schemas/V12* contracts/db-schemas/V13* \ + game-cloud/yudao-server/src/main/resources/db/migration/V12* \ + game-cloud/yudao-server/src/main/resources/db/migration/V13* 2>/dev/null; echo "exit=$? # 期望 exit=2(无此文件)" +# ⚠️ 不要用 `grep -r 'V12\|V13'` 判占用——它是内容匹配,会命中 +# V11.0.0__create_passport_player_invite.sql 的让位声明注释(两处副本各一行,实测返 2 行),命中仅此注释属预期、非真实占用。 +# ② 错误码段占用校验(应全返 0) +grep -rn '1_107\|1_110' game-cloud/ | wc -l # 期望 0 +# ③ 契约 yaml 占用校验(应不存在) +ls contracts/api-schemas/community.yaml contracts/api-schemas/biz.yaml 2>/dev/null; echo "exit=$? # 期望 exit=2" +``` + +> 亲核 2026-06-11(dev/2.0.0):① exit=2(V12/V13 均无);② 返 0;③ exit=2。**三项均空 = 可安全预占。** + +### §0.2 预占登记表(单 agent 写死锁定) + +| 资源 | community | biz | 一致性要求 | +|---|---|---|---| +| Flyway 主版本 | `V12.0.0__create_game_community.sql` | `V13.0.0__create_game_biz.sql` | 两处副本(`contracts/db-schemas/` + `yudao-server/.../db/migration/`)**字节一致**(`md5sum` 全等;命名 `V{x.y.z}__{desc}.sql`,**只增不改旧**——改 V1–V11 任一 → `flyway validate` 阻断 CI) | +| 错误码段 | `1_107_***_***`(`-api` 的 `ErrorCodeConstants.java`) | `1_110_***_***`(同) | `contracts/README.md §四:79` **已在册**(复用不改);段内子码按 `new ErrorCode(1_107_00X_***, "中文")` 分块、文件头列全 13 段防重叠 | +| 契约 yaml | `contracts/api-schemas/community.yaml` | `contracts/api-schemas/biz.yaml` | 独占新文件 | +| W4 埋点加列占位 | — | — | **V14 预留**给 W4 埋点加列(本波不写,仅在登记表占号防后续撞 V12/V13) | + +> **Flyway 副本数裁决(recon `errorcode-flyway-ddlconv` 必裁项)**:**取「两副本」**(`contracts/db-schemas/` 授权源 + `yudao-server/.../db/migration/` 唯一执行副本),**不放单模块 `-server/db/migration/`**。依据:V10/V11(跨模块件)已开「两副本」先例;单体部署下 yudao-server 聚合全部 migration,单模块副本是冗余且多一处漂移风险;放进单模块 classpath jar 反而可能触发同版本 Flyway 在多 jar 重复校验失败(V11 守门①根因)。**community/biz `-server` 下不放任何 sql。** + +### §0.3 收口动作 + +单 agent 一次性创建并提交:两个 V12/V13 SQL(各两副本字节一致)+ 两个空 yaml 骨架(或含完整端点)+ root `game-cloud/pom.xml` 的 `` 注册 + yudao-server pom 依赖。**先提交 push,community/biz 实现 agent 再从此提交 pull**,避免并行同改 root pom/Flyway 冲突。 + +--- + +## 5. 逐模块克隆关键步骤(community 先,biz 后) + +> 通用「克隆 5 步骨架」(评审版 §8):cp 黄金模块 → 改 3 个 pom artifactId → **① root pom 注册 `` + ② yudao-server pom 注册 `-server` 依赖** → -api 写 `ErrorCodeConstants`+`ApiConstants`+枚举+DTO+Feign → -server 写 controller/service/dal/convert/ApiImpl@Primary。**装配=两处**(§2.2),克隆开篇先复述,勿以旧 skill 为唯一依据。 + +### 5.1 community 建设步骤(13 步,逐步可勾) + +| # | 步骤 | 关键点 / 防雷 | +|---|---|---| +| C0 | 完成 §0 预占收口(V12 两副本 + community.yaml + root pom `` + yudao-server pom 依赖),提交 push | 单 agent 收口;biz 从此提交 pull | +| C1 | `cp -r game-module-project game-module-community`,包路径 `project`→`community`、artifactId 三处改(api/server/聚合 pom) | 包名固定 `cn.wanxiang.game.module.community.*`;⚠️ 克隆后清理 community-server pom 中 project 发布编排专用、community 不依赖的跨模块 -api(compliance-api/runtime-api/feed-api/system PlayerApi 等)——community 作为通知收件 + 等级引擎**自包含、不调任何跨模块 -api**(卫生项,留孤儿依赖与「最小变更」相悖;与黄金模块列集一致由 §0 装配点保障,非 pom 依赖集) | +| C2 | 改 `ApiConstants.java`:`NAME="community-server"`、`PREFIX=RPC_API_PREFIX+"/community"` | ⚠️ **不改则 Feign 服务名撞 project-server**(R8) | +| C3 | 改 `ErrorCodeConstants.java`:段 `1_107_000_000` 起,文件头列全 13 段防重叠 | ⚠️ **顺手复制 project 100 段忘改 → 撞段**(R8) | +| C4 | 写 `CommunityNotifyApi.java`(四方法,§4.3 签名)+ DTO | `@FeignClient(name=ApiConstants.NAME)` | +| C5 | 写 `CommunityNotifyApiImpl.java`(`@RestController @Validated @Primary`),入口注入 `LoginUser(id=0)`+finally `clearContext` | ⚠️ **R3 系统身份写雷**(§8.2),单测 mock 测不到 | +| C6 | 写三表 DO(`extends TenantBaseDO`,`@TableName` 改对应新表名+`@KeySequence` 名随表改如 `game_community_message_seq` 或删除)+ Mapper(`BaseMapperX`,显式列禁裸 select*,客户侧查询强制 `getLoginUserId()` 归属谓词,`published_count` 原子自增方法)+ Convert | ⚠️ 克隆继承 `@KeySequence("game_project_seq")`(实查 ProjectDO:19,MySQL 忽略但残留语义错乱)须改名或删;`level` 自增用 `UPDATE … SET published_count=published_count+1 WHERE creator_user_id=?`(§6.4 防并发丢更新) | +| C7 | 写 `service/message/`(投递/已读/未读数)+ `service/notify/`(编排:路由四类→去重→写站内信)+ `service/level/`(计数累加→里程碑判定→插 reward) | `@Transactional` 只加 service 层 | +| C8 | 写 `controller/app/message/`(5 个 app 端点,§4.4,**Mapper 强制 `getLoginUserId()` 归属谓词**)+ `controller/admin/notification/`(mock-trigger,`@Profile({"dev","staging"})` 本仓首例 或 `@ConditionalOnProperty` 灰度开关,§4.4/§8.4 二选一) | mock-trigger 须限定 staging,否则成匿名伪造投递入口(§8.4);客户侧端点隔离靠显式 user_id 谓词,非 @DataPermission(黄金模块未用) | +| C9 | 接挂点1(aigc):`DifyCallbackServiceImpl` succeeded 后调 `notifyGenerateDone`(§6.5);aigc `-server/pom.xml` 加 `game-module-community-api` 依赖 | try-catch 吞异常 | +| C10 | 接挂点2(project):`AdminProjectController.reviewProject` 返回后调 `notifyReviewResult`,`isApprove` 时多调 `notifyProjectPublished`(§6.5);project `-server/pom.xml` 加 community-api 依赖 | reject 须传 `reqVO.getReason()` | +| C11 | 接挂点3(trade):`SettlementServiceImpl` settle 循环 `firstRecord` 分支调 `notifyIncomeChanged`(§6.5);trade `-server/pom.xml` 加 community-api 依赖 | 仅首次入账才通知,幂等不重发 | +| C12 | Nacos 配置注册(§7.3)+ 单测(`extends BaseMockitoUnitTest`,mock insert/updateById 用 `any(XxxDO.class)` 消歧) | 单测覆盖:投递/已读/计数累加/里程碑触发幂等 | +| C13 | mini-desktop 工程门 + staging 链路①实测(§9.2/§9.3) | 「编译过≠可验收」 | + +### 5.2 biz 建设步骤(11 步) + +| # | 步骤 | 关键点 / 防雷 | +|---|---|---| +| B0 | 从 §0 收口提交 pull(V13 两副本 + biz.yaml + root pom + yudao-server pom 已就位) | — | +| B1 | `cp -r game-module-project game-module-biz`,包/artifactId 改 `biz` | 包名 `cn.wanxiang.game.module.biz.*` | +| B2 | 改 `ApiConstants.java`:`NAME="biz-server"`、`PREFIX=…+"/biz"`;`ErrorCodeConstants.java` 段 `1_110` | ⚠️ R8 同 community | +| B3 | 写 `BizLeadStatusEnum.java`(单线性五态 + `of`/`canTransit`,§6.6 范本)+ 错误码(`BIZ_LEAD_NOT_EXISTS`/`BIZ_LEAD_STATUS_ILLEGAL_TRANSITION`/`BIZ_DEMO_NOT_READY`) | 合法流转矩阵:仅 `current→current+1` 单跳 | +| B4 | 写四表 DO + Mapper + Convert(§4.2 DDL 对应) | ⚠️ 客户侧查询 Mapper 强制以 `getLoginUserId()`=`customer_user_id` 作 WHERE 谓词(范本 `ProjectMapper.selectMyPage`,黄金模块**无** @DataPermission 可抄,勿假定框架已隔离);克隆后清理 biz-server pom 中 project 发布编排专用、biz 不依赖的跨模块 -api(compliance-api/feed-api/system PlayerApi 等),**保留 runtime-api**(demo 取包用,project-server pom :44 已携带、克隆即继承);DO 上 `@KeySequence` 名随表改(如 `game_biz_lead_seq`)或删除(本工程恒 MySQL,该注解被忽略),`@TableName` 改对应新表名 | +| B5 | 写 `service/lead/BizLeadServiceImpl.java`:提单/报价/`advanceStatus`(写前 `canTransit` 校验,→3 额外 `validateDemoPreviewable`,§6.6) | 流转校验内联,**不引 Flowable** | +| B6 | `validateDemoPreviewable`:同进程注入 `RuntimePackageApi`(`@Primary` 本地实现)调 `getStatus(versionId)` 判 demo 可预览(§6.7 负路径) | ⚠️ demo 取包失败降级,不透传 500 | +| B7 | 写 `controller/app/biz/`(7 个客户端点,§4.5)+ `controller/admin/biz/`(7 个 admin 端点,`@PreAuthorize("@ss.hasPermission('biz:...')")`) | 端前缀框架自动加 | +| B8 | 写 `BizTemplateService`:返 aigc 4 可玩模板(clicker/merge/idle/tycoon);dodge/runner/match 仅契约未实现**不返** | §6.7 | +| B9 | 权限点 SQL 登记(`system_menu`,§7.4)+ Nacos 配置(§7.3)+ 单测(`BizLeadServiceImplTest`:流转合法/非法/→3 无 demo 拒) | mock RuntimePackageApi.getStatus 测负路径 | +| B10 | mini-desktop 工程门 + staging 链路②实测(§9.2/§9.3)+ 客户/admin UI 入口走查跟进项(§9.4) | 「编译过≠可验收」 | + +--- + +## 6. 关键实现细节(业务逻辑 + 接线) + +### 6.1 community 通知编排(T-CMU-10) + +四类消息(type 1系统/2公告/3审核/4收益)统一经 `notify` service:路由 type → 组装 title/content(审核拒绝含 reason)→ **幂等去重 `(user_id,type,biz_ref)`**(重复投递不重复落库)→ `mapper.insert(MessageDO)`。 + +### 6.2 community 站内信(T-CMU-06) + +`AppCommunityMessageController` 5 端点:列表(按 type/readStatus 过滤、cursor 分页)/ 标记已读(`read_status=1`+`read_time=now`)/ 未读数(按 type 分组 count)/ 等级查询 / 奖励记录列表。**归属隔离=Mapper 查询强制以 `SecurityFrameworkUtils.getLoginUserId()` 作 `user_id` WHERE 谓词**(范本 `ProjectMapper.selectMyPage`「强制按当前用户过滤」,黄金模块**无** @DataPermission;标记已读须先校验该消息 `user_id==当前登录 id` 再落 read,防越权改他人消息已读态)。 + +### 6.3 community 等级引擎(T-CMU-11) + +`level` service:`notifyProjectPublished` 触发 → **原子自增** `published_count`(§6.4)→ 同事务内判 `published_count == publish-threshold`(默认 3)→ 达标且 `uk_creator_milestone` 不存在则插一条 `game_community_reward`(`milestone=publish_3`、`status=0` 已触发待发放、`reward_type`/`reward_amount` 按配置)。**本波终态=触发可观测+本地记账,不跨模块发放。** + +### 6.4 P-INC-01 计数权威源(方案 a 落地,§0 单 agent 已拍) + +- **计数权威源 = project 发布成功**(非 feed 互动回流)。实查 ProjectApi **无** `countPublishedByCreator` 方法(只有 getCreatorUserId/getCurrentVersionId/getStatus/getFeedMeta/createProject/unlistIfPublished),故方案 b 需 project 新增端点;**取方案 a = project 发布成功后同进程 notify community 计数**(上游单点改动、与三上游 notify 同范式)。 +- **⚠️ 计数交付口径 = 「尽力计数」(R-high 对齐,承认 at-most-once 边界)**:方案 a 的 notify 在事务提交后调用且 try-catch 吞异常(§6.5 挂点2),本质是 **at-most-once 投递**——notify 因瞬时故障/重启丢一次,`published_count` 不会再被补发(project 侧无重放、ProjectApi 无 `countPublishedByCreator` 兜底真相源)。因此本波**明确把 `published_count` 定为「尽力计数」**:允许极端情况下少计/漏触发里程碑,不宣称「绝对权威强一致」(与 reward 的 `uk_creator_milestone` 强幂等是两件事——后者只保证「到达阈值后只发一次」,不保证「计数一定到得了阈值」)。**M4 真实记账时**由 project 补 `countPublishedByCreator` 只读端点 + community 读时对账重建,届时升为可对账权威源(§10.2 ①资金流债已含此项)。AC-CMU-5 据此订正为「计数到达阈值即触发」(§9.1)。 +- **并发安全(recon 必落项)**:`notifyProjectPublished` 并发到达同一 creator 时,`published_count` 累加**须 DB 原子 `UPDATE … SET published_count=published_count+1 WHERE creator_user_id=?`**(Mapper default 方法),避免读改写丢更新导致计数偏少漏触发里程碑;累加后**同事务**判 `count==threshold` 才插 reward,`uk_creator_milestone` 兜底幂等(重复发布不重触发)。 +- **首次初始化**:`game_community_level` 一创作者一条(`uk_creator`),首次 notify 时 `INSERT … ON DUPLICATE KEY` 或先查后插(幂等)。 + +### 6.5 三上游 notify 挂点接线(事务提交后 + try-catch 吞异常) + +> **通用规则**:三上游写方法本身均 `@Transactional`,notify 必在本地事务**提交后**调用(外层 caller 或循环内已提交分支),避免通知失败回滚业务。**统一 `try-catch` 包裹、仅记 error log**,不得让通知失败中断业务/结算循环。 + +- **挂点1 P-NTF-02(aigc)**:`DifyCallbackServiceImpl.handleCallback`(亲核 `:50-71` `return txService.handleCallbackTx(reqVO);`,内层 `@Transactional` 三表写入链于此提交;成功回填动作=内层 `DifyCallbackTxService.handleCallbackTx` 步骤⑦调 `aigcTaskService.completeWithVersion(taskId, versionId)`,**不是** `DifyCallbackService` 的「成功路径」——勿误引)。 + - **⚠️ 三处硬约束(R-blocker,照字面无法落地,必须按下方处方)**: + 1. **`handleCallbackTx` 返回值无法判别真成功**:实查内层 succeeded(:204)/ config_invalid 业务性失败(:166)/ failed(:155)/ 幂等短路(:122)**全部 `return Boolean.TRUE`**,故 `result==TRUE` **不能**作为 succeeded 判据(照搬会对生成失败/重复回调误发「生成完成」)。 + 2. **外层作用域无 gameId/versionId/creatorUserId**:`DifyCallbackReqVO` 字段仅 `traceId/status/templateId/gameConfig/assets/qualityScore/failureReason`(**无三者**);`versionId` 是内层事务步骤④新建的局部变量、不随 `Boolean` 返回;`gameId/creatorUserId` 是内层经 `aigcTaskMapper.selectByTraceId` 反查的 `task` 字段。三个 notify 实参在外层均无来源。 + 3. **不走 `projectApi.getCreatorUserId(gameId)`**:该路径前提 `gameId` 本身在外层不可达;且 `AigcTaskDO`(实查 :37/:41/:45)已自带 `creatorUserId/gameId/versionId` 三字段,回查任务一次即全得,更直接。 + - **可落地处方(实查 `selectByTraceId` 已存在、`AigcTaskDO` 三字段齐备,aigc 自有 `AigcTaskMapper` 可直接注入)**:内层事务提交后,在外层 `handleCallback` 末尾按 traceId 回查任务,仅真实成功终态才发: + ```java + Boolean result = txService.handleCallbackTx(reqVO); // 内层 @Transactional 已提交 + // 提交后回查任务:仅 SUCCEEDED 且 versionId 已回填 = 真实成功终态, + // 天然排除 config_invalid/failed(status=FAILED)与幂等短路无新版本场景 + try { + AigcTaskDO task = aigcTaskMapper.selectByTraceId(reqVO.getTraceId()); + if (task != null + && Objects.equals(task.getStatus(), AigcTaskStatusEnum.SUCCEEDED.getStatus()) + && task.getVersionId() != null) { + communityNotifyApi.notifyGenerateDone(task.getCreatorUserId(), task.getGameId(), task.getVersionId()); + } + } catch (Exception e) { log.error("[handleCallback] 生成完成通知失败(不回滚业务)traceId={}", reqVO.getTraceId(), e); } + return result; + ``` + > 注:`notifyGenerateDone` 的触发判定**不是** `result==TRUE`,而是「回查任务为 SUCCEEDED 终态且 versionId 已回填」(§4.3 签名注释同步修正)。aigc `-server` 已依赖 project/runtime `-api`,本处只新增对 community-api 的依赖;`AigcTaskMapper`/`AigcTaskStatusEnum` 为 aigc 自有,直接注入/引用。 +- **挂点2 P-NTF-03 + P-INC-01(project)**:`ProjectServiceImpl.reviewProject`(亲核 `:206-242` `@Transactional`,落 review_record + 状态机 +(APPROVE)发布编排 :240)。notify **不可放方法内**(会被回滚牵连)。真实位置=**外层 caller** `AdminProjectController.reviewProject`(亲核 `:55-59`,`projectService.reviewProject(reviewReqVO, reviewerUserId)`)。 + - **⚠️ 作用域处方(R-medium,caller 侧 `creatorUserId`/`isApprove` 均不在原作用域,须显式取得)**:实查 `AdminProjectController.reviewProject` 方法体只有 `reviewReqVO`(字段 `gameId/versionId/decision/reason`,**无 creatorUserId**)与 `reviewerUserId`;`isApprove` 概念仅存在于 `ProjectServiceImpl` 内部(:213),控制器取不到。caller 须自行派生(`AdminProjectController` 增注入 `ProjectApi`,project-server 已依赖 project-api,自调 `@Primary` ProjectApiImpl 可达): + ```java + projectService.reviewProject(reviewReqVO, reviewerUserId); // service @Transactional 已提交 + try { + Long creatorUserId = projectApi.getCreatorUserId(reviewReqVO.getGameId()).getCheckedData(); + if (creatorUserId != null) { // 游戏不存在/已删时 null,跳过 notify + Integer decision = reviewReqVO.getDecision(); + boolean isApprove = Objects.equals(decision, ReviewDecisionEnum.APPROVE.getDecision()); + // ★ 仅对「通过(1)/拒绝(2)」发审核结果通知;下架(3, 含封禁联动)本波不发(消除未定义文案分支) + if (isApprove || Objects.equals(decision, ReviewDecisionEnum.REJECT.getDecision())) { + communityNotifyApi.notifyReviewResult(creatorUserId, reviewReqVO.getGameId(), decision, reviewReqVO.getReason()); + } + if (isApprove) { communityNotifyApi.notifyProjectPublished(creatorUserId, reviewReqVO.getGameId()); } + } + } catch (Exception e) { log.error("[reviewProject] 审核/发布通知失败(不回滚审核)gameId={}", reviewReqVO.getGameId(), e); } + ``` + - ⚠️ **owner 钉准(评审版 §6.2 注 / recon `community-design` risk)**:Doc C 标 P-NTF-03 辅技术功能=compliance「审核状态机事件源」,但**实查审核结果落库点是 project 侧 `ProjectServiceImpl.reviewProject`**(写 review_record + 状态机),compliance `ComplianceGateServiceImpl.evaluate` 只做 Gate 二态不碰 project 审核态。故 notify 挂 **project.reviewProject 外层 caller,不挂 compliance**(否则 reason 取不到)。**该改挂与「唯一设计依据」评审版 §2.1/§2.2/AC-CMU-3 的 compliance 上游源矛盾,已回流评审版勘误#1(§11)订正——验收以 project 挂点为准。** + - ⚠️ **UNLIST 第二调用路径漏通知(R-medium,已知不通知项)**:`ProjectApi.unlistIfPublished`(`ProjectServiceImpl:282-297`)以 `SYSTEM_REVIEWER_ID` **直接调 service 层 `reviewProject(reqVO, SYSTEM_REVIEWER_ID)`(:295)绕过 AdminProjectController**,供 compliance 封禁联动调用。本波 notify 钩子只在 controller,故**经封禁联动/系统下架路径一律不发通知**——属本波**非 P0 的已知不通知项**(下架通知本就不在 community 5 P0 内),不补钩子(补则须下沉 service 用 `TransactionSynchronization.afterCommit`,超本波边界)。 + - ⚠️ **published_count 计数幂等口径(R-high,承认边界)**:`notifyProjectPublished` 仅在 `isApprove` 分支调用,而 `reviewProject` 受状态机「仅审核中(REVIEWING)可通过」约束(再审被挡死),故正常每款游戏只产生一次 approve→一次计数。**但 P-INC-01 计数权威源建立在「事务提交后 notify + try-catch 吞异常」=at-most-once 投递上**,存在两类已知风险:① notify 因瞬时故障/重启丢失 → 该次发布永久少计;② framework 重试/前端重复提交致同一 gameId 二次 approve 通知 → 计数偏大。`uk_creator_milestone` 只防「重复发钱」、不防「count 本身偏差」。**本波取「尽力计数」口径**(见 §6.4 收紧 + AC-CMU-5 订正),不引入按 gameId 的计数级幂等表(属 M4 真实记账时统一对账范畴)。 +- **挂点3 P-NTF-05(trade)**:`SettlementServiceImpl.settle`(亲核 :55-89 循环内 `if (firstRecord) { newlyRecorded++; }` 分支;`recordIncome` 为 `@Transactional` 单条已提交,循环体非事务)。 + - **⚠️ `net` 是幽灵变量(R-medium,照字面编译不过)**:实查 settle 循环作用域只有 `revenue`(`AdRevenueRespDTO`,含 `getRevenueAmount()`=gross 毛额 / `getCreatorUserId()` / `getId()`)与 `creatorShare`(`@Value("${trade.creator-share:0.80}")`);`net` 在 `IncomeServiceImpl.recordIncome` **内部**由 `calcNet(gross, shareRate)` 算(`BigDecimal.valueOf(gross).multiply(shareRate).setScale(0, DOWN)`,:86-89),且 `recordIncome` 返回 `boolean`(**不返 net**)。settle 局部无 `net` 这个名字。 + - **可落地处方(二选一,钉准取 a 现算——口径与 recordIncome 落账完全一致、零跨改)**: + - **(a) settle 内现算 net**(推荐):`firstRecord` 分支内先 `long net = BigDecimal.valueOf(revenue.getRevenueAmount()).multiply(creatorShare).setScale(0, java.math.RoundingMode.DOWN).longValue();`(与 `IncomeServiceImpl.calcNet` 同公式同舍入,保证通知净额=落账净额),再 `try { communityNotifyApi.notifyIncomeChanged(revenue.getCreatorUserId(), TradeIncomeSourceEnum.AD.getSource(), String.valueOf(revenue.getId()), net); } catch (Exception e) { log.error("[settle] 收益变动通知失败(不中断结算循环)revenueId={}", revenue.getId(), e); }`(仅首次真实入账才通知,幂等复跑不重发)。 + - (b) 改 `recordIncome` 返回入账净额(或入账 DO 含 net),settle 据返回值传——但 `recordIncome` 返回值语义(首次入账 boolean)被结算回标逻辑依赖,改签名需回归 trade 现有调用方,故不取。 + - 访问器核实:`TradeIncomeSourceEnum.AD`(实查 :22 `AD(1,"广告")`,`@Getter` 于字段 `source`)访问器为 `getSource()`(文档用法正确)。 + - ⚠️ **事务边界 nuance(recon `community-design` risk)**:settle 是定时任务、循环体非事务、内层 `recordIncome` 单条已提交;`firstRecord` 分支调 notify 满足「入账已提交」,但若后续条目或回标失败,已发 notify 无法回滚——评审版接受此口径(通知失败不回滚业务、反之亦然),故 notify 异常**须 try-catch 吞掉 + 告警日志**,不得中断结算循环。 + +### 6.6 biz 轻量状态机(范本 project ProjectStatusEnum/ProjectServiceImpl) + +```java +// game-module-biz-api/.../enums/BizLeadStatusEnum.java +// 合法流转:0待跟进→1已报价→2制作中→3待验收→4已交付(仅相邻正向单跳,不可跳级/回退) +// 非法流转由 BizLeadService 写前拒(抛 BIZ_LEAD_STATUS_ILLEGAL_TRANSITION) +// 硬规则:→3待验收 必须已有可玩 demo(demo_version_id 非空且 runtime 取包可预览),否则抛 BIZ_DEMO_NOT_READY +@Getter @AllArgsConstructor +public enum BizLeadStatusEnum { + PENDING(0, "待跟进"), QUOTED(1, "已报价"), PRODUCING(2, "制作中"), ACCEPTING(3, "待验收"), DELIVERED(4, "已交付"); + private final Integer status; private final String name; + public static BizLeadStatusEnum of(Integer status){ return Arrays.stream(values()).filter(e->Objects.equals(e.status,status)).findFirst().orElse(null); } + /** 合法流转矩阵:仅允许 current→current+1 单跳(单线性,5 态 4 条边) */ + public static boolean canTransit(Integer from, Integer to){ return from!=null && to!=null && to == from + 1 && of(from)!=null && of(to)!=null; } +} +``` + +```java +// game-module-biz-server/.../service/lead/BizLeadServiceImpl.java(流转校验骨架,范本 ProjectServiceImpl:207 同款事务边界) +// ★ 必须 @Transactional:两写(改 lead.status + insert progress 进度行)须原子,否则崩溃会留下 +// 「状态已翻但时间线缺该节点行」或反之的不一致,看板与状态机错位(事务边界铁律 §3 红线) +@Transactional(rollbackFor = Exception.class) +public void advanceStatus(Long leadId, Integer toStatus, Long operatorUserId){ + BizLeadDO lead = bizLeadMapper.selectById(leadId); + if (lead == null) throw exception(BIZ_LEAD_NOT_EXISTS); + Integer from = lead.getStatus(); + if (!BizLeadStatusEnum.canTransit(from, toStatus)) throw exception(BIZ_LEAD_STATUS_ILLEGAL_TRANSITION); // 写前拒非法流转 + // ★ →3待验收 守门:必须有可玩 demo(复用 runtime 取包判定,无产物/编译失败/versionId 缺失即拒) + if (Objects.equals(toStatus, BizLeadStatusEnum.ACCEPTING.getStatus())) { validateDemoPreviewable(lead.getDemoVersionId()); } + BizLeadDO update = new BizLeadDO(); update.setId(leadId); update.setStatus(toStatus); bizLeadMapper.updateById(update); + // 落进度工单(看板时间线);若由非 Web 线程/系统身份触发,入口须 LoginUser(id=0)+finally clearContext(否则 updater NOT NULL 拒绝 500) + BizProgressDO p = new BizProgressDO(); p.setLeadId(leadId); p.setFromStatus(from); p.setToStatus(toStatus); p.setOperatorUserId(operatorUserId); bizProgressMapper.insert(p); +} +``` + +> **confirm 端点跨表原子性(R-medium,必规约)**:客户 `POST /app-api/biz/acceptances/{leadId}/confirm`(§4.5)涉及**两表写 + 状态机 3→4**——置 `game_biz_acceptance.confirm_status=1`+`confirmed_time` **与** `advanceStatus(leadId, 4, ...)`(推进 lead 3→4 + 落 progress 行)**必须在同一 `@Transactional(rollbackFor=Exception.class)` 内**,任一失败整体回滚,杜绝「已确认验收但 lead 未推进到已交付」的跨表不一致。前置约束:`canTransit` 仅允许相邻正向单跳,confirm 走 3→4 合法,但**前置态须确为 3(待验收)**,否则 `canTransit(非3, 4)` 抛 `BIZ_LEAD_STATUS_ILLEGAL_TRANSITION`——端点文案须说明「仅待验收态可确认」。 + +### 6.7 biz demo 预览取包接线(复用 runtime 双路由 + 负路径降级) + +- **模板列表 P-BIZ-02**:`BizTemplateService` 返 aigc **4 可玩模板**(clicker/merge/idle/tycoon,`contracts/templates/` 已有 runtime 真实实现);dodge/runner/match 仅契约落盘未实现,**不返**。 +- **demo 取包**:复用 runtime 双路由 `GET /app-api/runtime/package/{versionId}`(清单 meta,biz 看板「是否可预览」走此条)+ `/manifest`(原始 JSON)。 +- **`validateDemoPreviewable(demoVersionId)`(→3 守门 + 负路径)**:同进程注入 `RuntimePackageApi`(`@Primary` 本地实现)调 `getStatus(versionId)`;判定口径=`status∈{0预览就绪,1已发布}` 即可预览,`null`=无就绪包=不可预览。**负路径(R1 必验)**:`demoVersionId` 缺失/非法、无编译产物(runtime 抛 `RUNTIME_PACKAGE_NOT_READY` 1-102-001-001)、编译失败时,**抛 `BIZ_DEMO_NOT_READY`**(看板降级显示「该模板暂不可预览」),**不透传 500**;状态机**不允许在无可玩 demo 时推进到「待验收」**。 +- ⚠️ **预览鉴权口径缺口(R-low 拆分表述,勿合并为单一警告)**:两条路径不同,须分别处理: + - **「是否可预览」判定(`validateDemoPreviewable` 走 `RuntimePackageApi.getStatus`)= owner-agnostic、不受 owner 校验影响**。实查 `RuntimePackageApi.getStatus`(:60-63)委托 `RuntimePackageService` **纯只查 status 单列、不做 owner/scene 校验**,故此判定路径**不会**撞 `RUNTIME_PACKAGE_PREVIEW_NOT_OWNER`——实现 agent 勿误以为 getStatus 会被 owner 拦。 + - **客户真实试玩取包(走控制器 `GET /app-api/runtime/package/{versionId}`)才有 owner+scene 门禁**:preview 场景 `userId==null` 即拒;biz 客户(`customer_user_id`)≠版本创作者(运营代建)时可能触发 `RUNTIME_PACKAGE_PREVIEW_NOT_OWNER`(1-102-001-003)。**建议**:biz demo 走 **play 场景(status=1 已发布)** 而非 preview 场景,或 biz demo 先发布到 status=1 后再供客户试玩(避免 owner 校验拦截)。**此 owner 风险仅存在于真实试玩取包路径,与上一条 getStatus 判定无关**。落地前与 runtime owner 确认此口径。 + +--- + +## 7. 配置与权限登记 + +### 7.1 装配点(§2.2 复述) + +两处:① root `game-cloud/pom.xml` `` 注册 `game-module-community`+`game-module-biz`(line34 后);② `yudao-server/pom.xml` `` 注册两个 `-server` 依赖(studio-server 后)。 + +### 7.2 上游 -server pom 加 community-api 依赖 + +aigc / project / trade 三个 `-server/pom.xml` 各加 `game-module-community-api` 依赖(照 `project-server/pom.xml:34-53` 加 compliance-api/runtime-api/feed-api 的写法)。 + +### 7.3 Nacos 模块配置注册(对齐 add-business-module.md 步骤6) + +| 配置项 | 默认值 | 用途 | +|---|---|---| +| `community.sms.enabled` | `false` | 短信发送开关(收益高优可选短信;默认关,站内信成功即 AC 达标) | +| `community.level.publish-threshold` | `3` | 新人里程碑阈值(发满 3 作品触发 reward) | +| `biz.state-machine.timeout-hours` | (评审版列)| 状态机节点超时(如需;硬编码则可跳过) | + +`deploy/nacos/` 加配置项并导入,验证 Nacos 控制台可见。**若 community/biz 通知模板/阈值硬编码无模块级配置,本步可精简**(评审版未强制要求新增 Nacos 配置)。 + +### 7.4 biz admin 权限点登记(⚠️ recon `biz-design` 必做缺口) + +`/admin-api/biz/*` 用 `@PreAuthorize("@ss.hasPermission('biz:...')")`,权限点 `biz:lead:query/create/assign/advance`、`biz:quote:create/query`、`biz:acceptance:sign` **需在 yudao `system_menu` 登记后 RBAC 才放行**——否则 admin 端点建好但管理员无权限调用(403)。本文要求:execution 落地时以 **V13 或单独菜单初始化 SQL** 登记这些权限点(范本=`project:review:query` 在 yudao 的登记方式)。 + +--- + +## 8. 边角失败路径(必落) + +### 8.1 事务提交后 notify + try-catch 吞异常 + +见 §6.5:三上游 notify 均在事务提交后调用 + try-catch 仅记 error log,通知失败不回滚业务、不中断结算循环。 + +### 8.2 系统身份写防 updater NOT NULL 雷(R3,单测 mock 测不到) + +> community 通知投递 / reward 记账 / biz 状态机推进若由**非 Web 线程或系统身份**触发 → 无登录态 → `updater` NOT NULL 拒绝 → 链路 500/任务卡死。**单测 mock 测不到此雷(项目记忆铁律)。** + +范本(照 `EventIngestServiceImpl.injectSystemIdentityIfAnonymous`,`game-module-telemetry/.../service/event/EventIngestServiceImpl.java:187-198` + finally `:144-147`,canonical 源头=`AigcGenerateExecutor.executeWithSystemIdentity`): + +```java +// CommunityNotifyApiImpl 四方法入口 + BizLeadServiceImpl 系统/异步触发入口 +LoginUser loginUser = new LoginUser().setId(0L).setUserType(UserTypeEnum.ADMIN.getValue()).setTenantId(1L); +SecurityContextHolder.setContext(new SecurityContextImpl(...)); // 注入系统身份 +try { + // ... 业务写(DefaultDBFieldHandler 自动填 creator/updater='0') +} finally { + SecurityContextHolder.clearContext(); // 防 Web 线程上下文泄漏 +} +``` + +### 8.3 demo 取包失败降级 + +见 §6.7:`validateDemoPreviewable` 对取包失败/无产物/versionId 缺失抛 `BIZ_DEMO_NOT_READY`、看板降级「该模板暂不可预览」、状态机拒推进到待验收,**不透传 500**。 + +### 8.4 mock-trigger 安全边界 + +`/admin-api/community/notifications/{type}/mock-trigger` 须 `@Profile({"dev","staging"})`(**本仓首例**,全 game-module 无 @Profile 先例,须验证 staging profile 下 Bean 注册生效)**或** `@ConditionalOnProperty`(与现有 `AIGC_EXECUTOR_ENABLED` 灰度开关风格一致,推荐)**或**网关层关闭,**生产一律关闭**——否则成匿名伪造投递入口(安全须在可信边界 enforce,前端不够)。三者二选一钉死给最小代码范本。 + +### 8.5 @Primary Bean 冲突(R7) + +新模块 Feign 接口 + 本地 `@Primary` 实现可能 Bean 冲突致启动失败(M1 实证 4 个 CommonApi)。`ApiImpl` 标 `@RestController @Primary`;仍冲突则给 `@FeignClient` 补 `primary=false`(修法 commit d7fac90);启动盯 `BeanDefinition` 冲突日志。 + +--- + +## 9. 验证方法(community 5 + biz 6 P0 AC + 工程门 + staging 实测 + UI 走查) + +### 9.1 AC 逐条口径 + +**community(5 P0)** + +| # | 口径 | +|---|---| +| AC-CMU-1 P-NTF-01 | 四类消息(系统/公告/审核/收益)可投递、`/app-api/community/messages` 列表可读、`{id}/read` 后 `read_status=1` 且 unread-count 递减 | +| AC-CMU-2 P-NTF-02 | aigc 生成完成后 `notifyGenerateDone` 被调→通知到达消息中心(前提:aigc 已接挂点1 或经 mock-trigger 验投递) | +| AC-CMU-3 P-NTF-03 | 审核出结果后 `notifyReviewResult` 被调→落站内信。**仅 `decision=1通过/2拒绝` 发通知**;`decision=3下架`(含封禁联动经 `unlistIfPublished` 系统身份路径)**本波不发通知**(非 P0 已知边界,caller 以 `decision∈{1,2}` 守门,§6.5 挂点2)。`decision=2拒绝`须带 reason——但实查 `ReviewReqVO.reason` 仅 `@Size(max=500)`、**无 @NotNull**,全链路不强制 reject 时 reason 非空,故 community 侧 notify service 对 `decision=2 且 reason 空`须给兜底文案(如「未提供原因」)或记 warn,避免落「拒绝原因为空」站内信(配合 P-OPN-02) | +| AC-CMU-4 P-NTF-05 | trade 结算入账后 `notifyIncomeChanged` 被调→站内信(+`community.sms.enabled=true` 时可选短信)送达 | +| AC-CMU-5 P-INC-01 | **计数到达阈值(默认 3,`community.level.publish-threshold`)即触发**:计数权威源=project 发布成功 `notifyProjectPublished` 累加 `published_count`(**尽力计数口径**——at-most-once 投递,承认丢通知会漏触发,§6.4),到达阈值后等级引擎自动写一条 `game_community_reward`(`uk_creator_milestone` 幂等,重复发布不重触发);本波终态=触发可观测+本地记账,`status=0`待发放;**记入钱包/流量包+现金真实到账、以及计数可对账重建(project 补 `countPublishedByCreator` 兜底)留 M4** | + +**biz(6 P0)** + +| # | 口径 | +|---|---| +| AC-BIZ-1 P-BIZ-01 | POST `/app-api/biz/leads` 提交即落 `game_biz_lead` 一行 `status=0`待跟进 + 落 `game_biz_progress` 首条 `to_status=0`。Swagger/curl 可验 | +| AC-BIZ-2 P-BIZ-02 | GET `/app-api/biz/templates` 返 aigc 4 可玩模板;选定后经 runtime 双路由预览(`/package/{versionId}` + `/manifest`)。**★负路径(R1 必验)**:versionId 缺失/非法、无编译产物、编译失败(`RUNTIME_PACKAGE_NOT_READY`)时看板降级「该模板暂不可预览」而非透传 500;`advanceStatus` 到 `toStatus=3` 时 `validateDemoPreviewable` 拒→抛 `BIZ_DEMO_NOT_READY` | +| AC-BIZ-3 P-BIZ-03 | 状态机各节点可经 `/admin-api/biz/leads/{id}/advance` 推进、`/app-api/biz/leads/{id}/progress` 取看板时间线(**后端 API 层=本波验收**)。客户侧看板(game-studio WS3)+admin 队列(WS4)=完成条件增强项,随对应工位排期走真人 UI 走查 | +| AC-BIZ-4 P-BIZ-04 | 客户「试玩+反馈+确认」三态走通——feedback 落 `game_biz_acceptance`、confirm 置 `confirm_status=1` 且状态机 3→4。签署/收款留 M4,本波线下签署+`sign-offline` 兜底 | +| AC-BIZ-5 P-BIZ-08 | 经 `/admin-api/biz/leads`(代客发起)+`/admin-api/biz/leads/{id}/quotes`(报价)Swagger/curl 可发起品牌营销定制单并完成报价(报价≠可支付订单)。运营代客发起 UI(WS4)=独立跟进项 | +| AC-BIZ-6 P-BIZ-12 | 文旅场景(`bizType=2`)可发起定制单并走验收。P0 子集=场景需求 CRUD+模板复用+专属看板必交(与 P-BIZ-01/03 同能力栈);P1「快速交付增强」列 M4 | + +### 9.2 工程门精确 CLI(mini-desktop 执行,逐项可勾) + +```bash +# 1) 编译(community;biz 换路径) +mvn -DskipTests clean install -pl game-cloud/game-module-community/game-module-community-server -am +# 2) 单测(继承 BaseMockitoUnitTest 绿) +# community: 投递/已读/计数累加/里程碑触发幂等;biz: BizLeadServiceImplTest 流转合法/非法/→3 无 demo 拒 +# 3) 启动 yudao-server → 应用层 Flyway 自动 migrate V12/V13(两处副本字节一致校验:md5sum 全等) +# 4) Swagger 可见(返回非空且含新端点) +curl http://localhost:48080/v3/api-docs?group=community # biz 换 group=biz +# 5) doc.html 可见 community/biz 分组;Spring 启动无 @Primary Bean 冲突日志 +``` + +### 9.3 staging 两条系统身份写链路实测(⚠️ R3 必测,单测 mock 测不到,「骨架编译过≠可验收」) + +- **链路①=community 通知投递**:上游 notify(或 staging `mock-trigger`)→ 写 `game_community_message`,验**系统身份注入 `LoginUser(id=0)` 生效、`updater` 落 `'0'`、不 500**。 +- **链路②=biz 状态机推进**:admin 推进定制单状态(`/admin-api/biz/leads/{id}/advance`)→ 写 `game_biz_lead`/`game_biz_progress`(含 reward 触发记账若由非 Web 线程触发),验**非 Web 线程/系统身份写 `updater` NOT NULL 不 500**。 + +### 9.4 UI 入口真人走查(完成条件增强项,跨工位绑定,不阻塞本波后端收口) + +> 项目记忆铁律「编排器旁路/批跑 API 全绿掩盖 UI 缺陷」(`frontend-link-ui-walk-publish-fix`)——UI 走查是唯一手段。 + +- **community**:消息中心列表/已读/未读角标在 game-studio(WS3)真人走查(6c6g CDP 走查配方:`run_in_background` 起 chrome + `player_cdp.CdpSession`,记忆 `frontend-link-ui-walk-publish-fix`)。 +- **biz**:客户侧进度看板(game-studio `/biz-orders/my-orders` 或邀请链,WS3)+ 运营代客发起/处理队列(game-admin,WS4,权限点 §7.4 登记后)真人走查。 + +--- + +## 10. 完成条件 + M4 留债 + 回滚 + +### 10.1 完成条件(全勾才算 done,「无验证证据不得 claim 完成」) + +1. §0 单序列资源预占收口提交(V12/V13 两副本字节一致 + 错误码段 + yaml + 两处装配点),三项前置校验证据入提交说明。 +2. community/biz 各自 `mvn -DskipTests clean install -pl … -am` 通过(mini-desktop)+ 单测绿。 +3. 启动 yudao-server,Flyway migrate V12/V13 成功(两副本 md5 全等)+ `curl …group=community`/`group=biz` 非空含新端点 + doc.html 可见 + 无 @Primary Bean 冲突。 +4. community 5 P0 + biz 6 P0 AC(§9.1)逐条满足(含 AC-BIZ-2 负路径、AC-CMU-5 幂等)。 +5. **staging 两条系统身份写链路实测通过**(§9.3,`updater` 落 '0' 不 500)——**这是「骨架编译过≠真实可验收」的硬验收**。 +6. UI 入口真人走查(§9.4)作为完成条件增强项跟进(绑 WS3/WS4 排期,不阻塞本波后端收口)。 +7. 收口后按知识沉淀铁律回写 `.agents/skills/add-business-module.md`(107/110 升「已落地」、补 root pom 注册独立步、补「跨模块通知优先同进程 `-api` notify-push 而非 MQ」范式)+ 新踩坑,更新 `.agents/README.md`;更新 `docs/mvp/MVP进度总账.md` + 两模块 `.agent`。 + +### 10.2 M4 留债(两类债分离,评审版 R1) + +**①资金流债(受 pay 桩 + ICP/支付进件日历闸门制约)** + +| 留债 | 说明 | +|---|---| +| biz T-BIZ-08 收费 | 报价转可支付订单 → pay 进件后接 `PayOrderApi` 收款(`amount_fen` 已按「分」口径预留,与 trade 同,直接复用) | +| biz T-BIZ-03 在线电子签章 | 三方签章;本波 `game_biz_acceptance.signed_offline` 线下兜底过渡 | +| community reward 真实记账/到账 | 记入钱包/流量包+现金到账=trade 暴露 `IncentiveCreditApi` + feed 暴露流量包分配 `-api`(**当前均不存在**)后补 seam;本波只写 `game_community_reward.status=0`,M4 回写 `granted_ref`+`status=1` | + +**②前端 UI 入口债(跨工位协调,可与本波后端并行,非 M4 资金流债)** + +| 留债 | 工位 | +|---|---| +| 客户侧进度看板 game-studio `/biz-orders/my-orders` 或邀请链 | WS3 | +| 运营代客发起 + 处理队列 game-admin(权限点 §7.4 登记) | WS4 | +| community 消息中心 UI | WS3 | + +### 10.3 回滚 + +- **Flyway**:已合入迁移**禁止修改**,回滚只写新补偿迁移(`V12.0.1`/`V13.0.1` DROP,两副本字节一致);CI 跑 `flyway validate` 阻断改旧。 +- **代码**:两模块纯新增(克隆),回滚=root pom 撤 `` + yudao-server pom 撤依赖 + 删两模块目录 + 撤三上游 notify 调用点(三处 try-catch 块)+ 撤 community.yaml/biz.yaml;对已建 11 模块**零行为变更**,回滚面收敛。 +- **上游接线回滚**:三个 notify 调用点均 try-catch 独立块,注释/删除即恢复上游原行为(不影响 aigc/project/trade 既有事务)。 + +--- + +## 11. 与评审版的勘误回流登记(治理铁律:以评审版为准 → 矛盾须回流不得单方改写) + +> 本文 line6 自定铁律「本文不得与评审版矛盾;执行中发现矛盾,停工上报,以评审版为准」。落地实查发现评审版 3 处结论与真实代码不符,**已回流评审版勘误段(HJ-WAVE4-001 顶部「⚠️ 勘误 ERRATA」+ §2.1/§2.2/AC-CMU-3 inline 标注)**,本文相应处方与之一致,验收口径唯一。 + +| 勘误# | 评审版原结论 | 实查订正 | 本文落点 | +|---|---|---|---| +| 勘误#1 | P-NTF-03 上游源 = **compliance**「审核状态机事件源」(§2.1/§2.2 Mermaid + P0 表 + AC-CMU-3) | 审核态权威在 **project** `ProjectServiceImpl.reviewProject`(写 review_record + 状态机 + reason);compliance 不持审核态(Gate 二态 + 封禁)。notify 挂 project `AdminProjectController.reviewProject` 外层 caller | §6.5 挂点2(含作用域处方)+ §2.4 挂点表 | +| 勘误#2 | (评审版未展开 decision 态数)approve/reject 两态 | `ReviewDecisionEnum` 三态(1通过/2拒绝/3下架);本波仅 1/2 发通知,3下架(含 `unlistIfPublished` 系统身份第二路径)不发 | §4.3 签名注释 + §6.5 挂点2 + AC-CMU-3 | +| 勘误#3 | P-INC-01 计数权威源(隐含强一致语义) | at-most-once 投递→「尽力计数」,丢通知会漏触发;可对账重建留 M4 | §6.4 + AC-CMU-5 | + +> **未采纳项(实现 agent 已能直接定、无需回流评审版的实现细节)**:aigc 挂点1 的「succeeded 判别 + ID 取数」走提交后 `selectByTraceId` 回查(评审版未涉及代码级取数,属 execution 细节);trade 挂点3 的 `net` 现算(同上);biz advanceStatus/confirm 的 `@Transactional` 边界(同上)。这些是 execution 版职责内的可落地化,不构成与评审版的结论矛盾,故只在本文钉准、不回流。 + +--- + +*证据边界(2026-06-11 亲核 dev/2.0.0):root pom `` :10-35(末 game-module-studio :34);yudao-server pom `` :23-…(末 game-module 依赖 studio-server :80);Flyway 两副本 ceiling=V11(V1–V11 各两副本,最高 V11.0.0__create_passport_player_invite.sql;V10 golden-loop + V11 passport 均在两处),V12/V13 文件名占用 `ls` exit=2(未占用);错误码段 `grep '1_107\|1_110' game-cloud/` 返 0;community.yaml/biz.yaml 不存在(exit=2);ProjectApi 现有方法=getCreatorUserId/getCurrentVersionId/getStatus/getFeedMeta/createProject/unlistIfPublished(**无** countPublishedByCreator,故 P-INC-01 取方案 a);aigc `DifyCallbackServiceImpl.handleCallback` 内 `return txService.handleCallbackTx(reqVO)`(内层 @Transactional 三表写入链);project `AdminProjectController.reviewProject:55-57` 调 `projectService.reviewProject`(service @Transactional :207,APPROVE 同事务发布编排 :237-239);trade `SettlementServiceImpl.settle` 循环 `firstRecord`/`newlyRecorded++` 分支(recordIncome @Transactional 单条提交、循环体非事务)。设计依据=评审版 HJ-WAVE4-001 + 两轮评审纪要 + 四份深度 recon(含可直接入文 DDL/端点/挂点 artifacts)。行号漂移以语义定位为准。本文为 execution 设计产出,未实跑 mvn/Flyway/单测——落地后须经 §9 工程门 + staging 两链路实测 + UI 走查验证方可 claim 完成(评审版 R10 铁律)。* diff --git a/docs/agent-specs/2026-06-11-Wave4-community-biz-review-评审纪要.md b/docs/agent-specs/2026-06-11-Wave4-community-biz-review-评审纪要.md new file mode 100644 index 00000000..8504831a --- /dev/null +++ b/docs/agent-specs/2026-06-11-Wave4-community-biz-review-评审纪要.md @@ -0,0 +1,100 @@ +# Wave4(community + biz)评审版 spec · 两轮对抗评审纪要 + +> 关联 spec:[`2026-06-11-Wave4-community-biz-review.md`](./2026-06-11-Wave4-community-biz-review.md)(编号 HJ-WAVE4-001) +> 类型:评审纪要(findings 列表 + 逐条处置存档)| 2026-06-11 +> 范围:第一轮(R1,24 条)+ 第二轮(R2,6 条)对抗评审 findings 全量列出与逐条处置。 +> 结论:**两轮全部接受或部分接受/重定向,无「拒绝」条目**;spec 已据此定稿(状态=已定稿待创始人拍板)。 +> 说明:spec 正文末已内嵌「评审整改纪要(R1)」「评审整改纪要(R2)」两段为 spec 自洽;本文是**独立存档**,把两轮原始 findings 与处置归并一处,供审计与后续波次复用。 + +--- + +## 0. 两轮总览 + +| 轮次 | findings 数 | 严重度分布 | 处置结论 | 最大变更 | +|---|---|---|---|---| +| **R1** | 24(含合并近重复) | blocker×1 / high×多 / medium×多 / low×多 | 全接受/部分接受/重定向,无拒绝 | community 触达机制定型=同进程 `-api` notify-push(取代「事件驱动/MQ」含糊口径),消除 MQ/事件契约整组前置依赖 | +| **R2** | 6 | medium×2 / low×4 | 全接受(含 1 条事实订正后仍采纳改进诉求),无拒绝 | 校正 6 处精度/口径 + 新增 §0.1 三项战略决策置顶(D-A biz 形态 / D-B community 优先级 / D-C ip seam 是否本波接) | + +**两轮共性**:findings 多为「口径不一致 / 落点不精确 / 缺中央登记 / 范式引用错」类精度问题,无方向性推翻——印证「克隆零创新」范式判断成立。R1/R2 各有 1 条触及**事实订正**(R1:无 `ProjectStateService.java`;R2:M5 实有定义),均以「采纳问题但按实情调整解法」处置。 + +--- + +## 1. 第一轮(R1)findings 逐条处置 + +> 处置口径:**接受**=已改对应节;**部分接受/重定向**=采纳问题但解法按实查证据调整;**拒绝**=保留原口径(本轮无)。多条近重复 finding 合并处置(标 ⊃,主条吸收子条)。 + +### 1.1 贯穿性裁决(一处定死多条收敛) + +本轮多条 finding(blocker events.schema.json / high「事件基建不存在」/ R1 风险项 / 多条 medium 触发机制)实为**同一未决架构点的不同侧面**:community 用什么机制触达。R1 据实查证据**一次定死 = 同进程 `-api` notify-push**(spec §3.3)。连锁后果: +- (a) community **不走 events.schema.json**,故 blocker「先在 events.schema.json 注册三事件」的前置门禁**不再需要**——底层关切(只改契约不改枚举=静默 rejected)以「保留口径」写入 §3.3 备未来 MQ 路线; +- (b) 上游不发域事件,改为新增 3 个 notify 调用点,工作量画像在 §3.3 表与 §4 显式登记; +- (c) INC-01 计数权威源改 project、记账留 M4,消除孤儿子链路。 + +### 1.2 R1 逐条表 + +| # | Finding(severity|定位) | 处置 | 改了哪 / 理由 | +|---|---|---|---| +| R1-1 | 事件基建整体不存在,应框为「机制未建+选型未定」(high|§2.2/§3.3/R1) ⊃ blocker(events.schema.json 三事件未注册) ⊃ medium(事件消费手工触发形态未明) | **接受(核心整改)** | §0/§2.1/§2.2/§3.3 全面重写:机制定型为同进程 `-api` notify-push;§3.3 新增四维对比表+工作量画像+staging `mock-trigger` 端点;R1 风险行改「已缓解」。blocker 关切以「保留口径」并入 §3.3,不再设 events.schema.json 前置门禁 | +| R1-2 | P-INC-01 触发+记账依赖 trade 发放/feed 流量是半截孤儿链路(medium|§2.2/AC-CMU-5) ⊃ high(feed 流量包/trade 发放无 -api 契约) ⊃ medium(INC-01 计数权威源歧义 A/B) | **接受** | §2.2 新增「P-INC-01 落地形态」:计数权威源**定为 project 发布成功**(二选一 project notify / `ProjectApi.countPublishedByCreator`),**不依赖 feed 计数 seam**;「记入钱包/流量包」本波只写自有 reward 触发表,跨模块写留 M4;AC-CMU-5 收紧 | +| R1-3 | AC-BIZ-3/5 硬依赖 game-admin UI 而 admin 端零视图(medium|§6.2/R5) ⊃ high(AC-BIZ-5 依赖 WS4 未排期 UI 违反独立可交付) | **接受** | §6.2 加「UI 落点与跨工位」说明 + AC-BIZ-3/5 拆两层(后端 API 本波独立验收 / UI 入口列增强项绑 WS3·WS4 走 UI 走查);§1 非目标补「两类债分开记账」;§7-6 同步 | +| R1-4 | biz 模板预览只描述 happy path,缺取包失败影子路径(medium|§2.3/AC-BIZ-2) | **接受** | §2.3 加「B-02 模板预览影子路径」(无产物/编译失败/versionId 缺失降级 + 状态机不许无 demo 推进);AC-BIZ-2 补负路径必验 | +| R1-5 | Flyway V12/V13、错误码、yaml 文件名缺中央预占登记动作(low|§4/R6/R8/§8) | **接受** | §8 新增「§0 全局单序列资源预占登记」为 execution 开篇第一节(字节一致 + 前置校验 + 单 agent 收口) | +| R1-6 | 异步写链路 staging 实测未点名 community 通知/biz 状态机两条(low|§6.3/R3) | **接受** | §6.3 把两条异步/系统身份写链路逐条点名为必测项;R3 同步具体化 | +| R1-7 | §2.4「唯一公共装配点」与 §4 blast radius 不一致,漏 root 聚合 pom(high|§2.4/§4) ⊃ high(克隆 pom 注册点不清,未明 game-cloud vs yudao-server) | **接受** | §2.4 改「公共装配点=两处」(① root `game-cloud/pom.xml` 注册 `` ② `yudao-server/pom.xml` 注册 `-server` 依赖);§4 首行同步;§8 把 root pom 注册列独立一步 | +| R1-8 | P0 计数术语含糊,未陈述 Wave4 总新增 P0 数(high|§2.3/§7-4) | **接受** | §0 与 §2.3 明确「Wave4 总交付=11 P0=community 5 + biz 6;P-BIZ-11 经 Doc C 划归 compliance(compliance +1)」;§7-4 同步 | +| R1-9 | BIZ-02 轻量状态机「替代 BPM」是缩水还是等价替代表述不清(high|§2.3/R2) | **接受** | §3.1 新增「轻量状态机 vs BPM 等价替代而非缩水」澄清段(本波 6 P0 仅需单线性五态、多线流程留 M4+Flowable);R2 风险行重写 | +| R1-10 | Flyway V12/V13 三处一致性未验证(medium|§4/R6/§6 脚注) | **接受** | §6 脚注记两处 ceiling=V11 一致;§8 §0 补 grep 校验 + 字节一致校验 | +| R1-11 | BIZ-03 客户端入口形态未明(medium|§2.3/§6.2) | **接受** | §2.3 表 P-BIZ-03 + AC-BIZ-3 明确客户侧入口=game-studio `/biz-orders/my-orders`(WS3)或邀请链 | +| R1-12 | §1 非目标(资金流 M4)与 R5(UI 入口缺失)混为一个债项(medium|§1/R5/§7) | **接受** | §1 非目标新增「两类留债分开记账」(①资金流债→M4 ②前端 UI 入口债→跨工位并行);§8 M4 留债段同结构分离 | +| R1-13 | feed↔community 计数回流机制不清(反写/读表/异步事件)(medium|§4) | **接受(重定向)** | 采纳「机制须写死」,但计数权威源改 project 后**此 seam 取消**:§4 加澄清「feed.yaml line84 指归属权非反写」,旧「feed 信号→community 计数串行依赖」项删除 | +| R1-14 | §6.3 工程门缺 exact CLI(medium|§6.3) | **接受** | §6.3 补三组精确 CLI(`mvn -DskipTests clean install -pl ... -am` / `curl .../v3/api-docs?group=community` / staging 两链路实测) | +| R1-15 | §8 遗漏 Nacos 模块配置注册落点(low|§8) | **接受** | §8 补 Nacos 配置注册条(community.sms.enabled / community.level.publish-threshold / biz.state-machine.timeout-hours + deploy/nacos 导入验证) | +| R1-16 | P-BIZ-12 P0 子集「可下单+进度看板」模糊、与 01/03 是否重叠(low|§2.3/§7) | **接受** | §2.3 表 P-BIZ-12 + AC-BIZ-6 + §7-5 收紧为「场景需求 CRUD + 模板复用 + 专属看板(与 01/03 同能力栈、无额外定制)」;P1 列 M4 | +| R1-17 | T-PRJ-06 轻量状态机引用不精确,缺代码路径(low|§2.4/§3.1/§8) | **接受(含修正事实错误)** | 实查**无 `ProjectStateService.java`**——全文将「复用 T-PRJ-06/ProjectStateService」更正为「复用 project 范式=`ProjectStatusEnum`(流转判定)+ `ProjectServiceImpl`(写前流转校验)」(§2.1/§2.3 图/§2.4/§8)。原 finding 建议落点亦不存在,按实情改 | + +> **R1 小结**:24 条(合并后呈 17 行,含 ⊃ 吸收的近重复子条)全部接受/重定向。3 条(R1-13 feed↔community 计数机制、R1-1 内 blocker events.schema.json 前置门禁、R1-17 T-PRJ-06 引用)采纳问题本身但**解法与原 suggestion 不同**——实查证据显示原 suggestion 落点(feed 反写 / events.schema.json 路线 / ProjectStateService.java)与本仓现状或更优范式不符。 + +--- + +## 2. 第二轮(R2)findings 逐条处置 + +> 输入:R2 findings JSON(6 条:medium×2 + low×4)。**每条均先实查仓库核实再处置**,逐条证据见 §3「R2 实查证据」。 + +| # | Finding(severity|定位) | 实查核实结论 | 处置 | 改了哪 | +|---|---|---|---|---| +| R2-1 | §8 §0 预占校验 `grep -r 'V12\|V13'` 不返 0(会命中 V11 让位声明注释 2 行),实现者开篇第一步被自身注释绊住(low|§8 §0 行334) | **属实**:实测返 2 行(`V11…invite.sql:13` 两处副本各一行,内容匹配非文件名匹配);`ls V12*/V13*` 实测 exit=2 空 | **接受** | §8 §0 校验改「按文件名」:`ls contracts/db-schemas/V12* … V13* …`(应空/exit≠0);显式标注「不要用 grep 内容匹配,命中仅 V11 让位声明注释属预期非占用」;错误码段仍用 `grep -rn '1_107\|1_110'`(应返 0) | +| R2-2 | `add-business-module.md` 旧版仅列「步骤5=yudao-server pom」、未把 root `game-cloud/pom.xml` `` 注册列独立步,与 spec §8「装配点=两处」口径不一致,只读 skill 者漏 root pom 注册触发 compile 失败(low|§2.4/§8/skill 行66) | **属实**:`grep 'game-cloud/pom.xml\|\|聚合 pom'` 该 skill **零命中**;步骤表开头明写「无需改 Application/yaml,只需 yudao-server/pom.xml 加依赖」 | **接受**(不改 skill 内容、加 spec 提示——回写 skill 维持「本波收口后」原计划,避免开工前动 canonical) | §2.4 加 ⚠️ 硬提示:『skill 步骤表尚未含 root pom 注册(待本波收口回写),本波克隆以本节「装配点=两处」为准,勿以旧 skill 为唯一依据』;execution 版克隆步骤开篇须复述 | +| R2-3 | biz 取包端点写 `/app-api/runtime/package/{versionId}`,真实路由带 `/manifest` 后缀;属端点精度问题不影响可行性结论(low|§2.3 B-02 行161/AC-BIZ-2 行293) | **部分属实**:实查 `AppRuntimeController` 两端点**都存在**——`GET /package/{versionId}`(line60, 清单 meta)+`GET /package/{versionId}/manifest`(line75, 原始 JSON);原稿写的是真实端点之一,但漏列 `/manifest` 那条 | **接受**(按真实双路由钉准,不止补 /manifest) | §2.3 B-02 新增「取包端点真实路由」段(两端点真实路由+用途+门禁同源);AC-BIZ-2 同步双路由;提示 execution 版端点清单两条写全 | +| R2-4 | §266 兼容性段把 events.schema.json 列为「须串行收口的公共写点」,与 §239/§322「本波零改该文件」自相矛盾(medium|§266 vs §239 vs §322) | **属实**:§239 影响面表标「本波零改/无」、§322(§7-7)标「本波不改」,§266 却列入公共写点 | **接受** | §266 改写:公共写点仅 root pom/yudao-server pom 须串行收口;events.schema.json 本波零改、无写点竞争、不在串行收口范围(显式标 R2 校正消歧义) | +| R2-5 | §7 第 2 项「community 被 M5 依赖」中 M5 未在全文定义,理由对拍板无参考且不可验证(medium|§312 待确认项 2) | **订正**:M5 **实有定义**(`mvp-scope-and-milestones.md:101`/`MVP进度总账.md:32`=MVP 全链路交付里程碑 6/27 Day15);但「被 M5 依赖」确属同义反复(M5 含全部模块) | **接受**(采纳 suggestion(b):改自洽可验证表述) | §7-2 改:以「触达机制最独立、无外部业务卡点、复用面集中、风险最低」排序;补注 M5 定义 + 说明不以「被 M5 依赖」作依据(同义反复) | +| R2-6 | P-INC-01 计数权威源「二选一」(project notify vs ProjectApi.countPublishedByCreator) 在待确认项未显式决策,execution 期或重复思考(low|§2.2/§7-2~4) | **属实**:原稿仅在 §2.2 正文列二选一,§7 无对应决策行 | **接受**(采纳 suggestion:补 §7 决策点 + 标「可由 execution §0 单 agent 直接拍 (a)」) | §7 新增第 8 项(计数权威源二选一,推荐 (a) project notify,标可单 agent 定无须创始人介入);§8 §0 同步标「单 agent 拍 (a)」 | + +> **R2 顺带补强(非 finding 直接要求,同一处自洽性收口)**: +> ① 新增 spec §0.1「待创始人拍板的关键决策」汇总段,提炼 **D-A(biz 形态)/D-B(community 功能优先级)/D-C(ip seam 是否本波接)** 三项战略决策置顶供拍板; +> ② §7 表给战略项标 ⭐ 并与 §0.1 互链,区分「创始人决策项 vs execution 单 agent 可定的实现细节」; +> ③ 据实查(D5 决策「ip 寄宿 compliance」+ Wave4 仅 P-BIZ-02 沾 T-IP-09 且本波复用 aigc 模板绕开)补 **D-C 推荐「本波不接 ip seam、维持 D5 推迟」**,并入 §7 第 10 项。 + +--- + +## 3. R2 实查证据(2026-06-11 仓库核实) + +| 证据 | 命令 / 文件 | 结论 | +|---|---|---| +| ① §0 预占校验口径 | `grep -r 'V12\|V13' contracts/db-schemas/ game-cloud/yudao-server/.../db/migration/` | **返 2 行**(`V11.0.0__create_passport_player_invite.sql:13` 让位声明注释,两处副本各一行),非「应全返 0」;`ls V12*/V13*` 实测 **exit=2、空**(确未占用)→ 改按文件名判定 | +| ② runtime 取包真实路由 | `AppRuntimeController.java` | 两端点:`@GetMapping("/package/{versionId}")`(line60, 返 `RuntimePackageRespVO` 清单 meta) + `@GetMapping("/package/{versionId}/manifest")`(line75, 返原始 JSON);门禁同源(同走 `getPackageManifest` 做 scene+status 权威判定)。原稿写其一、漏 `/manifest` 那条 | +| ③ skill 缺 root pom 注册步 | `.agents/skills/add-business-module.md` 步骤表(行66 起);`grep 'game-cloud/pom.xml\|\|聚合 pom'` | 步骤表仅「步骤5=yudao-server/pom.xml 加 -server 依赖」,开头明写「无需改 Application/yaml」;root pom 注册 **零命中** → skill 待本波收口回写,本波以 spec §2.4「装配点=两处」为准 | +| ④ M5 定义 | `.agents/knowledge/mvp-scope-and-milestones.md:101` / `docs/mvp/MVP进度总账.md:32` | M5=「MVP 交付」全链路交付里程碑(6/27 Day15)。M5 **有定义**,但「community 被 M5 依赖」属同义反复(M5 含全部模块)→ 改独立性/风险理由排序 | +| ⑤ ip seam(D-C 决策证据) | `docs/mvp/MVP进度总账.md:53`/`:95`;`需求模块映射.md:207` | D5 决策「ip 不独立建、seam 寄宿 compliance」、line95 标推迟 TODO;Wave4 11 P0 中**仅 P-BIZ-02 协同含 `T-IP-09 模板市场`**,community 5 P0 零 ip 关联;本波 P-BIZ-02 复用 aigc 4 模板即不触 T-IP-09 → D-C 推荐「本波不接、维持 D5」 | +| ⑥ §266 矛盾 | spec §239 / §266 / §322 | §239 影响面表标 events.schema.json「本波零改/无」、§322(§7-7)标「本波不改」,§266 却列入「须串行收口的公共写点」→ 自相矛盾,已订正 | + +--- + +## 4. 拍板与遗留 + +- **拍板项(创始人,详见 spec §0.1 + §7 标 ⭐ 行)**: + - **D-A** biz 形态:轻量 lead form 先行(推荐)vs 全 biz; + - **D-B** community 功能优先级:通知底座最小集(推荐)vs 全量 T-CMU; + - **D-C** 是否本波即接 ip seam(寄宿 compliance):推荐**本波不接、维持 D5 推迟**。 +- **可由 execution 版单 agent 直接定的实现细节**(spec §7 未标 ⭐ 行):建设次序(先 community)、P-BIZ-12 P0 子集、UI 入口排期、并行/串行收口、P-INC-01 计数权威源(拍 (a) project notify)。 +- **残留风险(不阻塞拍板,execution 期管控,见 spec §5 R1-R10)**:匿名/非 Web 线程写审计列雷(staging 必测两链路)、Flyway 撞号、@Primary Bean 冲突、错误码段误复制、「骨架编译过≠真实可验收」。 +- **收口回写计划**:Wave4 收口后把克隆实证回写 `.agents/skills/add-business-module.md`(107/110 升「已落地」、补 root pom 注册独立步、补「跨模块通知优先同进程 `-api` notify-push 而非 MQ」范式)+ 新踩坑,更新 `.agents/README.md` 索引。 diff --git a/docs/agent-specs/2026-06-11-Wave4-community-biz-review.md b/docs/agent-specs/2026-06-11-Wave4-community-biz-review.md new file mode 100644 index 00000000..498c4f63 --- /dev/null +++ b/docs/agent-specs/2026-06-11-Wave4-community-biz-review.md @@ -0,0 +1,434 @@ +# Wave4(community + biz)建模块 · 评审版 + +> 编号 **HJ-WAVE4-001** | 2026-06-11 | 类型:Review(结论先行,供创始人拍板;定稿后出 execution 版) +> 任务:为 MVP 最后两个未建业务模块 **community(社区/通知/激励)+ biz(B/G 端定制)** 写评审版 spec。 +> 状态:**已定稿(两轮对抗评审整改收口),待创始人拍板**。本文是「须双 spec」铁律下的**第一份(评审版)**;execution 版(含可落地代码级细节、DDL、端点清单、克隆步骤)**待创始人拍板后再出**,本文不含代码级细节以降认知负荷。 +> **修订**:R1 第一轮对抗评审整改 + R2 第二轮对抗评审整改(均 2026-06-11),逐条处置见文末「评审整改纪要(R1)」「评审整改纪要(R2)」。**R1 最大变更=community 通知机制定型为同进程 `-api` notify-push(取代含糊的「事件驱动/MQ」口径),消除 MQ/事件契约整组前置依赖;R2 修订=校正 6 处精度/口径(§0 预占校验口径按文件名、§266 events.schema.json 兼容性矛盾、§7 M5 排序理由、runtime 取包真实双路由、skill 与本节装配点口径差提示、P-INC-01 计数方案显式决策点)。** + +> ## ⚠️ 勘误 ERRATA(execution 落地回流,2026-06-11,权威订正本文上游源结论) +> execution 版(HJ-WAVE4-EXEC-001)实查仓库后回流以下勘误,**以勘误为准**(本文正文 §2.1/§2.2 Mermaid、§2.2 P0 表、§6.1 AC-CMU-3 相应处已 inline 标注「勘误#1」,未及处以本段为准): +> - **勘误#1(P-NTF-03 审核结果通知上游源:compliance → project)**:本文多处(§2.1 Mermaid `CMP` 框、§2.2 Mermaid `E2` 框、§2.2 P0 表「compliance 新增 1 处调用」、AC-CMU-3「compliance 审核出结果后…」)写 **compliance** 为 P-NTF-03 上游事件源。**实查证据订正为 project**:审核结果落库点是 `ProjectServiceImpl.reviewProject`(写 review_record + 状态机 + reason),`ReviewReqVO` 含 `gameId/versionId/decision/reason`;而 compliance 不持审核态(`ComplianceGateServiceImpl.evaluate` 只做锁风门 Gate 二态、`UserBanService` 只管封禁,均不碰 project 审核态)。**故 P-NTF-03 的 `CommunityNotifyApi` 调用挂在 project 侧 `AdminProjectController.reviewProject` 外层 caller(service 提交后),不挂 compliance**(挂 compliance 取不到 reason)。验收(AC-CMU-3)以 project 挂点为准。execution §6.5 挂点2 含完整可落地处方。 +> - **勘误#2(P-NTF-03 通知边界三态)**:`ReviewDecisionEnum` 实为三态(1通过/2拒绝/3下架)。本波 notify **仅覆盖 1/2**;`decision=3下架`(含 compliance 封禁经 `ProjectApi.unlistIfPublished` 系统身份直调 service 的第二路径)**本波不发通知**,属非 P0 已知不通知项。 +> - **勘误#3(P-INC-01 计数口径=尽力计数)**:方案 a 的 `notifyProjectPublished` 在事务提交后 try-catch 调用=at-most-once 投递,`published_count` 丢一次不补发。本波明确为「尽力计数」(允许极端少计/漏触发),AC-CMU-5 订正为「计数到达阈值即触发」;可对账重建(project 补 `countPublishedByCreator`)留 M4。 + +--- + +## 0. 结论(一句话) + +**两模块都按已验证的「黄金模块克隆 + 契约先行 + 轻量状态机」范式建,零创新风险;唯一真正的产品决策在 biz——推荐「轻量 lead form 先行」而非「全 biz」,因为 biz 的 6 个 P0 没有一个直接依赖支付/分账(pay/trade),资金流本就不在 P0 必经路径,全 biz 的 BPM/Flowable/在线收款是远期形态、且被 pay 桩与 ICP/支付进件日历闸门卡死。** + +**Wave4 总交付 = 11 P0 = community 5 + biz 6**(P-BIZ-11 学生防沉迷经 Doc C 已重划至 compliance,由 compliance 承接,不计入 biz;详见 §2.3 与 §7 第 4 项)。 + +- **community(5 P0)**:本质是「通知底座 + 新人激励」。**触达机制(R1 定型)= 同进程 `-api` notify-push**——aigc 生成完成 / **project 审核出结果(勘误#1:原写 compliance,见顶部勘误段)** / trade 收益变动后,由各自 service 在本地事务提交后**同进程调用 community 暴露的 `CommunityNotifyApi`**(与 feed↔project 同范式,复用 `@Primary` 本地实现),**不新建 MQ 生产者/消费者,也不走 telemetry 的 events.schema.json 事件总线**(该总线是前端遥测专用,非后端域事件,见 §3.3)。风险最低、被全链路 M5 依赖,**优先建**。 +- **biz(6 P0)**:本质是「询单 → 报价 → 进度看板 → demo 试玩确认」的**信息流闭环**,复用面极大(aigc 模板/runtime 沙箱/admin 队列/轻量状态机全是已建资产);**资金流(收款/分账/在线签章)留 M4**,本波只交付信息流。 + +**全文证据边界**:模块克隆范式、错误码段、Flyway ceiling、契约文件、**上游事件发射基建(compliance/trade 零域事件、telemetry MQ 消费者仅骨架、feed↔project 同进程 `-api` 注入为真范式)**均经 2026-06-11 实查仓库核实(见 §6 脚注与 §3.3);P0 计数依 Doc A/B/C 三文档交叉核对。BPM/Flowable 装配状态、pay/trade 真实度引自项目总账(标注为桩)。 + +--- + +## 0.1 ⭐ 待创始人拍板的关键决策(汇总,详表见 §7) + +> 本文已两轮对抗评审定稿,结论可直接执行;以下 **3 项是真正需要创始人拍板的战略决策**(其余 §7 第 2/5/6/7/8 项属实现细节,可由 execution 版单 agent 直接定,已在 §7 标注)。**按推荐拍即可开 execution 版。** +> +> **✅ 2026-06-11 创始人拍板:D-A / D-B / D-C 全按推荐采纳**(D-A=biz 轻量 lead form 先行、资金流留 M4;D-B=community 通知底座最小集、社交/成长域留接口桩;D-C=本波不接 ip seam、维持 D5 寄宿 compliance 推迟)。execution 版据此启动(`2026-06-11-Wave4-community-biz-execution.md`)。 + +| ⭐ | 关键决策 | 推荐 | 不这样做的代价 | +|---|---|---|---| +| **D-A** | **biz 形态:轻量 lead form 先行 vs 全 biz** | **lead form 先行**——只交付「询单→报价→进度→demo 确认」信息流闭环,资金流(收款/分账/在线签章)留 M4 | 全 biz 强依赖 yudao-bpm(未装配)+Flowable(未建表)+pay(桩)+三方签章,被 ICP/支付进件日历闸门卡死;且 6 P0 无一直接依赖 pay/trade,全 biz 是远期形态、近期投产即孤儿 | +| **D-B** | **community 功能优先级:通知底座最小集 vs 全量 T-CMU** | **通知底座最小集**——T-CMU-10 编排+T-CMU-06 站内信+T-CMU-11 等级引擎(+可选短信 T-CMU-09),一次覆盖 5/5 P0;SOC/GRW 域(关系图谱/排行/成就/扇出,全 P1/P2)只留接口桩 | 全量铺开会做大量非 MVP 验收项,违反最小交付;触达机制已 R1 定型为同进程 `-api` notify-push(不建 MQ、不动 events.schema.json),此项无悬念 | +| **D-C** | **是否本波即接 ip seam(D5 已定「寄宿 compliance、不独立建模块」)** | **本波不接、维持 D5 推迟**——Wave4 P0 中**仅 P-BIZ-02 协同涉及 IP 模板市场 T-IP-09**,而本波 P-BIZ-02 推荐**直接复用 aigc 4 模板 + runtime 沙箱**(绕开 T-IP-09 模板市场),故**无需 ip seam**;ip seam 维持「寄宿 compliance」的 D5 决策,待真做素材/IP 市场或 DMCA 下架(P-IPX-05,P1+/远期)时再接 | 本波若强接 ip seam=为唯一沾边的 P-BIZ-02 引入一整套未被 P0 必需的 IP 授权/版权链(T-IP-02/04/05/09),与「最小交付+复用优先」相悖;且 compliance 当前 style/ip 原子本就是桩(总账 line51),强接即叠桩上桩 | + +> **D-C 证据**(2026-06-11 实查):`docs/mvp/MVP进度总账.md:53` D5 决策「ip 不独立建、seam 寄宿 compliance」,line95 标为推迟 TODO;`需求模块映射.md` 中 Wave4 11 P0 仅 **P-BIZ-02 协同含 `T-IP-09 模板市场`**(line207)、community 5 P0 零 ip 关联;本波 P-BIZ-02 推荐复用 aigc 模板(§2.3)即天然不触 T-IP-09。**故 D-C 推荐「不接」不是回避,是本波 P0 真实依赖边界本就不含 ip。** + +--- + +## 1. 背景与目标 + +- **背景**:MVP 13 模块中已建 11 个(9 个 `game-module-*` + passport 寄宿 system + studio)。Doc B(技术架构与模块.md:10)明确 **community/biz = 「未建(Wave4)」**,是最后两块。两模块同归 **WS5(数据与变现工位)**。 +- **目标**:交付 community 5 P0 + biz 6 P0 **可验证**,且不破坏已建 11 模块(错误码段/Flyway 序号/前缀零冲突)。 +- **非目标(硬边界,写入 spec 防孤儿/缩水)**: + - biz **不收款结算**(Doc B:308 明文 OUT)——T-BIZ-08 收费、T-BIZ-03 在线电子签章降级为 M4 跟进项,本波只到「状态机走通 + 线下签署后台标记」。 + - community 不全量铺开 T-CMU 全家桶——SOC/GRW 域(关系图谱/排行/成就/扇出)全是 P1/P2,本波只留接口桩、不实现。 + - 不引入 Flowable / yudao-bpm(当前注释未装配)——复用 project 已落地的轻量状态机范式。 + - **本波两类留债须分开记账(R1,勿混为一谈)**:**①资金流债**(biz 签署/收款、community reward 记入钱包/流量包与现金到账)= 受 pay 桩 + ICP/支付进件日历闸门制约,统一并入 **M4**;**②前端 UI 入口债**(biz 运营代客发起=WS4 / 客户进度看板=WS3)= 跨工位协调项,可与本波后端**并行**推进,与 M4 资金流债是不同维度,不应捆在一起延期。 + +--- + +## 2. 推荐方案(结论先行 + Mermaid) + +### 2.1 两模块定位与依赖(一图看清边界) + +```mermaid +graph TB + subgraph W4["Wave4 本波新建(信息流闭环)"] + CMU["community
错误码段 1-107
Flyway V12
暴露 CommunityNotifyApi"] + BIZ["biz
错误码段 1-110
Flyway V13"] + end + subgraph UPSTREAM["上游 notify 调用点(本波在各上游新增 3 处同进程调用)"] + AGC["aigc
生成完成后调 CommunityNotifyApi"] + CMP["project(勘误#1:原写 compliance)
reviewProject 审核出结果后调 CommunityNotifyApi"] + TRD["trade
收益变动后调 CommunityNotifyApi"] + end + subgraph REUSE["可直接复用资产(biz 复用,零新建)"] + TPL["aigc 4 模板库 T-AGC-03"] + SANDBOX["runtime 沙箱预览 / 取包链路"] + ADMINQ["admin 审核队列 + 轻量状态机
(范本=project ProjectStatusEnum
+ ProjectServiceImpl 流转校验)"] + end + subgraph M4["M4 变现真实化(本波留债,明确推迟)"] + PAY["pay 微信支付进件(桩)"] + SIGN["在线电子签章 T-BIZ-03(三方)"] + SETTLE["trade 真实分账/打款(桩)"] + end + + AGC -.同进程 -api 调用(提交后).-> CMU + CMP -.同进程 -api 调用(提交后).-> CMU + TRD -.同进程 -api 调用(提交后).-> CMU + TPL --> BIZ + SANDBOX --> BIZ + ADMINQ --> BIZ + BIZ -.询单/报价/进度/demo确认(信息流,本波交付).-> BIZ + BIZ -.收款/在线签章(资金流,留债).-> M4 + CMU -.新人现金奖励真实到账.-> M4 + + classDef deb fill:#fee,stroke:#c33; + class PAY,SIGN,SETTLE deb; + classDef done fill:#efe,stroke:#3a3; + class TPL,SANDBOX,ADMINQ deb2; + classDef new fill:#eef,stroke:#36c; + class AGC,CMP,TRD new; +``` + +> **图例说明(R1 校正)**:上游三框是**本波要新增的 notify 调用点**,不是「已有事件源」——实查证实 compliance/trade 当前**零域事件发射**(`grep publishEvent/ApplicationEvent/RocketMQTemplate` 全空),telemetry 唯一的 MQ 消费者 `TelemetryEventConsumer` 仍是**无 `@RocketMQMessageListener` 注解的骨架**(类注释明写「MQ 消费 待对接」)。本仓真正在跑的跨模块范式是**同进程 `-api` 注入**(`FeedServiceImpl` 注入 `ProjectApi`/`RuntimePackageApi`,`@Primary` 本地实现就地解析)。community 沿用此范式。 + +### 2.2 community:通知底座最小集(一次到位覆盖 4/5 P0) + +**核心洞察**:5 个 P0 里 4 个是通知类(NTF-01/02/03/05),共用同一底座;第 5 个(INC-01 新人激励)加一个等级引擎即可。 +**触达机制(R1 定型)= 同进程 `-api` notify-push**:community 暴露 `CommunityNotifyApi`(`@Primary` 本地实现),上游 aigc/**project(勘误#1:P-NTF-03 源 compliance→project,见顶部勘误段)**/trade 在各自 service 的本地事务**提交后**同进程调用之,写入站内信表。**不新建 MQ,不走 events.schema.json**(理由见 §3.3)。 + +```mermaid +graph LR + subgraph CALL["上游 notify 调用点(本波在上游各加 1 处提交后同进程调用)"] + E1["aigc 生成完成
→ CommunityNotifyApi"] + E2["project 审核出结果(勘误#1:原写 compliance)
→ CommunityNotifyApi"] + E3["trade 收益变动
→ CommunityNotifyApi"] + E4["project 发布成功计数
→ 等级引擎(计数权威源=project)"] + end + subgraph CORE["community 通知底座(CommunityNotifyApi 入口)"] + ORCH["T-CMU-10 通知编排
(路由/模板/去重)"] + INBOX["T-CMU-06 站内信
(投递/已读)"] + LEVEL["T-CMU-11 等级引擎
(里程碑触发)"] + end + subgraph OUT["产出"] + MSG["站内消息中心
四类聚合"] + SMS["短信(可选)
T-CMU-09"] + REWARD["新人奖励触发记录
(本波写自有 reward 触发表;
记入钱包/流量包 → M4)"] + end + E1 & E2 & E3 --> ORCH --> INBOX --> MSG + ORCH -.高优.-> SMS + E4 --> LEVEL --> REWARD + + classDef new fill:#eef,stroke:#36c; + class E1,E2,E3,E4 new; +``` + +| P0 | 语义 | 落在底座哪一块 | 上游接入(R1 校正:均为同进程 `-api` notify,非「已有事件」) | +|---|---|---|---| +| P-NTF-01 站内消息中心 | 系统/公告/审核/收益四类聚合 | 站内信 T-CMU-06 + 编排 T-CMU-10 | 无(自身聚合) | +| P-NTF-02 生成完成通知 | AI 生成完成推送创作者 | 编排 T-CMU-10 | **aigc 新增 1 处 `CommunityNotifyApi` 调用** | +| P-NTF-03 审核结果通知 | 通过/拒绝(拒绝带原因)推送 | 编排 T-CMU-10 | **(勘误#1:原写 compliance)project `AdminProjectController.reviewProject` 外层 caller 新增 1 处 `CommunityNotifyApi` 调用**(compliance 不持审核态,挂 compliance 取不到 reason) | +| P-NTF-05 收益变动通知 | 结算/到账变动推送 | 编排 T-CMU-10(+可选短信 T-CMU-09) | **trade 新增 1 处 `CommunityNotifyApi` 调用** | +| P-INC-01 新人任务奖励 | 发满 3 作品领流量包+现金 | 等级引擎 T-CMU-11 | 计数权威源=**project 发布成功**(见下「P-INC-01 落地形态」);发放/流量包记入 → M4 | + +> **P-INC-01「触发+记账」落地形态(R1 定型,消除孤儿子链路)**: +> - **计数权威源 = project 发布成功**(非 feed 互动信号回流)。实查 project 当前无 `project_published` 域事件、无 `publishCount` 字段、ProjectApi 也无对应查询方法——故 execution 版二选一收口:**(a)** project 发布成功后**同进程 notify** community 计数(与三上游同范式,推荐);或 **(b)** community 经 ProjectApi 新增的 `countPublishedByCreator(creatorUserId)` 主动读数。**两者都不依赖 feed 计数 seam**,让 community 可独立验收。 +> - **「记入钱包/流量包」本波不跨模块写**:实查 `feed.yaml` 无「流量包发放」端点(仅有 `boost`/`pinned` 运营加权)、`trade.yaml` 无「向 community 发放激励」端点(无 grant/发放/incentive 接口)。两个跨模块写方向**当前均无契约 seam**。故本波 community **只写自有的 reward 触发记录表**(达标即落一条可观测触发记录),**「实际记入钱包/流量包」连同现金到账一并挂 M4**(届时由 trade 暴露 `IncentiveCreditApi`、feed 暴露流量包分配 `-api` 后补 seam 一致性校验)。AC-CMU-5 口径据此收紧(见 §6.1)。 + +### 2.3 biz:轻量 lead form(信息流闭环,资金流留 M4) + +```mermaid +graph LR + subgraph IN["B/G 端 / 运营代客发起"] + FORM["①需求询单表单
P-BIZ-01"] + PICK["②模板库选择+预览
P-BIZ-02"] + QUOTE["④⑤询单+报价
P-BIZ-08/12(报价≠下单)"] + end + subgraph FLOW["③轻量状态机(复用 project 范式:枚举守门流转,不引 Flowable)"] + S1["待跟进"] --> S2["已报价"] --> S3["制作中"] --> S4["待验收"] --> S5["已交付"] + end + subgraph ACCEPT["⑥交付验收 P-BIZ-04"] + PLAY["试玩(runtime沙箱)"] --> FB["反馈"] --> CONFIRM["确认"] + CONFIRM -.签署/收款.-> DEBT["留债 M4
(线下签署+后台标记兜底)"] + end + FORM --> S1 + PICK --> S1 + QUOTE --> S2 + S4 --> PLAY + classDef deb fill:#fee,stroke:#c33; + class DEBT deb; +``` + +**biz 本波 6 P0 = P-BIZ-01/02/03/04/08/12**(Doc C 交叉核对)。**P-BIZ-11 学生账号与防沉迷经 Doc C 已划归 compliance**(映射 = `T-CMP-36 防沉迷 · T-CMP-37 实名`,主模块=compliance),**不计入 biz**,由 compliance 承接(compliance 因此 +1 P0)。 + +| P0 | 语义 | 本波交付形态 | 留债 | +|---|---|---|---| +| P-BIZ-01 需求表单提交 | B/G 端在线提交定制需求 | 纯 CRUD 落库(CRM = 工单表 biz_lead) | — | +| P-BIZ-02 模板库选择+预览 | 按场景浏览模板并预览 | **复用 aigc 4 模板 + runtime 沙箱取包**(零新建);**须处理取包失败影子路径**(见下) | — | +| P-BIZ-03 定制进度看板 | 客户看各阶段进度 | 轻量状态机驱动 + admin 处理队列;**客户侧入口=game-studio `/biz-orders/my-orders`(WS3 横算)或邀请链** | — | +| P-BIZ-04 交付验收 | 试玩→反馈→确认→签署 | 做到「试玩+反馈+确认」三态 | **签署/收款留 M4** | +| P-BIZ-08 品牌营销定制 | 报价+编排(端=运营) | 参数化估价表单 + 落工单(报价≠可支付订单) | 收款留 M4 | +| P-BIZ-12 文旅定制 | 景区/博物馆等(端=运营,P0/P1 混标) | **P0 子集=场景需求 CRUD + 模板选择复用 + 专属看板**(与 P-BIZ-01/03 同能力栈,无额外定制) | P1「快速交付增强」列 M4 后增量 | + +> **biz 形态 = lead form ≠「缩水验收」**:6 P0 逐条核对 Doc C 映射,**无一直接依赖 pay/trade**(pay/trade 仅出现在 P1 的 P-BIZ-06 套餐 / P-BIZ-10 教育)。所以「只交付信息流」是按真实依赖边界交付,不是偷工。 +> +> **B-02 模板预览影子路径(R1 补,落地易断处)**:biz 复用 runtime 取包,已证存在且**有失败态**——runtime `-api` 实查含 `RUNTIME_PACKAGE_NOT_READY`(1-102-001-001) 等 1-102 段错误码 + `BuildRespVO.failCode/failReason`。 +> +> **取包端点真实路由(R2 校正,execution 版按真实路由钉准)**:runtime 实暴露**两个**取包端点(`AppRuntimeController` 实查)——**①`GET /app-api/runtime/package/{versionId}`**(返 `RuntimePackageRespVO` 清单 meta,包 `CommonResult`,宿主据此渲染 iframe/做完整性校验;biz 看板取「是否可预览」判定走此端点最直接);**②`GET /app-api/runtime/package/{versionId}/manifest`**(返 manifest 原始 JSON 文本、不包 `CommonResult`,供宿主算 sha256 严格比对后注入运行容器)。两端点门禁同源(同走 Service `getPackageManifest` 的 scene+status 权威判定)。**execution 版端点清单须把两条按真实路由写全**(含 `/manifest` 后缀那条),勿只写其一。故 biz 作为复用方**必须处理**:B 端选的模板版本**无编译产物 / 编译失败 / versionId 缺失或非法**时,看板降级显示「该模板暂不可预览」而非透传 500;状态机**不允许在无可玩 demo 时推进到「待验收」**。AC-BIZ-2 据此补负路径验收(见 §6.2)。 +> +> **B-12 P0 子集(R1 收紧,防范围蔓延)**:P-BIZ-12 P0 必交 = 上行三栏(CRUD + 模板复用 + 专属看板),与 P-BIZ-01/03 同能力栈、不引入额外定制开发;P1「快速交付增强」单列 M4 增量。 + +### 2.4 建模块技术范式(已验证,零创新) + +两模块均**克隆黄金模块 game-module-project**(双 Maven 子模块:`-api` 契约层 + `-server` 实现层)。**单体装配 = 零改 Application/yaml**(`YudaoServerApplication` 已通配扫描 `cn.wanxiang.game.module`)。 + +**公共装配点 = 两处(R1 校正,与 §4 影响面表一致)**,缺任一处克隆即卡: +- **① `game-cloud/pom.xml`(root 聚合 pom)**:`` 块追加 `game-module-community` + `game-module-biz`——**缺此步 `mvn -pl game-module-community compile` 找不到父 pom 聚合定义而失败**(已知漏注册点); +- **② `yudao-server/pom.xml`**:`` 块追加两个 `-server` ``(`game-module-community-server` / `game-module-biz-server`,单体把全部模块编进一个 JAR)。 + +**轻量状态机范式 = 复用 project 的「枚举守门流转」**(实查:无独立 `ProjectStateService.java`;范本 = `ProjectStatusEnum`(含 `isDraft` 等合法流转判定)+ `ProjectServiceImpl` 内在写之前校验合法流转、非法即 `throw exception(...)`)。biz 照此落 `BizLeadStatusEnum` + service 流转校验,**不引 Flowable**。 + +详见 `.agents/skills/add-business-module.md`,execution 版展开 6 类落点清单(含 root pom 注册作为独立一步)。 + +> ⚠️ **canonical skill 与本节口径差(R2 提示,execution 版克隆步骤开篇须复述)**:`add-business-module.md` 步骤表当前**仅列「步骤5 = yudao-server/pom.xml 加 -server 依赖」,未把 root `game-cloud/pom.xml` 的 `` 注册列为独立步骤**(开头甚至明写「新模块无需改 Application/yaml,只需在 yudao-server/pom.xml 加依赖」)。该 root pom 注册步**计划在本波收口后才回写进 skill**(见行末知识沉淀计划)。故**本波克隆以本节「公共装配点=两处」为准,勿以旧 skill 为唯一依据**——只读 skill 会漏掉 root pom 的 `` 注册,触发 §4/R 项已点名的「缺此步父 pom 找不到聚合定义 → `mvn -pl … compile` 失败」。 + +--- + +## 3. 关键权衡 + +### 3.1 biz:轻量 lead form 先行 vs 全 biz(核心决策) + +| 维度 | 方案 A:轻量 lead form 先行 ✅推荐 | 方案 B:全 biz | +|---|---|---| +| **范围** | 询单/报价/进度/demo 确认(信息流闭环) | + BPM 实例编排 + 在线收款 + 在线电子签章 + 完整 CRM | +| **依赖就绪度** | 复用已建资产(模板/沙箱/admin 队列/轻量状态机),**零外部阻塞** | 强依赖 yudao-bpm(注释未装配)+ Flowable(建表未覆盖)+ pay(桩)+ 三方签章 | +| **被日历闸门卡** | 否 | 是(ICP 7-20 工作日 + 微信支付进件 1-3 周 + Flowable 装配) | +| **工程量** | 小(4 张能力,大量复用) | 大(BPM+签章+CRM+收费全新建) | +| **服务现金线** | **直接命中**:先把「线索→报价→demo 交付」跑通,首批成交走线下合同/对公转账即可 | 在线收款对首批 B 端成交非必需(线下合同即可成交) | +| **风险** | 低 | 高(多依赖桩 + 远期形态 + 易做成依赖桩的孤儿设计) | + +**推荐 A 的三条理由**:① 6 P0 无一直接依赖 pay/trade,资金流不在 P0 必经路径;② 复用面极大;③ 直接服务总账「近期现金靠 B 端」命门(单位经济模型:自有端 IAA 是万级 DAU 才回本的远期账,B 端项目制收费是近期现金线,命门 = 「拿到询单 + 能报价 + 能交付 demo」而非「平台内在线收款」)。 + +> **方案 B 不是错,是时序错**:全 biz 的资金流闭环(在线收款/预付尾款/分账)应与 ad 自有端 IAA 一起并入 **M4 变现真实化波**,与 pay 接线 + 支付进件日历闸门对齐。本波 lead form 把信息流跑通,M4 补资金流。 + +> **「轻量状态机 vs BPM」是等价替代而非缩水(R1 澄清)**:Doc B 中 T-BIZ-02 原起为完整 BPM 编排。本波 6 P0 的真实流程需求 = P-BIZ-03/04 的**单线性五态推进**(待跟进→已报价→制作中→待验收→已交付),**复用 project 已落地的枚举守门流转范式即完全满足**,并非「规避 BPM 这一标准」。BPM/Flowable 真正解决的「多级审批 / 会签 / 可视化流程配置」是 P1+远期场景的诉求,本波 P0 不涉及——故**多线/分支流程推迟到 M4 与 Flowable 一起补**,本波用轻量状态机是按 P0 真实复杂度选型,不是降级。 + +### 3.2 community:通知底座最小集 vs 全量 T-CMU + +| 方案 | 范围 | 取舍 | +|---|---|---| +| **最小底座 ✅推荐** | T-CMU-10 编排 + T-CMU-06 站内信 + T-CMU-11 等级引擎 + T-CMU-09 短信(可选) | 一次覆盖 5/5 P0;SOC/GRW(全 P1)留接口桩 | +| 全量 T-CMU | + 关系图谱/计数/排行/成就/扇出/邮件 | 做大量非 MVP 验收项,违反最小交付 | + +**推荐最小底座**:5 个 MVP P0 只实际用到 4 个 T-CMU 能力,其余服务的 SOC/GRW 全是 P1。 + +### 3.3 community 触达机制:同进程 `-api` notify-push(R1 定型,取代「事件驱动/MQ」含糊口径) + +> **这是本评审该定的架构决策,R1 在此一次定死,消除实现者无法据本文开工的歧义。** + +**结论:community 通知触达 = 同进程 `-api` notify-push。** community 暴露 `CommunityNotifyApi`(`@Primary` 本地实现),上游 aigc/**project(勘误#1:P-NTF-03 源 compliance→project)**/trade 在各自 service 的**本地事务提交后**同进程调用之写站内信。**不新建 MQ 生产者/消费者,不走 telemetry 的 `events.schema.json` 事件总线。** + +**为什么选 notify-push 而非 MQ/事件总线(实查证据)**: + +| 维度 | 同进程 `-api` notify-push ✅推荐 | MQ / events.schema.json 事件总线 | +|---|---|---| +| **本仓现状** | **唯一在跑的跨模块范式**:`FeedServiceImpl` 注入 `ProjectApi`/`RuntimePackageApi`,`@Primary` 本地实现就地解析(同进程方法调用,与调用方同 `@Transactional`) | **整组基建不存在**:compliance/trade `grep publishEvent/ApplicationEvent/RocketMQTemplate` **全空**;9 个 game-module 中**无一个 MQ 生产者目录**;唯一 MQ 消费者 `TelemetryEventConsumer` 是**无 `@RocketMQMessageListener` 注解的骨架**(「MQ 消费 待对接」),telemetry 这条「先例」**从未真正跑通 MQ** | +| **events.schema.json 适配性** | 不涉及——community 自有契约 `community.yaml` | **不匹配**:该 schema 是 **telemetry 专用**(单 topic `telemetry-event`、单消费者、`additionalProperties:false`),`generate_succeeded/income_settled` 是**前端上报的遥测串**、不是后端发的域事件;强塞会污染遥测契约 | +| **工作量画像** | 符合「克隆零创新」:**新增 3 个上游 notify 调用点 + 1 个 `CommunityNotifyApi` 契约**(与 feed↔project 同范式,复用 `@Primary`) | **远超「零创新」**:telemetry MQ listener 从骨架补成真实可跑 + 三上游各新增 producer + RocketMQ 中间件就绪(mini-infra 缺 RocketMQ,见项目记忆) | +| **事务/可靠性** | 上游本地事务**提交后**调用(避免把通知失败回滚业务);同步路径异常可观测 | 至少一次投递 + 幂等 + DLQ,可靠性更强但本波无收益(无削峰需求、无跨进程) | + +**联调与独立验收(不被上游阻塞死)**:上游 3 个 notify 调用点未接通前,community 暴露**仅 staging/dev 生效**的手工触发 mock 端点 `@PostMapping /admin-api/community/notifications/{type}/mock-trigger`,供 community 单独验收四类通知投递;上游接通后此端点仅留联调用途。 + +> **保留口径(给坚持 MQ 路线者)**:若未来确需把某类通知改走 MQ/遥测事件,则 `engineering-conventions.md §「契约事件登记须同步后端枚举守门人」` 红线生效——**只改 `events.schema.json` 不同步 `TelemetryEventEnum` = 事件被静默 rejected**(accepted:0 信封仍 code:0,极难发现)。本波选 notify-push 正是为**绕开这道易错的双处同步**;execution 版**不**在 `events.schema.json` 新增 community 事件,故无此前置门禁。 + +--- + +## 4. 影响面(blast radius) + +| 落点 | 改动 | 性质 | 风险 | +|---|---|---|---| +| `game-cloud/pom.xml`(root 聚合 pom) | `` 追加 `game-module-community` + `game-module-biz` | **公共文件**(两波共享写点;**克隆必经第①点**) | 并行同改冲突 → 建议串行注册或单 agent 收口;缺此步父 pom 找不到 → compile 失败 | +| `yudao-server/pom.xml` | `` 追加两个 `-server` ``(装配第②点) | **公共文件** | 同上 | +| `YudaoServerApplication.java` / `application.yaml` | **零改**(通配扫描已覆盖 `cn.wanxiang.game.module`) | 无 | 无(明确写入 spec 降认知负荷) | +| `contracts/db-schemas/` + `yudao-server/.../db/migration/` | 新增 V12(community)+ V13(biz),**两处副本字节一致** | 全局单线性序列 | 撞号 → flyway validate 阻断(须中央预占,见 §8 execution 预告第 0 节) | +| `contracts/api-schemas/` | 新增 community.yaml + biz.yaml(独占新文件) | 各自独占 | 无冲突 | +| `contracts/README.md §四` | community=107/biz=110 **已在册**(无需改,只复用) | 只读确认 | 无 | +| **aigc/compliance/trade `-server`**(上游) | **各新增 1 处 `CommunityNotifyApi` 调用点**(提交后同进程)+ community `-api` 暴露 `CommunityNotifyApi` 契约 | 新增跨模块同进程调用(与 feed↔project 同范式) | 上游 service 改动有限,须在事务提交后调用以免通知失败回滚业务 | +| `events.schema.json` + 后端 `TelemetryEventEnum` | **本波零改**(community 走 notify-push,**不新增遥测事件**,见 §3.3) | 不涉及 | 无(主动规避「只改契约不改枚举静默 rejected」双处同步坑) | +| `game-admin`(WS4) | biz 运营代客发起 + 处理队列 UI 入口(BIZ-08/12 端=运营) | 跨工位 | 无 UI 入口 → 孤儿;本波 biz 后端 API 可独立验收,UI 入口与 WS4 排期绑定(见 §6.2/§7 第 6 项) | +| **project → community 计数 seam** | project 发布成功后 notify community 计数(或 community 经 ProjectApi 新增 `countPublishedByCreator` 读数);**计数权威源=project,非 feed** | 新增跨模块同进程调用 | 二选一收口(§2.2「P-INC-01 落地形态」);**不依赖 feed 计数回流改造**,community 可独立验收 | + +> **feed↔community 计数 seam 澄清(R1)**:`feed.yaml` 注释「累计计数最终态归 project/community」指的是**计数最终态的归属权**,**非 feed 反写 community**。INC-01 的「已发布作品数」计数权威源**定为 project 发布成功**(见 §2.2),与 feed 互动信号无关——故 §旧表「feed 信号→community 计数串行依赖」这一项**取消**,避免 community 被 feed 计数回流改造卡成串行依赖。 + +**新增表**:community 表(站内信/通知/等级 + **reward 触发记录表**,V12)、biz 表(biz_lead 工单/报价/进度,V13)。每张表强制带 Yudao 七审计列(creator/updater/create_time/update_time/deleted/tenant_id 全 NOT NULL)+ 唯一键含 (业务键, deleted, tenant_id)。 + +--- + +## 5. 风险与兼容 + +> 问题按严重度排序,每条给影响 / 根因 / 缓解方向。 + +| # | 风险 | 严重度 | 影响 / 根因 | 缓解方向(execution 版须给细节) | +|---|---|---|---|---| +| R1 | **community 触达机制曾未定型(已在 §3.3 定死)** | 🔴 高→已缓解 | 原稿把 community 框成「上游已有事件的消费方」,但实查上游**零域事件、MQ 消费者仅骨架**——实现者无法据本文开工。**根因=机制未建+选型未定,非「上游未就绪」** | **R1 已定型=同进程 `-api` notify-push**(§3.3):新增 3 上游 notify 调用点 + 1 个 `CommunityNotifyApi` 契约,复用 `@Primary` 本地实现,**不建 MQ、不动 events.schema.json**;上游未接通前以 staging 限定 mock 端点独立验收 | +| R2 | **biz 用轻量状态机替代 BPM(等价替代,非缩水)** | 🟠 中 | T-BIZ-02 原义是 BPM 编排;本波 6 P0 仅需单线性五态推进,**复用 project 枚举守门流转即满足**(§3.1 已澄清非「规避标准」) | 复用 project 范式(`ProjectStatusEnum` + `ProjectServiceImpl` 流转校验,非独立 `ProjectStateService`);spec 标注「多级审批/会签等多线流程回补 Flowable 属 M4+远期」 | +| R3 | **匿名写 / 非 Web 线程写审计列雷** | 🟠 中 | community 通知投递、biz 状态机推进、reward 触发记账几乎必含**非 Web 线程写或系统身份写** → 无登录态 → updater NOT NULL 拒绝 → 链路 500/任务卡死。**单测 mock 测不到此雷(项目记忆铁律)** | 入口注入系统身份 LoginUser(id=0)+finally clearContext(范本 EventIngestServiceImpl / AigcGenerateExecutor);**工程门把两条具体异步/系统身份写链路点名为 staging 必测项**(见 §6.3):①community 通知投递链路(上游 notify→写站内信表)、②biz 状态机推进链路(admin 推进→写状态/看板表) | +| R4 | **资金流子环节受 pay 桩制约** | 🟠 中 | biz P-BIZ-04 签署/收款、community P-INC-01 现金奖励真实到账、P-NTF-05 收益最终落 pay 打款;pay 桩 + ICP/支付进件日历闸门 | 明确归 M4:lead form 交付信息流,资金流(收款/分账/提现/在线签章)记债挂 M4 轨,不阻塞本波 | +| R5 | **BIZ-08/12 端=运营,无 UI 入口成孤儿** | 🟠 中 | 两项端为运营而非 B 端自助,更可能由运营后台代客发起;game-admin 无入口则后端能力建好却无 UI | 优先在 game-admin 做运营代客发起入口;与 WS4 确认排期(编排器旁路掩盖 UI 缺陷模式,UI 走查是唯一验收手段) | +| R6 | **Flyway 撞号 / 改旧迁移** | 🟠 中 | 单体单库全局单序列;两 agent 各自起 V12 → validate 阻断;改 V1-V11 任何旧文件 → validate 失败 CI 阻断 | 中央预分配 V12(community)/V13(biz)/V14(W4 埋点加列);**只增不改旧迁移**红线写入 spec | +| R7 | **@Primary 多实例 Bean 冲突** | 🟡 低 | 新模块若新增 Feign 接口,单体下 @FeignClient 代理与本地 @Primary 实现可能 Bean 冲突致启动失败(M1 实证 4 个 CommonApi) | ApiImpl 标 @RestController @Primary;仍冲突的给 @FeignClient 补 primary=false(修法 commit d7fac90);启动盯 BeanDefinition 冲突日志 | +| R8 | **错误码段误复制 project 100 段** | 🟡 低 | 顺手复制 project 骨架忘改段号 → 与 project 重叠 | 务必用预分配 community=1_107 / biz=1_110;ApiConstants.NAME 改 community-server/biz-server(否则 Feign 服务名撞 project) | +| R9 | **P-BIZ-12 P0/P1 边界模糊导致范围蔓延** | 🟡 低 | Doc A 标 P0/P1 混合,P1「快速交付增强」边界未拆分 | 开工前界定 P0 必交子集(可下单+进度看板),P1 列 M4 增量 | +| R10 | **骨架编译过 ≠ 真实可验收** | 🟡 低 | 项目记忆教训:批跑/API 全绿 ≠ 真实可验收 | spec 完成条件要求 mvn 编译+单测+Flyway migrate+Swagger doc.html 可见+真实写链路 staging 实测,「无验证证据不得 claim 完成」 | + +**兼容性**:本波纯新增模块(克隆范式),对已建 11 模块**零行为变更**;**公共写点(root `game-cloud/pom.xml` 的 `` / `yudao-server/pom.xml` 的 ``)须串行收口防并行冲突**;**`events.schema.json` 本波零改**(community 走 notify-push、不新增遥测事件,见 §3.3/§4),故**无写点竞争**、不在串行收口范围内(R2 校正:原稿误把 events.schema.json 列入公共写点,与 §239/§322「本波零改」自相矛盾)。 + +--- + +## 6. 验收标准(community 5 P0 + biz 6 P0 可验证) + +> 逐项可勾,强调「无验证证据不得 claim 完成」。 + +### 6.1 community(5 P0) + +> **通用前提(R1)**:AC-CMU-2/3/4 的上游触达均为**同进程 `-api` notify**(aigc/compliance/trade 各新增 1 处对 `CommunityNotifyApi` 的调用,见 §3.3),**不经 events.schema.json**;上游 notify 调用点未接通前,用 staging 限定 `mock-trigger` 端点独立验收四类投递。 + +| # | 验收口径 | +|---|---| +| AC-CMU-1 | **P-NTF-01**:四类消息(系统/公告/审核/收益)可投递、消息中心列表可读、已读状态正确 | +| AC-CMU-2 | **P-NTF-02**:aigc 生成完成后 `CommunityNotifyApi` 被调用 → 通知到达消息中心(与 P-CRT-13 超时通知互补)。**验收前提:aigc 已接入 notify 调用点(或经 mock-trigger 验投递)** | +| AC-CMU-3 | **P-NTF-03**:(勘误#1:上游源 compliance→project)**project `reviewProject` 审核出结果后** `CommunityNotifyApi` 被调用 → 通知落站内信,**仅 decision=1/2 发、拒绝须带原因**(配合 P-OPN-02;decision=3 下架本波不发,勘误#2)。**验收前提:project 已接入 notify 调用点(或经 mock-trigger 验投递)** | +| AC-CMU-4 | **P-NTF-05**:trade 收益结算后 `CommunityNotifyApi` 被调用 → 站内信(+可选短信)送达。**验收前提:trade 已接入 notify 调用点(或经 mock-trigger 验投递)** | +| AC-CMU-5 | **P-INC-01**:创作者发布第 3 款(计数权威源=**project 发布成功**,见 §2.2)后,等级引擎自动触发并**写一条可观测的 reward 触发记录**(本波到「触发事件可观测 + 本地记账」)。**「记入钱包/流量包 + 现金真实到账」连同 trade `IncentiveCreditApi`/feed 流量包 `-api` 一并落 M4**(本波这两个跨模块写 seam 尚不存在,见 §2.2/§4) | + +### 6.2 biz(6 P0) + +> **UI 落点与跨工位(R1)**:biz 的 admin 队列(AC-BIZ-3)、运营代客发起入口(AC-BIZ-5)属 **game-admin(WS4)**;客户侧进度看板属 **game-studio(WS3)**。本项目记忆铁律「编排器旁路/API 全绿掩盖 UI 缺陷」——故下表凡依赖 UI 的口径**拆两层**:**本波 biz 后端 API 层可独立验收(Swagger/curl)**;**UI 入口列为完成条件增强项、与 WS3/WS4 排期绑定并走真人 UI 走查闭环**,不以另一工位未交付的 UI 阻塞本波 biz 收口。 + +| # | 验收口径 | +|---|---| +| AC-BIZ-1 | **P-BIZ-01**:需求表单提交即落库,开启一条轻量状态机实例(待跟进态) | +| AC-BIZ-2 | **P-BIZ-02**:可按场景检索 aigc 模板并 **runtime 沙箱预览**(复用 4 款可玩模板,取包走 runtime 真实路由 `GET /app-api/runtime/package/{versionId}`(清单 meta)/ `…/{versionId}/manifest`(原始 JSON),execution 版按真实双路由钉准,见 §2.3 B-02)。**负路径(R1 必验):选无编译产物/编译失败/versionId 缺失或非法的模板时,看板降级显示「该模板暂不可预览」而非透传 500;状态机不允许在无可玩 demo 时推进到「待验收」** | +| AC-BIZ-3 | **P-BIZ-03**:状态机各节点状态可经 `/admin-api/biz/*` 查询并推进(**后端 API 层=本波验收**);**客户侧进度看板(game-studio `/biz-orders/my-orders` 或邀请链,WS3)+ admin 处理队列视图(WS4)=完成条件增强项**,随对应工位排期走 UI 走查 | +| AC-BIZ-4 | **P-BIZ-04**:客户「试玩 + 反馈 + 确认」三态走通(**签署/收款留 M4**,本波线下签署+后台标记已签兜底) | +| AC-BIZ-5 | **P-BIZ-08**:**本波 biz 侧验收 = 经 `/admin-api/biz/*` Swagger/curl 可发起品牌营销定制单并完成报价输出**(报价≠可支付订单)+ 编排(后端能力可独立验收);**game-admin 运营代客发起 UI 入口(WS4)= 独立跟进项**,需 WS4 排期并走 UI 走查闭环 | +| AC-BIZ-6 | **P-BIZ-12**:文旅场景模板可发起定制单并走验收(**P0 子集=场景需求 CRUD + 模板复用 + 专属看板必交**,后端 API 层本波验收;P1 快速交付增强列 M4) | + +### 6.3 工程门(两模块共用,逐项可勾) + +`mvn -pl game-module-{name}-server compile 通过` → `单测 XxxServiceImplTest(继承 BaseMockitoUnitTest)绿` → `Flyway migrate 通过(V12/V13)` → `yudao-server 编译通过` → `启动后 doc.html 可见新模块分组` → `Spring 无 @Primary Bean 冲突` → `staging 实测两条具体异步/系统身份写链路不 500(逐条点名,见下)`。 + +**精确 CLI(execution 版固化,R1 补,避免「Flyway migrate/doc.html/staging」笼统口径)**: +1. **编译 + Flyway**:`mvn -DskipTests clean install -pl game-cloud/game-module-community/game-module-community-server -am`(biz 同理)→ 启动后由 yudao-server 应用层 Flyway 自动 migrate(V12/V13);migrate 一致性校验 = `contracts/db-schemas/` 与 `yudao-server/.../db/migration/` 两处副本**字节一致**。 +2. **Swagger 可见**:`curl http://localhost:48080/v3/api-docs?group=community`(biz 换 `group=biz`)返回非空且含新端点。 +3. **staging 实测两条异步/系统身份写链路(R3 点名,单测 mock 测不到)**,逐条验「非 Web 线程 / 系统身份写 `updater` NOT NULL 不 500」: + - **链路①=community 通知投递**:上游 notify(或 staging `mock-trigger`)→ 写站内信表,验系统身份注入 `LoginUser(id=0)` 生效、`updater` 落值、不 500; + - **链路②=biz 状态机推进**:admin 推进定制单状态 → 写状态/看板表(含 reward 触发记账若由非 Web 线程触发),验同上。 + +--- + +## 7. 待确认项(每项带推荐) + +> 标 **⭐** 者为创始人战略决策(已在 §0.1 汇总);未标者属实现细节,可由 execution 版单 agent 直接按推荐落地。 + +| # | 决策 | 推荐 | +|---|---|---| +| 1 ⭐ | **D-A · biz 形态:轻量 lead form 先行 vs 全 biz** | **lead form 先行**(6 P0 无一依赖 pay/trade;复用面大;命中近期现金线;资金流并入 M4) | +| 2 | community 与 biz 建设次序 | **先 community 后 biz**(community 触达机制最独立、无外部业务卡点、复用面集中、风险最低;biz 外部对接多 + 受 pay 桩制约,适合后段)。
注:M5=MVP 全链路交付里程碑(`.agents/knowledge/mvp-scope-and-milestones.md` §5,6/27 Day15),通知底座是全链路收尾必备项;此处不以「被 M5 依赖」作排序依据(M5 含全部模块,为同义反复),改以上述可自洽验证的独立性/风险理由排序 | +| 3 | **community 触达机制(R1 已定型,供拍板复核)** | **同进程 `-api` notify-push**(§3.3):新增 3 上游 notify 调用点 + `CommunityNotifyApi` 契约,复用 `@Primary`,**不建 MQ、不动 events.schema.json**;上游未接通前以 staging 限定 `mock-trigger` 端点独立验收 | +| 4 | biz P0 清单 + Wave4 总数确认 | biz 本波 **6 P0 = P-BIZ-01/02/03/04/08/12**;**P-BIZ-11 学生防沉迷经 Doc C 已划归 compliance(compliance +1 P0),不计入 biz**;**Wave4 总交付 = 11 P0 = community 5 + biz 6** | +| 5 | P-BIZ-12 的 P0 必交子集边界 | **场景需求 CRUD + 模板复用 + 专属看板 = P0 必交**(与 P-BIZ-01/03 同能力栈、无额外定制);P1「快速交付增强」列 M4 后增量 | +| 6 | BIZ-08/12 运营 UI 入口(game-admin / WS4)是否排期 | **本波 biz 后端 API 独立验收(Swagger/curl);UI 入口(运营代客发起=WS4 / 客户看板=WS3)列完成条件增强项**,与对应工位排期绑定走 UI 走查,**不阻塞本波 biz 收口** | +| 7 | 是否允许两模块并行建设 | 模块内实现可并行;**全局单序列资源(root pom / yudao-server pom / Flyway 序号 / 错误码段 / 契约 yaml 文件名)一次性预占后串行收口或单 agent 聚合接线**(见 §8 execution 预告第 0 节);**events.schema.json 本波不改** | +| 8 | **P-INC-01 计数权威源落地方案(R2 补,二选一须显式定)** | **(a) project 发布成功后同进程 notify community 计数**(与三上游同范式,**推荐**——上游单点改动、与现行 `@Primary` notify 一致);或 (b) community 经 ProjectApi 新增 `countPublishedByCreator(creatorUserId)` 主动读数。**两者都不依赖 feed 计数 seam**,均可让 community 独立验收。**此项可由 execution 版 §0 单 agent 直接拍 (a) 落地,无须创始人介入**(属实现细节,列此仅为消除 execution 期重复思考) | +| 9 ⭐ | **D-B · community 功能优先级:通知底座最小集 vs 全量 T-CMU**(§3.2) | **通知底座最小集**——T-CMU-10 编排+T-CMU-06 站内信+T-CMU-11 等级引擎(+可选短信),一次覆盖 5/5 P0;SOC/GRW 域(关系图谱/排行/成就/扇出,全 P1/P2)仅留接口桩,不实现 | +| 10 ⭐ | **D-C · 是否本波即接 ip seam(D5 已定「寄宿 compliance、不独立建模块」)** | **本波不接、维持 D5 推迟**——Wave4 仅 P-BIZ-02 协同涉及 IP 模板市场 T-IP-09,而本波 P-BIZ-02 复用 aigc 4 模板(绕开 T-IP-09),故无需 ip seam;待真做素材/IP 市场或 DMCA 下架(P-IPX-05,P1+/远期)再接(证据见 §0.1 D-C 行脚注) | + +--- + +## 8. 下一步(双 spec 铁律) + +创始人拍板后出 execution 版(`2026-06-11-Wave4-community-biz-execution.md`),展开: + +- **§0 全局单序列资源预占登记(R1 补,开篇第一节,一次性写死锁定,防 V12 双占/文件名碰撞/错误码撞段)**:一个可勾的中央登记动作,由**单 agent 收口提交**—— + - Flyway:`community = V12.0.0__create_game_community.sql` / `biz = V13.0.0__create_game_biz.sql`(**含 W4 埋点加列 V14 占位**);**两处副本**(`contracts/db-schemas/` 与 `yudao-server/.../db/migration/`)须**字节一致**; + - 错误码段:`community = 1_107` / `biz = 1_110`(已在 `contracts/README.md §四` 在册,复用即可,`-api` 的 `ErrorCodeConstants` 登记); + - 契约文件名:`contracts/api-schemas/community.yaml` · `biz.yaml`(独占新文件); + - 预占前置校验(R2 校正:Flyway 占用须**按文件名而非内容**判定,否则会被 V11 让位声明注释绊住): + - **Flyway 文件名占用校验(应全为空/无此文件,exit≠0 即未占用)**:`ls contracts/db-schemas/V12* contracts/db-schemas/V13* game-cloud/yudao-server/src/main/resources/db/migration/V12* game-cloud/yudao-server/src/main/resources/db/migration/V13* 2>/dev/null`; + - ⚠️ **不要用 `grep -r 'V12\|V13' …` 判占用**——它是内容匹配,会命中 `V11.0.0__create_passport_player_invite.sql:13` 的让位声明注释「…Wave4 community/biz 原规划的 V10/V11 顺延 V12/V13」(两处副本各一行,实测返 2 行非 0),**命中仅此注释属预期、非真实占用**; + - **错误码段占用校验(应全返 0)**:`grep -rn '1_107\|1_110' game-cloud/`。 +- **克隆 5 步骨架**(cp 黄金模块 → 改 3 个 pom artifactId → **① root `game-cloud/pom.xml` 注册 ``(独立一步,缺则父 pom 找不到)+ ② `yudao-server/pom.xml` 注册 `-server` 依赖** → -api 写 ErrorCodeConstants(1_107/1_110)+ApiConstants(改 community-server/biz-server 防 Feign 服务名撞 project)+枚举+DTO+Feign → -server 写 controller/service/dal/convert/ApiImpl@Primary);execution 版列出特定 artifactId; +- **community 通知底座 DDL(V12,含 reward 触发记录表)+ `CommunityNotifyApi` 契约 + 三上游 notify 调用点接入(aigc/compliance/trade 各 1 处,提交后同进程)+ 等级引擎触发(计数权威源=project;§0 由单 agent 直接拍 §7 第 8 项推荐方案 (a)=project 发布成功后同进程 notify community 计数,无须创始人介入)+ staging 限定 `mock-trigger` 联调端点**; +- **biz lead form DDL(V13)+ 轻量状态机定义(`BizLeadStatusEnum` + service 流转校验,范本=project `ProjectStatusEnum`/`ProjectServiceImpl`)+ 模板预览取包接线(含取包失败/无产物/versionId 缺失负路径降级)+ 进度看板/admin 队列后端 API**; +- **契约 community.yaml/biz.yaml 端点清单**(/app-api/community/* + /admin-api/community/*(含 `CommunityNotifyApi` Feign + `mock-trigger`);biz 偏 /admin-api/biz/*); +- **Nacos 模块配置注册(R1 补,对齐 add-business-module.md 步骤 6)**:community(短信发送开关 `community.sms.enabled` / 里程碑阈值 `community.level.publish-threshold`)+ biz(状态机节点超时 `biz.state-machine.timeout-hours`);`deploy/nacos/` 导入并验证 Nacos 控制台可见; +- **审计列系统身份注入范本预置(LoginUser(id=0)+finally clearContext)+ staging 实测计划(点名两条异步/系统身份写链路,见 §6.3)**; +- **M4 留债登记段(两类债分离,R1)**:**①资金流债**(biz:T-BIZ-08 收费、T-BIZ-03 在线签章;community:reward 记入钱包/流量包 = trade `IncentiveCreditApi` + feed 流量包分配 `-api`、现金真实到账)= M4 留债清单;**②前端 UI 入口债**(biz 运营代客发起 UI=WS4 / 客户看板=WS3)= 跨工位协调(可与本波并行),走 UI 走查闭环。 + +收口后按 §7 知识沉淀铁律:把 Wave4 克隆实证回写 `.agents/skills/add-business-module.md`(把 107/110 从「推断顺延」升为「已落地」、补 root pom 注册为独立步、补「跨模块通知优先同进程 `-api` notify-push 而非 MQ」范式)+ 新踩坑,更新 `.agents/README.md` 索引。 + +--- + +*证据边界(2026-06-11 实查):Flyway ceiling = V11(contracts/db-schemas 与 yudao-server/.../db/migration 两处一致,最高 V11.0.0__create_passport_player_invite.sql);contracts/api-schemas 现 10 份,community.yaml/biz.yaml 不存在(可新建无冲突);现有 9 个 game-module-*(ad/aigc/compliance/feed/project/runtime/studio/telemetry/trade);错误码段实占 grep 确认 1_107(community)/1_110(biz) 均 0 占用、空闲。P0 计数依 Doc A 产品需求清单.md / Doc B 技术架构与模块.md / Doc C 需求模块映射.md 三文档交叉核对(community=5 / biz=6;P-INC-01 映射 = `T-TRD-12 发放 · T-FED-03 流量 · T-CMU-10`,line168;P-BIZ-11 主模块=compliance `T-CMP-36/37`,line216)。BPM/Flowable 装配状态、pay/trade 真实度引自 docs/mvp/MVP进度总账.md(标注为桩),属现状引用非本审计动作。错误码段权威源 = contracts/README.md §四 line79(engineering-conventions.md §1.3 措辞为「推断顺延」,以 README/契约固定值为准)。* + +*R1 整改新增实查证据(2026-06-11):①跨模块事件基建——`grep publishEvent/ApplicationEvent/RocketMQTemplate` 在 compliance/trade 全空(零域事件);9 个 game-module 无 MQ 生产者目录;唯一 MQ 消费者 `TelemetryEventConsumer.java` 无 `@RocketMQMessageListener` 注解、类注释「MQ 消费 待对接」(telemetry MQ 从未跑通);真范式 = `FeedServiceImpl` 注入 `ProjectApi`/`RuntimePackageApi`(`@Primary` 本地实现)。②events.schema.json eventRegistry 仅 telemetry 事件,`generate_succeeded/income_settled` 为前端遥测串非后端域事件;`TelemetryEventEnum` 为上报真守门人(engineering-conventions §「契约事件登记须同步后端枚举守门人」)。③轻量状态机范本——无独立 `ProjectStateService.java`;实为 `ProjectStatusEnum`(`isDraft` 等流转判定)+ `ProjectServiceImpl` 写前流转校验。④runtime 取包有失败态——`RuntimePackageApi`/`ErrorCodeConstants` 含 1-102 段错误码(`RUNTIME_PACKAGE_NOT_READY` 1-102-001-001)+ `BuildRespVO.failCode/failReason`。⑤feed/trade 契约缺口——`feed.yaml` 仅 `boost`/`pinned`(无流量包发放端点)、`trade.yaml` 无 grant/发放/incentive 端点;`feed.yaml` line84「累计计数最终态归 project/community」指归属权非反写。⑥project 侧——无 `project_published` 事件、无 `publishCount` 字段,ProjectApi 现有方法 = getCreatorUserId/getCurrentVersionId/getStatus/getFeedMeta/createProject/unlistIfPublished。* + +*R2 整改新增实查证据(2026-06-11):①§0 预占校验口径——`grep -r 'V12\|V13' contracts/db-schemas/ game-cloud/yudao-server/.../db/migration/` 实测**返 2 行**(`V11.0.0__create_passport_player_invite.sql:13` 让位声明注释,两处副本各一行),非「应全返 0」;改用 `ls V12*/V13*`(实测 exit=2、空,确未占用)。②runtime 取包真实路由——`AppRuntimeController` 实暴露**两端点**:`GET /package/{versionId}`(line60,返 `RuntimePackageRespVO` 清单 meta)+ `GET /package/{versionId}/manifest`(line75,返原始 JSON),门禁同源(同走 `getPackageManifest`);原稿简写 `/package/{versionId}` 是真实端点之一但漏列 `/manifest` 那条。③`add-business-module.md` 步骤表(行 66)实测**仅 `步骤5=yudao-server/pom.xml`、无 root `game-cloud/pom.xml` `` 注册步**,开头明写「无需改 Application/yaml,只需 yudao-server/pom.xml 加依赖」——`grep 'game-cloud/pom.xml\|\|聚合 pom'` 该文件**零命中**,证实 skill 待回写。④M5 定义——`.agents/knowledge/mvp-scope-and-milestones.md:101` 与 `docs/mvp/MVP进度总账.md:32` 均定义 M5=「MVP 交付」全链路里程碑(6/27 Day15),故「community 被 M5 依赖」属同义反复(M5 含全部模块),改以独立性/风险理由排序。⑤ip seam(D-C 决策证据)——`docs/mvp/MVP进度总账.md:53` D5 决策「ip 不独立建、seam 寄宿 compliance」、line95 标推迟 TODO;`需求模块映射.md` 中 Wave4 11 P0 仅 P-BIZ-02 协同含 `T-IP-09 模板市场`(line207),community 5 P0 零 ip 关联,本波 P-BIZ-02 复用 aigc 模板即不触 T-IP-09。⑥§266 与 §239/§322 矛盾——§239 影响面表与 §322(§7-7)均明写 events.schema.json「本波零改/不改」,§266 兼容性段却把它列入「须串行收口的公共写点」,自相矛盾,已订正。* + +--- + +## 评审整改纪要(R1) + +> 第一轮对抗评审 findings 逐条处置(2026-06-11)。**接受=已改对应节;部分接受/重定向=采纳问题但解法调整,理由在备注;拒绝=保留原口径,理由在备注。** 多条近重复 finding 合并处置(标 ⊃)。 + +**贯穿性裁决(一处定死多条收敛)**:本轮多条 finding(blocker events.schema.json / high「事件基建不存在」/ R1 / 多条 medium 触发机制)实为**同一个未决架构点的不同侧面**:community 用什么机制触达。R1 据实查证据**一次定死 = 同进程 `-api` notify-push**(§3.3)。此决策的连锁后果:(a) community **不走 events.schema.json**,故 blocker「先在 events.schema.json 注册三事件」的前置门禁**不再需要**——但其底层关切(只改契约不改枚举=静默 rejected)以「保留口径」写入 §3.3 以备未来 MQ 路线;(b) 上游不发域事件,改为新增 3 个 notify 调用点,工作量画像在 §3.3 表与 §4 显式登记;(c) INC-01 计数权威源改 project、记账留 M4,消除孤儿子链路。 + +| Finding(severity|定位) | 处置 | 改了哪 / 理由 | +|---|---|---| +| 事件基建整体不存在,应框为「机制未建+选型未定」(high|§2.2/§3.3/R1) ⊃ blocker(events.schema.json 三事件未注册) ⊃ medium(事件消费手工触发形态未明 §3.3/§6.1) | **接受(核心整改)** | §0/§2.1/§2.2/§3.3 全面重写:机制定型为同进程 `-api` notify-push;§3.3 新增四维对比表 + 工作量画像 + staging `mock-trigger` 端点;R1 行改为「已缓解」。blocker 关切以「保留口径」并入 §3.3,不再设 events.schema.json 前置门禁(本波不改该文件)。 | +| P-INC-01 触发+记账依赖 trade 发放/feed 流量是半截孤儿链路(medium|§2.2/AC-CMU-5) ⊃ high(feed 流量包/trade 发放无 -api 契约) ⊃ medium(INC-01 计数权威源歧义 A/B 两路) | **接受** | §2.2 新增「P-INC-01 落地形态」段:计数权威源**定为 project 发布成功**(二选一:project notify / `ProjectApi.countPublishedByCreator`),**不依赖 feed 计数 seam**;「记入钱包/流量包」本波只写自有 reward 触发表,跨模块写(trade `IncentiveCreditApi`/feed 流量包 `-api`)连同现金到账**留 M4**;AC-CMU-5 口径相应收紧。 | +| AC-BIZ-3/5 硬依赖 game-admin UI 而 admin 端零视图(medium|§6.2/R5) ⊃ high(AC-BIZ-5 依赖 WS4 未排期 UI 违反独立可交付) | **接受** | §6.2 加「UI 落点与跨工位」前置说明 + AC-BIZ-3/5 拆两层(后端 API 本波独立验收 / UI 入口列完成条件增强项绑 WS3·WS4 排期走 UI 走查);§1 非目标补「两类债分开记账」;§7 第 6 项同步。 | +| biz 模板预览只描述 happy path,缺取包失败影子路径(medium|§2.3/AC-BIZ-2) | **接受** | §2.3 加「B-02 模板预览影子路径」段(无产物/编译失败/versionId 缺失降级 + 状态机不许无 demo 推进);AC-BIZ-2 补负路径必验口径。 | +| Flyway V12/V13、错误码、yaml 文件名缺中央预占登记动作(low|§4/R6/R8/§8) | **接受** | §8 新增「§0 全局单序列资源预占登记」为 execution 开篇第一节(含字节一致 + grep 前置校验 + 单 agent 收口)。 | +| 异步写链路 staging 实测未点名 community 通知/biz 状态机两条(low|§6.3/R3) | **接受** | §6.3 把两条异步/系统身份写链路逐条点名为必测项;R3 行同步具体化。 | +| §2.4「唯一公共装配点」与 §4 blast radius 不一致,漏 root 聚合 pom(high|§2.4/§4) ⊃ high(克隆 pom 注册点不清,未明 game-cloud vs yudao-server) | **接受** | §2.4 改为「公共装配点=两处」(① root `game-cloud/pom.xml` 注册 `` ② `yudao-server/pom.xml` 注册 `-server` 依赖);§4 首行同步细化;§8 克隆步骤把 root pom 注册列为独立一步。 | +| P0 计数术语含糊,未陈述 Wave4 总新增 P0 数(high|§2.3/§7-4) | **接受** | §0 与 §2.3 明确「Wave4 总交付 = 11 P0 = community 5 + biz 6;P-BIZ-11 经 Doc C 划归 compliance(compliance +1 P0)」;§7 第 4 项同步。 | +| BIZ-02 轻量状态机「替代 BPM」是缩水还是等价替代表述不清(high|§2.3/R2) | **接受** | §3.1 新增「轻量状态机 vs BPM 是等价替代而非缩水」澄清段(本波 6 P0 仅需单线性五态、多线流程留 M4+Flowable);R2 行重写为「等价替代」。 | +| Flyway V12/V13 三处一致性未验证(medium|§4/R6/§6 脚注) | **接受** | §6 脚注已记两处 ceiling=V11 一致;§8 §0 预占段补 `grep -r 'V12\|V13'` 两处应返 0 + 字节一致校验。 | +| BIZ-03 客户端入口形态未明(medium|§2.3/§6.2) | **接受** | §2.3 表 P-BIZ-03 行 + AC-BIZ-3 明确客户侧入口 = game-studio `/biz-orders/my-orders`(WS3 横算)或邀请链。 | +| §1 非目标(资金流 M4)与 R5(UI 入口缺失)混为一个债项(medium|§1/R5/§7) | **接受** | §1 非目标新增「两类留债分开记账」(①资金流债→M4 ②前端 UI 入口债→跨工位并行);§8 M4 留债段同结构分离。 | +| feed↔community 计数回流机制不清(反写/读表/异步事件)(medium|§4) | **接受(重定向)** | 采纳「机制须写死」,但**计数权威源改 project 后此 seam 取消**:§4 加澄清「feed.yaml line84 指归属权非反写」,旧「feed 信号→community 计数串行依赖」项删除,community 不再被 feed 计数回流卡成串行。 | +| §6.3 工程门缺 exact CLI(medium|§6.3) | **接受** | §6.3 补三组精确 CLI(`mvn -DskipTests clean install -pl ... -am` / `curl .../v3/api-docs?group=community` / staging 两链路实测)。 | +| §8 遗漏 Nacos 模块配置注册落点(low|§8) | **接受** | §8 补 Nacos 配置注册条(community.sms.enabled / community.level.publish-threshold / biz.state-machine.timeout-hours + deploy/nacos 导入验证)。 | +| P-BIZ-12 P0 子集「可下单+进度看板」模糊、与 01/03 是否重叠(low|§2.3/§7) | **接受** | §2.3 表 P-BIZ-12 行 + AC-BIZ-6 + §7 第 5 项收紧为「场景需求 CRUD + 模板复用 + 专属看板(与 01/03 同能力栈、无额外定制)」;P1 列 M4。 | +| T-PRJ-06 轻量状态机引用不精确,缺代码路径(low|§2.4/§3.1/§8) | **接受(含修正事实错误)** | 实查**无 `ProjectStateService.java`**——全文将「复用 T-PRJ-06/ProjectStateService」更正为「复用 project 范式 = `ProjectStatusEnum`(流转判定)+ `ProjectServiceImpl`(写前流转校验)」(§2.1 图/§2.3 图/§2.4/§8)。原 finding 建议的「ProjectStateService.java」落点亦不存在,故按实情改。 | + +**无「拒绝」条目**:本轮 24 条 findings 全部接受或部分接受/重定向。其中 3 条(feed↔community 计数机制、blocker events.schema.json 前置门禁、T-PRJ-06 引用)采纳了问题本身但**解法与原 suggestion 不同**——因实查证据显示原 suggestion 的落点(feed 反写、events.schema.json 路线、ProjectStateService.java)与本仓现状或更优范式不符,已在上表备注理由。 + +--- + +## 评审整改纪要(R2) + +> 第二轮对抗评审 findings 逐条处置(2026-06-11,6 条:medium×2 + low×4)。**全部经实查仓库核实后接受**(含 1 条事实订正:M5 实有定义)。逐条证据见文末「R2 整改新增实查证据」脚注。 + +| Finding(severity|定位) | 实查核实结论 | 处置 | 改了哪 | +|---|---|---|---| +| §8 §0 预占校验 `grep -r 'V12\|V13'` 不返 0(会命中 V11 让位声明注释 2 行),实现者开篇第一步即被自身注释绊住(low|§8 §0 行334) | **属实**:实测返 2 行(`V11…invite.sql:13` 两处副本各一行,内容匹配非文件名匹配);`ls V12*/V13*` 实测 exit=2 空 | **接受** | §8 §0 校验口径改「按文件名」:用 `ls contracts/db-schemas/V12* … V13* …`(应空/exit≠0);显式标注「不要用 grep 内容匹配,命中仅 V11 让位声明注释属预期非占用」;错误码段仍用 `grep -rn '1_107\|1_110'`(应返 0) | +| `add-business-module.md` 旧版仅列「步骤5=yudao-server pom」、未把 root `game-cloud/pom.xml` `` 注册列独立步,与本 spec §8「装配点=两处」口径不一致,只读 skill 者漏 root pom 注册触发 compile 失败(low|§2.4/§8/skill 行66) | **属实**:`grep 'game-cloud/pom.xml\|\|聚合 pom'` 该 skill **零命中**;步骤表开头明写「无需改 Application/yaml,只需 yudao-server/pom.xml 加依赖」 | **接受**(不改 skill 内容,加 spec 提示——回写 skill 维持「本波收口后」原计划,避免开工前动 canonical) | §2.4 加 ⚠️ 硬提示:『skill 步骤表尚未含 root pom 注册(待本波收口回写),本波克隆以本节「装配点=两处」为准,勿以旧 skill 为唯一依据』;execution 版克隆步骤开篇须复述 | +| biz 取包端点写 `/app-api/runtime/package/{versionId}`,真实路由是带 `/manifest` 后缀那条;属端点精度问题不影响可行性结论(low|§2.3 B-02 行161/AC-BIZ-2 行293) | **部分属实**:实查 `AppRuntimeController` 两端点**都存在**——`GET /package/{versionId}`(line60,清单 meta)+`GET /package/{versionId}/manifest`(line75,原始 JSON);原稿写的是真实端点之一,但漏列 `/manifest` 那条 | **接受**(按真实双路由钉准,不止补 /manifest) | §2.3 B-02 新增「取包端点真实路由」段,列两端点真实路由+用途+门禁同源;AC-BIZ-2 同步双路由;提示 execution 版端点清单两条写全 | +| §266 兼容性段把 events.schema.json 列为「须串行收口的公共写点」,与 §239/§322「本波零改该文件」自相矛盾(medium|§266 vs §239 vs §322) | **属实**:§239 影响面表标「本波零改/无」、§322(§7-7)标「本波不改」,§266 却列入公共写点 | **接受** | §266 改写:公共写点仅 root pom/yudao-server pom 须串行收口;events.schema.json 本波零改、无写点竞争、不在串行收口范围(显式标 R2 校正消歧义) | +| §7 第 2 项「community 被 M5 依赖」中 M5 未在全文定义,理由对拍板无参考且不可验证(medium|§312 待确认项 2) | **订正**:M5 **实有定义**(`mvp-scope-and-milestones.md:101`/`MVP进度总账.md:32` = MVP 全链路交付里程碑 6/27 Day15);但「被 M5 依赖」确属同义反复(M5 含全部模块) | **接受**(采纳 suggestion(b):改自洽可验证表述) | §7 第 2 项改:以「触达机制最独立、无外部业务卡点、复用面集中、风险最低」排序;补注 M5 定义+说明不以「被 M5 依赖」作依据(同义反复) | +| P-INC-01 计数权威源「二选一」(project notify vs ProjectApi.countPublishedByCreator) 在待确认项未显式决策,execution 期或重复思考(low|§2.2/§7-2~4) | **属实**:原稿仅在 §2.2 正文列二选一,§7 无对应决策行 | **接受**(采纳 suggestion:补 §7 决策点 + 标「可由 execution §0 单 agent 直接拍 (a)」) | §7 新增第 8 项(计数权威源二选一,推荐 (a) project notify,标可单 agent 定无须创始人介入);§8 §0 同步标「单 agent 拍 (a)」 | + +**R2 顺带补强(非 finding 直接要求,但同一处自洽性收口)**:①新增 §0.1「待创始人拍板的关键决策」汇总段,提炼 **D-A(biz 形态)/D-B(community 功能优先级)/D-C(ip seam 是否本波接)** 三项战略决策置顶供拍板;②§7 表给战略项标 ⭐ 并与 §0.1 互链,区分「创始人决策项 vs execution 单 agent 可定的实现细节」;③据实查(D5 决策「ip 寄宿 compliance」+Wave4 仅 P-BIZ-02 沾 T-IP-09 且本波复用 aigc 模板绕开)补 D-C 推荐「本波不接 ip seam、维持 D5 推迟」,并入 §7 第 10 项。 + +**无「拒绝」条目**:R2 全 6 条接受(含 1 条事实订正后仍采纳其改进诉求)。三处「采纳但解法/事实微调」(取包端点真实是两条非一条、M5 实有定义、skill 回写维持收口后计划改为加 spec 提示)已在上表「实查核实结论」列说明。 diff --git a/docs/mvp/MVP作战清单.md b/docs/mvp/MVP作战清单.md index a88f364b..77cb9773 100644 --- a/docs/mvp/MVP作战清单.md +++ b/docs/mvp/MVP作战清单.md @@ -65,4 +65,4 @@ ## 下一步(立即) - 🧑 **你**(五件 · 2026-06-10 晚拍板侧已收口 ③⑤全拍 + ①②的决策项全拍,**剩线下动作**):① **奇绩申报投递**(✅ 终稿候选已定稿:★1 品牌=绘境/★2 已签框架协议/★3 招募中 三项全拍定;品牌清扫+禁投/禁外发横幅已落——剩=**你投递 + 面试备好可出示的 IP 框架协议文件**〔仓内仅见谈判承诺书草案,★2 口径以你确认的线下已签文件为据;若实未签署须改回口头意向〕);② **A1 闸门线下提交**(✅ 看板已建 [`A1闸门看板.md`](./A1闸门看板.md) + 律所 brief 已备 [`律所合规咨询brief.md`](./律所合规咨询brief.md),主体已确认可用——剩=**本周四件:域名+ICP 提交 · 短信报备提交 · 律所约谈 · LLM 实名口径确认**);③ ~~R4 模板集拍板~~ ✅ **已拍(2026-06-10):维持 D2、按 D2 改建**——M-c 建 merge→idle→tycoon(clicker 保留),dodge/runner/match 降 P1 留契约(回填:mvp业务决策 D2 复审注 + tech-decisions 待拍表#6);④ 修 new-api 网关二厂系通道(deepseek key 未激活、glm/qwen/kimi token 过期)——仍待你;⑤ ~~鉴权评审版拍 7 项 + R3 假设值~~ ✅ **全拍**(鉴权七项=评审版 §9.1;**R3 已收口 2026-06-10**:叠加规则=IP 从创作者份额出·净额基数·平台恒 20%,eCPM 档=15/30/60——敏感性模型一页已落 [`单位经济敏感性模型.md`](./单位经济敏感性模型.md),关键结论=自有端 IAA 是万级 DAU 才回本的规模游戏、近期现金只能靠 B 端现金线;D3 复审注/glossary/BP:299 勘误已回填)。**至此五件中的拍板项全部清零,剩你的线下动作(②本周四件+①投递+BP 3★)。** -- 🤖 **我**:①**审计修复 W1·文档回填波** ✅ **2026-06-10 落地**(技术决策版头部回填状态+C2/D1/沙箱独立源红线/风险章+3项/§9 降级注/决策索引;Doc B 权威读序+建设形态+4 处📌勘误+未编目组件;开发团队版废止横幅+AGENTS.md 必读#4 加注;tech-decisions 回填+待拍表更新;规则档沙箱红线+LLM 消毒+views/wanxiang 校正;本清单回填铁律——对照 HJ-AUDIT-001 §7 清单逐项核)→ ②第二批 20 创意 ✅(batch-002+002b 综合 95%)→ ③审计 W2 对外材料清洗 ✅ **2026-06-10 全收口**(★口径拍定+品牌清扫/横幅 + **BP 改造版红线清洗完成**:四红线/独家/绝对化/未实测指标全清、06-10 实证硬货+三线排序叙事入文、grep 自检正文红线词=0;**留 3 处 ★ 待你确认:团队表述×2 + 唯一融资口径**)→ ④审计 W3 Doc A 合规换血(待律所意见)→ ⑤审计 W4 单位经济埋点(**✅ R3 已拍解锁**:按 [`单位经济敏感性模型.md`](./单位经济敏感性模型.md) §7 测量计划扩 B2 回路——LLM token 计费/素材成本/广告产值/创作者应得四类按游戏粒度埋点,待排期)→ ⑥**鉴权 execution 版 ✅ 已出**(`2026-06-10-真实鉴权与匿名玩家-execution.md`:落点=system 内扩展、Flyway=V11(community/biz 顺延 V12/V13)、@PermitAll 清单 14 条带 file:line、邀请码全生命周期、R6 安全前置;**新发现硬边界:game_runtime_session.player_user_id NOT NULL 须 V11 ALTER**)→ **✅ 建设+e2e 全收口 2026-06-10**(契约/三面建设/构建门 648 测绿/三部署窗/两轮 e2e 18 代理:逮修 2 P0+1 枚举缺陷,E1-E13 全过——详 `2026-06-10-真实鉴权与匿名玩家-e2e报告.md`)→ ⑦**M-c 模板波**:review 版+四项拍板 → execution v2(四镜头核验整改 `12e0c8f`)→ **✅ 批① merge 全链收口 2026-06-10**(契约/三面建设/构建门/五级验收门全过:merge 五条 AND 全绿+clicker 回归五绿+Golden v1.1.2 PASS+**校准批 accept 10/10 零 infra**+金丝雀 10 条入 feed;逮修 3 真缺陷〔鉴权守卫×取证导航/verify_package merge 取参/pom 资源漏复制〕;提交链 `b1fa30d`→`8bd3ebf`;收口报告 `2026-06-10-Mc模板波-批①收口报告.md`;**新模板接入配方已蒸馏** `.agents/skills/add-game-template.md`)→ **✅ 20 条正式批过门 2026-06-10**(`merge-prod-20` accept 19/20=**95%**,infra=0,金丝雀 10/10 触顶+feedVisible,**feed→50 款**;观察项=对抗稳定性复测连续两批 2/3〔累计 4/6,Golden 门兜底,v1.1.3 候选〕)→ **✅ 批② idle+tycoon 全收口 2026-06-11**(五级门:构建/在线五AND/Golden/校准批 10+10 全 1.0/金丝雀——级5 tycoon 抽检分歧 R8 冻结观察项;越模板守卫终裁降 Golden 观察项 `26319b3`;**模板 1→4 全可生成可玩可进 feed**)→ ⑧**批② 遗留债·回包链路专项修 ✅ 2026-06-11 收口**(根因=v-for 模板 ref 数组语义致 `HostBridge.targetWindow=undefined`、**host→game 全方向自建成起静默断**〔storage/ad/pay 回包+init〕;探针四锚点+判别实验锁凶,单文件修复+部署 :4173 实证:**idle 离线补发往返闭合**〔存档→刷新→val=obj〕+clicker 回归绿+init 首次送达;**ad/pay 判定=同断同愈(机制级),业务级闭环留 M4 验收**;三条新红线入 engineering-conventions §1.2+常备双边探针 `probe_bridge_channel.py`;报告 `2026-06-11-回包链路专项修-收口报告.md`)→ ⑨**W4 成本①+new-api 计费集成评审 ✅ 2026-06-11**:评审版 `2026-06-11-newapi计费平面集成-review.md`(结论=生成侧用 new-api·收益侧留 trade 硬边界)→ **两轮对抗评审(4 镜头)推翻初稿**(v2 整改:admin 无法替他人铸 token 硬阻塞 / new-api 3000 公网暴露〔创始人裁暂不动〕/ 成本¥0.02-0.04→¥0.009 修正 / 全量计量前置于支付通道 3-5 周阻塞)→ **创始人拍板=本周薄片余推迟**→ **薄片①已落** `newapi_cost.py` 直读 logs.quota 权威成本(merge-prod-20 实测 ¥0.59/批·¥0.031/款·生成裁判分列·平台vs创作者归属),客户端估算降 fallback。**推迟(支付真实化后)**:per-creator 开通+支付→配额同步+订阅。→ 队列=**前端接线(admin+studio 接 staging 5 链路)/ Wave4(community+biz)/ W4 ③④广告·创作者后端埋点**。 +- 🤖 **我**:①**审计修复 W1·文档回填波** ✅ **2026-06-10 落地**(技术决策版头部回填状态+C2/D1/沙箱独立源红线/风险章+3项/§9 降级注/决策索引;Doc B 权威读序+建设形态+4 处📌勘误+未编目组件;开发团队版废止横幅+AGENTS.md 必读#4 加注;tech-decisions 回填+待拍表更新;规则档沙箱红线+LLM 消毒+views/wanxiang 校正;本清单回填铁律——对照 HJ-AUDIT-001 §7 清单逐项核)→ ②第二批 20 创意 ✅(batch-002+002b 综合 95%)→ ③审计 W2 对外材料清洗 ✅ **2026-06-10 全收口**(★口径拍定+品牌清扫/横幅 + **BP 改造版红线清洗完成**:四红线/独家/绝对化/未实测指标全清、06-10 实证硬货+三线排序叙事入文、grep 自检正文红线词=0;**留 3 处 ★ 待你确认:团队表述×2 + 唯一融资口径**)→ ④审计 W3 Doc A 合规换血(待律所意见)→ ⑤审计 W4 单位经济埋点(**✅ R3 已拍解锁**:按 [`单位经济敏感性模型.md`](./单位经济敏感性模型.md) §7 测量计划扩 B2 回路——LLM token 计费/素材成本/广告产值/创作者应得四类按游戏粒度埋点,待排期)→ ⑥**鉴权 execution 版 ✅ 已出**(`2026-06-10-真实鉴权与匿名玩家-execution.md`:落点=system 内扩展、Flyway=V11(community/biz 顺延 V12/V13)、@PermitAll 清单 14 条带 file:line、邀请码全生命周期、R6 安全前置;**新发现硬边界:game_runtime_session.player_user_id NOT NULL 须 V11 ALTER**)→ **✅ 建设+e2e 全收口 2026-06-10**(契约/三面建设/构建门 648 测绿/三部署窗/两轮 e2e 18 代理:逮修 2 P0+1 枚举缺陷,E1-E13 全过——详 `2026-06-10-真实鉴权与匿名玩家-e2e报告.md`)→ ⑦**M-c 模板波**:review 版+四项拍板 → execution v2(四镜头核验整改 `12e0c8f`)→ **✅ 批① merge 全链收口 2026-06-10**(契约/三面建设/构建门/五级验收门全过:merge 五条 AND 全绿+clicker 回归五绿+Golden v1.1.2 PASS+**校准批 accept 10/10 零 infra**+金丝雀 10 条入 feed;逮修 3 真缺陷〔鉴权守卫×取证导航/verify_package merge 取参/pom 资源漏复制〕;提交链 `b1fa30d`→`8bd3ebf`;收口报告 `2026-06-10-Mc模板波-批①收口报告.md`;**新模板接入配方已蒸馏** `.agents/skills/add-game-template.md`)→ **✅ 20 条正式批过门 2026-06-10**(`merge-prod-20` accept 19/20=**95%**,infra=0,金丝雀 10/10 触顶+feedVisible,**feed→50 款**;观察项=对抗稳定性复测连续两批 2/3〔累计 4/6,Golden 门兜底,v1.1.3 候选〕)→ **✅ 批② idle+tycoon 全收口 2026-06-11**(五级门:构建/在线五AND/Golden/校准批 10+10 全 1.0/金丝雀——级5 tycoon 抽检分歧 R8 冻结观察项;越模板守卫终裁降 Golden 观察项 `26319b3`;**模板 1→4 全可生成可玩可进 feed**)→ ⑧**批② 遗留债·回包链路专项修 ✅ 2026-06-11 收口**(根因=v-for 模板 ref 数组语义致 `HostBridge.targetWindow=undefined`、**host→game 全方向自建成起静默断**〔storage/ad/pay 回包+init〕;探针四锚点+判别实验锁凶,单文件修复+部署 :4173 实证:**idle 离线补发往返闭合**〔存档→刷新→val=obj〕+clicker 回归绿+init 首次送达;**ad/pay 判定=同断同愈(机制级),业务级闭环留 M4 验收**;三条新红线入 engineering-conventions §1.2+常备双边探针 `probe_bridge_channel.py`;报告 `2026-06-11-回包链路专项修-收口报告.md`)→ ⑨**W4 成本①+new-api 计费集成评审 ✅ 2026-06-11**:评审版 `2026-06-11-newapi计费平面集成-review.md`(结论=生成侧用 new-api·收益侧留 trade 硬边界)→ **两轮对抗评审(4 镜头)推翻初稿**(v2 整改:admin 无法替他人铸 token 硬阻塞 / new-api 3000 公网暴露〔创始人裁暂不动〕/ 成本¥0.02-0.04→¥0.009 修正 / 全量计量前置于支付通道 3-5 周阻塞)→ **创始人拍板=本周薄片余推迟**→ **薄片①已落** `newapi_cost.py` 直读 logs.quota 权威成本(merge-prod-20 实测 ¥0.59/批·¥0.031/款·生成裁判分列·平台vs创作者归属),客户端估算降 fallback。**推迟(支付真实化后)**:per-creator 开通+支付→配额同步+订阅。→ ⑩**Round1 双 lane(Ultracode,2026-06-11)✅ 收口**:**Lane A=链路② admin 审核台真 UI 走查闭合**(CDP-on-mini:game-admin 接 staging :4174→登录→`/wanxiang/review`→点 9137「通过」+确认→项目 PUBLISHED+feed_rank 写入,队列 3→2+DB+截图实证;附带修 admin 构建腐化 vite8→5.1.4 清装;配方入记忆 `admin-ui-walk-cdp-recipe`);**Lane B=Wave4(community+biz)评审版 spec 定稿**(`HJ-WAVE4-001`+评审纪要,两轮对抗评审 R1 23+R2 6 全接受,**3 战略决策**:D-A biz=轻量 lead form 先行 / D-B community=通知底座最小集 / D-C 本波不接 ip seam)→ **✅ 2026-06-11 创始人按推荐全拍(D-A/D-B/D-C)→Wave4 execution 版定稿**(`HJ-WAVE4-EXEC-001`,4 路深度 recon→起草→三镜头评审 21 findings 全接受;**评审逮 1 blocker**=aigc 回调 `handleCallbackTx` 返 Boolean.TRUE 判不出真成功→改 selectByTraceId 回查 + status==SUCCEEDED&&versionId!=null 才发;**治理回流**=P-NTF-03 源 compliance→project 订正入评审版 ERRATA;主 agent 抽查 file:line/PII 零@DataPermission/结构 全属实)→ **待创始人点头开 Round 2 建设**(§0 资源预占单 agent → community 先 → biz 后 + 集成/验证门);余队列=**studio 链路③⑤ UI 走查(A2 recon 逮 5 bug)/ W4 ③④后端埋点**。 diff --git a/docs/mvp/MVP进度总账.md b/docs/mvp/MVP进度总账.md index bc87f275..70113497 100644 --- a/docs/mvp/MVP进度总账.md +++ b/docs/mvp/MVP进度总账.md @@ -63,7 +63,7 @@ | # | 链路 | 状态 | 真实进度 | |---|---|---|---| | 1 | 创作 → 生成 → 预览试玩 | 🟢 | **前端 UI 实证全绿(2026-06-11 走查 HJ-FE-WALK-001)**:一句话→4 模板(clicker/merge/idle/tycoon)→开始生成→`studio/generate`→`/task`轮询(0%)→~18s→`/preview` canvas 500×700 可玩(《霓虹深空实验室》能量0/5),鉴权守卫/受控输入/异步轮询/自动转场/沙箱 runtime 逐项真实,0 error | -| 2 | 发布 → 审核 → 游戏流 | 🟢→🟡 | **创作者发布段 UI 已通(2026-06-11 HJ-FE-WALK-001 修+重走查闭环)**:去发布→选专区(创作广场UGC)→提交发布→「✓已提交审核·进审核队列」截图实证;**逮的 2 缺陷已修+重部署验证**(A `project.currentVersionId` 生成回写 GameVersionServiceImpl=9137 实证 currentVersionId=93109;B `feed/zones` 落实 getZones=返 2 专区、launchZoneId=2 实证;jar 含两修+四模板+M1+回包,admin-api 200 无回归)。**余审核→feed(admin 侧锁风门/审核台 approve)**:UI 发布后置「审核中」status=1,审核通过才入 feed=admin 台surface(未接) | +| 2 | 发布 → 审核 → 游戏流 | 🟢 | **全段 UI e2e 闭合**。创作者段(2026-06-11 HJ-FE-WALK-001):去发布→选专区→提交→「✓已提交审核」+ currentVersionId/launchZoneId 落值。**admin 审核段已闭合(2026-06-11 链路②波,CDP-on-mini 真 UI 走查)**:game-admin 接 staging(:4174)→登录(admin/admin123)→`/wanxiang/review` 队列渲染 3 条真实待审游戏→点 9137「通过」+确认→队列 3→2 条(9137 移除)→DB 实证 `game_project 9137 status=4(PUBLISHED)` + `game_feed_rank(zone2,status1,score0)` 写入(同事务)。sort_score=0 因新游戏无试玩排尾(正确,被玩后 quality 升→上爬,B2′ 已证)。**附带修 admin 构建腐化(vite8→钉 5.1.4 清装)**。观察项:row1 旧 smoke 数据标题乱码(灌数编码,非代码) | | 3 | 游戏流 → 试玩 → 互动 → 分享 | 🟢 | **真人试玩闭环 e2e 真实(B2′)**:feed(真标题)→点卡→自研 Canvas clicker 真玩通关→game_play_start/end 真遥测→quality→**feed 重排(浏览器实证翻转)**;like/share 接真遥测;真实编译打包/分享落地页=桩 | | 4 | 广告展示 → 收益 → 钱包可见 | 🟡 | 骨架;**真实广告/pay=桩** | | 5 | 遥测 → 质量分 → 推荐优化(数据回路) | 🟢 | **数据回路 e2e 真实(B2)**:事件→quality_score→feed 重排(B超A)+重放幂等;MQ 异步化/真实试玩前端待补 | @@ -132,6 +132,8 @@ | **M-c 正式批(merge-prod-20,spec §8-3 验收)** | 2026-06-10 续开:20 条正式批(14 具象+3 跨域+3 模糊,与校准批零重叠)→ **accept 19/20=95% ≥80% 量级过门**(结构 20/20=1.0,infra=0 全自动零中断,830s/57 次 LLM);1 kill=fix 后 P1 残留(QA 门诚实在岗);fix 闭环 3 轮 2 成;金丝雀 10/10 触顶发布+feedVisible(**feed 内容池→50 款**),9 个 accept 按 ≤10/批 配额留池。**观察项**:对抗稳定性复测连续两批 2/3(累计 4/6,样本小、Golden 门 PASS 兜底,prompt v1.1.3 候选议题)。**操作事故复盘**:批产物 tar 回本地入库后 mini 端残留原件卡死后续 pull(git stderr 先出、tail -1 误读「Updating」为成功)→ 首发批秒死「创意文件不存在」;铁律=入库后立清 mini 原件 + pull 看全量输出/HEAD 对账 | `1ab2d15`(创意)+`b7c9d33`(产物);批报告 `runs/merge-prod-20/` | 主 agent 读 report.json 终判+金丝雀 feedVisible 机器验证×10+执行器恢复 banner 双模板 | | **M-c 模板波批②(HJ-MC-TPL-EXEC-002 idle+tycoon 全链收口)** | 2026-06-10~11:execution 双 spec 四镜头核验整改+创始人拍板不升 patch → 契约+五主题并行建设(runtime 追加 **initIdle 等待型**〔离线补发+在线点击产料+自动产出+升级,finish at targetResource〕+**initTycoon 经营型**〔进货→带客→售出配对赚差价,finish at targetCoin〕;后端纯加行〔白名单+idle/tycoon、资源 Map 两行、getTemplateList 2→4〕;编排器 `_play_idle_once`/`_play_tycoon_once` 双策略;host storage 三端〔contract+GamePlayer 受信边界四闸+inject 应答端〕;Golden harness 六守卫)→ 构建门(四模板 isReady banner `templates=[clicker,merge,idle,tycoon]`+体积 9577B<12288 软门+63 后端测+八资源内嵌 fat jar)→ **五级验收门**:①构建✅ ②在线五 AND✅(idle 200 通关+tycoon 153 通关真包全绿+clicker/merge 回归五绿+果园/奶茶店视觉验收,按钮布局契约双侧 0.28/0.72 实证)③Golden✅(两轮 VERDICT=PASS,**题文不符 kill×四模板+accept 不误杀全稳**)④校准批✅(idle-cal-10 + tycoon-cal-10 各 **accept 10/10**,infra=0,report.json acceptRate=1.0 亲读)⑤金丝雀🟡(idle 10/10 入 feed;tycoon 抽检模型分歧 50%>10% 被 R8 冻结=观察项)。**越模板机制守卫口径终裁**:spec §4.5 起草标 kill→创始人 6/11 拍板 P2 放行(依细则①)→同输入温 0.2 重采样实证**软边界抖动**(idle k-1d1ebeefca 2/4、merge/tycoon 0/4,fresh 回归轮 tycoon 亦翻 kill)→**降为 Golden 观察项**(expectation=observe 不纳入 verdict;两向 fail-safe;prompt 正文零改动仍 v1.1.2;让模型稳定输出 P2 属 v1.1.3 backlog)。**⚠ 遗留债·idle 离线补发运行时未通**(P1:host→iframe `host_to_game` 回包方向运行时未闭合,疑同样波及 ad/pay 回包闭环;优雅降级为无离线态不影响在线;创始人拍板=记债先收口在线核心;专项 follow-up=深调回包链路+验 ad/pay 同断) | `f82d480`(storage)→`91d651e`(后端)→`4864853`(runtime)→`83233d0`(ideas)→`f70cc13`(Golden harness)→`509acb7`(spec v2)→`26319b3`(越模板终裁+Golden 取证);批报告 `runs/idle-cal-10/`+`runs/tycoon-cal-10/`+Golden `runs/golden-regression-v1.1.2-b2/`+收口入本总账 | 主 agent 独立核验:四模板 isReady banner+体积门+八资源内嵌 grep;idle/tycoon 真包五 AND 亲验;两轮 Golden 亲跑 VERDICT=PASS+越模板同输入重采样 12 次取证(idle 2/4 实锤抖动);cal-10 acceptRate=1.0 亲读;执行器恢复 banner 四模板装载 | +| **链路②闭合 + Wave4 评审 spec(Round1 双 lane · Ultracode)** | 2026-06-11 用户选 Ultracode 双 lane 并发:**Lane A**=链路② admin 审核台真 UI 走查闭合(CDP-on-mini:登录→`/wanxiang/review`→点 9137「通过」+确认→项目 PUBLISHED+写 feed_rank)+修 admin 构建腐化(vite8→钉 5.1.4 清装)+admin 接 staging(:4174);**Lane B**=Wave4(community+biz)评审版 spec(4 路 recon→起草→两轮对抗评审 R1 23+R2 6 全接受/重定向→定稿,12 子代理/~39min) | 未提交(工作树改动 + 新 spec 两档 + 走查 harness 三件套入 orchestrator/) | **链路②**:队列 3→2 条(9137 移除)+DB `status=4`/`feed_rank(zone2)` 实证+前后截图;**Wave4**:`HJ-WAVE4-001` spec+评审纪要,3 战略决策(D-A lead form/D-B 通知底座最小集/D-C 不接 ip seam)待创始人拍板 | + > 各波次详细 spec:`docs/agent-specs/`(review + execution 双 spec)。 ---