zizi 38729ffdaa fix(contracts): studio.yaml 契约评审跟进——收紧 modifyPatch + idempotency 语义(单轮 codex 评审)
codex xhigh 契约评审 FREEZE-WITH-FIXES(零 P0 破坏)。逐条收口:
- P1 payload 必填 + mode×target.kind 锁定映射(后端可信边界校验,不用 oneOf 保 mock/codegen 简单)
- P1 target path/id anyOf 强制至少其一
- P1 payload value/intent 按 mode 必填(后端校验)
- P2 AssetContextItem 补 provider + 后端派生 assets[].id 说明
- P2 idempotency 语义(同 userId+op+key 命中返既有 taskChain/缺省不去重/TTL 24h)
- P2 execution §5.6 JSON 指针示例纠错 /gameDefinition/config/speed→/config/speed(config 顶层,对齐 keystone)

复验:YAML 绿、14 $ref 零悬空、六类对齐 keystone、现有端点/VO 零破坏。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 16:00:52 +00:00

330 lines
21 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.0.3
# 契约 #1 API | 模块studiogame-module-studioWave3 创作主链路最小版)| ownerstudio 路
# 职责(架构 Doc B §71-76有状态创作工作台后端。编排「建草稿(委托 project createProject)→一句话生成(委托 aigc)
# →进度轮询→重生成→模板应用」。不跑 LLM委托 aigc、不持久化项目版本交 project、不做锁风裁决调 compliance
# 与 game-studio 前端仓区分:本契约是后端 studio 模块(错误码段 112game-studio 是产品端前端仓名,二者不同。
# 端:/app-api产品端 game-studio用户 Token前缀由 huijing 框架按 controller.app 包名自动添加。
# 错误码段studio = 1-112-***-***R4 治理新登记,本模块独占,禁与他模块重叠:
# 100 project/101 aigc/102 runtime/103 feed/104 telemetry/105 pay/106 trade/107 community/108 ip/109 compliance/110 biz/111 ad/112 studio
# 响应统一 Huijing CommonResult 信封:{ code, data, msg }code=0 成功。
# 编排链路MVP 轮询非 MQD4
# /template/list (透传 aigc 模板注册表) → /draft (调 project ProjectApi.createProject 拿 gameId upsert 会话)
# → /generate (调 aigc AigcApi.submitGenerate 落任务链回填 aigc_task_id) → /task/{id} (透传 aigc 进度轮询)
# → /task/{id}/regenerate (调 aigc AigcApi.retryTask 新建 retry_of 任务链)
# x-feign-contractsstudio 是调用方,依赖以下对方 -api seam本契约不对外暴露 Feign
# - project-api ProjectApi.createProject(creatorUserId, title, templateId) → gameId写类 RPC显式 creatorUserId归属校验在 project service
# - aigc-api AigcApi.submitGenerate / getTask / retryTask / getTemplateListWave3 新增 seam纯暴露层委托 AigcTaskService
# attachmentsR7StudioGenerateReqVO.attachments 本波仅持久化studio 侧落库),不透传 aigcaigc reqDTO 无 attachments 字段P1 再透传。
# 狠切留 seam 不实现P1rig/对白树/可视化批量资产图仅槽位表game_studio_asset+ 单步调度,不六路并发。
# ---------------------------------------------------------------------------
# 生命周期编排扩展(契约 C3 | 2026-06-17 固定架构+SAA studio execution §5.6/§12-⑥):
# 范式:游戏 = 长生命周期源项目LLM as 工作室)。新增 create/modify/extend 三生命周期操作additive不破现 draft/generate/regenerate 两步路)。
# - POST /create = draft+generate 一步式封装templateId 缺省 generic一句话直生免选模板对齐 generic 桥接 7534bdf9
# - POST /modify = 改源不改打包产物:在 baseVersion 源项目上改一处(换美术/调参/改关卡=确定性免 LLM改玩法=LLM 重生成单 behavior→ 重构建出新预览版;
# 不动 currentVersion、不自动入 feed仍走发布审核B1 链);失败=不建新版、base 不动。
# - POST /extend = additive 内容扩展(加关卡/机制/内容,不重写既有部件)→ 新预览版。
# 三者均委托 aigcjob 模型 mode/baseVersionId/modifyPatch/idempotency→ SAA 固定架构图产源项目 → 唯一确定性构建 → handleCallback 唯一写入路径落包;
# 产物均经 taskChain 轮询(复用 /task/{id}),完成后 versionId=新(预览)版。
# 关联冻结契约contracts/agent-loop/source-project.schema.json源项目工件、modifyPatchexecution §5.6)、六类 assetContext取代旧 attachmentsadditive
# x-feign-contracts新增 seamstudio 为调用方,本契约不对外暴露 Feign
# - aigc-api AigcApi.submitGenerate 扩 mode/baseVersionId/modifyPatch/idempotencyKey生命周期编排参数additive后端线落地
# - project-api ProjectApi 取 baseVersion 源项目引用 + 新预览版落库version 产物权威仍在 project源项目独立 game_source_project 表存 V18
# idempotency 语义create/modify/extend 共用):同 userId+operation+idempotencyKey 命中则返回既有 taskChain不重复触发生成缺省=不去重;建议 TTL 24h。
info:
title: 造梦AI studio 模块 API
version: 1.0.0
description: >-
创作主链路最小版("做得出"工作台)。创作者进工作坊建草稿(移交 project 建游戏拿 gameId→ 一句话生成(委托 aigc
轮询进度直至终态(透传 aigc→ 失败可重生成(委托 aigc retryTask。MVP 轮询非 MQ前端据此 vite-plugin-mock 自动生成 mock。
servers:
- url: http://localhost:48080
description: 本地Swagger/Knife4j http://localhost:48080/doc.html
paths:
# ===========================================================================
# 产品端 /app-api创作工作台用户 Token
# ===========================================================================
/app-api/studio/template/list:
get:
tags: [app-studio]
summary: 玩法模板列表(模板浏览/应用 P-TPL-01/03透传 aigc 模板注册表)
description: 透传 aigc 模板注册表T-AGC-03MVP 骨架返回空列表占位,待模板注册表接入。
responses:
'200':
description: 成功(玩法模板列表)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTemplateList' }
/app-api/studio/draft:
post:
tags: [app-studio]
summary: 建草稿(进工作坊 P-CRT-10 + 草稿移交 project P-TPL-03
description: >-
编排:服务端调 project ProjectApi.createProject(creatorUserId=当前登录用户, title, templateId) 建游戏拿 gameId
回写会话,再落会话(status=0 草稿)。attachments 仅持久化R7。createProject 是硬前置,返回空 gameId 视为移交失败。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/StudioDraftCreateReqVO' }
responses:
'200':
description: 成功(新建会话,含回写的 gameId
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioSession' }
/app-api/studio/generate:
post:
tags: [app-studio]
summary: 一句话生成P-CRT-01委托 aigc 提交生成)
description: >-
编排:校验 session 归属 → 落任务链(status=1 生成中) → 委托 aigc AigcApi.submitGenerate(带 prompt/templateId/gameId/userId)
→ 回填 aigc_task_id → 立即返回任务链句柄,前端据 taskChainId 轮询。同步直写无 MQD4
attachments 仅持久化R7不入 aigc reqDTO。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/StudioGenerateReqVO' }
responses:
'200':
description: 成功(任务链句柄,含 aigc_task_id
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
/app-api/studio/task/{id}:
get:
tags: [app-studio]
summary: 进度轮询P-CRT-12透传 aigc 进度/状态)
description: >-
编排:校验任务链归属 → 委托 aigc AigcApi.getTask 取进度/状态 → 映射链态
aigc succeeded → 链完成回填 version_id + 会话完成failed/timed_out → 链失败 + 会话失败;其余保持生成中仅回写 progress
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: '创作任务链 IDtaskChainId' }
responses:
'200':
description: 成功(任务链最新态,完成后 versionId 非空)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
/app-api/studio/task/{id}/regenerate:
post:
tags: [app-studio]
summary: 重生成迭代P-CRT-09委托 aigc retryTask
description: 编排:校验原任务链归属 → 委托 aigc AigcApi.retryTask 重提生成 → 新建一条任务链(retry_of=原 id)回填新 aigc_task_id。
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: '原创作任务链 ID' }
responses:
'200':
description: 成功(新建的重试任务链句柄)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
# ---------------------------------------------------------------------------
# 生命周期操作(契约 C3execution §5.6/§12-⑥additive不破现两步路
# ---------------------------------------------------------------------------
/app-api/studio/create:
post:
tags: [app-studio]
summary: 一步式创作C3draft+generate 封装P-CRT-01/10
description: >-
编排:建草稿(委托 project createProjecttemplateId 缺省 generic→ 立即委托 aigc submitGenerate → 落任务链(status=1)
→ 返回任务链句柄供前端轮询(复用 /task/{id})。一步式封装,不破现 draft+generate 两步路additive
六类 assetContext 透传生成上下文(取代旧 attachments
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/StudioCreateReqVO' }
responses:
'200':
description: 成功(任务链句柄,含 aigc_task_id
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
/app-api/studio/modify:
post:
tags: [app-studio]
summary: 修改C3改源不改产物P-CRT-09 进阶)
description: >-
编排:取 baseVersion 源项目 → 应用 modifyPatchmode=deterministic 确定性编辑免 LLM / regenerate-module LLM 只重生成单 behavior
→ 唯一确定性构建出新预览版 → 落任务链供轮询。产物=新预览版(不动 currentVersion、不自动入 feed仍走发布审核
失败=不建新版、base 不动。modifyPatch 字段见 execution §5.6。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/StudioModifyReqVO' }
responses:
'200':
description: 成功(新预览版任务链句柄,完成后 versionId=新预览版、baseVersionId=被改版本)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
/app-api/studio/extend:
post:
tags: [app-studio]
summary: 扩展C3additive 内容扩展)
description: >-
编排:在 baseVersion 源项目上做 additive 内容扩展(加关卡/机制/内容)→ 重构建出新预览版 → 落任务链供轮询。
语义=additive不重写既有部件产物处置同 modify新预览版、不动 currentVersion、走发布审核
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/StudioExtendReqVO' }
responses:
'200':
description: 成功(新预览版任务链句柄)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultStudioTaskChain' }
components:
schemas:
# ========== 请求 VO ==========
StudioDraftCreateReqVO:
type: object
required: [title, templateId]
properties:
title: { type: string, maxLength: 128, description: '作品标题(透传 project createProject', example: '我的放置小镇' }
templateId: { type: string, description: '玩法模板 ID透传 project/aigc', example: 'idle' }
prompt: { type: string, maxLength: 1000, description: '一句话 Prompt建草稿可空generate 必填)', example: '做一个躲避陨石的小游戏' }
attachments: { type: string, description: '附件上下文 JSONP-CRT-04本波仅持久化P1 透传 aigc' }
StudioGenerateReqVO:
type: object
required: [sessionId, prompt]
properties:
sessionId: { type: integer, format: int64, description: '创作会话 ID建草稿返回的 sessionId', example: 1024 }
prompt: { type: string, maxLength: 1000, description: '一句话 Prompt透传 aigc submitGenerate', example: '做一个躲避陨石的小游戏' }
attachments: { type: string, description: '附件上下文 JSONP-CRT-04【本波仅持久化P1 透传 aigc】——studio 侧落库持久化,本波不透传 aigc' }
# ---- 生命周期操作请求 VO契约 C3execution §5.6----
StudioCreateReqVO:
type: object
required: [title, prompt]
description: 一步式创作draft+generate 封装templateId 缺省 genericassetContext 取代旧 attachments
properties:
title: { type: string, maxLength: 128, description: '作品标题(透传 project createProject', example: '我的放置小镇' }
prompt: { type: string, maxLength: 1000, description: '一句话 Prompt一步式必填透传 aigc', example: '做一个躲避陨石的小游戏' }
templateId: { type: string, description: '玩法模板/品类引导 ID缺省 generic一句话直生免选模板对齐 7534bdf9', example: 'generic' }
assetContext: { type: array, items: { $ref: '#/components/schemas/StudioAssetContextItem' }, description: '六类素材引用(取代旧 attachmentsadditive空=纯一句话生成' }
idempotencyKey: { type: string, maxLength: 64, description: '幂等键C3防重复提交触发重复生成可空' }
StudioModifyReqVO:
type: object
required: [baseVersionId, mode, target, payload]
description: >-
改源不改产物execution §5.6 modifyPatch产新预览版不动 currentVersion走发布审核。
【mode×target.kind 锁定映射——后端可信边界校验,违则 1-112-*** 拒schema 不用 oneOf 编码以保 mock/codegen 简单)】:
mode=deterministic ⇔ target.kind∈{asset,config,level} ∧ payload.value 必填(确定性覆写免 LLM
mode=regenerate-module ⇔ target.kind=behavior ∧ payload.intent 必填LLM 只重生成那一个 behavior
properties:
baseVersionId: { type: integer, format: int64, description: '被改的 base 产物版本 IDproject.game_version.id', example: 4096 }
mode: { type: string, enum: [deterministic, regenerate-module], description: 'deterministic=确定性编辑源文件免 LLMregenerate-module=LLM 只重生成单 behavior 模块', example: 'deterministic' }
target: { $ref: '#/components/schemas/StudioModifyTarget' }
payload: { $ref: '#/components/schemas/StudioModifyPayload' }
idempotencyKey: { type: string, maxLength: 64, description: '幂等键C3可空' }
StudioModifyTarget:
type: object
required: [kind]
anyOf:
- { required: [path] }
- { required: [id] }
description: 寻址源项目部件path 与 id 至少其一JSON 指针或部件 id
properties:
kind: { type: string, enum: [asset, config, level, behavior], description: '换美术=asset / 调参=config / 改关卡=level(scene) / 改玩法=behavior' }
path: { type: string, description: '源项目内 JSON 指针(如 /config/speed、/assets/0、/gameDefinition/scenes/2', example: '/config/speed' }
id: { type: string, description: '或按部件 id 寻址behaviors[].id / assets[].id' }
StudioModifyPayload:
type: object
description: mode=deterministic 用 value直接覆写免 LLM必填mode=regenerate-module 用 intent喂 LLM 重生成意图,必填);后端按 mode 校验(见 StudioModifyReqVO 锁定映射)
properties:
value: { type: object, additionalProperties: true, description: 'deterministic 新值(确定性覆写)' }
intent: { type: string, maxLength: 1000, description: 'regenerate-module 改玩法逻辑的自然语言意图(只重生成那一个 behavior', example: '把陨石下落改成会左右摇摆' }
StudioExtendReqVO:
type: object
required: [baseVersionId, intent]
description: additive 内容扩展(加关卡/机制/内容),不重写既有部件
properties:
baseVersionId: { type: integer, format: int64, description: '在哪个产物版本上扩展', example: 4096 }
intent: { type: string, maxLength: 1000, description: '扩展意图(加关卡/机制/内容)', example: '再加一个 boss 关卡' }
assetContext: { type: array, items: { $ref: '#/components/schemas/StudioAssetContextItem' }, description: '扩展用的新增六类素材引用(可空)' }
idempotencyKey: { type: string, maxLength: 64, description: '幂等键C3可空' }
StudioAssetContextItem:
type: object
required: [category, ref]
description: 六类素材引用(输入态,对齐 source-project.schema.json assetSpec.category取代旧 attachments。后端 asset 节点据此派生 sourceProject.assets[].id项目内 id、provider 默认 mmx-cli§5.5)。
properties:
category: { type: string, enum: [sprite, character, effect, scene, ui, music], description: '六类:图元/角色/特效/场景/界面/音乐' }
ref: { type: string, description: '平台 assetId/ref', example: 'asset_88231' }
url: { type: string, format: uri, description: '只读镜像 URL可空' }
provider: { type: string, description: '素材来源 provider可空默认 mmx-cli切 provider 不动消费侧§5.5', example: 'mmx-cli' }
# ========== 响应 VO ==========
StudioTemplateRespVO:
type: object
description: 玩法模板(透传 aigc 模板注册表,对齐 aigc.yaml TemplateRespVO
properties:
templateId: { type: string, description: '模板 ID', example: 'dodge' }
name: { type: string, description: '模板名称', example: '躲避' }
description: { type: string, description: '模板说明' }
coverUrl: { type: string, description: '模板封面/示意图 URL' }
examplePrompt: { type: string, description: '示例 PromptP-CRT-11', example: '做一个躲避陨石的小游戏' }
StudioSessionRespVO:
type: object
properties:
id: { type: integer, format: int64, description: '创作会话 IDsessionId', example: 1024 }
gameId: { type: integer, format: int64, description: '移交后的游戏 IDproject.game_project.id', example: 2048 }
templateId: { type: string, description: '玩法模板 ID', example: 'idle' }
title: { type: string, description: '作品标题', example: '我的放置小镇' }
prompt: { type: string, description: '一句话 Prompt' }
status: { type: integer, description: '会话状态0草稿 1生成中 2完成 3失败', example: 0 }
StudioTaskChainRespVO:
type: object
properties:
id: { type: integer, format: int64, description: '创作任务链 IDtaskChainId轮询用', example: 1024 }
sessionId: { type: integer, format: int64, description: '所属创作会话 ID', example: 512 }
aigcTaskId: { type: integer, format: int64, description: '委托的 aigc 生成任务 ID', example: 8888 }
gameId: { type: integer, format: int64, description: '关联游戏 IDproject.game_project.id', example: 2048 }
versionId: { type: integer, format: int64, description: '产物版本 IDproject.game_version.id完成后非空', example: 4096 }
status: { type: integer, description: '任务链状态0初始 1生成中 2完成 3失败', example: 1 }
progress: { type: integer, description: '进度百分比 0-100透传 aigc', example: 60 }
retryOf: { type: integer, format: int64, description: '若为重生成,指向原任务链 IDP-CRT-09 血缘)', example: 1000 }
baseVersionId: { type: integer, format: int64, description: 'modify/extend 血缘:本预览版派生自的 base 版本create/generate 为空additive', example: 4096 }
# ========== CommonResult 信封 ==========
CommonResultStudioTemplateList:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: array, items: { $ref: '#/components/schemas/StudioTemplateRespVO' } }
msg: { type: string }
CommonResultStudioSession:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/StudioSessionRespVO' }
msg: { type: string }
CommonResultStudioTaskChain:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/StudioTaskChainRespVO' }
msg: { type: string }