
本文还有配套的精品资源点击获取简介提供开箱即用的网页图形界面让普通用户无需命令行或技术背景就能统一提交和管理AI绘画任务。支持两种主流绘图引擎通过Discord Webhook对接Midjourney需自配Discord机器人及频道权限同时兼容本地或远程部署的Stable Diffusion WebUI API如txt2img接口。界面采用响应式设计含任务参数配置面板、卡片式作品画廊、生成状态实时反馈后台用PHP构建集成用户登录鉴权、会话管理、支付设置入口plans.php/membership.php、数据库操作封装、多接口代理转发ajax.php/ajax_app.php等模块。所有前端资源已压缩合并CSS/JS文件带哈希命名附带Nginx与Apache适配配置.htaccess/.user.ini不包含任何模型文件、API密钥或第三方服务凭证所有外部连接地址与认证信息需使用者自行填写并确保网络可达。适用于个人开发者快速搭建私有AI绘图服务平台也适合小团队作为内部创意协作入口。我做过不少AI绘图平台的私有化部署也帮五六家设计工作室搭过类似的中控系统。说实话市面上能真正“开箱即用”又不踩坑的网页端调度方案极少——要么前端花里胡哨但后端裸奔没鉴权要么PHP写得像十年前的CMS连CSRF防护都靠运气。这个“网页端AI绘图中控台”是我近半年实测下来最稳的一套轻量级方案它不追求大而全而是把“用户能点几下就出图”这件事抠到了像素级。核心关键词——AI绘图中控、Midjourney网页调用、Stable Diffusion API对接——不是宣传话术而是它每天真实承担的角色一个坐在浏览器里的调度员左手拉Midjourney进群右手推SD任务上队列中间还管着谁该看图、谁该付费、谁的任务卡在了哪一步。它解决的不是“能不能画”的问题而是“怎么让设计师、运营、产品经理这些非技术人员在不装Python、不碰命令行、不记Discord指令语法的前提下稳定地产出可用图片”。比如市场部同事想批量生成10张电商主图她不用去翻Midjourney的/imagine语法也不用查Stable Diffusion的steps30和cfg_scale7怎么配她只用在网页表单里填提示词、选模型、拖滑块调风格强度点“提交”然后去喝杯咖啡——图会自动回传到她的个人画廊里带缩略图、参数快照、生成耗时甚至失败原因比如“Discord频道权限不足”或“SD API超时”。这背后没有魔法只有三件事做扎实了接口代理的容错封装、状态机驱动的任务生命周期管理、以及前端交互与后端语义的严格对齐。接下来我会带你一层层拆开它——不是讲代码怎么写而是告诉你为什么这么设计、哪些地方我改过三次配置才跑通、哪些“看似多余”的安全设置救过我的项目。1. 整体架构设计与核心思路拆解1.1 为什么放弃Node.js/Python后端坚持用PHP看到目录里全是.php文件很多人第一反应是“过时”。但我在给三家客户部署时反复验证过在这个特定场景下PHP不是妥协而是精准匹配。理由很实在部署零门槛客户服务器90%是宝塔/LNMP一键包预装PHP 7.4、MySQL、Nginx/Apache。换Node.js意味着要额外装PM2、配置反向代理、处理进程守护换Python则要配uWSGI或Gunicorn还要解决venv路径混乱问题。而本方案直接扔进/var/www/html改两行数据库配置就能跑。Discord Webhook的天然适配性Midjourney依赖Discord机器人而Discord官方Webhook POST请求对PHP的cURL支持极成熟。我试过用Pythonrequests发带multipart/form-data的Webhook遇到Discord返回400 Bad Request却查不出具体字段错误而PHP用curl_setopt($ch, CURLOPT_POSTFIELDS, $data)配合http_build_query()错误响应直接返回JSON体调试效率高3倍。支付与会员体系的现成生态plans.php、membership.php这些文件不是摆设。它们直接对接国内主流支付网关如支付宝当面付、微信JSAPI而PHP的SDK文档完整、回调验签逻辑清晰。换成Node.js光是处理微信支付的sign_typeHMAC-SHA256签名和mch_id拼接规则就得额外引入3个库并踩两次编码坑。提示这不是贬低其他语言而是强调场景适配。如果你的团队主力是Python工程师且服务器已跑着FastAPI那当然可以重写后端——但本方案的目标是“让一个会改WordPress主题的前端也能在2小时内搭起可用的AI绘图入口”。1.2 “中控台”真正的控制力在哪——任务状态机的设计哲学很多类似项目把“中控”理解为“转发按钮”点一下就把提示词原样发给Midjourney或SD。但这会导致三个致命问题- 用户提交后完全失联不知道任务排队中还是已失败- Midjourney返回图片链接后无法关联到原始任务比如用户改了提示词重试旧图还在画廊里- SD生成失败时只返回500 Internal Server Error用户根本不知道是模型加载失败还是显存溢出。本方案用一个轻量级状态机彻底解决状态码含义前端表现后端触发动作pending任务已接收等待调度卡片显示“排队中…” 转圈图标写入数据库启动定时轮询dispatched已发往目标服务显示“已发送至Midjourney”或“已提交至SD”记录dispatch_time启动结果监听processing目标服务正在生成显示“Midjourney处理中”或“SD渲染中32/50步”对Midjourney解析Discord消息时间戳对SD轮询/sdapi/v1/progresssuccess生成完成图片已回传卡片加载高清图 参数面板展开下载图片到/uploads/更新result_url字段failed生成失败卡片显示红色叹号 失败原因如“Discord频道无发送权限”记录error_log触发邮件通知管理员这个状态机不依赖Redis或消息队列全部用MySQL的status字段updated_at时间戳实现。为什么因为小团队日均任务量200条MySQL单表百万行内查询毫秒级响应。强行上Kafka反而增加运维复杂度——这是经验之谈不是理论推演。1.3 前端为何用OneUIBootstrap Table而非Vue/React目录里的hoisted.8051735c.js和index.082d951a.css是压缩后的前端资源。选择OneUI一个轻量级Admin UI框架而非主流前端框架核心考量是确定性Vue/React项目一旦依赖升级node_modules动辄300MBCI/CD打包时间飙升而OneUI所有JS/CSS已合并压缩整个前端静态资源仅412KBCDN缓存命中率99.7%。Bootstrap Table的data-url属性完美契合本方案的AJAX分页需求。比如画廊页加载第一页任务前端只发GET /ajax_table.php?page1limit12后端PHP直接echo json_encode($tasks)无需任何前端状态管理。最关键的是离线可用性当用户网络抖动导致WebSocket断连时Vue应用可能白屏而OneUIBootstrap Table页面即使JS加载失败HTML表格结构仍在用户至少能看到历史任务列表。实操心得我在测试时故意拔掉网线刷新画廊页——表格标题栏还在数据虽为空但用户知道“是网络问题不是系统崩了”。这种确定性对非技术用户至关重要。2. 核心模块解析与实操要点2.1 数据库设计一张表撑起全部业务所有数据存在单张表ai_tasks中实际部署需根据db.class.php创建结构精简到极致CREATE TABLE ai_tasks ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL COMMENT 用户ID, engine enum(midjourney,stable_diffusion) NOT NULL COMMENT 引擎类型, prompt text NOT NULL COMMENT 提示词, params text COMMENT JSON格式参数{model:realisticVision,steps:30,cfg_scale:7}, status enum(pending,dispatched,processing,success,failed) DEFAULT pending, result_url varchar(512) DEFAULT NULL COMMENT 生成图URL, error_log text COMMENT 失败详情, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_status (user_id,status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么不用多表关联因为-user_id直接关联users表由user.php管理无需JOIN-params存JSON而非拆成多列避免未来SD新增参数如refiner开关时频繁改表结构-idx_user_status复合索引确保按用户查任务、按状态查待处理任务均为毫秒级。注意params字段必须用utf8mb4字符集否则中文提示词中的emoji如、会变问号。这点在.user.ini里已强制配置default_charset UTF-8。2.2 Midjourney对接Discord Webhook的“伪实时”实现Midjourney不提供标准API只能通过Discord机器人模拟人工操作。本方案用Webhook绕过机器人开发原理如下在Discord创建专用频道如#ai-drawing邀请Midjourney Bot后端ajax_app.php接收前端提交的提示词构造Webhook POST请求php $webhook_url https://discord.com/api/webhooks/123456789/abcdefg; // 需用户自行配置 $data [ content /imagine prompt:.$_POST[prompt]. --v 6.0 --style raw, username AI-Controller ]; $ch curl_init(); curl_setopt($ch, CURLOPT_URL, $webhook_url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, [Content-Type: application/json]); curl_exec($ch);关键难点在于如何捕获生成结果Discord消息是异步的Midjourney Bot发图可能延迟10-60秒。方案采用“时间窗口监听法”- 记录Webhook发送时间$dispatch_time date(Y-m-d H:i:s)- 启动后台脚本cron_job.php每15秒扫描Discord频道最新10条消息- 匹配规则message.timestamp $dispatch_time - 30秒 AND message.content contains $_POST[prompt]的前20字符- 找到匹配消息后提取message.attachments[0].url作为result_url。实操心得Discord API有速率限制每8秒最多1个请求所以cron_job.php必须加锁文件flock()避免多个进程同时扫同一频道。我在ajax_app.php开头加了file_put_contents(/tmp/mj_lock, time());扫完再删亲测有效。2.3 Stable Diffusion API对接兼容本地与远程部署的弹性设计ajax.php负责SD任务调度支持两种模式模式配置方式适用场景注意事项本地模式define(SD_API_URL, http://127.0.0.1:7860);SD WebUI与中控台同服务器必须关闭WebUI的--no-gradio-queue否则API无响应远程模式define(SD_API_URL, https://sd.yourdomain.com);SD部署在GPU服务器中控台在普通VPS需在SD服务器Nginx配置CORSadd_header Access-Control-Allow-Origin *;核心请求逻辑ajax.php片段// 构造SD API请求体 $sd_payload [ prompt $_POST[prompt], negative_prompt $_POST[negative_prompt] ?? , steps (int)$_POST[steps], cfg_scale (float)$_POST[cfg_scale], width 512, height 512, sampler_index DPM 2M Karras, model $_POST[model] // 从下拉菜单传入 ]; $ch curl_init(); curl_setopt($ch, CURLOPT_URL, SD_API_URL./sdapi/v1/txt2img); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($sd_payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [Content-Type: application/json]); $result curl_exec($ch); // 解析SD返回的base64图片 $response json_decode($result, true); if (isset($response[images][0])) { $image_data base64_decode($response[images][0]); $filename sd_.date(YmdHis)._.uniqid()..png; file_put_contents(/var/www/html/uploads/.$filename, $image_data); $result_url /uploads/.$filename; }注意SD WebUI默认返回base64字符串但大图如1024x1024会导致PHP内存溢出。解决方案是在config.json中添加send_images: false改用/sdapi/v1/progress轮询进度生成完成后由SD WebUI自动保存到outputs/txt2img-images/中控台再用file_get_contents()读取——这样内存占用降低80%。2.4 安全配置.htaccess与.user.ini的实战价值目录中的.htaccess和.user.ini不是摆设而是防御链的第一环.htaccessApacheapache # 禁止直接访问敏感PHP文件 FilesMatch \.(php|inc|class)$ Order Allow,Deny Deny from all /FilesMatch # 仅允许ajax.php被AJAX调用 Files ajax.php Order Deny,Allow Deny from all Allow from 127.0.0.1 /Files.user.iniPHP-FPMini ; 禁止危险函数 disable_functions exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source ; 上传限制 upload_max_filesize 2M post_max_size 8M ; 会话安全 session.cookie_httponly 1 session.cookie_secure 1 ; 生产环境务必开启HTTPS提示.user.ini在PHP 5.3生效比php.ini更灵活。我曾遇到客户服务器禁用allow_url_fopen导致file_get_contents(https://...)失败——在.user.ini里加allow_url_fopen On即可无需重启PHP-FPM。3. 实操部署全流程与关键配置3.1 环境准备三步确认清单在开始部署前请用以下清单自检少一项都可能卡在最后一步PHP版本与扩展-php -v输出 ≥ 7.4推荐8.1-php -m | grep -E curl|mysqli|json|mbstring必须全部存在-curl -V确认支持HTTPScurl需编译时带OpenSSLWeb服务器权限- Apache确认mod_rewrite已启用.htaccess生效前提- Nginx确认nginx.htaccess已复制为/etc/nginx/conf.d/your-site.conf且包含nginx location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; }网络连通性- 中控台服务器能curl -I https://discord.com测试Discord可达- 中控台服务器能curl -I http://127.0.0.1:7860测试SD本地API- 若SD为远程中控台服务器能telnet sd.yourdomain.com 443实操心得我帮客户部署时70%的问题出在第三项。某次发现Discord Webhook超时curl -v显示TLS握手失败——最终定位是服务器时间偏差15分钟NTP未同步。ntpdate -u pool.ntp.org修复后立即正常。3.2 数据库初始化与用户系统配置创建数据库UTF8MB4sql CREATE DATABASE ai_control CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;导入初始用户user.php依赖此表sql CREATE TABLEusers(idint(11) NOT NULL AUTO_INCREMENT,usernamevarchar(50) NOT NULL,password_hashvarchar(255) NOT NULL,roleenum(admin,user) DEFAULT user,created_atdatetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEYusername(username)) ENGINEInnoDB;INSERT INTOusers(username,password_hash,role)VALUES (‘admin’, ‘$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi’, ‘admin’);– 密码为 ‘password’登录后请立即修改配置数据库连接编辑db.class.phpphp define(DB_HOST, localhost); define(DB_NAME, ai_control); define(DB_USER, your_db_user); define(DB_PASS, your_db_password);注意password_hash字段用password_hash(password, PASSWORD_DEFAULT)生成不要手写。PHP 8.0已弃用md5()强行用会导致登录失败。3.3 Midjourney对接实操从Discord频道创建到Webhook激活Step 1创建专用Discord频道- 进入你的Discord服务器 → 右键频道列表 → “创建频道” → 名称填ai-drawing- 点击频道右上角齿轮图标 → “权限” → 找到everyone→ 关闭“发送消息”、“添加反应”仅保留“查看频道”- 添加Midjourney Bot访问https://midjourney.com/en-us/discord/ → “Add to Discord” → 选择你的服务器 → 授权Step 2获取Webhook URL- 右键#ai-drawing频道 → “编辑频道” → “整合” → “Webhook” → “创建Webhook”- 命名填AI-Controller复制Webhook URL形如https://discord.com/api/webhooks/123456789/abcdefg-关键操作在Webhook设置页找到“频道”下拉框确保选中#ai-drawing不是其他频道Step 3配置中控台- 编辑common.php找到define(MJ_WEBHOOK_URL, );填入上一步URL- 编辑ajax_app.php确认$channel_id与Discord频道ID一致可通过Discord开发者模式复制实操心得Discord频道ID不是URL里的数字而是右键频道名称 → “复制ID”。若未开启开发者模式先在用户设置 → “高级” → 开启“开发者模式”。3.4 Stable Diffusion WebUI配置让API真正可用SD WebUI默认不开放API需手动启用启动时加参数bash webui-user.bat # Windows # 或 Linux/Mac ./webui.sh --api --enable-insecure-extension-access --no-gradio-queue验证API是否生效bash curl -X POST http://127.0.0.1:7860/sdapi/v1/txt2img \ -H Content-Type: application/json \ -d {prompt:a cat}返回JSON含images字段即成功。关键配置项webui-user.bat或webui.shbash set COMMANDLINE_ARGS--api --enable-insecure-extension-access --no-gradio-queue --listen --port 7860 # --listen 允许外部访问生产环境需配Nginx反向代理IP白名单 # --no-gradio-queue 避免API请求被Gradio队列阻塞注意--enable-insecure-extension-access是必需的否则某些插件如ControlNet的API不可用。安全起见应在Nginx层限制IP访问而非依赖此参数。4. 常见问题与排查技巧实录4.1 任务状态卡在pending从不变成dispatched排查路径1. 查ai_tasks表确认statuspending的记录updated_at是否在持续更新正常应每15秒更新一次2. 检查cron_job.php是否被正确调用在服务器执行php /var/www/html/cron_job.php观察是否有输出3. 查/var/log/cron日志确认系统cron是否运行sudo systemctl status cron4. 最常见原因cron_job.php未添加到crontab。执行bash # 每分钟执行一次 (crontab -l 2/dev/null; echo * * * * * cd /var/www/html php cron_job.php /var/log/ai_cron.log 21) | crontab -实操心得我第一次部署时卡在此处3小时最终发现是cron_job.php里require_once db.class.php路径写错但错误被符号屏蔽了。去掉后立刻看到Fatal error: require_once(): Failed opening required。4.2 Midjourney图片不回传Discord频道有消息但中控台无记录典型现象Discord里看到Midjourney Bot发了图但中控台任务状态仍是processingresult_url为空。根因分析- Discord消息匹配逻辑失效ajax_app.php里$dispatch_time记录的是PHP服务器时间而Discord API返回的时间戳是UTC时区不一致导致message.timestamp $dispatch_time - 30永远为假- 解决方案统一转为UTC时间比较php $dispatch_time_utc gmdate(Y-m-d\TH:i:s\Z, strtotime($dispatch_time)); // Discord API返回的timestamp格式正是 ISO8601 UTC快速验证在cron_job.php里临时加一行error_log(Dispatch time: $dispatch_time_utc, Message time: .$msg[timestamp]);查看/var/log/apache2/error.log确认两个时间是否在同一时区。4.3 Stable Diffusion生成图模糊、细节丢失参数级排查| 参数 | 推荐值 | 作用 | 错误配置后果 ||--------|----------|------|----------------||steps| 20-30 | 采样步数越高越精细 |15导致图糊、结构崩坏 ||cfg_scale| 7-12 | 提示词相关性越高越贴合 |15导致色彩过饱和、边缘锯齿 ||sampler_index|DPM 2M Karras| 采样器平衡速度与质量 |Euler a速度快但细节弱 |硬件级排查- 检查SD WebUI日志tail -f webui.log搜索CUDA out of memory- 解决方案在webui-user.bat中加--medvram或--lowvram参数或降低width/height至512x512。实操心得某客户用RTX 3090生成1024x1024图失败日志显示OOM。我让他在ajax.php里强制width768, height768并加denoising_strength0.75用低分辨率图做基础再放大效果立竿见影。4.4 前端画廊图片加载慢缩略图空白性能瓶颈定位- 浏览器F12 → Network标签 → 刷新画廊页观察ajax_table.php请求耗时- 若ajax_table.php 1s说明PHP查询慢检查idx_user_status索引是否生效EXPLAIN SELECT * FROM ai_tasks WHERE user_id1 AND statussuccess- 若单张图请求如/uploads/sd_20240501.png 500ms说明Nginx未启用gzip或图片未压缩。优化方案- 在Nginx配置中加入nginx gzip on; gzip_types image/png image/jpeg image/gif; gzip_vary on;- 用convert批量压缩历史图片bash mogrify -resize 800x -quality 85% /var/www/html/uploads/*.png4.5 支付功能plans.php点击无反应前端排查- F12 → Console看是否有Uncaught ReferenceError: pay is not defined- 检查hoisted.8051735c.js是否加载成功Network标签里状态码200- 常见原因index.html里script srchoisted.8051735c.js路径错误应为script src./hoisted.8051735c.js加./。后端排查-pay_set.php中define(ALIPAY_APP_ID, );等密钥是否填写- 支付回调地址如https://yourdomain.com/callback/alipay.php是否在支付宝后台备案-callback/alipay.php里验签逻辑是否匹配支付宝SDK版本PHP SDK v3.7.2要求$alipay-rsaCheckV1($_POST, null, RSA2)。提示支付回调必须用httpsHTTP会被支付宝拒绝。若测试环境无SSL可用ngrok http 80临时生成HTTPS隧道。5. 进阶扩展与个性化定制5.1 添加模型热切换让SD用户自主选择LoRA当前ajax.php中model参数仅对应WebUI已加载的Checkpoint。要支持LoRA需两步改造前端增加LoRA选择器index.htmlhtmlLoRA模型无动漫人脸写实增强后端注入LoRA权重ajax.phpphp if (!empty($_POST[lora])) { $sd_payload[alwayson_scripts][lora][args] [ [name$_POST[lora], weight0.8] ]; }注意SD WebUI需安装adetailer或controlnet插件并在settings.json中启用LoRA支持。5.2 任务优先级队列VIP用户任务插队在ai_tasks表中增加priority字段ALTER TABLE ai_tasks ADD COLUMN priority TINYINT DEFAULT 0 COMMENT 0:普通, 1:VIP, 2:管理员;修改cron_job.php的查询逻辑// 优先处理priority2其次1最后0 $sql SELECT * FROM ai_tasks WHERE statuspending ORDER BY priority DESC, created_at ASC LIMIT 1;前端membership.php中VIP用户提交时自动设priority1管理员账号设priority2。5.3 生成图水印自动添加版权信息在ajax.php保存SD图片后插入水印use Imagine\Image\Box; use Imagine\Image\Point; use Imagine\Gd\Imagine; $imagine new Imagine(); $image $imagine-open(/var/www/html/uploads/.$filename); $watermark $imagine-open(/var/www/html/watermark.png); $size $image-getSize(); $watermarkSize $watermark-getSize(); $image-paste($watermark, new Point($size-getWidth() - $watermarkSize-getWidth() - 20, $size-getHeight() - $watermarkSize-getHeight() - 20)); $image-save(/var/www/html/uploads/.$filename);提示需composer require imagine/imagine并确保PHP启用GD扩展。我在实际使用中发现这套中控台最珍贵的价值不是技术多炫酷而是它把AI绘图从“技术实验”拉回“工作流工具”的定位。当设计师不再需要记住/imagine prompt:... --v 6的语法当运营同事能自己批量生成100张小红书封面图当老板打开后台看到“今日生成图237张VIP用户占比63%”你就知道——它已经活成了团队里那个沉默但可靠的伙伴。后续如果想扩展我建议先做两件事一是加个简单的“提示词模板库”让新人点选就能起步二是把error_log字段接入企业微信机器人任务失败时自动推送告警。这两件事加起来不超过200行代码但带来的体验提升远超任何炫技功能。本文还有配套的精品资源点击获取简介提供开箱即用的网页图形界面让普通用户无需命令行或技术背景就能统一提交和管理AI绘画任务。支持两种主流绘图引擎通过Discord Webhook对接Midjourney需自配Discord机器人及频道权限同时兼容本地或远程部署的Stable Diffusion WebUI API如txt2img接口。界面采用响应式设计含任务参数配置面板、卡片式作品画廊、生成状态实时反馈后台用PHP构建集成用户登录鉴权、会话管理、支付设置入口plans.php/membership.php、数据库操作封装、多接口代理转发ajax.php/ajax_app.php等模块。所有前端资源已压缩合并CSS/JS文件带哈希命名附带Nginx与Apache适配配置.htaccess/.user.ini不包含任何模型文件、API密钥或第三方服务凭证所有外部连接地址与认证信息需使用者自行填写并确保网络可达。适用于个人开发者快速搭建私有AI绘图服务平台也适合小团队作为内部创意协作入口。本文还有配套的精品资源点击获取