
简介这是一套完整的智能AI创作全栈服务系统源码面向高校学生、开发者及AI应用实践者解决多模态内容生成文本、图像、音乐的一站式网页化部署需求适用于毕业设计、课程设计、大创项目及技术学习复刻。资源包共2002个文件以1913个JavaScript核心逻辑文件为主辅以81个CSS样式文件、3个JSON配置文件及少量HTML与Markdown文档总大小104.31MB结构清晰覆盖Web、H5、小程序及App多端适配层。已有88人下载学习项目经实测功能完整答辩平均分达96分附带详细说明文档与可运行工程环境。用户可直接复现ChatGPT对话、Midjourney风格绘画、Suno音乐生成三大能力模块代码已按功能分层组织如登录鉴权、API代理、前端渲染、模型调用封装等并预留扩展接口便于二次开发与功能增强是难得的高质量AI集成实践范例。1. 这不是“一键部署AI全家桶”而是面向生产环境的多模态服务集成方案很多人看到“ChatGPTMidjourneySuno网页服务”这个标题第一反应是找一个开箱即用的压缩包解压运行——但现实是.zip 里不可能封装真正可用的 AI 服务链路。ChatGPT 官方 API 不开放图像生成Midjourney 无公开 HTTP 接口Suno 尚未开放商用 SDK三者在协议层、鉴权机制、资源调度模型上完全异构。所谓“web小程序h5app端”也绝非简单套壳而是需分别处理 Web 端的 CORS 与 Service Worker 兼容性、小程序的 wx.request 域名白名单与 code2Session 链路、H5 在 iOS WebView 中的 UA 识别与音频自动播放策略、App 端的原生桥接与离线缓存策略。本方案不提供虚假“免配置包”而是基于真实工程约束给出可落地的四端统一接入架构用反向代理收敛 AI 调用入口用 JWT OAuth2.0 混合鉴权隔离用户会话用 WebSocket 流式透传多模态响应并针对微信小程序wx.uploadFile上传限制、Suno 音频生成超时重试、Midjourney 图像 URL 有效期不足 24 小时等高频问题设计兜底逻辑。适合已有 Node.js/Python 后端能力、需快速构建 AI 创作类 SaaS 产品的技术负责人或全栈工程师。2. 构建统一 AI 网关用 Nginx 反向代理 FastAPI 聚合三层异构服务2.1 为什么必须自建网关直连官方服务的三大硬伤直接在前端调用 ChatGPT、Midjourney、Suno 的公开接口如https://api.openai.com/v1/chat/completions看似简单但在实际项目中会立即触发三类不可绕过的问题跨域与证书问题ChatGPT 官方 API 强制 HTTPS 且校验 OriginWeb 端直连必然触发CORS policy: No Access-Control-Allow-Origin header小程序wx.request对域名有严格白名单且不支持自定义 header如Authorization: Bearer sk-xxx无法携带密钥密钥安全暴露风险前端硬编码 API Key 违反最小权限原则一旦被反编译或抓包Key 泄露即等于账户失控协议不一致导致状态难管理ChatGPT 返回 JSON 流text/event-streamMidjourney 依赖 Discord webhook 回调Suno 使用轮询GET /v1/audio/{id}获取结果——三者响应模型、错误码、重试逻辑完全不同前端需维护三套状态机代码耦合度爆炸。因此必须通过后端网关做协议归一化。我们选用 FastAPIPython作为核心聚合层因其原生支持流式响应StreamingResponse、异步 HTTP 客户端httpx.AsyncClient、以及与 Nginx 的无缝协作能力。2.2 FastAPI 网关核心实现统一/api/v1/generate接口# app/main.py from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from pydantic import BaseModel import httpx import json import asyncio app FastAPI(titleAI Creative Gateway, version1.0.0) oauth2_scheme OAuth2PasswordBearer(tokenUrltoken) class GenerateRequest(BaseModel): type: str # text, image, audio prompt: str model: str gpt-4-turbo # 仅对 text 生效 style: str realistic # 仅对 image 生效 duration: int 30 # 仅对 audio 生效单位秒 app.post(/api/v1/generate) async def generate( req: GenerateRequest, token: str Depends(oauth2_scheme) ): # 1. 校验 JWT Token 并提取用户 ID此处省略 decode 实现 user_id verify_jwt_token(token) # 2. 根据 type 分发请求 if req.type text: async with httpx.AsyncClient() as client: resp await client.post( https://api.openai.com/v1/chat/completions, headers{ Authorization: fBearer {get_user_api_key(user_id)}, Content-Type: application/json }, json{ model: req.model, messages: [{role: user, content: req.prompt}], stream: True # 关键保持流式 }, timeout60 ) if resp.status_code ! 200: raise HTTPException(status_coderesp.status_code, detailresp.text) # 3. 将 OpenAI SSE 流转换为标准 JSONL 流 return StreamingResponse( convert_openai_sse_to_jsonl(resp.aiter_bytes()), media_typeapplication/x-ndjson ) elif req.type image: # Midjourney 无官方 API此处模拟通过 Discord Bot 提交任务 # 实际需部署 mj-bot 并监听 webhook此处仅返回任务 ID task_id await submit_to_midjourney_bot(req.prompt, req.style, user_id) return {task_id: task_id, status: queued, expires_in: 3600} elif req.type audio: # Suno API 调用示例需申请测试 Key async with httpx.AsyncClient() as client: resp await client.post( https://api.suno.ai/api/v1/generate, headers{Authorization: fBearer {get_suno_api_key(user_id)}}, json{prompt: req.prompt, duration: req.duration}, timeout120 ) if resp.status_code 201: return resp.json() # 返回 {id: ..., status: submitted} else: raise HTTPException(status_coderesp.status_code, detailresp.text) else: raise HTTPException(status_code400, detailUnsupported type)提示convert_openai_sse_to_jsonl函数需手动解析data: {...}\n\n格式剥离event:和id:字段每行输出一个 JSON 对象如{delta: {content: Hello}}。这是前端 JavaScriptfetch().body.getReader()能直接消费的标准格式。2.3 Nginx 配置解决 Web 端 Service Worker 注册失败与跨域问题常见报错error: could not register service worker: invalidstatee根本原因是 Service Worker 要求页面必须通过 HTTPS 或 localhost 提供且scope路径需匹配。Nginx 必须确保静态资源与 API 同源# /etc/nginx/sites-enabled/ai-creative.conf upstream ai_gateway { server 127.0.0.1:8000; # FastAPI 运行端口 } server { listen 443 ssl http2; server_name creative.example.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; # 关键所有 /api/* 请求代理到网关 location /api/ { proxy_pass https://ai_gateway; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; # 解决流式响应中断问题 proxy_buffering off; proxy_cache off; proxy_read_timeout 300; } # 静态资源Vue/React 构建产物 location / { root /var/www/creative-web/dist; try_files $uri $uri/ /index.html; add_header Service-Worker-Allowed /; } # 小程序 H5 版本专用路径避免与主站冲突 location /mini/ { alias /var/www/creative-mini/dist/; try_files $uri $uri/ /index.html; } }注意add_header Service-Worker-Allowed /;是注册 Service Worker 的必要条件否则浏览器拒绝注册。同时proxy_buffering off确保 SSE 流不被 Nginx 缓存截断。3. 四端适配Web、小程序、H5、App 的差异化实现策略3.1 Web 端用 Fetch ReadableStream 处理流式文本与轮询图像/音频Web 端需同时支持 ChatGPT 的实时流式输出和 Midjourney/Suno 的异步轮询。关键在于状态分离// web/src/composables/useAI.js export function useAI() { const generate async (type, prompt) { const controller new AbortController(); const response await fetch(/api/v1/generate, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${localStorage.getItem(token)} }, body: JSON.stringify({ type, prompt }), signal: controller.signal }); if (type text) { // 流式读取 const reader response.body.getReader(); while (true) { const { done, value } await reader.read(); if (done) break; const chunk new TextDecoder().decode(value); // 解析 JSONL按换行分割每行 JSON.parse() chunk.split(\n).filter(line line.trim()).forEach(line { try { const data JSON.parse(line); if (data.delta?.content) { console.log(AI says:, data.delta.content); } } catch (e) { /* 忽略解析失败 */ } }); } } else { // 非流式获取 task_id 后轮询 const result await response.json(); if (type image) { await pollMidjourneyResult(result.task_id); } else if (type audio) { await pollSunoResult(result.id); } } }; const pollMidjourneyResult async (taskId) { let attempts 0; const maxAttempts 20; while (attempts maxAttempts) { const res await fetch(/api/v1/task/${taskId}); const data await res.json(); if (data.status finished) { console.log(Image ready:, data.url); return data.url; } else if (data.status failed) { throw new Error(data.error); } await new Promise(r setTimeout(r, 3000)); attempts; } throw new Error(Timeout waiting for Midjourney result); }; return { generate }; }3.2 微信小程序端规避wx.request域名限制与 SessionKey 安全传递小程序无法直接调用https://api.openai.com必须走自己域名。更关键的是用户登录态不能依赖 Cookie小程序不支持必须用code2Session 自定义登录态// mini/pages/index/index.js Page({ data: { prompt: }, async onSubmit() { // 1. 获取临时登录凭证 const { code } await wx.login(); // 2. 用 code 换取自定义 token后端实现 const loginRes await wx.request({ url: https://creative.example.com/api/v1/login, method: POST, data: { code }, success: (res) { if (res.data.token) { wx.setStorageSync(token, res.data.token); this.generate(); } } }); }, async generate() { const token wx.getStorageSync(token); // 3. 所有请求带 Authorization Header const res await wx.request({ url: https://creative.example.com/api/v1/generate, method: POST, header: { Authorization: Bearer ${token} }, data: { type: text, prompt: this.data.prompt } }); // 4. 小程序不支持 ReadableStream文本用普通 JSON图像/音频用轮询 if (res.data.task_id) { this.pollTask(res.data.task_id); } }, pollTask(taskId) { wx.request({ url: https://creative.example.com/api/v1/task/${taskId}, success: (res) { if (res.data.status finished) { this.setData({ imageUrl: res.data.url }); } else { setTimeout(() this.pollTask(taskId), 3000); } } }); } });注意小程序wx.request默认不发送 Cookie所有鉴权必须通过header传递Authorization。后端需在Access-Control-Allow-Headers中显式声明Authorization。3.3 H5 与 App 端WebView 与原生桥接的关键差异点H5 页面如/mini/路径运行在微信内置浏览器或手机 Safari 中需额外处理iOS Safari 自动播放限制Suno 生成的音频需用户手势触发如点击按钮才能play()UA 识别分流通过navigator.userAgent判断是否为微信环境加载不同 SDK如微信 JS-SDK 调用wx.downloadFile保存图片Android WebView 缓存策略强制禁用Cache-Control: no-cache避免旧版 JS 脚本残留。App 端iOS/Android需原生桥接iOS用WKWebView的evaluateJavaScript注入 token并监听window.postMessage接收结果Android通过JavascriptInterface暴露saveImage(url)方法由前端 JS 调用共性所有端必须实现离线队列—— 当网络中断时将用户输入暂存localStorage或SQLite恢复后自动重发。4. 生产级健壮性设计超时控制、降级策略与审计日志4.1 三层超时控制防止请求堆积与资源耗尽AI 服务响应时间波动极大ChatGPT 通常 2~10sMidjourney 1~5minSuno 30~120s必须分层设置超时组件超时值作用配置位置Nginxproxy_read_timeout300s防止长连接空闲断开nginx.confFastAPIhttpx.AsyncClient(timeout...)text:60s, image:300s, audio:120s控制下游请求时限Python 代码中前端轮询setTimeout(..., 3000)3s 间隔 × 20次 60s防止无限轮询JavaScript特别注意Midjourney 任务实际可能需 5 分钟以上前端轮询应采用指数退避3s → 6s → 12s → 24s而非固定间隔。4.2 服务降级当某 AI 服务不可用时的优雅 fallback不能因 Midjourney 故障导致整个创作流程中断。FastAPI 网关需实现熔断# app/fallback.py from circuitbreaker import circuit circuit(failure_threshold5, recovery_timeout60) async def call_midjourney(prompt, style): # 正常调用 Discord Bot pass app.post(/api/v1/generate) async def generate(req: GenerateRequest): if req.type image: try: return await call_midjourney(req.prompt, req.style) except CircuitBreakerError: # 降级返回占位图 提示 return { url: /static/fallback-image.png, status: fallback, message: Midjourney 服务繁忙请稍后再试 }4.3 审计日志记录每个请求的 token 消耗与生成质量AI 成本监控是生产系统刚需。在 FastAPI 中注入日志中间件# app/middleware.py from fastapi import Request, Response import time import logging logger logging.getLogger(ai_audit) app.middleware(http) async def log_requests(request: Request, call_next): start_time time.time() response await call_next(request) process_time time.time() - start_time # 提取关键字段 user_id request.state.user_id if hasattr(request.state, user_id) else unknown req_body await request.body() try: data json.loads(req_body) model data.get(model, unknown) prompt_len len(data.get(prompt, )) except: model unknown prompt_len 0 logger.info( fUSER:{user_id} TYPE:{request.query_params.get(type,unknown)} fMODEL:{model} PROMPT_LEN:{prompt_len} fSTATUS:{response.status_code} TIME:{process_time:.2f}s ) return response日志格式示例INFO:ai_audit:USER:u_abc123 TYPE:image MODEL:unknown PROMPT_LEN:42 STATUS:200 TIME:124.35s该日志可直接接入 ELK 或 Grafana绘制「每用户日均调用量」「各模型平均延迟」「错误率 Top3 接口」看板。5. 小程序备案与 Web 工程部署的实操细节5.1 小程序备案备注信息填写规范非技术但致命微信小程序上架前必须完成备案备案备注信息不是可有可无的描述而是审核关键项。根据最新《互联网信息服务算法备案规定》需明确说明 AI 功能边界正确写法本小程序提供 AI 辅助创作服务包括① 基于用户输入文本生成回复使用 OpenAI GPT-4 Turbo 模型② 根据文本提示生成图像调用 Midjourney Discord Bot 服务③ 根据文本提示生成背景音乐调用 Suno AI API。所有生成内容均由用户主动触发不存储用户原始输入生成结果仅临时缓存 24 小时。禁止写法使用 ChatGPT、Midjourney、Suno 技术未说明具体用途易被判定为“未备案算法”AI 自动创作模糊表述未体现用户主动控制免费体验 AI 绘画“免费”可能触发资质审查需同步提交《增值电信业务许可证》。提示备案时需同步上传《算法安全评估报告》重点描述「内容过滤机制」——例如在 FastAPI 网关中加入敏感词检测中间件对prompt字段调用腾讯云TextModerationAPI命中则返回HTTP 400并记录日志。5.2 Nginx 部署多个 Web 项目避免端口冲突与静态资源覆盖一个服务器常需同时托管 Web 前端、小程序 H5 版、管理后台。Nginx 需按路径隔离# 主站Web location / { root /var/www/web/dist; try_files $uri $uri/ /index.html; } # 小程序 H5 版独立路径 location /mini/ { alias /var/www/mini/dist/; try_files $uri $uri/ /index.html; } # 管理后台假设用 Vue Admin location /admin/ { alias /var/www/admin/dist/; try_files $uri $uri/ /admin/index.html; } # API 接口统一前缀 location /api/ { proxy_pass http://127.0.0.1:8000; # ... 其他 proxy 设置 }关键点alias与root区别alias /path/表示将/mini/xxx映射到/path/xxxroot /path表示将/xxx映射到/path/xxxtry_files必须指定 fallback 文件如/index.html否则 Vue Router 的 history 模式会 404所有静态路径末尾必须加/如/mini/否则alias会拼接错误。5.3 解决dsh web authentication required; reopen the url printed by dsh web.类错误该错误常见于使用dshDocker Swarm Helper部署时本质是反向代理未正确透传认证头。根本原因在于dsh web启动的容器默认监听localhost:8000而 Nginxproxy_pass若写成http://localhost:8000在 Docker 网络中会指向宿主机而非容器正确做法是创建自定义 Docker 网络并用服务名代替localhost# 创建网络 docker network create ai-net # 启动 FastAPI指定网络别名 docker run -d --name ai-gateway \ --network ai-net \ -p 8000:8000 \ -v $(pwd)/config:/app/config \ ai-gateway:latest # Nginx 配置中 proxy_pass 改为 location /api/ { proxy_pass http://ai-gateway:8000; # 使用服务名非 localhost # ... }此时dsh web输出的 URL 应为https://creative.example.com而非http://localhost:8000自然不再出现认证提示。本文还有配套的精品资源点击获取