AI_Painting_V2.0/docs/superpowers/specs/2026-06-09-platform-architecture-design.md

188 lines
6.7 KiB
Markdown
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.

# 平台化架构设计
## 目标
将 Painting / Video 两套硬编码分支重构为统一的平台描述符架构,使加入新平台只需新建一个文件夹并实现标准接口,零改动 dialogBox。
## 背景
当前 `dialogBox/index.vue`792 行)通过 `v-if="type === 'Painting'"` / `v-if="type === 'Video'"` 承载两套完全不同的逻辑:
- **模型列表**Painting 走后端 APIVideo 走静态 JSON
- **参数 schema**Painting 走本地 JS 文件Video 走远程 workflow JSON
- **UI 控件**Painting 有 proportion/dimension/quality/quantityVideo 有 pattern/proportion/time
- **任务 body**Painting 扁平 modelParamsVideo `{ workflowId, nodeInfoList }`
后续模型参数后端化后,所有平台将统一为"API 获取模型列表 + API 获取参数 schema"模式。
## 核心设计:平台描述符模式
每个平台封装为一个文件夹,导出 `definePlatform()` 工厂函数返回标准接口对象。dialogBox 退化为纯渲染引擎。
### 平台接口
```js
// src/platforms/<name>/index.js
export function definePlatform() {
// 响应式状态(各平台自定义)
const model = ref(defaultValue)
const modelType = ref(defaultValue)
const state = reactive({ ... })
// 模型选择器组件
const ModelSelector = markRaw(Component)
// 参数控件列表(有序,决定渲染顺序)
const controls = [
{
name: 'proportion',
component: markRaw(ProportionComponent),
show: (config) => config?.params?.some(p => p.ui === 'proportion'),
props: (config) => ({ /* 额外 props */ }),
},
// ...
]
// 图片上传器(可选)
const ImageUploader = markRaw(Component) | null
return {
id: 'painting', // 平台标识
label: 'AI绘画2026', // 显示标题
ModelSelector, // 模型选择器组件
controls, // 有序控件列表
ImageUploader, // 图片上传器组件(可选)
state, // 平台自定义响应式状态
model, // 当前模型 ref
modelType, // 当前模型类型 ref
async loadModels() { }, // 获取模型列表
async loadConfig(modelName) { }, // 获取模型参数配置
buildTaskBody(state) { }, // 构造请求 body
getDefaultModel() { }, // 默认模型名称
isImageRequired(state) { }, // 是否必须上传图片
}
}
```
### 控件绑定约定
dialogBox 渲染控件时自动处理 `name``state` 的 v-model 绑定:
- `modelValue``state[name]`
- `onUpdate:modelValue``state[name] = v`
控件 descriptor 仅需定义 `show` 条件(基于 modelConfig 上下文)和额外 `props`
### 平台注册表
```js
// src/platforms/registry.js
import { definePaintingPlatform } from './painting/index.js'
import { defineVideoPlatform } from './video/index.js'
const registry = { Painting, Video }
export function createPlatform(type) {
const factory = registry[type]
if (!factory) throw new Error(`未找到平台: ${type}`)
return factory()
}
```
### dialogBox 角色变化
dialogBox 接收 `type` prop → 调用 `createPlatform(type)` → 获得 descriptor → 据 descriptor 渲染一切:
1. 渲染 `<component :is="platform.ModelSelector">`
2. 遍历 `platform.controls``show` 返回 true 的渲染 `<component :is>`
3. `handleStart()` 委托给 `platform.buildTaskBody(state)` → 调用 `taskPolling.generate(body)`
4. `watch(model)` 委托给 `platform.loadConfig(name)`
## 数据流
```mermaid
flowchart TD
subgraph dialogBox["dialogBox 编排层"]
A["platform.loadModels()"] --> B[模型选择器渲染]
B --> C["watch(model) → platform.loadConfig(name)"]
C --> D[计算 visibleControls]
D --> E["v-for 渲染 controls自动 v-model"]
E --> F["handleStart → platform.buildTaskBody()"]
F --> G["taskPolling.generate(body)"]
end
subgraph platform["platform 包"]
H["loadModels() → API"]
I["loadConfig(name) → API"]
J["buildTaskBody() → 扁平 body"]
end
G --> K[POST /suanli/v1/tasks]
K --> L[轮询 → displayStore 更新虚拟滚动列表]
```
## 目录结构
```
src/
├── platforms/ # 平台包(新增)
│ ├── painting/
│ │ ├── index.js # definePlatform()
│ │ ├── modelSelector.vue
│ │ ├── imageUploader.vue
│ │ └── controls/
│ │ ├── proportion.vue
│ │ ├── dimension.vue
│ │ ├── quality.vue
│ │ └── quantity.vue
│ ├── video/
│ │ ├── index.js
│ │ ├── modelSelector.vue
│ │ ├── imageUploader.vue
│ │ └── controls/
│ │ ├── pattern.vue
│ │ ├── proportion.vue
│ │ └── time.vue
│ └── registry.js
├── components/
│ ├── dialogBox/index.vue # 精简后 ~200 行
│ ├── Popover/ # 共享基础组件(不变)
│ ├── Select/ # 共享基础组件(不变)
│ ├── Img/ # 共享基础组件(不变)
│ └── virtual-scroller/ # 共享基础组件(不变)
├── apis/ # API 层(不变)
├── utils/
│ ├── taskPolling.js # 任务轮询(不变)
│ ├── request.js # Axios不变
│ └── modelApi.js # 平台 API 封装
├── config/
│ ├── models/ # 逐步废弃
│ ├── runninghub/ # 逐步废弃
│ └── plugins.js # 不变
```
### 迁移路径
| 步骤 | 内容 | 影响范围 |
|------|------|----------|
| 1 | 新建 `src/platforms/` + `registry.js`,不删旧代码 | 纯新增 |
| 2 | Painting 迁入 descriptordialogBox 切换读取路径 | dialogBox 精简 |
| 3 | Video 迁入 descriptor | dialogBox 继续精简 |
| 4 | 删除旧代码:`config/models/`、`config/runninghub/`、`modelConfig.js` | 清理 |
| 5 | 后端化:各平台 `loadConfig()` 改为调 API | 仅改 descriptor 内部 |
每步独立提交,方便回滚。
## 不变更的部分
- `taskPolling.js`:任务创建和轮询逻辑通用,不变
- `displayStore`:虚拟滚动列表状态通用,不变
- `Popover`、`Select`、`Img`、`virtual-scroller`:共享 UI 组件
- `home/index.vue`:仍然传 `type` prop不变
- `apis/`、`request.js`HTTP 层不变
- `config/plugins.js`、router、stores不变