Wave4 分享OG修实测:jar 内确含「我在绘境AI玩」字面量但 strings|grep 绘境AI玩 返 0(多字节UTF-8提取不可靠)→curl 运行时拿到真 ogDescription 才证修复已入 jar。补进 engineering-conventions §1.2「重打包必须 clean+产物内嵌核验」红线:核验符号优先取 ASCII,中文符号核验改 curl/javap -c 常量池。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 KiB
工程编码与协作硬规范
本文是造梦AI 全栈工程师 / 前端 / AI 工程师 / QA 必须遵守的硬规则。违反即不通过 review。 蒸馏来源:
docs/architecture/系统概要设计-开发团队版.md(§4 编码 / §5 Git / §6 联调 / §7 测试 / §11 Checklist)、docs/architecture/系统概要设计-技术决策版.md(§7.6-7.8 工程治理)、docs/superpowers/specs/mvp-execution-spec-design.md(§8 技术约束)。 配套:可靠性/安全红线见security-and-reliability.md;元流程见../workflows/ai-development-protocol.md;事实蓝图见../knowledge/product-and-architecture.md;模块落地手册见../skills/add-business-module.md。
1. 后端(Java)编码规范
| 规则 | 硬要求 |
|---|---|
| 包命名 | cn.wanxiang.game.module.{模块}.{层},如 cn.wanxiang.game.module.aigc.service。模块名小写,与 game-module-{模块} 一致 |
| 类命名 | 后缀严格统一:XxxController / XxxService+XxxServiceImpl / XxxMapper / XxxDO / XxxVO / XxxDTO |
| 异常处理 | 业务异常一律 throw exception(错误码枚举)(Yudao ServiceException),禁止裸抛 RuntimeException、禁止吞异常返回 null |
| 日志 | 类上 @Slf4j;关键链路成功 INFO、失败 ERROR,错误日志必须含 trace_id 与关键业务 ID(如 taskId/versionId) |
| 注释 | 类注释 +复杂方法注释 +字段注释,一律简体中文;对外接口、错误路径、补偿逻辑必须有注释 |
| 事务 | @Transactional 只加在 Service 层,范围尽量小;事务内禁止远程调用(Feign/HTTP/MQ 发送放事务提交后) |
1.1 DO / VO / DTO 分层语义(不可混用)
| 类型 | 定位 | 所在包 | 约束 |
|---|---|---|---|
| DO | 数据库映射对象,字段与表一一对应 | -server 的 dal/dataobject |
不出现在 Controller 出入参;不做业务组合 |
| VO | 视图对象,面向前端,可裁剪/组合 | -server 的 controller/.../vo |
仅 Controller 层使用;分 ReqVO/RespVO |
| DTO | 模块间传输对象(Feign 契约) | -api 包 |
跨模块共享,消费方引用 -api,不得引用对方 -server |
1.2 SQL 规范
- 禁止
select *,必须显式列出字段。 - 大表查询必须命中索引;
WHERE/ORDER BY/JOIN字段需有对应索引,新查询要确认执行计划。 - 分页用游标或
LIMIT;禁止深分页LIMIT 100000,20这类全表扫描。 - 复杂查询走 MyBatis XML,简单 CRUD 用 MyBatis-Plus。
- JSON 列写入:映射 MySQL
json列的 DO 字段必须存合法 JSON 文本——用JsonUtils.toJsonString(obj)序列化,严禁Map/对象.toString()(产出{k=v}非合法 JSON,MySQL 抛Invalid JSON text致整批事务回滚)。⚠️ 单测 mock mapper 测不到此约束,须靠集成测试(Testcontainers)或 staging 实测兜底(B2 实测教训)。 - CJK 写入连接字符集:用 mysql 客户端 /
docker exec mysql灌含中文的 SQL(种子/修数据)必须带--default-character-set=utf8mb4,否则 UTF-8 字节被按 latin1 解析→双重编码存入 utf8mb4 列→读出/接口返回乱码(HEX 表现为C3A5..而非E5B08F=「小」)。前端/接口看似乱码 bug,实为灌数环节编码错(B2′ 实测,浏览器渲染才暴露)。 - feed/stream 消费红线:参数名是
size(单页上限 30,超出 400「单页条数不能超过 30」;传错参数名如count不报错只回默认 10 条,极易误判卡数);nextCursor当前为骨架占位(恒空串,FeedServiceImpl TODO),翻页续接未实现——程序化全量核对用size=30单页或自实现探测翻页(batch-001 postcheck 误熔断实测教训)。 - runtime manifest 取包场景参数:
/app-api/runtime/package/{vid}与.../manifest两端点scene缺省play(仅放行 status=1 已发布包)——预览未发布包必须显式?scene=preview,裸 GET 拿到的是错误 envelope(HTTP 200+code 1102001002),宿主/脚本对其算 sha256 必败误判 demo 兜底(部署窗口冒烟⑥实测;后端 manifestUrl 已按请求 scene 拼参 9517f4e)。 - 可开关组件禁组件扫描:yudao 单体里 MQ 消费者自动配置自带全局
@EnableScheduling(无条件装配)——任何「配置开关控制启停」的@Scheduled组件必须只经@Bean方法注册(配置类上挂@ConditionalOnProperty),类上禁@Component/@Service,否则组件扫描使开关失效、回滚开关形同虚设(M-b spec 核验实证)。 - ON DUPLICATE KEY UPDATE 与租户拦截器:原生
@Insert ... ON DUPLICATE KEY UPDATE 列=列+#{增量}经 MyBatis-Plus 租户拦截器(jsqlparser)运行期改写实证兼容(staging 断言通过 b87511b);注意 wrapper 式 CAS update 不触发 MP 字段自动填充,时间戳依赖列上ON UPDATE CURRENT_TIMESTAMP。 - 非 Web 线程写库必须注入系统身份:
@Scheduled/job/MQ 消费线程无登录态 →DefaultDBFieldHandler.updateFill不填 updater,而 MPfill=INSERT_UPDATE字段无条件进 UPDATE SET(null 直出)→ 表updater NOT NULL拒绝 → 主链与补偿写共用 updateById 同死、任务卡死无终态(M-b 冒烟实证,Mockito 测不到)。修法=线程边界注入系统 LoginUser(id=0)+finally clearContext(范本AigcGenerateExecutor.executeWithSystemIdentity);同款雷潜伏 SettlementJob 等一切非 Web 写路径,启用前必须带上。 - staging 前端构建必须
--mode staging:mini-desktop 的 vite preview 直接 serve 工作树 dist——任何人跑默认npm run build即事实上改部署(缺 staging env → mock 中间件接管 → 宿主静默 demo 兜底且 2b 分支无 warn,M-b② 验证构建实翻车一次)。在 mini-desktop 构建 game-studio 一律npm run build -- --mode staging。 - 匿名(@PermitAll)写路径=非 Web 线程同款审计列雷:匿名 HTTP 请求无登录态,与调度线程同根——上一条的全部结论适用(鉴权波 e2e 实证:匿名会话/遥测/注册/ad 计费四链全断 500)。修法同款=入口注入系统身份 LoginUser(id=0)+finally clearContext(Web 线程版范本
EventIngestServiceImpl.injectSystemIdentityIfAnonymous),单写点可 DO 显式setCreator/setUpdater("0")兜底;「MP 会跳过 null 字段」的静态推断在 yudao fill 体系下不成立(markAggregated UPDATE SET updater=null 运行时证伪),凡 DB 写过的链路必须部署后实测。新增 ALTER 放宽业务列时必须同查继承审计列(V11 漏 creator/updater 即此雷)。 - 契约事件登记须同步后端枚举守门人:
contracts/events.schema.jsoneventRegistry 只是文档源,TelemetryEventEnum才是上报通道真守门人——只改契约不改枚举=事件被静默 rejected(accepted:0 信封仍 code:0,极难发现;鉴权波 user_login e2e 实证)。新增事件=契约+枚举两处同步,缺一即断。 - 重打包必须 clean+产物内嵌核验:
mvn -pl yudao-server package增量模式会秒级"SUCCESS"但不重建 fat jar(旧 BOOT-INF/lib 原样)——部署级构建一律clean install联合反应堆(改动模块+yudao-server 同 -pl),部署前unzip -p fat.jar BOOT-INF/lib/<模块>*.jar | strings | grep 新符号核验产物(鉴权波部署 #3 实测假成功一次;注意 telemetry 等模块产物为定制 finalName 无版本号)。核验符号优先取 ASCII(类名/方法名/英文常量)——strings\|grep 中文字面量因多字节 UTF-8 提取不可靠会假阴性,中文符号改用 curl 运行时实证或javap -c -p常量池(Wave4 分享OG修实测:jar 内确含「我在绘境AI玩」字面量但strings\|grep 绘境AI玩返 0,curl 拿到真 ogDescription 才证修复已入 jar)。 - 重启脚本与含 jar 名的命令必须拆 ssh 会话:
start-app.sh内pkill -f "yudao-server.jar"会击杀 cmdline 含同串的承载 shell 本身(远程 bash -c 的命令文本计入匹配)——部署拷贝(含 jar 路径)与重启调用分两次 ssh;进程匹配宁用窄模式pkill -f 'java .*yudao-server.jar'(鉴权波两例实证:e2e E9 代理+主 agent 部署 #2 各中一次)。 - mini-desktop 跑 yudao-module-system 测试必须改嵌入式 Redis 端口:宿主 16379 已被 staging redis 容器占用且带密码,而 yudao 测试基座
RedisTestConfiguration起嵌入式 RedisServer 失败被catch(ignore)吞掉 → 测试静默连上真 Redis 报 NOAUTH。跑 system-server(或任何 BaseDbAndRedisUnitTest)一律SPRING_DATA_REDIS_PORT=26379 mvn test ...(环境变量可穿透 surefire fork;鉴权波构建门实证 8 测假红→改口全绿)。 - v-for 内模板 ref 是数组:Vue 3 对
v-for内部的模板 ref 填充【数组】而非元素——即使只渲染 1 个;按元素直读.contentWindow类属性得undefined且 TS 类型声明不报(声明可以撒谎)。取用必须显式Array.isArray解包(范本GamePlayer.currentIframe())。此雷曾致HostBridge.targetWindow=undefined、host→game 全方向静默断(storage/ad/pay 回包+init),穿过两个波次五级门未现形(2026-06-11 回包链路专项修实证)。 - 跨边界出口的静默降级必须配挂接时刻告警:fire-and-forget 出口(如
bridge.post的if (!win) return)静默丢弃是稳定性正确姿势,但出口哑火必须在挂接/初始化时刻打一次console.warn,否则断链不可观测(同上实证:回包断链唯一症状=5s 超时兜底,无任何日志)。 - 「从未有人消费过的方向」不算被验证过:双向信道只押单向门禁=另一方向可能自建成起即断(host→game 即此例——iframe 自启动兜底掩盖 init 丢失)。新增首个反向消费者前,先跑双边探针
orchestrator/probe_bridge_channel.py(四锚点:CALL/RECV × TOP/SUB,常备诊断件)。
1.3 错误码分配(每模块独占一段,禁止冲突)
错误码格式 1-{模块段}-{业务}-{细分},各模块独占一段:
| 段 | 模块 | 段 | 模块 |
|---|---|---|---|
1-001-***-*** |
system | 1-104-***-***(推断顺延) |
telemetry |
1-002-***-*** |
infra | 1-105-***-***(推断顺延) |
pay |
1-100-***-*** |
project | 1-106-***-***(推断顺延) |
trade |
1-101-***-*** |
aigc | 1-107-***-***(推断顺延) |
community |
1-102-***-*** |
runtime | 1-108-***-***(推断顺延) |
ip |
1-103-***-*** |
feed | 1-109-***-***(推断顺延) |
compliance |
1-002-***-*** 之后 |
bpm 等 Yudao 原生沿用 Yudao 既有段 | 1-110 / 1-111-***-***(推断顺延) |
biz / ad |
源档明确给出 system/infra/project/aigc/runtime/feed 六段;其余模块按"每模块一段顺延"规则分配(推断),新增模块时在
-api的错误码常量类登记,避免与既有段重叠。
2. 前端(TypeScript / Vue)编码规范
| 规则 | 硬要求 |
|---|---|
| 组件命名 | PascalCase 单文件组件,如 GameContainer.vue |
| API 文件 | 按模块一个文件(api/game/project.ts);函数名 = {HTTP方法}{资源}{动作},如 getProjectList / postProjectPublish |
| 状态管理 | Pinia,按业务域拆 store(一个域一个 store,禁止 god store) |
| 样式 | game-admin 用 Element Plus 变量;game-studio 用 Vant + CSS Variables(移动优先,适配 360-430px) |
| 类型 | 严格 TypeScript,禁止 any(必须用时注释说明充分理由) |
| 请求 | 统一使用封装的 request 实例,自动处理 Token 注入 / Refresh 刷新 / 错误提示;禁止裸 fetch/axios |
admin 二开:新页面放
views/wanxiang/、API 放api/wanxiang/(仓内实际目录,2026-06-10 审计校正;旧档所写views/game/已废),复用 Yudao CRUD 组件(XTable/XForm/Dialog),不改 Yudao 原生 views。
3. SDK(WanxiangGameSDK,TypeScript)编码规范
| 规则 | 硬要求 |
|---|---|
| 零依赖 | SDK 不引入任何第三方库 |
| 体积预算 | Core(Lifecycle+EventBus+Telemetry+ErrorTrack)压缩后 < 8KB;每个 Plugin < 5KB(Ad<5 / Pay<3 / Social<4 / Storage<2 / Debug<15,仅 dev);首屏只加载 Core |
| 异步安全 | 所有对外 API 不返回 Promise(fire-and-forget),零同步等待,不占游戏主线程 |
| 错误隔离 | 每个 Plugin 内部 try-catch,异常绝不向游戏抛;游戏主循环(requestAnimationFrame)永不被 SDK 阻塞 |
| 版本兼容 | 新版本只增字段/方法,不删不改已有签名;semver 管理,manifest 记录版本号 |
SDK 降级铁律与合规定位(同意即可用/未成年保护)属可靠性红线,详见
security-and-reliability.md。
4. API 路径与端鉴权规范
/app-api/** → 产品端接口(game-studio 调用),需要用户 Token
/admin-api/** → 管理后台接口(game-admin 调用),需要管理员权限
端前缀 /app-api·/admin-api 沿用 yudao 默认,由框架(YudaoWebAutoConfiguration + WebProperties)按 **.controller.app.**·**.controller.admin.** 包名自动添加;Controller 内 @RequestMapping 只写 /{模块},不含端前缀。
URL 格式:/{端前缀}/{模块}/{资源}/{动作}。示例(左列为含前缀的最终对外路径):
| 方法 + 路径 | 含义 |
|---|---|
POST /app-api/aigc/generate |
创作者发起生成 |
GET /app-api/feed/list |
游戏流列表 |
POST /app-api/feed/interaction |
点赞/收藏 |
GET /app-api/project/my |
我的项目 |
POST /admin-api/project/review |
审核操作 |
GET /admin-api/telemetry/dashboard |
运营看板 |
权限在网关 +注解双重校验;
/app-api/**走用户 Token + DataPermission(创作者只见自己数据),/admin-api/**走 RBAC。前端不可作为唯一边界,详见security-and-reliability.md。
5. API 契约与版本管理
| 维度 | 规则 |
|---|---|
| URL 版本 | URL Path 版本 /api/v1/...,大版本不兼容才升 v2 |
| 兼容判定 | 新增字段不算 breaking;删除/重命名字段 = 新版本 |
| 并行期 | 新版本上线后,旧版本保留 ≥ 3 个月;废弃用 Header Deprecation: true + Sunset: <date> |
| 服务间契约 | 每个模块 -api 包声明 Feign 接口 + DTO,消费方引用该包;API 变更必须在 PR 描述标注受影响的消费方 |
| 契约对齐 | 后端先写 -api 的 VO/DTO,前端据此定义 TS 类型;联调前锁定契约(见 ../skills/contract-first-development.md) |
5.1 事件 Schema 版本(telemetry / MQ 事件)
| 规则 | 说明 |
|---|---|
| 带版本号 | 每个事件类型带 schema_version(如 game_play_start.v2) |
| 向后兼容 | 新增字段给默认值;老消费者忽略未知字段 |
| 不兼容变更 | 用新事件名(如 game_play_start_v3),新老并行消费直到老版本下线 |
6. Git 协作规范
6.1 分支策略
main ← 始终可部署,保护分支(禁直推)
└── develop ← 集成分支,CI 通过才能合入
├── feature/{module}-{brief} ← 功能开发(MVP 期可用 feature/{ws}-{module}-{brief})
├── fix/{module}-{brief} ← Bug 修复
└── release/x.y.z ← 发版(冻结后只修 bug)
6.2 Commit 规范(Conventional Commits)
<type>(<scope>): <subject>
type: feat / fix / refactor / docs / test / chore / perf
scope: 模块名(aigc / project / feed / studio / admin / sdk ...)
subject: 动词开头,简明描述(中英文均可)
示例:feat(aigc): integrate Dify workflow API for game generation、fix(feed): cursor pagination returns duplicate games。
6.3 PR 规范
- 单次 < 500 行变更,超过必须拆分。
- 至少 1 人 review + CI 通过(main 强制)才能合入。
- PR 描述模板要点:
| 区块 | 必填内容 |
|---|---|
| 变更内容 | 新功能 / Bug 修复 / 重构 |
| 影响范围 | 影响的模块 / 影响的 API(如变更)/ 数据库迁移(如有) |
| 测试 | 单元测试通过 / 集成测试通过(涉及 DB/MQ)/ 本地手工验证 |
| 截图 | UI 变更附截图或录屏 |
7. 测试规范(测试金字塔)
| 层级 | 覆盖范围 | 工具 | 运行时机 | 目标覆盖率 |
|---|---|---|---|---|
| 单元 | Service / Util / Validator | JUnit 5 + Mockito | 每次 push | 核心逻辑 > 80% |
| 集成 | Controller + DB + MQ + 外部 API | Testcontainers(MySQL/Redis/RocketMQ) | PR merge | 核心链路 100% |
| E2E | 前端→后端→DB 全链路 | Playwright(前端)+ RestAssured(API) | 部署 staging 后 | 核心 happy path |
| 性能 | API 吞吐/延迟/并发 | k6 / wrk | Phase 3 末 + 上线前 | 满足性能指标 |
| 安全 | OWASP Top 10 / 渗透 | ZAP + 人工渗透 | 上线前 + 季度 | 无 Critical/High |
硬约束:
- 测试文件命名:单元测试
XxxServiceTest.java,集成测试XxxServiceIntegrationTest.java。 - 测试基类:继承
game-spring-boot-starter-test的BaseDbUnitTest/BaseDbAndRedisUnitTest。 - 单测应 Mock 外部依赖;需要真 DB 的场景一律用集成测试(Testcontainers),不要让单测连真库。
8. 数据库迁移规范(Flyway)
| 规则 | 硬要求 |
|---|---|
| 命名 | V{版本号}__{描述}.sql,如 V1.0.0__create_game_project.sql |
| 只增不回滚 | 已合入的迁移文件禁止修改;需回滚则写新的补偿迁移 |
| DDL/DML 分开 | 结构变更与数据变更拆成不同迁移文件 |
| CI 阻断 | CI 阶段执行 flyway validate,不通过则阻断合入 |
| 大表变更 | 用 gh-ost / pt-online-schema-change 不锁表 |
9. 模块开发 Checklist
新增一个业务模块时按下列清单逐项完成(完整 playbook 见 ../skills/add-business-module.md):
- 创建
game-module-{name}-api+game-module-{name}-server两个 Maven 模块(结构对齐 yudao-cloud fork) -api中定义 VO / DTO / 枚举 / Feign 接口 / 错误码段-server中创建controller/admin/+controller/app/+service/+dal/+convert/- 编写 Flyway 迁移脚本
V{x.y.z}__{desc}.sql yudao-server(聚合器,B 路线保留原名)的pom.xml引入新模块依赖;并在YudaoServerApplication扫描 +application.yaml类型别名纳入cn.wanxiang.game.module;Nacos 添加模块配置(如有)- 编写单元测试(Service 层)+ 集成测试(Controller 层含 DB)
- Swagger(Knife4j)验证 API 文档自动生成
- 更新模块速查表,并通知前端接口就绪(附 Swagger 地址 + 示例 curl)