技术栈是 UniApp(Vue2)+ 微信小程序,零 npm 依赖,纯 HBuilderX 工程。 后端提供人脸与图像处理能力,小程序负责上传、参数交互和结果展示。
小程序只做三件事:把照片传到对象存储、把对象 key 交给后端、把后端返回的结果图展示出来。
所有算法都在服务端跑,端上不承载模型。
有意思的一点是,后端五个处理接口名字都带 _ws 后缀,但对外其实是普通 HTTP POST:
后端内部通过 WebSocket 调算法服务,拿到结果后同步返回。所以小程序侧按普通请求调用即可,
不需要维护连接和心跳。
一次上传分三步,照片文件不经过自己的服务端:
// 1. 取一次性上传凭证
GET /file/qiniu/uploadkey?type=6 → { uploadkey, prefix }
// 2. 直传对象存储(华南 zone2)
uni.uploadFile({
url: 'https://upload-z2.qiniup.com',
formData: { token: uploadkey, key: 'temp/<时间戳>_<随机>.jpg' }
})
// 3. 以后端返回的真实 key 为准,再调处理接口
POST /image/beauty_ws { "key": "temp/..." }
insertOnly 策略拒绝prefix 可能已经带 /temp,拼接前先剥掉重复层级,避免出现 /temp/temp/xxx.jpg统一约定 { code, msg, data },code === 0 视为成功,返回 data.<功能>.url。
| 功能 | 接口 | 主要参数 |
|---|---|---|
| AI 换脸 | POST /image/swap_face_ws |
key、photo_id(母片,选填) |
| 换天空 | POST /image/sky_ws |
key 或 photo_id、sky_key(天空素材,选填) |
| 抠图换背景 | POST /image/matting_ws |
key、bg_key(选填,不传返回透明 PNG)、scale/left/top/right/bottom |
| 深度美颜 | POST /image/retouch_ws |
key、max_faces(GFPGAN + CodeFormer) |
| 经典美颜 | POST /image/beauty_ws |
key、smooth/whiten/slim_face/enlarge_eye/slim_nose(0–1)、style、style_strength |
五个功能的参数表单完全不同,但我只写了一个处理页 pages/process/process.vue。
做法是把功能差异抽成一份元信息配置,页面按 ?type= 读配置渲染:
// utils/features.js
beauty: {
title: '经典美颜',
api: 'beauty',
needBg: false, // 抠图才需要选背景
needStyle: true, // 美颜才有风格选项
needStrength: true // 美颜才有强度滑块
}
needBg / needStyle / needStrength 三个开关决定是否显示背景选择、风格 chips 和参数滑块buildParams() 按类型拼参数,滑块 UI 是 0–100,提交前除以 100 归一化成 0–1max_faces = 0 表示处理全部人脸,此时不传这个字段,让后端走默认值五个功能按钮沿着一个半圆形排布,中间是带 HOT 标记的 AI 换脸,5 秒自动轮播一次。 顶部背景视频会随着当前选中的功能切换素材,下方扫描器的颜色也跟着变。
按钮到中心的光柱需要长度和旋转角。先用固定布局做一次确定性计算,
再用 createSelectorQuery 实测 DOM 位置修正,用 atan2 求角度、hypot 求长度。
默认显示 AI 效果图,长按切换到原图,松手复原,用来对比前后差异。
网络图必须先 downloadFile 再 saveImageToPhotosAlbum。
失败时如果错误里带 auth / deny,就弹窗引导用户去设置页开权限。
一次最多选 9 张,用 reduce 串成 Promise 链逐张上传,
带 n/total 进度提示。并发上传很容易把存储打爆,也拿不到稳定的进度。
request.js 里约定 statusCode 200 且 code === 0 才 resolve,
并且只 resolve data;其余情况直接 reject 出 msg,页面统一 Toast 展示。
小程序用的是微信虚拟支付。这里最容易出问题的地方是: 不能在支付的 success 回调里就认为钱到账了。
wx.login → code
→ POST /pay/login // 换 token
→ POST /pay/create // 拿签名与订单号
→ wx.requestVirtualPayment
→ POST /pay/confirm // 服务端向微信查单后发货
/pay/confirm,由服务端查单确认后发放额度X-Auth-Token 选填,token 缺失也不阻断请求,游客可以直接试用/static/ 路径只在模拟器有效。:style 里调用方法在部分小程序端不会被求值insertOnly 限制,改为前端自造唯一 key 解决。navigateTo 跳转switchTab。这些都是真机调试时一个个撞出来的,记下来免得下次再踩一遍。