跟新需求文档

This commit is contained in:
xsl
2026-09-26 19:50:39 +08:00
parent ab9a39a8f3
commit 71a090fd75
26 changed files with 41746 additions and 712 deletions
+59 -21
View File
@@ -1,18 +1,21 @@
# 1'me 在线肌肤测试 H5
拍摄正面与左右侧面照片,调用 **Face++ 皮肤分析(高阶版)** 真实检测,即时生成肌肤状态分析与护理建议的移动端 H5。
引导式自动三连拍(正脸/右脸/左脸),调用 **Face++ 皮肤分析(专业版 skinanalyze_pro)** 三视图真实检测,即时生成三维度×6分类肌肤状态分析与个性化护理建议的移动端 H5。
- 前端:原生 HTML/CSS/JS,零依赖、零构建
- 前端:原生 HTML/CSS/JS,零依赖、零构建(face-api.js 已本地化到 vendor/,运行时零外部请求)
- 后端:`server.js`(Node ≥ 18,零依赖)静态托管 + Face++ API 中转,**api_secret 只存在服务端**
## 目录结构
```
h5/
├── index.html 页面结构(启动 / 档案 / 拍照 / 报告 + 弹层)
├── server.js 后端:静态托管 + POST /api/skin-analyze(Face++ 中转)
├── index.html 页面结构(启动 / 档案 / 拍照 / 报告 + 弹层 + 内联 SVG 取景蒙版)
├── server.js 后端:静态托管 + POST /api/skin-analyze(Face++ skinanalyze_pro 三视图中转)
├── css/style.css 全部样式(375px 基准)
├── js/app.js 全部交互(屏幕切换、肤质轮播、实时相机、结果映射)
├── js/app.js 全部交互(屏幕切换、肤质轮播、引导式三连拍、报告渲染)
├── js/skin-score.js 三维度×6分类评分引擎(纯逻辑 UMD,window.SkinScore,可 node 自测)
├── js/head-pose.js 头部姿态估算(纯逻辑 UMD,window.HeadPose,可 node 自测)
├── vendor/faceapi/ face-api.js 0.22.2 + 模型(本地化,见该目录 README)
├── assets/
│ ├── favicon.svg 站点图标
│ ├── img/ 背景图、肤质插画、拍照底图、报告检测/产品图
@@ -39,20 +42,39 @@ node server.js # 默认 8080,可用 PORT=xxx 修改
# 打开 http://127.0.0.1:8080
```
调试:`http://127.0.0.1:8080/?mock=1` 使用内置样本数据跳过真实检测(离线/自动化验证用)。
调试:
## 检测流程与数据映射
- `http://127.0.0.1:8080/?mock=1` 使用内置样本(`SkinScore.MOCK_RESULT`/`MOCK_SIDE`)跳过真实检测;不加载 face-api、不跑姿态检测,快门手动逐步推进(离线/自动化验证用)。
- `http://127.0.0.1:8080/?mock=1&screen=report` 直渲 mock 报告页(**仅调试用**,供无头浏览器截图验证布局;档案缺省按 28 岁/干性肌肤)。
1. 三连拍(正面/右脸/左脸):getUserMedia 实时取景,不支持/被拒时自动降级调起系统相机
2. **仅正面照**送检(Face++ 高阶版只接受正脸,yaw>45° 会报错)
3. 照片压缩到最长边 1280 JPEG 后 POST `/api/skin-analyze`,服务端转发 Face++ `skinanalyze_advanced`
4. 映射层(`mapSkinResult`):设计稿的 紧塑力/活力感/稳定力/综合评分 由真实维度合成——
- 紧塑力 ← 法令纹(+严重度)、眼袋(+严重度)
- 活力感 ← 黑眼圈、眼部细纹、鱼尾纹、抬头纹、眉间纹
- 稳定力 ← 敏感泛红(面积/强度)、痘痘、闭口、毛孔、黑头
- 肌肤年龄 = Face++ `skin_age` 真实值;综合评分 = 三维加权(0.35/0.33/0.32)×10
- 具体描述文案按检出项动态生成;报告第一/二张检测图使用用户实拍照片
5. 检测失败(无人脸/距离远/网络等)按错误码给出友好提示并退回档案页
## 拍照交互(引导式自动三连拍)
三步:1/3 正脸(target `front`)→ 2/3 右脸出镜(用户头向左转,target `right`,产物作 `right_image`)→ 3/3 左脸出镜(头向右转,target `left`,产物作 `left_image`)。
- **步骤圆点**:顶部三点,当前步高亮、完成步绿色打勾。
- **取景蒙版**:内联 SVG(`#cam-mask`)按步骤切换轮廓——正脸步为正脸线框(含耳部),侧脸步为对应方向的侧脸剪影(鼻朝向与转头方向一致);框外 58% 黑色半透明压暗(SVG mask 镂空),轮廓线 2.5px 半透明白。未检测到人脸时轮廓呼吸动画(1.8s);姿态 ok 时轮廓变绿并停止动效。
- **实时检测**:face-api 从本地 `vendor/faceapi/` 加载(`tinyFaceDetector` + `faceLandmark68Net`)。进入拍照页即开始加载模型(状态提示"检测引擎加载中…"),加载完成前快门为手动模式。检测循环 ~150ms 一帧,`detectSingleFace(video, TinyFaceDetectorOptions({inputSize:320, scoreThreshold:0.4})).withFaceLandmarks()` 后拿 68 特征点调 `HeadPose.check(pos, target, /* mirrored= */true)`;人脸框高度占比 <35% 提示"请靠近一点"、>80% 提示"请远一点"。提示一次一条,优先级:无人脸 > 距离 > 角度。
- **倒计时自动拍**:姿态 ok 连续维持 ≥600ms 进入 3s 倒计时(中央大数字,轮廓保持绿色);倒计时中任一帧不 ok 立即取消并回到对应提示。倒计时结束进入白屏补光。
- **白屏补光**:全屏纯白覆盖层瞬间点亮,~300ms 后抓全分辨率帧(镜像 + 最长边 1280 JPEG),保持 ~300ms 再淡出(总 600–900ms),同步 `navigator.vibrate(30)`。
- **手动兜底**:快门任何时刻可拍当前步(同样走白屏补光),不依赖检测;face-api/模型加载失败 → toast"自动识别不可用,已切换手动拍摄",全程手动。系统相机降级模式(getUserMedia 不可用/被拒)维持原样:无检测、无补光。
- **无进展提示**:单步 12s 未触发倒计时,底部浮出"识别不到?可点下方快门手动拍摄"(不阻塞流程)。
- **资源管理**:离开拍照页(cam-back/完成)停止检测循环、清全部定时器、释放摄像头;`camSeq`(流程)/`camSeqCam`(相机)双令牌防竞态;face-api 模型只加载一次(缓存 promise,失败后下次进入重试)。
## 检测流程与评分渲染
1. 三视图送检:POST `/api/skin-analyze`,请求 `{ front_image, left_image, right_image }`(base64 可带 dataURL 前缀;正脸=photos[0],左=photos[2],右=photos[1])。
2. 服务端转发 Face++ **专业版 `skinanalyze_pro`**(三视图必须走 `image_file` 文件形式),请求 `return_side_results=jawline_info` 取左右下颌线角度,`return_maps`/`return_marks` 取叠加图与标注;响应 `{ ok:true, result, face_rectangle, side:{ left, right } }`,失败 `{ ok:false, code, message }`(message 已是用户可读文案)。
3. 评分由 `js/skin-score.js` 引擎完成,公式/锁值/话术/产品推荐以 `docs/三维度评分与话术生成需求文档_V2.0.md` 为最高契约:
- 6 分类:轮廓(眼袋 severity × 下颌线角度)、皱纹(12 分区)、光滑(毛孔/黑头/纹理)、肤色水润(色素×水润)、敏感(泛红面积/红区分)、痘痘(分级映射×计数扣分);
- 三维度木桶 min:紧塑力(轮廓+皱纹)、活力感(光滑+肤色水润)、稳定力(敏感+痘痘);
- S = 7 + 3×P/100;档位:良好(P≥90)/改善(80–90)/预警(<80);肤龄钳制到实际年龄 ±3 并留痕;
- 精华推荐三重判据(均分档/最差项约束/年龄组档)取最强档,triggers 记录命中判据。
4. 前端 `SkinScore.buildViewModel(data.result, data.side, { realAge, skinType })` → viewModel 直渲报告页:
- 头部:综合评分(`vm.total`,缺失回退三维度 S 均分×10)、肤龄 `vm.skinAgeDisplay` + 钳制标记 `vm.ageClampNote`;三维度 S(1 位小数)+ 档位配色(良好绿/改善琥珀/预警红);
- 三个 Tab 面板各展示所辖两分类:分类名、S + 档位徽章、话术正文(`vm.cats.*.text`);P 值不直接展示,`P=null` 的分类显示"本次未检出有效数据";
- 报告底部:降级标注(`vm.notes` 非空才展示,如下颌线未参与评分)、综合分析(`vm.summary`)、生活方式建议(`vm.lifestyle`)、精华推荐卡(`vm.essence` + 触发判据);
- 近期优先关注 = 最差维度(`vm.focus`);检测图 img1=正脸实拍、img2=右脸实拍。
5. 检测失败按服务端 message 提示并退回档案页。
## 部署
@@ -62,6 +84,21 @@ node server.js # 默认 8080,可用 PORT=xxx 修改
PORT=8080 node server.js # 建议 pm2 / systemd 常驻
```
### Windows 本机部署(提测用)
```bat
h5\deploy-local.bat :: 默认 PORT=8088,日志写 h5\server.log
```
登记开机自启(用户级计划任务,需系统允许;若提示"拒绝访问",直接双击 `deploy-local.bat` 或手动运行即可):
```bat
schtasks /create /tn "JuziSkinH5" /tr "\"C:\Windows\System32\cmd.exe\" /c \"D:\juzi\h5\deploy-local.bat\"" /sc onlogon /f
:: 移除: schtasks /delete /tn "JuziSkinH5" /f
```
局域网提测访问 `http://<本机局域网IP>:8088/`。注意:① 首次监听若弹 Windows 防火墙提示需允许 node.exe;② **手机走局域网 HTTP 时 getUserMedia 不可用**,会自动降级为系统相机手动三连拍(分析链路完整),要测引导式自动拍照需在本机浏览器访问 `http://127.0.0.1:8088`,或上 HTTPS。
nginx 反代示例(静态与 API 都交给 server.js,nginx 只做 HTTPS 终结):
```nginx
@@ -73,7 +110,7 @@ server {
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
client_max_body_size 12m; # 与 server.js 请求上限一致
client_max_body_size 20m; # 与 server.js 请求上限一致
}
}
```
@@ -82,14 +119,15 @@ server {
## 已知占位清单
- 报告页「活力感 / 稳定力」两个 Tab 为占位文案("以实际检测报告为准")。
- 「相关隐私协议」点击弹出的是占位说明层,协议全文待法务提供后接入。
- 产品图(prod-1.jpg)为设计稿素材;检测图已替换为用户实拍。
- 相册、切换镜头、「了解更多」为开发中占位(Toast 提示)。
- 护理建议为设计稿静态文案,未按检测结果个性化。
- **侧脸角度符号与 yaw 系数需真机校准**:`head-pose.js` 的 `YAW_COEFF`/pitch 映射与 `mirrored` 符号在不同机型/镜像策略下可能相反,上线前用真机样本回归(校准接口:调用方 `mirrored` 标志与 `HeadPose.config`)。
- **face_maps 叠加图未回传前端**:服务端请求了 `return_maps` 但响应只保留键名列表(整幅 base64 体积过大),报告页暂不使用叠加图,需要时再加服务端裁剪/压缩通道。
## 技术说明
- 尺寸换算:设计稿 1080px 基准 ÷ 2.88 = 375px 落地基准。
- 图片性能:首屏背景 preload;首屏外 `loading="lazy"`。
- 快门防连点/流程令牌(camSeq)防竞态;拍照页 15% 压暗遮罩保证白字可读。
- 快门防连点/双流程令牌(camSeq/camSeqCam)防竞态;拍照页 15% 压暗遮罩 + 蒙版 58% 镂空压暗保证白字可读。
- face-api.js 0.22.2 与模型全部本地化(`vendor/faceapi/`,约 1.4MB),运行时零外部请求,微信 webview / iOS Safari 弱网与内网部署可用;许可证(MIT / Apache-2.0)见 `vendor/faceapi/README.md`。