计划书
1. 项目结构
text
styling-ai/
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
│
├── apps/
│ ├── web/ # 前端应用 (Next.js)
│ │ ├── package.json
│ │ ├── next.config.js
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ ├── public/
│ │ │ └── images/
│ │ └── src/
│ │ ├── app/
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx # 首页
│ │ │ ├── result/
│ │ │ │ └── [taskId]/
│ │ │ │ └── page.tsx # 结果页
│ │ │ └── globals.css
│ │ ├── components/
│ │ │ ├── upload/
│ │ │ ├── progress/
│ │ │ ├── result/
│ │ │ └── common/
│ │ ├── hooks/
│ │ ├── lib/
│ │ └── types/
│ │
│ └── api/ # 后端应用 (Express)
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
│ ├── index.ts # 入口
│ ├── app.ts # Express 应用
│ ├── config/
│ │ ├── index.ts # 配置加载
│ │ ├── models.yaml # 模型配置
│ │ └── prompts.yaml # 提示词配置
│ ├── routes/
│ │ ├── upload.ts
│ │ ├── status.ts
│ │ ├── result.ts
│ │ └── admin.ts
│ ├── controllers/
│ │ ├── uploadController.ts
│ │ ├── statusController.ts
│ │ └── resultController.ts
│ ├── services/
│ │ ├── taskOrchestrator.ts
│ │ ├── imageProcessor.ts
│ │ ├── storageService.ts
│ │ └── cleanupService.ts
│ ├── providers/
│ │ ├── vision/
│ │ │ ├── index.ts # 工厂
│ │ │ ├── base.ts # 基类
│ │ │ ├── openai.ts
│ │ │ ├── anthropic.ts
│ │ │ ├── google.ts
│ │ │ └── alibaba.ts
│ │ └── imageGen/
│ │ ├── index.ts # 工厂
│ │ ├── base.ts # 基类
│ │ ├── dalle.ts
│ │ ├── replicate.ts
│ │ └── fal.ts
│ ├── utils/
│ │ ├── promptParser.ts
│ │ ├── validator.ts
│ │ └── logger.ts
│ ├── middleware/
│ │ ├── errorHandler.ts
│ │ ├── rateLimiter.ts
│ │ └── validator.ts
│ └── types/
│ ├── task.ts
│ ├── models.ts
│ └── api.ts
│
├── packages/
│ └── shared/ # 共享代码
│ ├── package.json
│ └── src/
│ ├── constants/
│ │ └── errorCodes.ts
│ └── types/
│ └── common.ts
│
├── docker/
│ ├── Dockerfile.web
│ ├── Dockerfile.api
│ └── docker-compose.yml
│
└── docs/
├── api.md
└── deployment.md
2. 开发阶段划分
阶段概览
text
┌─────────────────────────────────────────────────────────────────┐
│ Phase 0: 项目初始化 [0.5 天] │
├─────────────────────────────────────────────────────────────────┤
│ Phase 1: 后端核心 - 模型适配层 [2 天] │
├─────────────────────────────────────────────────────────────────┤
│ Phase 2: 后端核心 - 任务编排与 API [2 天] │
├─────────────────────────────────────────────────────────────────┤
│ Phase 3: 前端开发 [2 天] │
├─────────────────────────────────────────────────────────────────┤
│ Phase 4: 集成测试与优化 [1 天] │
├─────────────────────────────────────────────────────────────────┤
│ Phase 5: 部署与上线 [0.5 天] │
└─────────────────────────────────────────────────────────────────┘
总计: 8 天
Phase 0: 项目初始化 [0.5 天]
任务清单
| 编号 |
任务 |
产出物 |
预计耗时 |
| 0.1 |
创建 monorepo 结构 |
项目骨架 |
1h |
| 0.2 |
配置 TypeScript、ESLint、Prettier |
配置文件 |
0.5h |
| 0.3 |
配置 pnpm workspace |
pnpm-workspace.yaml |
0.5h |
| 0.4 |
初始化 Next.js 前端应用 |
apps/web |
0.5h |
| 0.5 |
初始化 Express 后端应用 |
apps/api |
0.5h |
| 0.6 |
创建共享包 |
packages/shared |
0.5h |
| 0.7 |
配置环境变量模板 |
.env.example |
0.5h |
验收标准
pnpm install 成功
pnpm dev 可同时启动前后端
- TypeScript 编译无错误
Phase 1: 后端核心 - 模型适配层 [2 天]
Day 1: 配置系统与基础架构
| 编号 |
任务 |
产出物 |
预计耗时 |
| 1.1 |
实现配置加载器 |
config/index.ts |
2h |
| 1.2 |
创建 models.yaml 配置文件 |
config/models.yaml |
1h |
| 1.3 |
定义类型接口 |
types/models.ts |
1h |
| 1.4 |
实现 Vision Provider 基类 |
providers/vision/base.ts |
2h |
| 1.5 |
实现 ImageGen Provider 基类 |
providers/imageGen/base.ts |
2h |
Day 2: 具体 Provider 实现
| 编号 |
任务 |
产出物 |
预计耗时 |
| 1.6 |
实现 OpenAI Vision Provider |
providers/vision/openai.ts |
2h |
| 1.7 |
实现 DALL-E Provider |
providers/imageGen/dalle.ts |
2h |
| 1.8 |
实现 Replicate Provider |
providers/imageGen/replicate.ts |
2h |
| 1.9 |
实现 Provider 工厂 |
providers/*/index.ts |
1h |
| 1.10 |
编写单元测试 |
tests/providers/*.test.ts |
1h |
关键代码示例
TypeScript
export abstract class VisionProviderBase implements VisionProvider {
protected config: ProviderConfig;
protected httpClient: AxiosInstance;
constructor(config: ProviderConfig) {
this.config = config;
this.httpClient = axios.create({
timeout: config.timeout,
headers: this.getDefaultHeaders()
});
}
abstract analyze(request: VisionAnalysisRequest): Promise<VisionAnalysisResponse>;
protected abstract getDefaultHeaders(): Record<string, string>;
protected abstract formatRequest(request: VisionAnalysisRequest): unknown;
protected abstract parseResponse(response: unknown): VisionAnalysisResponse;
async healthCheck(): Promise<boolean> {
}
}
export class OpenAIVisionProvider extends VisionProviderBase {
async analyze(request: VisionAnalysisRequest): Promise<VisionAnalysisResponse> {
const payload = this.formatRequest(request);
const response = await this.httpClient.post(this.config.endpoint, payload);
return this.parseResponse(response.data);
}
protected formatRequest(request: VisionAnalysisRequest) {
return {
model: this.config.model,
messages: [
{ role: 'system', content: this.getSystemPrompt() },
{
role: 'user',
content: [
{ type: 'text', text: request.prompt },
{
type: 'image_url',
image_url: { url: `data:${request.mimeType};base64,${request.imageBase64}` }
}
]
}
],
max_tokens: this.config.maxTokens
};
}
验收标准
- OpenAI Vision 调用成功,返回结构化数据
- DALL-E 图像生成成功
- Provider 可通过配置文件切换
- 单元测试覆盖率 > 80%
Phase 2: 后端核心 - 任务编排与 API [2 天]
Day 3: 核心服务层
| 编号 |
任务 |
产出物 |
预计耗时 |
| 2.1 |
实现图片处理服务 |
services/imageProcessor.ts |
2h |
| 2.2 |
实现存储服务 |
services/storageService.ts |
2h |
| 2.3 |
实现任务编排器 |
services/taskOrchestrator.ts |
3h |
| 2.4 |
实现提示词解析器 |
utils/promptParser.ts |
1h |
Day 4: API 路由层
| 编号 |
任务 |
产出物 |
预计耗时 |
| 2.5 |
实现 upload 路由 |
routes/upload.ts |
2h |
| 2.6 |
实现 status 路由 |
routes/status.ts |
1h |
| 2.7 |
实现 result 路由 |
routes/result.ts |
1h |
| 2.8 |
实现错误处理中间件 |
middleware/errorHandler.ts |
1h |
| 2.9 |
实现速率限制中间件 |
middleware/rateLimiter.ts |
1h |
| 2.10 |
API 集成测试 |
tests/api/*.test.ts |
2h |
关键代码示例
TypeScript
export class TaskOrchestrator {
private tasks: Map<string, Task> = new Map();
private visionProvider: VisionProvider;
private imageGenProvider: ImageGenProvider;
constructor(
private modelFactory: ModelFactory,
private storageService: StorageService,
private imageProcessor: ImageProcessor
) {
this.visionProvider = modelFactory.getVisionProvider();
this.imageGenProvider = modelFactory.getImageGenProvider();
}
async createTask(imageBuffer: Buffer, mimeType: string): Promise<string> {
const taskId = uuidv4();
const processed = await this.imageProcessor.process(imageBuffer);
const imagePath = await this.storageService.saveOriginal(taskId, processed);
const task: Task = {
id: taskId,
status: 'pending',
progress: 0,
originalImage: { path: imagePath, mimeType },
createdAt: new Date(),
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)
};
this.tasks.set(taskId, task);
this.executeTask(taskId).catch(err => {
this.updateTask(taskId, { status: 'failed', error: err });
});
return taskId;
}
private async executeTask(taskId: string): Promise<void> {
const task = this.tasks.get(taskId);
this.updateTask(taskId, { status: 'analyzing', progress: 20 });
const imageBase64 = await this.storageService.readAsBase64(task.originalImage.path);
const analysisResult = await this.visionProvider.analyze({
imageBase64,
mimeType: task.originalImage.mimeType,
prompt: this.getPrompt()
});
const parsed = parseVisionResponse(analysisResult.rawResponse);
this.updateTask(taskId, {
analysis: { ...analysisResult, ...parsed },
progress: 50
});
this.updateTask(taskId, { status: 'generating', progress: 60 });
const generationResult = await this.imageGenProvider.generate({
prompt: parsed.imagePrompt,
referenceImageBase64: imageBase64
});
const generatedPath = await this.storageService.saveGenerated(
taskId,
generationResult.imageUrl
);
this.updateTask(taskId, {
status: 'completed',
progress: 100,
generation: {
imageUrl: generationResult.imageUrl,
localPath: generatedPath,
completedAt: new Date()
}
});
}
}
验收标准
- POST /api/upload 成功创建任务
- GET /api/status/:taskId 正确返回状态
- 完整流程端到端测试通过
- 错误场景正确处理
Phase 3: 前端开发 [2 天]
Day 5: 核心组件
| 编号 |
任务 |
产出物 |
预计耗时 |
| 3.1 |
实现 DropZone 上传组件 |
components/upload/DropZone.tsx |
2h |
| 3.2 |
实现图片预览组件 |
components/upload/ImagePreview.tsx |
1h |
| 3.3 |
实现进度展示组件 |
components/progress/* |
2h |
| 3.4 |
实现 API 调用封装 |
lib/api.ts |
1.5h |
| 3.5 |
实现状态轮询 Hook |
hooks/useTaskStatus.ts |
1.5h |
Day 6: 页面与集成
| 编号 |
任务 |
产出物 |
预计耗时 |
| 3.6 |
实现首页 |
app/page.tsx |
2h |
| 3.7 |
实现结果页 |
app/result/[taskId]/page.tsx |
2h |
| 3.8 |
实现前后对比组件 |
components/result/BeforeAfter.tsx |
1.5h |
| 3.9 |
实现造型说明组件 |
components/result/ReasoningCard.tsx |
1h |
| 3.10 |
样式与响应式 |
全局样式调整 |
1.5h |
关键代码示例
TypeScript
export function useTaskStatus(taskId: string | null) {
const [state, setState] = useState<TaskState>({
status: 'idle',
progress: 0,
message: '',
result: null,
error: null
});
useEffect(() => {
if (!taskId) return;
let isMounted = true;
let pollCount = 0;
const poll = async () => {
try {
const data = await api.getTaskStatus(taskId);
if (!isMounted) return;
setState(prev => ({
...prev,
status: data.status,
progress: data.progress,
message: getStatusMessage(data.status)
}));
if (data.status === 'completed') {
const result = await api.getTaskResult(taskId);
setState(prev => ({ ...prev, result }));
return;
}
if (data.status === 'failed') {
setState(prev => ({ ...prev, error: data.error }));
return;
}
pollCount++;
const interval = pollCount < 5 ? 2000 : 5000;
setTimeout(poll, interval);
} catch (error) {
if (isMounted) {
setState(prev => ({
...prev,
error: { message: '网络错误,请重试' }
}));
}
}
};
poll();
return () => { isMounted = false; };
}, [taskId]);
return state;
}
export function DropZone({ onUpload }: DropZoneProps) {
const [isDragging, setIsDragging] = useState(false);
const [preview, setPreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
if (file && validateImage(file)) {
handleFile(file);
}
}, []);
const handleFile = async (file: File) => {
const compressed = await compressImage(file, {
maxWidth: 2048,
quality: 0.9
});
const previewUrl = URL.createObjectURL(compressed);
setPreview(previewUrl);
onUpload(compressed);
};
return (
<div
className={cn(
"border-2 border-dashed rounded-xl p-8 text-center transition-colors",
isDragging ? "border-primary bg-primary/5" : "border-gray-300",
preview ? "border-solid" : ""
)} => { e.preventDefault(); setIsDragging(true); }} => setIsDragging(false)}
>
{preview ? (
<ImagePreview src={preview} => setPreview(null)} />
) : (
<>
<UploadIcon className="mx-auto h-12 w-12 text-gray-400" />
<p className="mt-4 text-lg">拖拽图片到这里,或点击上传</p>
<p className="mt-2 text-sm text-gray-500">支持 JPG、PNG、WebP,最大 10MB</p>
<input
ref={fileInputRef}
type="file"
accept="image/jpeg,image/png,image/webp"
className="hidden" => e.target.files?.[0] && handleFile(e.target.files[0])}
/>
<Button
className="mt-4" => fileInputRef.current?.click()}
>
选择图片
</Button>
</>
)}
</div>
);
}
验收标准
- 图片上传功能正常
- 进度展示实时更新
- 结果页正确展示前后对比
- 移动端适配良好
Phase 4: 集成测试与优化 [1 天]
| 编号 |
任务 |
产出物 |
预计耗时 |
| 4.1 |
端到端流程测试 |
测试报告 |
2h |
| 4.2 |
错误场景测试 |
测试用例 |
1h |
| 4.3 |
性能优化(图片加载、API响应) |
优化记录 |
2h |
| 4.4 |
模型切换测试 |
测试报告 |
1h |
| 4.5 |
Bug 修复 |
- |
2h |
测试用例清单
Markdown
## 正常流程
- [ ] 上传 JPG 图片 → 成功生成
- [ ] 上传 PNG 图片 → 成功生成
- [ ] 上传 WebP 图片 → 成功生成
## 边界情况
- [ ] 上传超大图片 (> 10MB) → 提示错误
- [ ] 上传非图片文件 → 提示错误
- [ ] 上传无人脸图片 → 提示错误(如果启用检测)
- [ ] 上传多人脸图片 → 提示错误(如果启用检测)
## 错误恢复
- [ ] Vision API 超时 → 重试后成功
- [ ] 图像生成失败 → 正确显示错误
- [ ] 网络中断 → 重连后恢复
## 模型切换
- [ ] 切换 Vision Provider → 功能正常
- [ ] 切换 ImageGen Provider → 功能正常
## 并发测试
- [ ] 同时 5 个请求 → 全部成功
- [ ] 触发速率限制 → 正确提示
Phase 5: 部署与上线 [0.5 天]
| 编号 |
任务 |
产出物 |
预计耗时 |
| 5.1 |
编写 Dockerfile |
docker/* |
1h |
| 5.2 |
配置 docker-compose |
docker-compose.yml |
0.5h |
| 5.3 |
配置生产环境变量 |
.env.production |
0.5h |
| 5.4 |
部署到服务器 |
运行实例 |
1h |
| 5.5 |
验证生产环境 |
验收记录 |
1h |
3. 里程碑与交付物
| 里程碑 |
时间点 |
交付物 |
| M0: 项目启动 |
Day 0.5 |
可运行的项目骨架 |
| M1: 模型层完成 |
Day 2.5 |
可配置切换的 AI 调用层 |
| M2: 后端完成 |
Day 4.5 |
完整可用的 API 服务 |
| M3: 前端完成 |
Day 6.5 |
完整可用的 Web 界面 |
| M4: 测试通过 |
Day 7.5 |
测试报告 + Bug 修复 |
| M5: 上线 |
Day 8 |
生产环境部署完成 |
4. 技术栈确认
| 层级 |
技术选型 |
版本 |
| 前端框架 |
Next.js |
14.x |
| UI 组件 |
TailwindCSS + Radix UI |
- |
| 后端框架 |
Express |
4.x |
| 语言 |
TypeScript |
5.x |
| 运行时 |
Node.js |
18.x LTS |
| 包管理 |
pnpm |
8.x |
| 图片处理 |
sharp |
0.33.x |
| HTTP 客户端 |
axios |
1.x |
| 配置文件 |
yaml |
- |
| 测试框架 |
Vitest |
1.x |
5. 风险与应对
| 风险 |
概率 |
影响 |
应对措施 |
| AI API 响应慢 |
高 |
中 |
增加超时配置、进度提示优化 |
| 人脸一致性差 |
中 |
高 |
提示词优化、支持切换到 InstantID |
| 模型 API 变更 |
低 |
中 |
适配器模式隔离变更 |
| 图片存储成本 |
中 |
低 |
严格的过期清理策略 |
6. 后续迭代建议
v1.1
- 增加 Anthropic Claude、Google Gemini Vision 支持
- 增加 Midjourney 集成(通过代理)
- 历史记录(本地存储)
v1.2
- 多风格选择(甜美/帅气/知性等)
- 用户反馈收集
- A/B 测试框架
v2.0
VibeCoding 导航:⬅️ 01-造型生成网站 - 设计稿 | 02-计划书 | ➡️ 01-项目文档(Project Overview)
💬 评论