基于 Django+Vue3 的 AI 海报生成平台开发(海报模块专项)
1、项目介绍
1.1、需求分析
1.1.1、功能性需求
从业务场景出发,AI 海报生成平台的核心需求围绕 “便捷、智能、可定制” 的海报创作全流程展开,具体包括:
- AI 辅助创作能力:提供提示词助手(多轮对话式 AI),帮助用户生成专业海报描述,降低创作门槛;支持通过 Coze 智能体、豆包视觉大模型实现 “文本→海报”“海报→优化海报” 的 AI 驱动创作。
- 海报生成功能:支持模板选择(热门、小红书、商业、节日等分类)、关键词推荐(如 “科技感未来城市”“简约风格插画”)、图片比例自定义(1:1、2:3、4:3 等),满足不同场景(社交分享、商业宣传、节日活动)的海报尺寸需求。
- 海报优化功能:支持海报内容修改(基于豆包视觉大模型)、画面清晰化、自定义添加 Logo 和二维码;允许上传本地海报进行二次编辑,覆盖 “生成→修改→完善” 的全链路需求。
- 数据持久化与隔离:用户生成的海报记录、对话会话需长期存储,且严格按用户隔离(通过用户 ID 关联数据),支持记录查询、筛选(按海报类型)、删除操作。
- 交互体验优化:提示词助手支持流式对话(实时返回 AI 回复),海报生成 / 修改过程显示加载状态,响应式布局适配 PC、平板、手机等设备。
1.1.2、非功能性需求
- 性能需求:AI 接口响应时间≤5s(流式对话支持增量返回,降低等待感知);图片上传大小限制≤5MB,避免占用过多带宽;页面加载时间≤2s。
- 兼容性需求:前端支持 Chrome、Edge、Safari 等主流浏览器;响应式适配PC端。
- 安全性需求:图片上传需过滤非图片格式文件;第三方 API 密钥(Coze、豆包)需通过环境变量或配置文件管理,避免硬编码;用户数据(海报记录、对话)需按用户 ID 隔离,防止越权访问。
- 可用性需求:操作流程直观(如模板 “一键使用”、关键词 “点击添加”);错误提示明确(如 “图片 URL 不能为空”“生成失败:网络错误”);支持本地图片拖拽上传,降低操作成本。
1.2、技术栈
围绕海报模块的开发需求,技术栈分为后端和前端两层,重点集成 AI 能力与前后端交互能力:
层面 | 技术 / 工具 | 用途说明 |
---|---|---|
后端核心 | Django 5.2.5 | Web 框架,提供请求路由、数据模型定义、中间件支持(如 CORS 跨域) |
Django REST Framework (DRF) | 构建 RESTful API,处理海报生成、对话等接口逻辑 | |
MySQL 8.0 | 存储用户海报记录(poster_records )、聊天会话(chat_sessions )等数据 | |
AI 能力集成 | Coze API | 提供工作流(海报生成)、智能体(提示词助手)能力 |
豆包视觉大模型(doubao-seededit) | 实现海报修改、画面优化的视觉生成能力 | |
前端核心 | Vue 3 (Composition API + TS) | 构建组件化、类型安全的前端界面 |
Element Plus | 提供模板选择、对话界面、表单等 UI 组件,支持响应式 | |
Pinia | 状态管理,存储海报模板、生成记录、对话历史等全局数据 | |
辅助工具 | Axios | 前端与后端 API 交互,处理流式响应、请求拦截(如添加 Token) |
Saaa | 实现响应式样式、渐变动画、自定义滚动条等视觉效果 | |
FreeImage.host + 代理服务器 | 处理本地图片上传,将本地文件转换为可访问的网络 URL |
1.3 项目运行效果图
(注:以下为功能页面描述,建议实际运行项目后截取对应截图插入)
海报生成页:
左侧为模板选择区(Tab 切换 “热门模板”“小红书海报” 等分类,模板卡片 hover 显示 “使用模板” 按钮),右侧为输入区(热门关键词标签、提示词文本框、比例选择器、“生成海报” 按钮),顶部为渐变风格标题栏(含 “智能生成”“多种模板” 等功能标识)。
提示词助手页:
左侧为会话列表(显示会话标题、最后一条消息预览、时间),右侧为聊天区(欢迎消息→用户输入→AI 流式回复,底部为输入框和发送按钮),支持会话新建、删除、切换。
3.修改海报页:
三栏布局 —— 左侧 “画面调整” 面板(修改描述输入、生成记录管理)、中间 “海报预览” 区(支持图片缩放、下载、取消预览)、右侧 “元素添加” 面板(Logo / 二维码上传、位置选择),支持本地海报拖拽上传。
1.4 项目目录结构
原项目目录已具备基础结构,但为提升可维护性,对海报相关目录进行优化(补充分层、明确职责),优化后结构如下:
1.4.1 后端目录(Django)

1.4.2 前端目录(Vue3)
2. 项目架构分析
2.1 架构图
2.2 架构图内容解析
(1)数据流向逻辑
- 用户操作→前端:用户在 “生成海报页” 选择模板、输入提示词,前端通过
posterApi.ts
将参数(prompt
、propotion
)发送给后端。 - 前端→后端:后端路由(
/api/posters/posters/
)将请求转发至PostersView
视图,视图调用CozeService
的run_coze_workflow()
方法,传入参数调用 Coze 工作流 API。 - 后端→第三方 AI:Coze 工作流生成海报 URL 后返回后端,后端将 URL 存入
poster_records
表(关联当前用户 ID),并将结果返回前端。 - 前端渲染:前端 Pinia 存储海报 URL,跳转至 “修改海报页” 预览,用户可进一步添加 Logo、二维码或优化画面。
(2)核心分层职责
前端层:负责交互与视觉渲染,通过 Pinia 管理全局状态(如模板列表、当前海报 URL),通过 Axios 处理请求(含流式响应,如提示词助手的实时回复)。
- 后端层:负责业务逻辑与数据管理,视图层处理请求参数校验,服务层封装 AI 接口调用,数据模型层确保用户数据隔离(如
PosterRecord
的user
外键关联SysUser
)。 - 第三方服务层:提供 AI 能力(Coze 生成海报、豆包优化画面)和图片存储(FreeImage.host),代理服务器解决图片上传的跨域问题。
3. 开发的主要功能分析(含代码、注释、效果图)
3.1 提示词助手
提示词助手是降低用户创作门槛的核心功能,基于 Coze 智能体实现多轮对话,支持会话记录持久化,核心解决 “用户不会写提示词” 的问题。
3.1.1 扣子(Coze)智能体的设计
核心逻辑:通过 Coze API 创建聊天机器人实例,支持文本交互与流式回复,封装CozeChatBot
类统一管理对话逻辑。
关键代码(后端services/coze_service.py
):
from cozepy import Coze, TokenAuth, Message, ChatEventType
from typing import List, Optional
import logging# 配置日志
logger = logging.getLogger(__name__)# Coze API配置(建议从环境变量读取,此处为示例)
COZE_API_TOKEN = 'pat_JfvLHMeGpuTHvi7tVNsFIrQs3xo4mbGQ45JCmNPHvR4Ziin7wwmUYul6jdINZg9m'
COZE_BASE_URL = 'https://api.coze.cn/v3'
BOT_ID = '7543072474888929280' # 预配置的海报提示词助手智能体IDclass CozeChatBot:def __init__(self, bot_id: str = BOT_ID, user_id: str = "default_user", conversation_history: List[Message] = None):"""初始化Coze聊天机器人:param bot_id: 智能体ID(Coze平台创建):param user_id: 用户ID(用于会话隔离):param conversation_history: 历史对话记录"""# 初始化Coze客户端self.coze_client = Coze(auth=TokenAuth(token=COZE_API_TOKEN),base_url=COZE_BASE_URL)self.bot_id = bot_idself.user_id = user_id# 对话历史(默认空列表)self.conversation_history: List[Message] = conversation_history or []def chat_stream(self, user_input: str):"""流式对话(核心!实时返回AI回复):param user_input: 用户输入的提示词需求(如“帮我写一个奶茶海报的提示词”):yield: AI回复的增量内容"""try:# 1. 构建用户消息(Coze要求的格式)user_message = Message.build_user_question_text(user_input)self.conversation_history.append(user_message)# 2. 调用Coze流式APIstream = self.coze_client.chat.stream(bot_id=self.bot_id,user_id=self.user_id,additional_messages=self.conversation_history # 携带历史对话)# 3. 处理流式响应bot_response = ""for event in stream:# 增量消息事件(实时返回每一段回复)if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:if event.message and event.message.content:bot_response += event.message.contentyield event.message.content # 增量返回给前端# 对话完成事件(记录Token使用)elif event.event == ChatEventType.CONVERSATION_CHAT_COMPLETED:logger.info(f"Token使用量: {event.chat.usage.token_count}")# 4. 保存AI回复到历史记录bot_message = Message.build_assistant_answer(bot_response)self.conversation_history.append(bot_message)except Exception as e:logger.error(f"流式对话失败: {str(e)}")yield f"错误: {str(e)}"
设计说明:
- 封装
CozeChatBot
类,屏蔽 Coze API 的细节,视图层只需调用chat_stream()
即可实现流式对话。 - 通过
user_id
参数关联当前登录用户,确保不同用户的对话历史隔离;conversation_history
参数支持多轮对话上下文关联。
3.1.2 多轮对话的实现
核心逻辑:前端通过流式 HTTP 响应接收 AI 增量回复,实时更新界面;后端通过ChatSession
(会话)和ChatMessage
(消息)模型管理多轮对话。
关键代码(前端views/poster/PosterChat.vue
):
// 发送流式消息
const sendStreamMessage = async (message: string) => {searchDataStore.isStreaming = true;searchDataStore.streamingMessage = "";try {// 调用后端流式接口const response = await fetch("/api/posters/chat/", {method: "POST",headers: {"Content-Type": "application/json","Authorization": `Bearer ${localStorage.getItem("token")}`},body: JSON.stringify({session_id: currentSessionId.value, // 会话ID(为空则新建)message: message,stream: true})});if (!response.ok) throw new Error("请求失败");// 处理流式响应const reader = response.body?.getReader();const decoder = new TextDecoder();let buffer = "";while (true) {const { done, value } = await reader!.read();if (done) break;// 解码二进制流const chunk = decoder.decode(value, { stream: true });buffer += chunk;const lines = buffer.split("\n");buffer = lines.pop() || "";// 解析每一行数据(SSE格式:data: {...})for (const line of lines) {if (line.startsWith("data: ")) {const data = JSON.parse(line.slice(6));if (data.type === "chunk") {// 增量更新AI回复searchDataStore.streamingMessage += data.content;await scrollToBottom(); // 滚动到底部} else if (data.type === "end") {// 对话完成,保存AI消息到本地状态searchDataStore.currentChatSession?.messages.push({id: data.message_id,message_type: "assistant",content: searchDataStore.streamingMessage,create_time: new Date().toISOString()});}}}}} catch (error: any) {ElMessage.error(`发送失败: ${error.message}`);} finally {searchDataStore.isStreaming = false;}
};
效果图描述:
用户输入 “帮我写一个咖啡店夏季海报的提示词” 后,输入框右侧显示 “发送中...”,AI 回复以 “打字机” 效果实时显示(如 “咖啡店夏季海报提示词建议:1. 风格:清新日系风,背景用浅薄荷绿... ”),无需等待完整回复,提升交互体验。
3.1.3 会话记录的长久储存与用户隔离
核心逻辑:通过ChatSession
(会话)和ChatMessage
(消息)模型关联用户 ID,实现 “用户→会话→消息” 的层级隔离;后端接口查询时过滤当前用户的会话,确保数据安全。
关键代码(后端models.py
):
from django.db import models
from user.models import SysUser
import uuidclass ChatSession(models.Model):"""聊天会话模型(用户→多会话)"""id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)user = models.ForeignKey(SysUser,on_delete=models.CASCADE,related_name="chat_sessions", # 反向查询:用户.chat_sessions.all()verbose_name="关联用户")title = models.CharField(max_length=200, default="新对话", verbose_name="会话标题")create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")update_time = models.DateTimeField(auto_now=True, verbose_name="最后更新时间")is_active = models.BooleanField(default=True, verbose_name="是否活跃")class Meta:db_table = "chat_sessions"ordering = ["-update_time"] # 按更新时间倒序(最新会话在前)class ChatMessage(models.Model):"""聊天消息模型(会话→多消息)"""MESSAGE_TYPE_CHOICES = [("user", "用户消息"), ("assistant", "AI回复")]id = models.AutoField(primary_key=True)session = models.ForeignKey(ChatSession,on_delete=models.CASCADE,related_name="messages", # 反向查询:会话.messages.all()verbose_name="所属会话")message_type = models.CharField(max_length=20, choices=MESSAGE_TYPE_CHOICES)content = models.TextField(verbose_name="消息内容")create_time = models.DateTimeField(auto_now_add=True, verbose_name="发送时间")class Meta:db_table = "chat_messages"ordering = ["create_time"] # 按发送时间正序(历史消息顺序)
查询逻辑(后端views.py
):
class ChatSessionDetailView(APIView):permission_classes = [IsAuthenticated] # 仅登录用户可访问def get(self, request, session_id):"""获取指定会话的消息(用户隔离)"""try:# 1. 过滤:仅查询当前用户的活跃会话session = ChatSession.objects.get(id=session_id,user=request.user, # 关键!用户隔离is_active=True)# 2. 获取会话下的所有消息messages = ChatMessage.objects.filter(session=session).order_by("create_time")# 3. 格式化返回message_list = [{"id": msg.id,"message_type": msg.message_type,"content": msg.content,"create_time": msg.create_time.strftime("%Y-%m-%d %H:%M:%S")} for msg in messages]return Response({"code": 200,"data": {"session_id": str(session.id), "messages": message_list}})except ChatSession.DoesNotExist:return Response({"code": 404, "message": "会话不存在"})
3.2 生成海报
3.2.1 扣子(Coze)工作流的设计
核心逻辑:通过 Coze 工作流串联 “参数接收→AI 生成→结果返回” 的流程,支持动态传入模板、提示词、比例等参数,无需后端编写复杂 AI 逻辑。
关键代码(后端services/coze_service.py
):
def run_coze_workflow(parameters: Dict[str, str]) -> Optional[str]:"""调用Coze工作流生成海报:param parameters: 工作流参数(img、prompt、propotion等):return: 生成的海报URL"""try:logger.info(f"执行Coze工作流,参数: {parameters}")# 1. 启动Coze工作流流式运行stream = coze_client.workflows.runs.stream(workflow_id="7541234832299802667", # Coze工作流IDparameters=parameters)# 2. 处理工作流事件流,提取海报URLreturn _handle_workflow_stream(stream)except Exception as e:logger.error(f"工作流执行失败: {str(e)}")raise Exception(f"生成海报失败: {str(e)}")def _handle_workflow_stream(stream: Stream[WorkflowEvent]) -> Optional[str]:"""处理工作流事件流,提取图片URL"""for event in stream:# 消息事件:工作流返回结果(含海报URL)if event.event == WorkflowEventType.MESSAGE:if event.message and event.message.content:try:# Coze工作流返回格式:{"output": "https://xxx.png"}content = json.loads(event.message.content)if "output" in content:return content["output"] # 返回海报URLexcept json.JSONDecodeError:logger.warning("解析工作流结果失败")# 错误事件:抛出异常elif event.event == WorkflowEventType.ERROR:error_msg = event.error.message if event.error else "未知错误"raise Exception(f"工作流出错: {error_msg}")# 未获取到URLraise Exception("工作流未返回海报URL")
Coze 工作流设计建议(补充优化):
在 Coze 平台创建工作流时,建议按以下节点设计:
- 参数接收节点:定义
img
(模板 URL)、prompt
(提示词)、propotion
(比例)等输入参数。 - AI 生成节点:调用 Coze 的 “图像生成” 能力,将参数注入生成 prompt(如 “根据模板 {img},按比例 {propotion},生成 {prompt} 风格的海报”)。
- 结果返回节点:将生成的图片 URL 按
{"output": "xxx"}
格式返回,便于后端解析。
3.2.2 模板和关键词
核心逻辑:前端通过 Pinia 存储模板数据和热门关键词,用户选择模板后自动填充对应的提示词,关键词支持 “点击添加”,降低输入成本。
关键代码(前端stores/posters.ts
):
// 热门关键词(优化后:按场景分类)
const hotKeywords = ref([{ label: "科技感", category: "风格" },{ label: "未来城市", category: "场景" },{ label: "简约插画", category: "风格" },{ label: "商业宣传", category: "用途" },// ... 更多关键词
]);// 海报模板数据(按类型分类)
const posterTemplates = ref({hot: [ // 热门模板{ id: "hot1", url: "/assets/images/posters/hot/hot1.png", prompt: "科技感未来城市海报,霓虹灯效果" },{ id: "hot2", url: "/assets/images/posters/hot/hot2.png", prompt: "简约风格产品宣传海报,清新色调" },],xiaohongshu: [ // 小红书模板{ id: "xh1", url: "/assets/images/posters/xiaohongshu/xh1.png", prompt: "小红书美食探店海报,暖色调,突出食物细节" },]
});// 选择模板:自动填充提示词
const selectTemplate = (template: PosterTemplate) => {searchValue.value = template.prompt; // 填充提示词currentTemplate.value = template; // 记录当前模板
};// 选择关键词:追加到提示词
const selectKeyword = (keyword: string) => {if (searchValue.value) {searchValue.value += ` ${keyword}`;} else {searchValue.value = keyword;}
};
效果图描述:
- 模板区域:以网格布局展示模板卡片,hover 时显示 “使用模板” 按钮,点击后右侧输入框自动填充该模板的默认提示词(如 “科技感未来城市海报,霓虹灯效果”)。
- 关键词区域:以标签云形式展示关键词,点击 “科技感” 后,输入框内容追加 “科技感”(如原内容为 “未来城市”,变为 “未来城市 科技感”)。
3.2.3 比例的选择
核心逻辑:支持 5 种常用海报比例(1:1、2:3、4:3、9:16、16:9),用户选择后将比例值传入后端,Coze 工作流按比例生成海报。
关键代码(前端components/poster/RatioSelector.vue
):
<template><el-selectv-model="selectedRatio"placeholder="选择海报比例"@change="handleRatioChange"><el-optionv-for="(item, key) in ratioOptions":key="key":label="`${item.label}(${item.value})`":value="key"><!-- 比例预览:小方块展示比例 --><div class="ratio-preview" :style="{ width: item.width, height: item.height }"></div></el-option></el-select>
</template><script setup lang="ts">
import { ref } from "vue";
import { useSearchDataStore } from "@/stores/posters.ts";const searchDataStore = useSearchDataStore();
const selectedRatio = ref("2"); // 默认2:3(竖版,适合小红书)// 比例配置(优化后:含预览尺寸)
const ratioOptions = ref({"1": { label: "正方形", value: "1:1", width: "40px", height: "40px" },"2": { label: "竖版照片", value: "2:3", width: "40px", height: "60px" },"3": { label: "标准照片", value: "4:3", width: "53px", height: "40px" },"4": { label: "长屏", value: "9:16", width: "22.5px", height: "40px" },"5": { label: "宽屏", value: "16:9", width: "71px", height: "40px" }
});// 比例变化:同步到Pinia
const handleRatioChange = (value: string) => {searchDataStore.setImageRatio(value, ratioOptions.value[value].value);
};
</script><style scoped>
.ratio-preview {display: inline-block;margin-right: 8px;border: 2px solid #667eea;border-radius: 4px;
}
</style>
3.3 修改海报
3.3.1 修改海报(豆包视觉大模型)
核心逻辑:用户上传原始海报 URL 和修改提示词(如 “将背景改为森林”),后端调用豆包视觉大模型(doubao-seededit-3-0-i2i-250628
)实现 “图生图” 优化。
关键代码(后端services/doubao_service.py
):
from volcenginesdkarkruntime import Ark
import os
import random
from dotenv import load_dotenv# 加载环境变量(优化:避免密钥硬编码)
load_dotenv()
ARK_API_KEY = os.environ.get("ARK_API_KEY")# 初始化豆包客户端
doubao_client = Ark(base_url="https://ark.cn-beijing.volces.com/api/v3",api_key=ARK_API_KEY
)def modify_poster(image_url: str, prompt: str) -> str:"""调用豆包视觉大模型修改海报:param image_url: 原始海报URL:param prompt: 修改提示词:return: 修改后的海报URL"""try:# 调用豆包图生图APIresponse = doubao_client.images.generate(model="doubao-seededit-3-0-i2i-250628", # 豆包视觉大模型IDprompt=prompt,image=image_url, # 原始图片(图生图)seed=random.randint(1, 10000), # 随机种子(避免重复)guidance_scale=8.5, # 引导力度(越高越贴近prompt)size="adaptive", # 自适应原始图片尺寸watermark=False # 不添加水印)# 提取结果if response.data and len(response.data) > 0:return response.data[0].urlraise Exception("豆包未返回修改后的图片")except Exception as e:raise Exception(f"修改海报失败: {str(e)}")
3.3.2 清晰化
核心逻辑:用户点击 “变清晰” 按钮时,后端不传入修改提示词,仅将原始海报 URL 传入 Coze 工作流,通过 AI 提升画面分辨率和清晰度。
关键代码(后端views.py
):
class PostersView(APIView):permission_classes = [IsAuthenticated]def post(self, request):# 获取参数img = request.data.get("img") # 原始海报URLprompt = request.data.get("prompt", "") # 默认为空(清晰化)# 1. 确定海报类型(清晰化:无prompt且有img)if img and not prompt:poster_type = "enhance" # 清晰化类型# 构造参数(仅传img,不传递prompt)parameters = {"img": img, "prompt": "提升画面清晰度,优化细节", "propotion": ""}else:# 其他类型(生成/修改)poster_type = "modify" if img else "generate"parameters = {"img": img, "prompt": prompt, "propotion": request.data.get("propotion")}# 2. 调用Coze工作流(清晰化)try:image_url = run_coze_workflow(parameters)# 3. 保存记录PosterRecord.objects.create(user=request.user,image_url=image_url,prompt="画面清晰化",poster_type=poster_type)return Response({"code": 200, "data": {"image_url": image_url}})except Exception as e:return Response({"code": 500, "message": f"清晰化失败: {str(e)}"})
3.3.3 添加 logo 和二维码
核心逻辑:用户上传 Logo / 二维码图片(本地或 URL),选择位置(左上、右上、右下等 9 个位置),后端将参数传入 Coze 工作流,实现元素叠加。
关键代码(前端components/poster/ElementAdd.vue
):
<template><!-- Logo上传 --><div class="element-upload"><h3>添加Logo</h3><el-uploadaction="/api/upload" # 后端上传接口(或代理服务器):on-success="handleLogoUpload":before-upload="checkImageSize"><div class="upload-area">点击上传Logo</div></el-upload><!-- 位置选择 --><el-select v-model="logoPosition" placeholder="选择Logo位置"><el-option label="左上" value="nw"></el-option><el-option label="右上" value="ne"></el-option><el-option label="右下" value="se"></el-option><!-- 其他位置 --></el-select></div><!-- 二维码上传(逻辑同上) --><div class="element-upload"><h3>添加二维码</h3><!-- 略... --></div><!-- 确认添加按钮 --><el-button @click="addElements">添加到海报</el-button>
</template><script setup lang="ts">
import { ref } from "vue";
import { useSearchDataStore } from "@/stores/posters.ts";
import { posterApi } from "@/api/posterApi.ts";const searchDataStore = useSearchDataStore();
const logoPosition = ref("nw"); // 默认左上
const qrPosition = ref("se"); // 默认右下
const logoUrl = ref("");
const qrUrl = ref("");// 检查图片大小(≤5MB)
const checkImageSize = (file: File) => {const isLt5M = file.size / 1024 / 1024 < 5;if (!isLt5M) {ElMessage.error("图片大小不能超过5MB");}return isLt5M;
};// Logo上传成功:保存URL
const handleLogoUpload = (response: any) => {logoUrl.value = response.data.image_url;
};// 添加元素到海报
const addElements = async () => {try {const result = await posterApi.addElements({img: searchDataStore.img_uml, // 当前海报URLlogo_img: logoUrl.value,logo_place: logoPosition.value,qr_img: qrUrl.value,qr_place: qrPosition.value});// 更新海报URLsearchDataStore.img_uml = result.data.image_url;ElMessage.success("元素添加成功");} catch (error) {ElMessage.error("添加失败");}
};
</script>
3.3.4 上传本地海报修改
核心逻辑:支持本地图片拖拽 / 点击上传,通过代理服务器(proxy-server.js
)上传到 FreeImage.host,转换为可访问的网络 URL,再进行修改。
关键代码(前端utils/imageUtils.ts
):
import { ElMessage } from "element-plus";export const uploadLocalImage = async (file: File): Promise<string> => {try {// 1. 检查文件类型if (!file.type.startsWith("image/")) {throw new Error("请上传图片文件");}// 2. 检查文件大小if (file.size > 5 * 1024 * 1024) {throw new Error("图片大小不能超过5MB");}// 3. 构造FormData(FreeImage.host要求的格式)const formData = new FormData();formData.append("key", "6d207e02198a847aa98d0a2a901485a5"); // FreeImage API KeyformData.append("source", file);formData.append("format", "json");// 4. 调用代理服务器(解决跨域)const response = await fetch("http://localhost:3000/upload", {method: "POST",body: formData});const data = await response.json();// 5. 提取网络URLif (data.status_code === 200 && data.image?.url) {return data.image.url;}throw new Error("上传失败: " + data.error?.message);} catch (error: any) {ElMessage.error(error.message);throw error;}
};
代理服务器作用:
FreeImage.host 的 API 存在跨域限制(前端无法直接调用),proxy-server.js
作为中间层,接收前端请求后转发至 FreeImage.host,再将结果返回前端,解决跨域问题。
3.3.5 生成记录的长久储存和用户隔离
核心逻辑:通过PosterRecord
模型关联用户 ID,存储海报 URL、提示词、类型等信息;前端支持按类型筛选(生成 / 修改 / 清晰化),用户仅能查看自己的记录。
关键代码(后端views.py
):
class PosterRecordsView(APIView):permission_classes = [IsAuthenticated]def get(self, request):"""获取用户的海报记录(支持筛选)"""# 1. 获取筛选参数poster_type = request.GET.get("poster_type") # 类型筛选search = request.GET.get("search") # 关键词搜索(提示词)# 2. 基础查询:当前用户的未删除记录queryset = PosterRecord.objects.filter(user=request.user,is_deleted=False)# 3. 应用筛选条件if poster_type:queryset = queryset.filter(poster_type=poster_type)if search:queryset = queryset.filter(prompt__icontains=search) # 模糊搜索# 4. 格式化返回records = [{"id": record.id,"image_url": record.image_url,"prompt": record.prompt,"poster_type": record.poster_type,"poster_type_display": record.get_poster_type_display(), # 中文显示(如“生成海报”)"create_time": record.create_time.strftime("%Y-%m-%d %H:%M:%S")} for record in queryset.order_by("-create_time")]return Response({"code": 200,"data": {"records": records,"total_count": len(records)}})
3.3.6 本地上传图片转换图片地址
核心逻辑:通过 FreeImage.host 的 API 将本地图片转换为网络 URL,前端调用代理服务器上传,后端存储转换后的 URL 用于后续修改。
关键代码(后端utils/image_utils.py
):
import requestsdef convert_local_image_to_url(file_path: str) -> str:"""本地图片文件转换为网络URL(后端备用方案,前端优先用代理):param file_path: 本地图片路径:return: 网络URL"""url = "https://freeimage.host/api/1/upload"data = {"key": "6d207e02198a847aa98d0a2a901485a5","format": "json"}files = {"source": open(file_path, "rb")}response = requests.post(url, data=data, files=files)response_data = response.json()if response_data["status_code"] == 200:return response_data["image"]["url"]raise Exception(f"图片转换失败: {response_data['error']['message']}")
4. 项目总结
4.1 核心功能实现
功能模块 | 实现要点 |
---|---|
提示词助手 | 基于 Coze 智能体的流式多轮对话、会话记录用户隔离、持久化存储 |
海报生成 | Coze 工作流驱动、模板 / 关键词辅助、多比例支持 |
海报修改 | 豆包视觉大模型(图生图)、画面清晰化、Logo / 二维码添加、本地图片上传转换 |
记录管理 | 海报记录 / 会话记录的用户隔离、筛选(类型 / 关键词)、删除 |
4.2 技术亮点
- 多 AI 服务协同:集成 Coze(工作流 + 智能体)和豆包(视觉大模型),分别负责 “海报生成 / 对话” 和 “海报修改”,优势互补。
- 流式交互体验:提示词助手采用流式响应,AI 回复实时增量显示,降低用户等待感知;海报生成 / 修改过程显示加载状态,提升交互透明度。
- 响应式设计:通过 SCSS 的
@media
查询适配多设备(PC / 平板 / 手机),模板卡片、输入区布局自动调整,确保移动端可用性。 - 数据安全隔离:所有用户数据(海报记录、对话)通过外键关联用户 ID,接口层强制过滤当前用户数据,防止越权访问。
4.3 存在的挑战和解决方案
问题一:图片资源访问限制与解决方案
一、问题背景
在使用“扣子工作流”处理图片时,系统对图片资源的访问存在明确限制:仅支持通过网络地址(URL)访问图片资源。然而,在开发环境中,由于开发者通常未部署正式服务器,上传的图片地址多为本地路径(如本地文件路径或本地服务地址),这些地址不具备公网访问能力,因此无法被“扣子工作流”识别和使用,从而影响业务流程的正常运行。
二、解决方案
为解决上述问题,可采取以下两种方案:
(一)临时过渡方案:使用第三方图片托管服务
在开发阶段,可临时采用第三方图片托管服务作为解决方案。例如,使用 freeimage.host 等服务,通过其提供的 API 接口,将本地图片上传至第三方服务器,由其托管并生成可公网访问的图片 URL。此方式可快速解决图片资源无法被“扣子工作流”识别的问题,保障开发与测试阶段的流程顺畅。
(二)项目化正式方案
根据项目规模和需求,可选择以下长期解决方案,以确保图片资源的稳定存储、高效访问及业务扩展性:
①小型项目:
采用 静态文件服务 + Nginx 方案,适合小型项目,满足基本的图片存储与访问需求。②中型项目:
采用 云存储服务,如 AWS S3 或 阿里云 OSS,提供稳定、高效的存储与访问能力。③大型项目:
采用 云存储 + CDN 加速方案,结合内容分发网络(CDN)提升访问速度与稳定性,适用于大规模项目。问题二
问题背景:
前端需要上传图片到第三方服务(freeimage.host),但直接调用会遇到浏览器CORS跨域限制,严重影响用户体验。解决方案
①架构流程:
前端 ---> 本地代理服务器 ---> 第三方图片托管服务(freeimage.host) ---> 返回公网URL
②原理解释:
浏览器直接发送请求到freeimage存在跨域问题,改用浏览器发送请求到代理服务器,代理服务器和freeimage直接实现端对端连接,解决了跨越问题。
问题三
一、问题背景:
生成海报的记录以及聊天会话记录的用户隔离和长久保存,需要支持多轮对话,保持上下文信息,同时要高效管理大量的会话数据。
二、解决方案:
①数据模型设计:
User和ChatSession以及ChatMessage的关联设计,UUID确保会话唯一性,软删除和数据归档策略。
问题四
问题背景:
需要同时集成Coze智能体和Coze工作流和豆包两个不同的AI平台,它们的API调用方式、参数格式、响应结构都完全不同。
解决方案:
①模块化设计架构:
为每个AI服务创建独立工具类,CozeTool.py:封装Coze工作流和聊天机器人,doubaoTool.py:封装豆包图像编辑API。
问题五
问题背景:
AI生成海报和聊天对话都需要实时反馈,传统的请求-响应模式无法满足用户体验要求。
二、解决方案:
①流式响应架构:
使用StreamingHttpResponse实现流式输出,Server-Sent Events (SSE) 数据格式。
②连接管理优化:
连接超时和心跳机制,客户端断线检测,资源自动清理。
问题一:图片资源访问限制与解决方案
一、问题背景
在使用“扣子工作流”处理图片时,系统对图片资源的访问存在明确限制:仅支持通过网络地址(URL)访问图片资源。然而,在开发环境中,由于开发者通常未部署正式服务器,上传的图片地址多为本地路径(如本地文件路径或本地服务地址),这些地址不具备公网访问能力,因此无法被“扣子工作流”识别和使用,从而影响业务流程的正常运行。
二、解决方案
为解决上述问题,可采取以下两种方案:
(一)临时过渡方案:使用第三方图片托管服务
在开发阶段,可临时采用第三方图片托管服务作为解决方案。例如,使用 freeimage.host 等服务,通过其提供的 API 接口,将本地图片上传至第三方服务器,由其托管并生成可公网访问的图片 URL。此方式可快速解决图片资源无法被“扣子工作流”识别的问题,保障开发与测试阶段的流程顺畅。
(二)项目化正式方案
根据项目规模和需求,可选择以下长期解决方案,以确保图片资源的稳定存储、高效访问及业务扩展性:
①小型项目:
采用 静态文件服务 + Nginx 方案,适合小型项目,满足基本的图片存储与访问需求。②中型项目:
采用 云存储服务,如 AWS S3 或 阿里云 OSS,提供稳定、高效的存储与访问能力。③大型项目:
采用 云存储 + CDN 加速方案,结合内容分发网络(CDN)提升访问速度与稳定性,适用于大规模项目。问题二
问题背景:
前端需要上传图片到第三方服务(freeimage.host),但直接调用会遇到浏览器CORS跨域限制,严重影响用户体验。解决方案
①架构流程:
前端 ---> 本地代理服务器 ---> 第三方图片托管服务(freeimage.host) ---> 返回公网URL
②原理解释:
浏览器直接发送请求到freeimage存在跨域问题,改用浏览器发送请求到代理服务器,代理服务器和freeimage直接实现端对端连接,解决了跨越问题。
问题三
一、问题背景:
生成海报的记录以及聊天会话记录的用户隔离和长久保存,需要支持多轮对话,保持上下文信息,同时要高效管理大量的会话数据。
二、解决方案:
①数据模型设计:
User和ChatSession以及ChatMessage的关联设计,UUID确保会话唯一性,软删除和数据归档策略。
问题四
问题背景:
需要同时集成Coze智能体和Coze工作流和豆包两个不同的AI平台,它们的API调用方式、参数格式、响应结构都完全不同。
解决方案:
①模块化设计架构:
为每个AI服务创建独立工具类,CozeTool.py:封装Coze工作流和聊天机器人,doubaoTool.py:封装豆包图像编辑API。
问题五
问题背景:
AI生成海报和聊天对话都需要实时反馈,传统的请求-响应模式无法满足用户体验要求。
二、解决方案:
①流式响应架构:
使用StreamingHttpResponse实现流式输出,Server-Sent Events (SSE) 数据格式。
②连接管理优化:
连接超时和心跳机制,客户端断线检测,资源自动清理。
5. 未来展望
5.1 功能扩展
- AI 风格推荐:基于用户历史生成记录,推荐相似风格的模板或提示词(如 “您之前喜欢科技感风格,推荐以下模板”)。
- 海报协作:支持多用户共同编辑一张海报(如设计师生成初稿,运营添加 Logo),通过 WebSocket 实现实时同步。
- 风格迁移:新增 “风格迁移” 功能(如 “将海报转换为梵高画风”),集成更多视觉大模型(如 Stable Diffusion)。
- 批量生成:支持批量上传提示词,一次性生成多张海报(如 “生成 10 张不同节日的促销海报”),提升效率。
5.2 性能与体验优化
- 缓存策略:对热门模板、用户常用比例的生成结果进行缓存(如 Redis),减少 AI 接口调用次数,提升响应速度。
- 图片优化:生成的海报自动压缩(如使用
pillow
库),降低图片加载时间;支持 WebP 格式,平衡画质与大小。 - 离线体验:通过 PWA 技术实现部分离线功能(如查看历史记录、编辑提示词),提升弱网环境可用性。
- 操作引导:新增新手引导流程(如 “点击模板→输入提示词→生成海报”),降低新用户学习成本。
5.3 生态和商业化
- 模板市场:推出付费模板(如设计师定制模板),支持模板创作者入驻,平台抽成,形成生态闭环。
- 会员体系:免费用户限制生成次数,会员用户解锁高清下载、批量生成、高级 AI 模型(如豆包 Pro)等特权。
- 企业版功能:针对企业用户推出 “品牌模板库”(统一企业 VI 风格)、“团队协作”、“海报数据统计”(如曝光量、下载量)。
- API 开放:将海报生成 / 修改能力封装为开放 API,供第三方平台调用(如电商平台集成海报生成功能),实现商业化变现。
5.4 技术探索
端侧 AI 集成:探索在前端集成轻量化 AI 模型(如 TensorFlow.js),实现简单的海报风格预览(如 “本地预览科技感风格”),减少服务器依赖。
- 多模态输入:支持语音输入提示词(集成科大讯飞语音识别)、手绘草图生成海报(如用户手绘布局,AI 生成完整海报),扩展输入方式。
- AI 自动化:实现 “一键生成全套海报”,用户输入活动主题(如 “618 促销”),AI 自动生成不同平台(小红书、朋友圈、电商)的海报,无需人工调整。
6. 总结和收获
6.1 总结
本项目基于 Django+Vue3 构建了 “AI 驱动的海报创作平台”,聚焦海报 “生成→修改→管理” 的全流程需求,通过 Coze 和豆包 AI 服务降低创作门槛,通过响应式设计和流式交互提升用户体验,通过数据隔离确保用户数据安全。项目不仅实现了核心功能,还通过分层设计(如后端services
、前端api
)提升了代码可维护性,为后续扩展奠定基础。
6.2 收获
- 前后端协作能力:深入理解前后端分离架构下的数据流向(如前端请求→后端视图→AI 服务→数据存储→前端渲染),掌握 Axios 请求封装、DRF 视图设计的最佳实践。
- AI API 集成经验:熟悉 Coze、豆包等 AI 平台的 API 调用方式,理解工作流、智能体、视觉大模型的应用场景,学会处理 API 异常和跨域问题。
- 用户体验设计意识:从 “流式响应”“模板辅助”“错误提示” 等细节出发,体会 “技术服务于体验” 的理念,提升产品化思维。
- 问题解决能力:面对跨域、流式响应、图片上传等挑战,通过查阅文档(Django/Coze API)、调试代码(如流式响应的 SSE 格式),培养独立解决技术问题的能力。
通过本项目,不仅掌握了 AI 驱动的 Web 应用开发流程,还意识到 “功能实现” 与 “用户体验”“可扩展性” 的平衡,为后续复杂项目开发积累了宝贵经验。