计划书

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

// providers/vision/base.ts
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> {
    // 实现健康检查逻辑
  }
}

// providers/vision/openai.ts
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

// services/taskOrchestrator.ts
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);

    // Step 1: Vision 分析
    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
    });

    // Step 2: 图像生成
    this.updateTask(taskId, { status: 'generating', progress: 60 });
    const generationResult = await this.imageGenProvider.generate({
      prompt: parsed.imagePrompt,
      referenceImageBase64: imageBase64
    });

    // Step 3: 保存结果
    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

// hooks/useTaskStatus.ts
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;
}

// components/upload/DropZone.tsx
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)