后端接口字段需求文档 - JSON 美化功能

文档版本: v1.0.0
创建日期: 2026-01-13
适用范围: 后端 API 扩展


一、需求背景

为支持前端 JSON 美化功能,后端需要在现有 API 响应中新增字段,用于存储和传递:

  1. JSON 模板定义 (创作者配置的 UI 模板)
  2. 模板元数据 (版本、审核状态等)
  3. AI 生成的结构化数据 (已包含在 content 字段中,无需额外修改)

二、涉及的 API 端点

2.1 核心游玩相关

API 路径说明是否需要修改
POST /engine-play/new开始新游玩✅ 需要返回模板
GET /engine-play/sessions/{id}/messages获取历史消息✅ 需要返回模板
POST /chat/sessions/{id}/choice提交选择 (轮询)⚠️ 无需修改 (AI 输出在消息中)
POST /engine-play/sessions/{id}/regenerate重新生成⚠️ 无需修改

2.2 引擎管理相关

API 路径说明是否需要修改
GET /engines/{id}获取引擎详情✅ 需要返回模板列表
POST /engines创建引擎✅ 需要接收模板数据
PUT /engines/{id}更新引擎✅ 需要接收模板数据

2.3 新增 API (推荐)

API 路径方法说明
GET /engines/{id}/templatesGET获取引擎的所有 JSON 模板
POST /engines/{id}/templatesPOST创建新模板
PUT /engines/{id}/templates/{templateId}PUT更新模板
DELETE /engines/{id}/templates/{templateId}DELETE删除模板
POST /engines/{id}/templates/{templateId}/validatePOST校验模板语法

三、字段详细定义

3.1 引擎详情响应扩展

API: GET /engines/{id}

现有响应结构 (部分):

{
  "data": {
    "id": "engine_123",
    "name": "星际迷航",
    "description": "...",
    "coverImageUrl": "...",
    "backgroundImageUrl": "...",
    "narrativeSettings": { ... },
    "characterCard": { ... }
  }
}

新增字段 (建议添加在 data 下):

{
  "data": {
    "id": "engine_123",
    // ... 现有字段 ...
    
    "jsonTemplates": [
      {
        "id": "template_001",
        "type": "status-card",
        "name": "状态卡片",
        "description": "显示指挥官状态的卡片组件",
        "html": "<div class=\"status\"><h3>{{title}}</h3><p>{{comment}}</p></div>",
        "style": ".status{padding:14px;border-radius:12px;background:skyblue;}",
        "version": 1,
        "isActive": true,
        "createdAt": "2026-01-13T10:00:00Z",
        "updatedAt": "2026-01-13T10:00:00Z"
      },
      {
        "id": "template_002",
        "type": "global-theme",
        "name": "全局主题",
        "description": "深色模式主题样式",
        "html": "",
        "style": ".markdown-body { background: #1f2933; color: #fff; }",
        "version": 1,
        "isActive": true,
        "createdAt": "2026-01-13T10:00:00Z",
        "updatedAt": "2026-01-13T10:00:00Z"
      }
    ]
  }
}

字段说明:

字段类型必填说明
jsonTemplatesArrayJSON 模板数组,引擎没有模板时为空数组
jsonTemplates[].idString模板唯一标识 (数据库主键)
jsonTemplates[].typeString模板类型标识符 (如 status-cardglobal-theme)
jsonTemplates[].nameString模板名称 (创作者可见)
jsonTemplates[].descriptionString模板描述
jsonTemplates[].htmlStringLiquidJS HTML 模板字符串
jsonTemplates[].styleStringCSS 样式字符串
jsonTemplates[].versionInteger模板版本号 (用于缓存失效)
jsonTemplates[].isActiveBoolean是否启用 (支持禁用而非删除)
jsonTemplates[].createdAtString创建时间 (ISO 8601)
jsonTemplates[].updatedAtString更新时间 (ISO 8601)

3.2 游玩会话响应扩展

API: POST /engine-play/new, GET /engine-play/sessions/{id}/messages

现有响应结构:

{
  "data": {
    "session": {
      "id": "session_456",
      "engineId": "engine_123",
      // ...
    },
    "currentScene": {
      "context": "欢迎来到星际迷航...",
      "choices": [...]
    },
    "storyHistory": [...]
  }
}

方案 A: 嵌套模板数据 (推荐)

data 下新增 jsonTemplates 字段 (与引擎详情保持一致):

{
  "data": {
    "session": { ... },
    "currentScene": { ... },
    "storyHistory": [ ... ],
    
    "jsonTemplates": [
      // 与 3.1 中的结构相同
    ]
  }
}

方案 B: 仅返回模板引用

如果模板数据量大,可仅返回 templateIds,前端再调用 GET /engines/{id}/templates:

{
  "data": {
    "session": { ... },
    "currentScene": { ... },
    "storyHistory": [ ... ],
    
    "templateIds": ["template_001", "template_002"]
  }
}

建议: 优先采用 方案 A,理由:

  • 减少 API 调用次数 (1 次 vs 2 次)
  • 避免模板数据与消息不同步
  • 模板数量通常不超过 20 个,数据量可控

3.3 引擎创建/更新请求扩展

API: POST /engines, PUT /engines/{id}

现有请求 Body (部分):

{
  "name": "星际迷航",
  "description": "...",
  "coverImageUrl": "...",
  "narrativeSettings": { ... },
  "characterCard": { ... }
}

新增字段:

{
  "name": "星际迷航",
  // ... 现有字段 ...
  
  "jsonTemplates": [
    {
      "type": "status-card",
      "name": "状态卡片",
      "description": "显示指挥官状态",
      "html": "<div class=\"status\"><h3>{{title}}</h3></div>",
      "style": ".status{padding:14px;}",
      "isActive": true
    }
  ]
}

字段说明:

  • 创建引擎时,jsonTemplates 可选 (默认空数组)
  • 更新引擎时,jsonTemplates全量替换 现有模板
  • 不需要传 idversioncreatedAtupdatedAt (后端自动生成)

3.4 独立模板管理 API (可选,但强烈推荐)

3.4.1 获取模板列表

API: GET /engines/{id}/templates

响应:

{
  "success": true,
  "data": [
    {
      "id": "template_001",
      "type": "status-card",
      "name": "状态卡片",
      "html": "...",
      "style": "...",
      "version": 2,
      "isActive": true,
      "createdAt": "2026-01-13T10:00:00Z",
      "updatedAt": "2026-01-13T12:00:00Z"
    }
  ]
}

3.4.2 创建模板

API: POST /engines/{id}/templates

请求 Body:

{
  "type": "inventory-card",
  "name": "物品栏卡片",
  "description": "显示玩家背包",
  "html": "<div>{{slot:item-list}}</div>",
  "style": ".inventory{background:#fff;}",
  "isActive": true
}

响应:

{
  "success": true,
  "data": {
    "id": "template_003",
    "type": "inventory-card",
    // ... 完整模板数据
  }
}

3.4.3 更新模板

API: PUT /engines/{id}/templates/{templateId}

请求 Body:

{
  "name": "物品栏卡片 V2",
  "html": "<div class=\"v2\">{{slot:item-list}}</div>",
  "style": ".v2{background:#f0f0f0;}",
  "isActive": true
}

响应:

{
  "success": true,
  "data": {
    "id": "template_003",
    "version": 3,  // 版本号自动递增
    "updatedAt": "2026-01-13T14:00:00Z",
    // ... 完整模板数据
  }
}

3.4.4 删除模板

API: DELETE /engines/{id}/templates/{templateId}

响应:

{
  "success": true,
  "message": "模板已删除"
}

软删除建议: 不物理删除记录,而是设置 isActive = false,避免历史会话中的模板失效。


3.4.5 校验模板 (推荐)

API: POST /engines/{id}/templates/validate

请求 Body:

{
  "html": "<div>{{title}}</div>",
  "style": ".test{color:red;}"
}

响应 (成功):

{
  "success": true,
  "data": {
    "isValid": true,
    "warnings": [
      "检测到未使用的 CSS 类: .unused"
    ]
  }
}

响应 (失败):

{
  "success": false,
  "data": {
    "isValid": false,
    "errors": [
      {
        "field": "html",
        "message": "LiquidJS 语法错误: Unexpected token at line 3"
      },
      {
        "field": "style",
        "message": "检测到危险 CSS: @import"
      }
    ]
  }
}

校验规则建议:

  • LiquidJS 语法检查
  • CSS 危险特性检测 (@import, url(), @font-face)
  • HTML 标签白名单检查
  • 插槽引用完整性检查 (如 {{slot:non-exist}} 不存在)

四、数据库设计建议

4.1 表结构

表名: engine_json_templates

字段类型约束说明
idString (UUID)PRIMARY KEY模板 ID
engine_idString (UUID)FOREIGN KEY关联的引擎 ID
typeString (50)NOT NULL模板类型标识符
nameString (100)NOT NULL模板名称
descriptionTextNULL模板描述
htmlTextNOT NULLHTML 模板
styleTextNULLCSS 样式
versionIntegerNOT NULL, DEFAULT 1版本号
is_activeBooleanNOT NULL, DEFAULT TRUE是否启用
created_atTimestampNOT NULL创建时间
updated_atTimestampNOT NULL更新时间

索引建议:

CREATE INDEX idx_engine_templates ON engine_json_templates(engine_id, is_active);
CREATE UNIQUE INDEX idx_engine_type ON engine_json_templates(engine_id, type) WHERE is_active = TRUE;

唯一性约束: 同一引擎下,同一 type 只能有一个启用的模板 (支持版本管理时,历史版本可保留)。


4.2 数据迁移策略

Phase 1: 向后兼容

  • 新增 jsonTemplates 字段为可选
  • 老引擎返回 jsonTemplates: []
  • 前端检测到空数组时,降级为纯 Markdown 渲染

Phase 2: 逐步迁移

  • 引擎编辑器 UI 中增加”JSON 模板”配置入口
  • 创作者可选择是否启用 JSON 美化
  • 后台统计使用率

Phase 3: 全量推广

  • 提供默认模板库 (官方精选)
  • 新引擎默认带 1-2 个示例模板

五、API 响应示例对比

5.1 修改前 (现有)

GET /engines/{id}:

{
  "data": {
    "id": "engine_123",
    "name": "星际迷航",
    "description": "探索未知星系",
    "coverImageUrl": "https://...",
    "narrativeSettings": {
      "genre": "sci-fi",
      "tone": "serious"
    }
  }
}

5.2 修改后 (新增字段)

GET /engines/{id}:

{
  "data": {
    "id": "engine_123",
    "name": "星际迷航",
    "description": "探索未知星系",
    "coverImageUrl": "https://...",
    "narrativeSettings": {
      "genre": "sci-fi",
      "tone": "serious"
    },
    
    // ✨ 新增字段
    "jsonTemplates": [
      {
        "id": "template_001",
        "type": "status-card",
        "name": "指挥官状态",
        "description": "显示当前任务状态",
        "html": "<div class=\"cmd-status\"><h3>{{title}}</h3><p>{{comment}}</p></div>",
        "style": ".cmd-status{padding:16px;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-radius:12px;}",
        "version": 1,
        "isActive": true,
        "createdAt": "2026-01-13T10:00:00Z",
        "updatedAt": "2026-01-13T10:00:00Z"
      },
      {
        "id": "template_002",
        "type": "crew-list",
        "name": "船员名单",
        "description": "显示当前船员状态",
        "html": "<div class=\"crew\">{{#each members}}<p>{{name}} - {{role}}</p>{{/each}}</div>",
        "style": ".crew{background:#fff;padding:12px;}",
        "version": 1,
        "isActive": true,
        "createdAt": "2026-01-13T10:00:00Z",
        "updatedAt": "2026-01-13T10:00:00Z"
      }
    ]
  }
}

六、前后端协作要点

6.1 模板加载时机

前端行为:

  1. 进入会话时,从 GET /engine-play/sessions/{id}/messages 响应中获取 jsonTemplates
  2. 调用 jsonRenderer.registerTemplate(template) 注册所有模板
  3. 渲染消息时,按 type 匹配模板

后端需保证:

  • 所有包含 JSON 消息的 API (新建会话、获取历史) 都返回 jsonTemplates
  • 模板数据与引擎版本一致 (避免引擎更新后模板不同步)

6.2 模板版本管理

场景: 创作者更新了模板,但历史会话仍在使用旧版本

解决方案:

  • 后端在 session 表中新增 template_snapshot 字段 (JSON 类型)
  • 会话创建时,快照当前引擎的所有模板
  • 历史会话恢复时,返回快照版本而非最新版本

示例:

// session 表
{
  "id": "session_456",
  "engine_id": "engine_123",
  "template_snapshot": [
    // 会话创建时的模板副本
  ],
  "created_at": "2026-01-10T10:00:00Z"
}

API 行为:

  • GET /engine-play/sessions/{id}/messages 返回 data.jsonTemplates = session.template_snapshot
  • 新会话创建时,自动生成快照

6.3 AI 输出内容格式

重要: 后端无需修改 AI 输出逻辑,JSON 块已包含在 content 字段中

示例:

{
  "currentScene": {
    "context": "你来到了指挥室...\n\n```json\n{\"type\":\"status-card\",\"title\":\"紧急状况\",\"comment\":\"敌舰逼近\"}\n```\n\n你需要做出决定。",
    "choices": [...]
  }
}

前端处理:

  1. 正则提取 ```json ... ```
  2. 清理 Markdown: 你来到了指挥室...你需要做出决定。
  3. 解析 JSON: {type: "status-card", ...}
  4. 分别渲染

七、性能与安全考虑

7.1 模板数据量控制

建议:

  • 单个引擎最多 20 个模板
  • 单个模板 HTML 不超过 10KB
  • 单个模板 CSS 不超过 5KB

超限处理:

  • API 返回 HTTP 400,错误码 TEMPLATE_SIZE_EXCEEDED
  • 编辑器 UI 实时提示字符数

7.2 模板内容安全

后端校验规则 (在 POST /engines/{id}/templates/validate 中实施):

  1. HTML 标签白名单: 仅允许 div, p, span, h1-h6, button, form, input, select, textarea
  2. HTML 属性白名单: 仅允许 class, data-action-*, data-display-*, data-target
  3. 禁止属性: onerror, onload, onclick, style, src, href (除非 href 值为内部路由)
  4. CSS 黑名单: 禁止 @import, url(), @font-face, expression()
  5. LiquidJS 语法检查: 使用 Liquid 引擎的 parse() 方法预校验

校验失败响应示例:

{
  "success": false,
  "errors": [
    {
      "field": "html",
      "message": "检测到危险标签: <script>"
    }
  ]
}

7.3 缓存策略

建议: 后端对模板数据启用 HTTP 缓存

响应头:

Cache-Control: public, max-age=3600
ETag: "template_version_2"

前端行为:

  • 使用 If-None-Match 条件请求
  • 模板版本号变化时,ETag 自动更新

八、实施检查清单

8.1 后端开发任务

  • 创建 engine_json_templates 数据表
  • 扩展 GET /engines/{id} 返回 jsonTemplates
  • 扩展 POST /engine-play/new 返回 jsonTemplates
  • 扩展 GET /engine-play/sessions/{id}/messages 返回 jsonTemplates
  • 扩展 POST /engines 接收 jsonTemplates
  • 扩展 PUT /engines/{id} 接收 jsonTemplates
  • 新增 GET /engines/{id}/templates API
  • 新增 POST /engines/{id}/templates API
  • 新增 PUT /engines/{id}/templates/{templateId} API
  • 新增 DELETE /engines/{id}/templates/{templateId} API
  • 新增 POST /engines/{id}/templates/validate API
  • 实现模板内容安全校验 (HTML/CSS)
  • 实现 session.template_snapshot 快照机制
  • 编写 API 单元测试
  • 更新 OpenAPI 文档 (Swagger)

8.2 前端联调准备

  • 更新 Orval 生成 TypeScript 类型
  • 验证 jsonTemplates 字段正确返回
  • 验证模板 CRUD 操作
  • 验证模板校验 API
  • 验证历史会话使用快照版本

九、FAQ

Q1: 如果引擎没有配置模板,API 返回什么?

A: 返回 jsonTemplates: [] (空数组),前端自动降级为纯 Markdown 渲染。

Q2: 模板 type 字段有命名规范吗?

A: 建议使用 kebab-case (如 status-card, inventory-list),长度 3-50 字符,仅允许英文字母、数字、连字符。

Q3: global-theme 类型的模板如何处理?

A: 与普通模板一致存储,前端会识别 type === 'global-theme' 并仅注入 style,不渲染 html

Q4: 模板更新后,旧会话会显示错误吗?

A: 不会。使用 template_snapshot 快照机制,历史会话固定使用创建时的模板版本。

Q5: 是否需要模板权限管理?

A: 当前版本不强制要求。如需权限,建议在 engine_json_templates 表增加 creator_id 字段,限制仅引擎创建者可修改模板。


十、总结

本文档详细定义了支持 JSON 美化功能所需的后端字段扩展,核心要点:

  1. 最小化侵入: 仅在现有 API 响应中新增 jsonTemplates 数组字段
  2. 向后兼容: 旧引擎返回空数组,不影响现有功能
  3. 推荐独立 API: 提供模板 CRUD 端点,便于前端独立管理
  4. 安全优先: 提供模板校验 API,防止 XSS/CSS 注入
  5. 版本管理: 通过 template_snapshot 确保历史会话稳定

下一步行动:

  1. 后端团队评审本文档
  2. 确认数据库表结构设计
  3. 实施 API 扩展开发
  4. 前端团队同步更新类型定义

📌 相关文档: