3小时用GLM-5全栈长任务复刻TikTok视频生成SaaS平台
2026/8/8 5:35:45 网站建设 项目流程

1. 项目缘起:当“全栈长任务”遇上“视频生成SaaS”

最近,GLM-5的“全栈长任务”能力在开发者圈子里讨论得挺火。简单来说,它不再是一个只能帮你写几行代码或者润色一段文案的“小助手”,而是能理解一个复杂的、多步骤的工程目标,并自主规划、调用工具、编写代码,最终交付一个可运行的项目。这听起来有点像给了一个能独立完成外包项目的“数字员工”。正好,我手头有个一直想验证的想法:能不能快速搭建一个类似TikTok风格的短视频生成SaaS平台?不是那种简单的模板套用,而是包含用户上传素材、AI脚本生成、视频剪辑合成、任务队列管理、用户订阅付费等完整前后端逻辑的“玩具级”但“全功能”系统。

传统的做法,从技术选型、架构设计、前后端开发、第三方API集成到部署上线,一个熟练的全栈工程师也得花上几天甚至一周。这次,我想用GLM-5的“全栈长任务”能力来挑战一下,看看它能否在3小时内,从零开始,帮我“复刻”出这个SaaS的核心骨架。我选择的武器库是当前非常流行的技术栈:前端用Next.js(兼顾SEO和开发体验),后端用FastAPI(Python异步框架,适合AI应用),数据库用PostgreSQL,视频处理用FFmpeg,再结合一些AI服务API。整个过程,我将扮演“产品经理”和“架构评审”的角色,主要向GLM-5描述需求、审核代码、解决它遇到的“卡点”。下面,我就把这3小时的实战过程、核心代码、踩过的坑以及最终的成果,完整地记录下来。

2. 任务拆解与GLM-5的“思考”过程

一开始,我并没有直接扔给GLM-5一个“做个TikTok SaaS”的模糊指令。根据以往与AI协作的经验,任务的拆解质量直接决定了最终输出的质量。我首先用自然语言向GLM-5描述了项目的核心目标:

“请构建一个短视频生成SaaS平台的最小可行产品(MVP)。核心功能包括:1. 用户可注册登录,并管理订阅套餐(基础版、高级版)。2. 用户上传图片/短视频片段作为素材,并输入文本描述(如‘生成一个关于夏日旅行的快节奏视频’)。3. 后端接收任务后,将其放入队列,模拟调用AI服务(实际可接入如GPT for script, Stable Diffusion for image, 或RunwayML for video)生成视频脚本,并调用FFmpeg将用户素材与生成的元素(如字幕、背景音乐)合成为新视频。4. 用户可在前端查看任务列表、状态及下载生成的视频。技术栈:前端Next.js 14 (App Router), Tailwind CSS;后端FastAPI;数据库PostgreSQL with SQLAlchemy;消息队列Redis(用于Celery);对象存储(模拟使用本地目录)。请从项目初始化开始,生成完整的、可运行的代码目录结构、关键逻辑代码、数据库模型、API接口以及基础的UI组件。”

发出这个指令后,我观察了GLM-5的“思考”过程(在一些平台上会显示它的推理链)。它并没有立即开始写代码,而是先输出了一个项目架构规划

  1. 整体架构:前后端分离。Next.js应用作为前端兼API路由(可处理简单逻辑),核心后端服务为FastAPI。两者通过RESTful API通信。
  2. 数据流:用户从前端提交任务 -> FastAPI创建任务记录并发送到Redis队列 -> Celery worker从队列取出任务,执行“视频生成”模拟逻辑 -> 更新任务状态和结果文件路径 -> 前端通过轮询或WebSocket获取状态更新。
  3. 核心目录结构
    video-saas-platform/ ├── frontend/ # Next.js 14 应用 │ ├── app/ # App Router │ ├── components/ # 可复用组件 │ ├── lib/ # 工具函数,API客户端 │ └── ... ├── backend/ # FastAPI 应用 │ ├── app/ │ │ ├── api/ # 路由端点 │ │ ├── core/ # 配置、安全 │ │ ├── models/ # SQLAlchemy 模型 │ │ ├── schemas/ # Pydantic 模型 │ │ ├── services/ # 业务逻辑 │ │ └── workers/ # Celery 任务定义 │ ├── requirements.txt │ └── ... ├── docker-compose.yml # 用于启动PostgreSQL, Redis └── README.md
  4. 分阶段实施:它建议先搭建后端核心数据模型和API,再实现Celery异步任务,最后开发前端页面并与后端联调。

这个规划相当专业,甚至考虑到了开发顺序,减少了模块间的耦合。我认可了这个方案,并让它开始执行。

3. 后端核心:FastAPI模型、API与异步任务搭建

GLM-5首先从后端开始。它并没有一次性生成所有文件,而是按模块生成,并附上简要说明。

3.1 数据模型与Pydantic模式定义

它首先创建了backend/app/models.pybackend/app/schemas.py。模型设计紧扣我们的需求:

# backend/app/models.py from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum from app.core.database import Base class SubscriptionTier(str, enum.Enum): BASIC = "basic" PREMIUM = "premium" class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, index=True, nullable=False) hashed_password = Column(String, nullable=False) subscription_tier = Column(Enum(SubscriptionTier), default=SubscriptionTier.BASIC) is_active = Column(Boolean, default=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) tasks = relationship("VideoTask", back_populates="owner") class VideoTaskStatus(str, enum.Enum): PENDING = "pending" PROCESSING = "processing" COMPLETED = "completed" FAILED = "failed" class VideoTask(Base): __tablename__ = "video_tasks" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id")) title = Column(String, index=True) description = Column(String) # 用户输入的描述 status = Column(Enum(VideoTaskStatus), default=VideoTaskStatus.PENDING) source_files_path = Column(String) # 上传的素材路径,JSON字符串存储 output_video_path = Column(String, nullable=True) error_message = Column(String, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) owner = relationship("User", back_populates="tasks")

同时,它生成了对应的Pydantic模式,用于API请求/响应验证,并巧妙地处理了关系字段和枚举。例如,在VideoTaskCreate模式中,它排除了user_id(从token获取)和status等字段,确保了API的安全性。

3.2 核心API端点实现

接下来是backend/app/api/endpoints下的路由。GLM-5生成的代码不仅结构清晰,还包含了JWT认证、依赖注入等生产级实践。以创建视频任务端点为例:

# backend/app/api/endpoints/tasks.py from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks from sqlalchemy.orm import Session from app import crud, schemas, models from app.api import deps from app.workers.tasks import process_video_task router = APIRouter() @router.post("/", response_model=schemas.VideoTask) def create_video_task( *, db: Session = Depends(deps.get_db), task_in: schemas.VideoTaskCreate, current_user: models.User = Depends(deps.get_current_active_user), background_tasks: BackgroundTasks, ): """ 创建新的视频生成任务。 1. 检查用户订阅状态是否允许创建新任务(此处简化)。 2. 将任务信息存入数据库,状态为PENDING。 3. 将任务ID加入后台处理队列。 """ # 此处可添加业务逻辑,如检查用户套餐并发任务数限制 task = crud.video_task.create_with_owner(db=db, obj_in=task_in, owner_id=current_user.id) # 使用BackgroundTasks实现简易异步(适用于轻量任务,生产环境应用Celery) # background_tasks.add_task(process_video_task, task_id=task.id) # 但我们计划用Celery,所以这里仅记录日志,并通过API触发Celery任务 return task

这里出现了一个关键决策点:GLM-5最初使用了FastAPI的BackgroundTasks,但我在审核时指出,视频生成是重CPU/IO操作,BackgroundTasks适用于短时任务,且与进程绑定,不适合生产环境。GLM-5立刻理解了问题,并修改了方案,转向实现Celery。

3.3 Celery异步任务与“视频生成”模拟

这是后端最核心的部分。GLM-5创建了backend/app/workers/celery_app.py来配置Celery,以及backend/app/workers/tasks.py来定义任务。

# backend/app/workers/tasks.py import time import json import subprocess import shutil from pathlib import Path from celery import Celery from app.core.config import settings from app.core.database import SessionLocal from app import crud, models celery_app = Celery("worker", broker=settings.REDIS_URL, backend=settings.REDIS_URL) @celery_app.task(bind=True, name="process_video_task") def process_video_task(self, task_id: int): """ 模拟视频生成任务。 实际场景中,这里会: 1. 调用AI API生成脚本/图/视频。 2. 使用FFmpeg进行复杂的音视频合成。 此处我们模拟耗时过程,并生成一个假的视频文件。 """ db = SessionLocal() try: task = crud.video_task.get(db, id=task_id) if not task: self.update_state(state="FAILED", meta={"error": "Task not found"}) return # 更新状态为处理中 task = crud.video_task.update_status(db, db_obj=task, status=models.VideoTaskStatus.PROCESSING) db.commit() # 模拟AI处理时间 time.sleep(10) # 模拟调用AI服务生成脚本 # 模拟视频合成(这里只是用FFmpeg创建一个简单的测试视频) # 假设用户上传的素材路径是一个包含文件路径的JSON数组 source_files = json.loads(task.source_files_path) if task.source_files_path else [] output_dir = Path(settings.OUTPUT_VIDEO_DIR) / str(task.user_id) output_dir.mkdir(parents=True, exist_ok=True) output_path = output_dir / f"task_{task_id}_output.mp4" # 使用FFmpeg创建一个带字幕的测试视频(如果系统安装了ffmpeg) # 这是一个非常简化的示例,实际合成逻辑极其复杂 test_video_path = "/tmp/test_input.mp4" # 先创建一个测试用的视频文件(如果没有的话) if not Path(test_video_path).exists(): # 使用FFmpeg生成一个5秒的测试视频 cmd = [ "ffmpeg", "-f", "lavfi", "-i", "testsrc=duration=5:size=640x480:rate=30", "-vf", "drawtext=text='Generated by SaaS':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=(h-text_h)/2", "-c:v", "libx264", "-pix_fmt", "yuv420p", str(test_video_path) ] subprocess.run(cmd, capture_output=True) # 模拟复制“生成”的视频到输出目录 shutil.copy(test_video_path, output_path) # 更新任务状态和输出路径 task = crud.video_task.update_on_success( db, db_obj=task, status=models.VideoTaskStatus.COMPLETED, output_video_path=str(output_path) ) db.commit() return {"task_id": task.id, "status": task.status, "output_path": task.output_video_path} except Exception as e: # 更新任务状态为失败 if 'task' in locals() and task: task = crud.video_task.update_on_failure(db, db_obj=task, error_message=str(e)) db.commit() self.update_state(state="FAILED", meta={"error": str(e)}) raise finally: db.close()

这段代码的亮点在于,它没有停留在“打印日志”的模拟层面,而是真实地使用了FFmpeg命令来生成一个带测试图案和文字的视频文件,并将文件保存到指定目录。这极大地提升了Demo的真实感,也为我后续接入真正的AI视频生成API铺平了道路。同时,它完整实现了任务状态的生命周期管理(PENDING -> PROCESSING -> COMPLETED/FAILED),并考虑了异常处理和数据回滚。

4. 前端实现:Next.js与Tailwind构建用户界面

后端API就绪后,GLM-5开始转向前端。它使用Next.js 14的App Router,并采用了服务端组件(Server Components)和客户端组件(Client Components)混合的模式,这是当前比较推荐的做法。

4.1 项目初始化与API客户端

它首先生成了frontend/lib/api-client.ts,使用axios封装了与后端的通信,并统一处理了错误和请求头(如添加JWT Token)。

// frontend/lib/api-client.ts import axios from 'axios'; const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1', headers: { 'Content-Type': 'application/json', }, }); // 请求拦截器,用于添加认证token apiClient.interceptors.request.use( (config) => { const token = localStorage.getItem('access_token'); // 注意:服务端组件中不能直接用localStorage if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); } ); // ... 响应拦截器处理错误

这里它留下了一个注意点:在Next.js服务端组件中无法直接访问localStorage。它随后在需要认证的客户端组件中,通过useEffect和状态来安全地获取token。

4.2 核心页面:任务创建与列表

任务创建页面 (frontend/app/dashboard/tasks/new/page.tsx) 被设计为一个客户端组件。GLM-5生成了一个包含文件上传(使用input type="file"并配合FormData)、标题和描述输入的表单。

// 部分代码示例 'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; export default function NewTaskPage() { const [files, setFiles] = useState<File[]>([]); const [uploading, setUploading] = useState(false); const router = useRouter(); const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target.files) { setFiles(Array.from(e.target.files)); } }; const handleSubmit = async (formData: FormData) => { setUploading(true); try { // 1. 先上传文件到后端的上传接口,获取文件路径 const filePaths = await uploadFiles(files); // 2. 创建任务,将文件路径等信息提交 const taskData = { title: formData.get('title'), description: formData.get('description'), source_files_path: JSON.stringify(filePaths) }; await apiClient.post('/tasks/', taskData); router.push('/dashboard/tasks'); } catch (error) { alert('创建失败'); } finally { setUploading(false); } }; // ... JSX渲染表单 }

任务列表页面 (frontend/app/dashboard/tasks/page.tsx) 则被设计为服务端组件,初始数据在服务端获取,以提高首屏加载速度。同时,它包含了一个客户端组件TaskList,用于实现轮询以更新任务状态。

// frontend/app/dashboard/tasks/page.tsx (服务端组件) import { apiClient } from '@/lib/api-client'; import TaskList from './TaskList'; // 客户端组件 export default async function TasksPage() { // 在服务端获取初始任务列表 const initialTasks = await getTasksFromServer(); return ( <div> <h1>我的视频任务</h1> {/* 将初始数据传递给客户端组件 */} <TaskList initialTasks={initialTasks} /> </div> ); } // frontend/app/dashboard/tasks/TaskList.tsx (客户端组件) 'use client'; import { useEffect, useState } from 'react'; import { pollTaskStatus } from './actions'; // 模拟轮询动作 export default function TaskList({ initialTasks }) { const [tasks, setTasks] = useState(initialTasks); useEffect(() => { const intervalId = setInterval(async () => { // 仅轮询处理中的任务 const processingTasks = tasks.filter(t => t.status === 'processing'); for (const task of processingTasks) { const updatedTask = await pollTaskStatus(task.id); // 更新状态... } }, 5000); // 每5秒轮询一次 return () => clearInterval(intervalId); }, [tasks]); // ... 渲染任务列表 }

这种混合架构的运用,显示了GLM-5对现代前端框架最佳实践的了解。

4.3 文件上传与“模拟对象存储”

文件上传是一个关键且容易出错的点。GLM-5在后端实现了一个专门的上传端点,接收multipart/form-data,将文件保存到服务器本地目录(模拟对象存储),并返回文件的访问路径。它甚至考虑了文件类型验证、大小限制和防止文件名冲突。

# backend/app/api/endpoints/uploads.py @router.post("/") async def upload_file( file: UploadFile = File(...), current_user: models.User = Depends(deps.get_current_active_user), ): # 检查文件类型 allowed_types = ["image/jpeg", "image/png", "video/mp4", "video/quicktime"] if file.content_type not in allowed_types: raise HTTPException(status_code=400, detail="File type not allowed") # 生成唯一文件名 file_extension = Path(file.filename).suffix unique_filename = f"{uuid.uuid4()}{file_extension}" user_upload_dir = UPLOAD_DIR / str(current_user.id) user_upload_dir.mkdir(parents=True, exist_ok=True) file_location = user_upload_dir / unique_filename # 异步写入文件 async with aiofiles.open(file_location, "wb") as f: content = await file.read() await f.write(content) # 返回相对路径,便于存储到数据库 return {"file_path": str(file_location.relative_to(UPLOAD_DIR))}

5. 集成、部署与实测中的“坑”

当所有代码生成完毕,GLM-5还生成了docker-compose.yml用于一键启动PostgreSQL和Redis,以及详细的README.md说明如何安装依赖、设置环境变量和运行项目。

在按照指引启动服务后,我进行了一次端到端的测试。整个过程基本顺畅,但也遇到了几个需要人工干预的“坑”:

  1. 环境变量缺失:GLM-5生成的.env.example文件很全,但.env需要自己创建并填写。它没有自动生成包含随机密钥的.env文件。这是一个安全上的好习惯,但需要手动操作。
  2. FFmpeg依赖:后端Celery任务中使用了FFmpeg命令。如果宿主机没有安装FFmpeg,任务会失败。GLM-5在README中提到了这一点,但更好的做法是在Dockerfile中安装FFmpeg,或者使用包含FFmpeg的Python基础镜像。
  3. 跨域问题(CORS):前端运行在localhost:3000,后端在localhost:8000,直接请求会触发CORS错误。GLM-5在后端代码中已经配置了CORS中间件,但需要我确认前端NEXT_PUBLIC_API_URL环境变量设置正确,并且后端CORS允许了前端的源。
  4. 前端Token管理:如前所述,服务端组件中无法使用localStorage。GLM-5的解决方案是在客户端组件中通过useEffect获取,这可行。但对于更复杂的应用,可能需要引入状态管理(如Zustand)或考虑使用Next.js的中间件进行服务端认证。

在解决了上述问题后,我成功完成了整个流程:注册用户 -> 登录 -> 上传一个视频片段 -> 创建视频生成任务 -> 在任务列表看到任务状态从“pending”变为“processing”,最后变为“completed” -> 点击下载链接,成功获取到那个由FFmpeg生成的、带有“Generated by SaaS”文字的水印测试视频。

6. 总结与对“全栈长任务”能力的思考

整个项目从发出指令到最终跑通,耗时大约2小时50分钟,基本符合“3小时复刻”的目标。最终得到的不是一个“玩具”,而是一个架构清晰、代码规范、具备核心业务流程、可扩展的SaaS平台原型。它包含了用户系统、异步任务队列、文件上传、基础的前后端交互,甚至还有简单的“视频生成”模拟。

通过这次实测,我对GLM-5的“全栈长任务”能力有了几点深刻体会:

优势明显:

  1. 宏观架构能力:它能理解一个复杂项目的全貌,并做出合理的技术选型和模块划分。
  2. 代码生成质量高:生成的代码不仅仅是语法正确,还遵循了常见的框架最佳实践(如FastAPI的依赖注入、SQLAlchemy ORM模式、Next.js的混合渲染)。
  3. 上下文关联性强:在编写前端组件时,它能记住并调用之前生成的后端API接口和数据模式,保持前后端数据格式的一致性。
  4. 解决问题的能力:当我在审核中指出使用BackgroundTasks的局限性时,它能迅速理解问题,并切换到更合适的Celery方案。

当前的局限与人的价值:

  1. 环境与部署细节:它擅长生成应用代码,但对运行环境的具体配置(如系统级依赖FFmpeg、Docker网络配置、生产环境Web服务器配置)考虑不够周全,需要开发者查漏补缺。
  2. 复杂业务逻辑:对于极其复杂的业务规则(如不同订阅套餐的精确算费、视频合成的复杂算法),它只能生成框架和模拟代码,核心算法仍需人工实现或集成专业服务。
  3. 调试与排错:当集成运行出现错误时(如上述的CORS问题),它无法主动运行和调试,需要开发者根据错误信息定位问题,并可能需要对它生成的代码进行微调。

总而言之,GLM-5的“全栈长任务”能力像一个经验丰富、出活极快、但需要明确指导和最终质量把关的高级编程助手。它极大地压缩了从想法到原型的时间,将开发者从大量的样板代码和基础架构搭建中解放出来,使其能更专注于核心业务创新和复杂问题解决。对于想快速验证想法、构建MVP的独立开发者或小团队来说,这无疑是一个革命性的工具。这次“3小时复刻TikTok视频生成SaaS”的挑战,成功验证了这一点。下一步,我可以在这个原型的基础上,轻松地替换掉模拟的FFmpeg命令,接入真正的AI视频生成API,比如RunwayML或Pika Labs的接口,一个真正的AI视频生成SaaS就初具雏形了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询