PR-FRONTEND-MEDIA-INLINE-BASE64 (2026-09-16) PR-WB-MP-INBOUND-MEDIA 配套 症状: 用户在前端上传图片成功后 (OSS URL 已返回), AI Agent 仍回复 "我这边并没有收到或看到任何图片附件". 根因链路: 1. backend Upload.php::buildPublicUrl 用 request()->domain() 拼 URL (LAN 部署返回 http://192.168.x.x:8050/upload/..., 公网部署返回公网域名) 2. 前端把 upload 响应直接透给 AI chat attachments[] 字段 3. backend MediaGuard::urlAllowed 把 192.168.x.x 当内网 IP (FILTER_FLAG_NO_PRIV_RANGE) 拒绝 → attachments 整批丢 4. 静默失败: MediaGuard 在 '白名单过滤后空集' 时返回 ok=true + media=[] → Kefu.php 不 return error, 业务继续走 5. WB 收到 0 个 media block → Agent 看不到附件 修复: - media-upload.js 新增 fileToBase64(filePath, opts) helper - H5: fetch(blobUrl) → blob → FileReader.readAsDataURL → strip data: 前缀 - MP-WEIXIN/APP-PLUS: uni.getFileSystemManager().readFileSync(base64) - SIZE_LIMIT_INLINE_BASE64 = 5MB, 与后端 INLINE_MEDIA_LIMIT 对齐 - 超限 / 失败返回 null, 调用方退回 URL 路径 - ai-chat-message.vue sendMessage 改为 await Promise.all 模式: - 对每个 pending item 调 fileToBase64(tempPath) 拿 base64 - 把 data + encoding='base64' 注入 attachment - enrichedPendingAttachments 同时用于 readyAttachments + userMessage.attachments - 复用同一份 base64, 不重复读 file 后端接收 data 字段后整条链路已对齐: - MessageMedia::normalizeInput 直接透传 data 字段 - MediaGuard::check data 非空时无条件放行, 不参与 URL 校验 - WbAgentOsClient::buildMediaBlocks (A 路) 路径 1 优先 data + image/audio - WbAcpClient::buildMediaBlocks (B 路) data 非空也走 inline base64 - inline_media_sanity.php 39 个断言全部覆盖 (test 3.1, 3.2, 4.1, 4.2, 7.6) 测试: - E2E test_inline_base64.py 10/10 通过 - mock 上传返回 LAN URL 模拟 MediaGuard 拦截场景 - 抓 /api/kefu/chat POST body, 验证 attachments[].data 是有效 PNG base64 - magic bytes 89504E47 验证通过 - 既有 test_ai_chat_upload.py 20/20 通过 (无回归) 边界: - >5MB 图片 fileToBase64 返回 null, 退回 URL 路径 (buildMediaBlocks 走 路径 2 fetchInline / 路径 3 resource_link); LAN 部署下仍可能被 MediaGuard 拦截, 但 5MB 以下的图片(主流微信/支付宝截图等)不受影响 - H5/MINIProgram/APP 三端都加了 #ifdef 编译条件
小程序及快应用前端源码
1. 📋 项目说明
该项目基于 uni-app 开发构建,请使用 HBuilderX 进行代码开发及构建发布。
2. 🛠️ 开发说明
- 源码基于 Vue2 版本
- SCSS 采用 dart-sass 进行编译输出
3. 🔧 开发调试说明
3.1 注意事项
- 应用访问等关键参数的配置来源于
./common/js/config.js
3.2 本地调试
.local.config.js.example 是本地调试配置示例。
拷贝 .local.config.js.example 为 .local.config.js 文件,默认开发调试模式会自动加载该文件
4. 📦 发布说明
4.1 小程序发布
4.1.1 基本操作步骤(通用版/定制化版)
##### ##### ##### 【前置准备】
- 在项目根目录打开终端安装依赖:
已有依赖可跳过此步骤
npm install
##### ##### ##### 【发布构建】
- 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
- 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成 ⚠️ 注意:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
- 构建完成后,自动执行补丁脚本(拷贝 site.js / config 文件 + 生成 ZIP 包),无需手动操作
- 找到项目根目录
/unpackage/dist/build下生成的 mp-weixin 压缩包 💡 示例:mp-weixin-2026-05-11-3bff91e.zip压缩包文件名末尾为 Git 提交 ID 同时该目录下会生成未压缩的mp-weixin目录
##### ##### ##### 【验证与重命名】
-
打开未压缩的
mp-weixin目录,找到site.js文件,将文件内的uniacid值改为当前客户编号(如:2812)并保存 -
打开微信开发者工具,导入「mp-weixin」目录,点击「编译」,在开发者工具控制台验证有无报错,以及能否返回对应客户的业务数据
-
确保无误后将生成的 mp-weixin 压缩包重命名
- 定制化版格式:
定制化-客户编号-域名-mp-weixin-当前日期-Git提交ID.zip - 通用版无需重命名
💡 示例:
custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-05-11-3bff91e.zip🚫 禁止:压缩包命名禁止包含
/ \ : * ? " < > |等特殊字符 - 定制化版格式:
##### ##### ##### 【交付与最终发布】🔍
- 将重命名后的文件发送给技术人员 📌通用版直接将生成的 mp-weixin 压缩包发送给技术人员
- 技术人员操作流程:
- 解压压缩包
- 确认
site.js中的uniacid为客户编号 - 用微信开发者工具导入解压后的代码目录
- 编译验证无误后,上传发布
4.1.2 鸿蒙小程序发布(mp-harmony)
##### ##### ##### 【发布构建】
- 使用 HBuilderX 打开项目
- 选择菜单栏「发行」 → 「HarmonyOS」→ 「发行」,等待构建完成
- 构建完成后,自动执行
harmony.patch.js补丁脚本(配置拷贝 + ZIP 打包) - 找到项目根目录
/unpackage/dist/build下生成的 mp-harmony 压缩包
##### ##### ##### 【交付与最终发布】🔍
- 将生成的
.zip文件发送给运维人员 - 运维人员操作流程:
- 解压压缩包
- 打开鸿蒙开发者工具,导入解压后的代码目录
- 验证代码无报错后,执行上传发布操作
##### ##### ##### 【本地设置】

4.2 快应用发布
4.2.1 基本操作(通用版/定制化版)
4.2.1.1 【发布构建】
-
使用 HBuilderX 打开项目
-
打开项目根目录的
manifest.json文件,切换至可视化配置界面:- 依次点击「Web 配置」→「运行的基础配置」
- 根据情况查看是否修改路径中/hwappx/????/的客户编号
📌 通用版保留原有
/hwappx/common/📌 测试版保留原有/hwappx/test/💡 个性化定制示例:/hwappx/2811/,其中 2811 为定制化版客户编号
-
选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本 💡 例如:网址是
5g.aigc-quickapp.com选H5-5g.aigc-quickapp.com -
修改以下配置:
- 网站标题:快应用
- 网站域名:当前客户域名
- 确认后点击「发行」等待构建完成
⚠️ 注意:构建完成后自动输出到
unpackage/dist/build/h5/,格式为{域名}-{base路径}.zip💡 示例:xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip
4.2.1.2 【交付与最终发布】🔍
- 将生成的
.zip文件发送给运维人员 - 运维人员操作流程:
- 解压压缩包
- 打开快应用开发者工具,导入解压后的代码目录
- 验证代码无报错后,执行上传发布操作
4.2.1.3 【测试流程】
H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点 如果是test就把hwappx/common/改为hwappx/test/----------改站点
5. 📝 构建与版本信息
5.1 自动注入机制
生产构建时,vue.config.js 中的 VersionInjectPlugin 会自动执行以下操作:
| 操作 | 适用平台 | 说明 |
|---|---|---|
__app_version 全局变量注入 |
所有平台 | 构建产物的 vendor.js(小程序)或 index.html(H5)中自动内联版本数据 |
| 构建版本信息查看 | 所有平台 | 首页右下角连击 5 次弹出模态框显示 commitId 和 buildTime |
| 极限压缩优化 | 小程序(mp-weixin / mp-harmony) | 生产构建自动应用多层次压缩配置,显著减小产物包体积 |
版本数据包含:
- commitId:当前构建的 Git 短提交 ID
- buildTime:构建时间
开发模式:HMR 热更新时由 webpack emit 钩子自动处理,不影响开发体验。
5.2 自动 ZIP 打包
H5 自定义发行构建完成后自动执行:
| 步骤 | 说明 |
|---|---|
| 重命名输出目录 | {域名}-{base路径用.连接}(如 xcx.aigc-quickapp.com-hwappx.common) |
| 创建 ZIP 文件 | 文件名格式:{目录名}-{Git提交ID}-{上海时区时间戳}.zip |
| 清理 | ZIP 创建完成后自动删除源目录 |
💡 示例 ZIP:xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip
微信小程序生产构建时自动执行:
- 拷贝
site.js/project.config.json/project.private.config.json文件到构建目录 - 创建 ZIP 包(文件名包含 Git 提交 ID)
鸿蒙小程序生产构建时自动执行:
- 拷贝必要配置文件到构建目录
- 创建 ZIP 包(文件名包含 Git 提交 ID)
5.3 环境配置
域名配置统一通过 package.json 中各脚本的 env.UNI_APP_DOMAIN 传入:
"h5-xcx.aigc-quickapp.com": {
"title": "...",
"env": {
"UNI_PLATFORM": "h5",
"UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/"
},
"define": { "PRODUCTION": true }
}
config.js 和 vue.config.js 均通过 process.env.UNI_APP_DOMAIN 读取,新增/删除脚本无需改动代码。
6. 🔄 版本历史
6.1 v1.5.1
VersionInjectPlugin:webpack emit 钩子自动注入__app_version到所有平台产物- 首页右下角连击 5 次显示构建版本信息(commitId + buildTime)
mp-weixin.patch.js精简为仅文件拷贝和 ZIP 打包- 新增
harmony.patch.js:鸿蒙小程序生产构建补丁脚本(配置拷贝 + ZIP 打包) open-directory.js:构建完成后自动打开输出目录- 极限压缩优化:集成
zion-uniapp-mp-load-package插件 + TerserWebpackPlugin 多层次压缩配置 - 修复
openWxWorkServiceChat方法中 fail 回调语法错误 - ZIP 文件名末尾使用 Git 提交 ID 替换时间戳
- 构建脚本幂等性修复,支持多次运行不产生重复注入
6.2 v1.3
- 修复微信小程序构建脚本,支持复制
project.config.json和project.private.config.json文件 - 增强构建脚本功能,添加命令行参数支持
- 在
package.json中添加相关 npm 脚本命令 - 优化脚本执行逻辑,提高可靠性和灵活性