214 lines
24 KiB
Markdown
214 lines
24 KiB
Markdown
# 1'me肌肤状态检测 H5
|
||
|
||
引导式自动三连拍(正脸/右脸/左脸),调用 **Face++ 皮肤分析(专业版 skinanalyze_pro)** 三视图真实检测,即时生成三维度×6分类肌肤状态分析与个性化护理建议的移动端 H5。
|
||
|
||
- 前端:原生 HTML/CSS/JS,零依赖、零构建(face-api.js 已本地化到 vendor/,运行时零外部请求)
|
||
- 后端:`server.js`(Node ≥ 18,零依赖)静态托管 + Face++ API 中转,**api_secret 只存在服务端**
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
h5/
|
||
├── index.html 页面结构(启动 / 档案 / 拍照 / 报告 + 弹层 + 内联 SVG 取景蒙版)
|
||
├── admin.html 后台查看页(记录列表 / 失败与异常 / 统计概览,零依赖,见下文「后台查看」)
|
||
├── server.js 后端:静态托管 + POST /api/skin-analyze(Face++ skinanalyze_pro 三视图中转)+ /api/admin/*(记录查询/重试/统计/导出)
|
||
├── css/style.css C 端全部样式(375px 基准)
|
||
├── css/admin.css 后台查看页样式
|
||
├── js/app.js 全部交互(屏幕切换、肤质轮播、引导式三连拍、报告渲染)
|
||
├── js/admin.js 后台查看页脚本(纯原生 ES5)
|
||
├── js/skin-score.js 三维度×6分类评分引擎(纯逻辑 UMD,window.SkinScore,可 node 自测;服务端评分复用同一份)
|
||
├── js/head-pose.js 头部姿态估算(纯逻辑 UMD,window.HeadPose,可 node 自测)
|
||
├── data/records.jsonl 检测记录库(一行一条 JSON,零依赖嵌入式存储,已 .gitignore)
|
||
├── data/photos/ 检测照片 data/photos/<record_id>/{front,left,right}.jpg(本地磁盘代替对象存储)
|
||
├── data/seed.js 演示种子数据(一次性:node h5/data/seed.js,覆盖重写 records.jsonl)
|
||
├── data/results/ 每次检测的 Face++ 原始返回落盘(复核用,已 .gitignore)
|
||
├── js/head-pose.js 头部姿态估算(纯逻辑 UMD,window.HeadPose,可 node 自测)
|
||
├── test/regression.js 真值回归(node h5/test/regression.js,对照 docs xlsx 两组三视图真值)
|
||
├── vendor/faceapi/ face-api.js 0.22.2 + 模型(本地化,见该目录 README)
|
||
├── assets/
|
||
│ ├── favicon.svg 站点图标
|
||
│ ├── img/ 背景图、肤质插画、拍照底图、报告检测/产品图
|
||
│ ├── mock/ mock 报告页数据源(report-fixture.js,真实案例1数据)
|
||
│ └── svg/ 品牌 Logo 与控件图标
|
||
└── preview/ 开发期整页截图(仅存档,部署时可整目录剔除)
|
||
|
||
../apikey.bash Face++ 密钥(export SKIN_API_KEY=... / SKIN_API_SECRET=...,已 .gitignore)
|
||
```
|
||
|
||
## 密钥配置
|
||
|
||
二选一:
|
||
|
||
1. 环境变量:`export SKIN_API_KEY=xxx SKIN_API_SECRET=xxx` 后启动 server.js
|
||
2. 密钥文件:项目根目录放 `apikey.bash`(内容为 `export SKIN_API_KEY=...` 等,server.js 会自动解析)
|
||
|
||
**密钥文件已被 .gitignore 排除,严禁提交到版本库。**
|
||
|
||
## 本地运行
|
||
|
||
```bash
|
||
cd h5
|
||
node server.js # 默认 8080,可用 PORT=xxx 修改
|
||
# 打开 http://127.0.0.1:8080
|
||
```
|
||
|
||
调试:
|
||
|
||
- `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 岁/干性肌肤);mock 数据源为 `assets/mock/report-fixture.js`(真实案例1三视图检测返回,含 face_maps 图层 data URL 与 marks 坐标),改该文件即可换样本。
|
||
|
||
## 拍照交互(动态捕捉自动三连拍)
|
||
|
||
三步严格按顺序,本步拍到合格照片才进入下一步(左右一律以用户本人为准):1/3 正对屏幕(target `front`)→ 2/3 右脸出镜(用户头向自己的左侧转,target `right`,产物作 `right_image`)→ 3/3 左脸出镜(头向自己的右侧转,target `left`,产物作 `left_image`)。送检前 `photosByView` 按 target 重排为 [正, 左, 右]。页面内没有返回按钮,微信/浏览器返回键经 History API 回到档案页并释放摄像头。
|
||
|
||
- **取景蒙版**:内联 SVG(`#cam-mask`)按步骤切换轮廓——正脸步为正脸剪影(含耳部),侧脸步为对应方向的侧脸剪影;**轮廓路径由 Figma 拍照示意图(`design/assets/images/*-拍照示意.png` 的 alpha 镂空区)逐像素描摹生成**,坐标为 Figma 375×812 整帧,viewBox 只取 y 86–812;框外 85% 黑,轮廓线 2.5px 半透明白。检测运行时轮廓内有自上而下的扫描带(`#cam-clip` 由 app.js 复制轮廓生成);未检测到人脸时轮廓呼吸动画;条件合格时轮廓变绿。
|
||
- **预览铺满**:`#cam-video` 保持现有 `object-fit:cover` 铺满与镜像策略,不改取景视觉。
|
||
- **动态提示**:根据真机难拍反馈适度放宽(参数版本 `2026-10-12-fourth-v2`),单人、检测置信度≥0.4、人脸框高占原始画面 30%~68%、水平中心偏离≤16% 屏宽、完整入镜且距原始帧边缘≥1%。正脸 |yaw|≤15°/|pitch|≤18°,侧脸转头幅度 30°~70°/|pitch|≤20°,两者 |roll|≤15°;脸部平均亮度 65~235。参数集中在 `js/capture-quality.js` 与 `js/head-pose.js`,均为经验值,不等价于服务端质量分。
|
||
- **合格后倒计时拍摄**:连续稳定至少 450ms 且至少 2 个合格检测帧;容许 yaw/pitch 漂移≤8°、中心移动≤原帧 5.5%、框高占比变化≤6.5 个百分点。漏检、超限漂移或帧间隔超过 2 秒均重置稳定累计;每张照片播放原有 `countdown-321.mp3`,等音频完整结束后再白屏抓拍,本地复验实际 JPEG,合格才推进。倒计时期间锁定快门及翻转,退出或隐藏页面取消未完成拍摄。网页内自动/降级手动抓拍均适用;系统相机降级的原生快门由操作系统控制。音频不支持、未解锁或加载失败时沿用无声抓拍,避免阻塞流程。
|
||
- **两张之间停顿**:正脸、右脸拍完后中央弹出停顿卡约 2s(「正脸拍摄完成 / 下一张:右脸出镜,请向左转头」),停顿期间不检测,结束后再切到下一步并播下一步引导,让用户对下一张有心理预期。
|
||
- **实时检测**:face-api 从本地 `vendor/faceapi/` 加载(`tinyFaceDetector` + `faceLandmark68Net`),进入拍照页开始加载(状态提示「检测引擎加载中…」)。检测循环 ~150ms 一帧,特征点取自原始帧(预览 CSS 镜像不影响),个别前置帧已镜像的机型由侧脸自校准翻转 `sideYawFlip`;连续漏检 ≥3 帧才报「未检测到人脸」。
|
||
- **镜像策略**:前置镜头预览镜像(照镜子习惯);后置预览不镜像,侧脸轮廓随之翻转(`.cam-mask.flip`)。**抓帧一律不镜像**,三张照片都是摄像头原始视角。
|
||
- **白屏补光**:全屏纯白覆盖层瞬间点亮,~300ms 后抓全分辨率帧(原始视角 + 最长边 1280 JPEG),保持 ~300ms 再淡出,同步 `navigator.vibrate(30)`。
|
||
- **快门只在无法实时识别时出现**:系统相机降级、face-api/模型加载失败、`?mock=1` 调试。文件照片共用同一套本地单人/距离/完整入镜/角度/光线校验,不合格停在当前步;系统照片可能已镜像,侧脸不判断左右,并标记 `direction_checked:false`。本地模型加载中最多等 8 秒;确实不可用时保留原有降级通路,但后台明确记录 `unchecked/model-unavailable`,不冒充检查通过。服务端质量结果仅记录,不新增重拍阻断。
|
||
|
||
- **报告等待与质量留痕**:本次正脸实拍覆盖 PSD 白色人脸占位,保持与拍摄页相同的等比铺满和前置预览镜像(仅显示,不改送检 JPEG);保留 PSD 框、文案、装饰图标与约 1 秒上下往返扫描。成功跳转等待至少一次实际动画循环,错误/取消立即恢复并清理等待页照片引用。PSD 素材加载失败仍显示照片、文字和取消入口。拍摄质量备注不在用户报告展示,后台列表、详情和 CSV 保留侧脸质量状态与前端参数版本。前端诊断为客户端自报,仅供校准,不作为服务端质量真值。
|
||
- **相册选图**:相机页「相册」按钮可选当前步的图片,与降级拍照同管线(压 1280 → 校验 → 推进)。
|
||
- **语音播报**:步骤引导/姿态提示/拍摄完成/分析中/失败,文案与文件名契约见 `../docs/语音播报内容设计文档_V1.0.md`。新增的提示(居中、抬头/低头、光线)暂无音频,清单缺失的条目静默跳过,只显示文字。
|
||
- **无进展提示**:单步 12s 仍未拍到,底部浮出「识别不到?请正对镜头,并到光线充足处」(不阻塞流程)。
|
||
- **资源管理**:离开拍照页停止检测循环、清全部定时器、释放摄像头;`camSeq`(流程)/`camSeqCam`(相机)双令牌防竞态;face-api 模型只加载一次。
|
||
|
||
## 档案页与报告页
|
||
|
||
2026-10-13 当前留存规则:仅微信登录记住本机基础信息、在后端保留最近 5 份完整报告并支持跨设备查看。游客使用仅内存的临时会话,不持久化身份/基础信息/历史报告,也不新增后台检测记录、照片或原始结果;本次报告仍可正常生成并查看,刷新后基础信息不回填。旧服务器数据不自动删除。详见 [游客不留存与样式调整](../docs/游客不留存与弹窗护理间距调整-2026-10-13.md)。下方早期的 localStorage 报告历史说明已被此规则替代。
|
||
|
||
- **档案页**:出生年月 / 性别 / 所在地区三个滚轮选择器同一风格(`vendor/mobile-select`,MIT),出生年月自动换算年龄;地区为省–市两级联动,数据为 `js/region-data.js`(由 china-division 2.7.0 `pc.json` 生成,另含港澳台与「海外」)。当前账号在本机浏览器记住已确认的基础信息和日常肤质,再次进入自动回填,滚轮也定位到原值;年龄按出生年月重新计算。肤质支持跟手拖动、松手吸附、轻扫切换及边界回弹,纵向手势仍滚动页面。详见 [基础信息记忆与肤质拖动](../docs/基础信息记忆与肤质拖动-2026-10-12.md)。
|
||
- **报告页**:无返回按钮与页头标题;总分环形图 + 肌肤年龄与「比实际年龄年轻/大 N 岁」;三维度分数 + 进度条(最低分标红并挂「优先关注」);维度详情为原布局——一张卡内紧塑力/活力感/稳定力三个 Tab,每个 Tab 两条「竖排标题 + 左图右文」;点检测图全屏放大,再点或按返回键回到报告。
|
||
|
||
## 检测流程与评分渲染
|
||
|
||
1. 三视图送检:POST `/api/skin-analyze`,请求 `{ front_image, left_image, right_image, profile? }`(base64 可带 dataURL 前缀;正脸=photos[0],左=photos[2],右=photos[1];`profile` 为可选档案 `{real_age,gender,region,daily_skin_type}`,后台记录用)。
|
||
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 已是用户可读文案)。`result.face_maps` 为对象,含报告页所需图层的完整 data URL(`red_area`/`brown_area`/`texture_enhanced_lines`/`texture_enhanced_pores`/`texture_enhanced_blackheads`,由 server.js 的 `REPORT_MAPS` 常量控制);`marks` 坐标(`acne`/`brown_spot`/`mole`/`closed_comedones`/`acne_pustule`/`acne_nodule`/`wrinkle_mark`/`melanin_mark`/`dark_circle_mark`)随 `result` 原样透传,与上传图片同一像素空间。
|
||
3. 评分由 `js/skin-score.js` 引擎完成,公式/锁值/话术/产品推荐以 `docs/三维度评分与话术生成需求文档_V2.3.docx` 为最高契约(V2.3 口径,含 §8.5 护理建议五段):
|
||
- 6 分类:轮廓 = min(眼袋映射分, 下颌线角度分);皱纹直接采用 `score_info.wrinkle_score` 整体分(不反向、不按分区严重度限分;分区仅作说明/可视化,缺失整体分则不回退旧规则);光滑(毛孔/黑头/纹理);肤色水润(色素×水润);敏感(泛红面积取 `red_spot.red_spot_area`/红区分);痘痘(分级映射×丘疹 `acne.count` 计数扣分);
|
||
- 三维度木桶 min:紧塑力(轮廓+皱纹)、活力感(光滑+肤色水润)、稳定力(敏感+痘痘);
|
||
- 痘痘计数:闭口、丘疹、脓包、结节、痘印分别取正/左/右三视图最大数量(不相加),权重1/2/4/6/1.5、合计扣分封顶50;整体 `acne_score` 仍取正脸。报告文案使用相同计分数量;缺失不当0,后台详情显示三视图及最终取值、扣分。
|
||
- 肤色项:色素扣斑分80%+`dark_circle_score`黑眼圈评分20%,与水分项各占50%得到肤色水润P;缺项按有效权重归一。肤色页在原照片上以30%强度叠加色素图(缺图时用真实色素坐标),黄褐斑/雀斑显示检出状态,没有对应坐标不画伪造定位框。
|
||
- S = 7 + 3×P/100;档位:良好(P≥90)/改善(80–90)/预警(<80);生物可视年龄限制在用户实际年龄±5岁:`max(实际年龄−5, min(实际年龄+5, 原始肌龄))`,如实际25、接口40展示30;原始肌龄和推荐规则不变。缺少实际年龄展示原始肌龄,缺少肌龄显示`--`;三视图版 18 条话术;
|
||
- 精华推荐三重判据(均分档/最差项约束/年龄组档)取最强档,triggers 记录命中判据。
|
||
4. 前端 `SkinScore.buildViewModel(data.result, data.side, { realAge, skinType })` → viewModel 直渲报告页:
|
||
- 头部:综合得分通过 `SkinScore.weightedTotal(vm.dims)` 计算,紧塑力×4+透亮感×3+稳韧力×3,使用维度S分数(两位小数)加权、四舍五入取整数,不使用接口 `total_score`;任一维度缺失显示 `--`。后台列表、详情、CSV采用相同公式,历史报告按已保存维度计算展示,不改写原文件;肤龄读取 `SkinScore.reportSkinAge(vm, result)`,原值缺失显示 `--`;三维度 S(1 位小数)+ 档位配色(良好绿/改善琥珀/预警红);
|
||
- 三个 Tab 面板各展示所辖两分类:分类名、S + 档位徽章、话术正文(`vm.cats.*.text`);P 值不直接展示,`P=null` 的分类显示"本次未检出有效数据";
|
||
- 报告底部:降级标注(`vm.notes` 中与侧脸/下颌线/补拍相关的条目,去掉接口质量码后展示)、护理建议五段(`vm.careAdvice`)、精华推荐卡(`vm.essence.name` + 命中判据;推荐正文已在护理建议第 4 段,卡内不重复);
|
||
- 近期优先关注 = 最差维度(`vm.focus`);6 分类各配检测可视化图:轮廓=正脸实拍、皱纹=正脸压暗+`texture_enhanced_lines` 皱纹增强线(线图层几乎全透明,直挂白底不可读,故与压暗沉脸合成)、光滑=正脸+色斑/痣/痤疮标记叠加、肤色=`brown_area` 色沉图、敏感=`red_area` 红区图、痘痘=正脸+闭口粉刺/痤疮标记叠加(marks 按原像素坐标叠加到正脸实拍)。
|
||
5. 浏览器冒烟:`node h5/test/e2e-browser.mjs`(需全局 `playwright` + `npx playwright install chromium`,服务跑在 8080)——mock 三连拍到报告页,并核对 14 条语音可拉取、流程语音完整播完不被截断。
|
||
6. 真值回归:`node h5/test/regression.js` 以 `docs/皮肤检测测试结果_三视图_20260926.xlsx`(sheet「三视图检测汇总」,数据第 3/4 行=案例1/案例2)两组三视图真值对照评分引擎输出。
|
||
7. **报告历史**:检测成功后按「检测」为单位存入 localStorage(`juzi.reports.v1`,上限 5 条,超配额逐条淘汰)——含三张照片、档案、全部评分数据与 marks 坐标,**不含 face_maps 整幅图层**(体积原因;回看时皱纹/肤色/敏感图降级为占位,光滑/痘痘标记叠加仍可用)。当前无 UI 入口(第9轮移除),数据仅作本地追溯。
|
||
8. **原始返回落盘**:服务端每次检测成功后把 Face++ 完整原始返回写入 `h5/data/results/<时间戳>-<request_id>.json`(复核/真值用,已 .gitignore;写盘失败不影响响应)。
|
||
9. 检测失败按服务端 message 提示并退回档案页。
|
||
|
||
## 后台查看(admin.html)
|
||
|
||
本地查看版后台,浏览器打开 **`http://127.0.0.1:8080/admin.html`**(无登录鉴权,仅内网/本地使用;上线前需加网关或鉴权)。请求体除三张照片外还带可选档案 `profile:{real_age,gender,region,daily_skin_type}`(前端 `js/app.js` 自动附带,年龄非法按 null),服务端据此落库并在检测成功时用 `SkinScore.buildViewModel` 现场评分(与 C 端同一份公式)。
|
||
|
||
- **数据落库**:每次有效调用(正脸照片校验通过起)生成 `record_id`,照片写 `data/photos/<record_id>/`,记录(档案/照片地址/拍摄与上传状态/检测状态与失败原因/评分/文案/精华/耗时/raw 文件名/重试关系)追加到 `data/records.jsonl`。C 端响应结构不变。
|
||
- **记录列表**:多条件筛选(时间区间/检测状态/失败原因/年龄/产品组别/维度+档位/记录ID),行点击开详情抽屉(照片大图与下载、状态、六分类与三维度评分表、文案全文、护理建议、精华与触发判据、计算备注、接口原始 JSON 折叠查看、手动重试)。
|
||
- **时间口径**:后台列表/异常/详情显示、纯日期筛选、今日及每日统计、CSV时间统一为北京时间(UTC+8),由 `js/beijing-time.js` 共享处理,不依赖服务器或浏览器时区。原始记录及JSON接口时间仍为UTC ISO;完整ISO筛选保留显式时区对应的时间点。
|
||
- **失败与异常**:失败原因分布条 + 失败列表(每条可一键重试:用原照片重新检测,生成新记录并挂版本链)。
|
||
- **统计概览**:今日/累计/成功率/平均耗时卡片,三维度档位分布,产品组别分布(纯 CSS 饼图),近 14 天每日检测量柱状图,按日期区间导出 CSV(UTF-8 BOM,不含 raw_json)。
|
||
- **演示数据**:`node h5/data/seed.js` 生成约 10 条演示记录(覆盖重写 records.jsonl,照片复制自 `reference/facepp-skinanalyze/examples/images/` 真实三视图)。
|
||
|
||
### Admin API(统一返回 `{ok:false,message}` 错误)
|
||
|
||
| 接口 | 说明 |
|
||
| --- | --- |
|
||
| `GET /api/admin/records` | 分页查询。参数:`from/to`(ISO 日期)、`detect_status`、`fail_code`、`age_min/age_max`、`group`、`dim`(firm/vita/stab)+`dim_level`、`q`(record_id 前缀)、`page`、`page_size`(默认 20)、`only_fail=1`。返回 `{total,page,page_size,items}`(倒序,含照片 URL,不含 raw_json 与文案全文) |
|
||
| `GET /api/admin/records/:id` | 完整记录 + `raw_json` 全文(读失败为 null)+ `photos` URL + `versions`(版本链:父记录与重试子记录) |
|
||
| `POST /api/admin/records/:id/retry` | 手动重试:原照片重新检测,生成新记录(`retry_of`=原 id),原记录 `retry_count+1`,返回新记录 |
|
||
| `GET /api/admin/photos/:id/:which` | 照片回显(`which`=front/left/right),`image/jpeg` inline;文件不存在 404 |
|
||
| `GET /api/admin/stats` | 今日/累计检测量、成功率、平均耗时、失败原因分布、三维度档位分布、产品组别分布、近 14 天每日检测量/成功数 |
|
||
| `GET /api/admin/export?from=&to=` | CSV 导出(UTF-8 BOM,Content-Disposition attachment 带日期区间文件名);禁止 raw_json 进单元格(Excel 单格 32767 字符截断) |
|
||
|
||
## 10.09 更新
|
||
|
||
新版已增加登录入口、后端账号隔离的最近 5 份完整报告、七项独立检测视图和新版话术/素材。
|
||
本机浏览器只缓存随机身份凭据,报告保存在服务器;微信需公网 HTTPS、具备网页授权权限的服务号及相应环境变量。
|
||
详见 [新版实施与验收](../docs/10.09新版H5实施与验收.md) 和 [微信登录接入与公网部署说明](../docs/微信登录接入与公网部署说明.md)。下方旧功能说明以这两份更新文档为准。
|
||
|
||
2026-10-15 护理展示更新:`ReportUI.build` 从七项独立小维度分中选最低三项,再将属于当前优先关注大维度的入选项前置;同分按关注优先、文档顺序确定。肤色、水分分析及护理各自独立分档,不再共用肤色水润合成分。新版护理全文以 `reference/AI测肤报告页话术修改.md` 为准;不改变原三维度/综合得分/产品推荐,不批量改写历史presentation。详见 [七项护理选三与关注优先](../docs/七项护理选三与关注优先-2026-10-15.md)。
|
||
|
||
随后用户确认取消合成分参与维度计分:透亮感改为独立肤色、水分、平滑度三项P的最低有效值,不再平均肤色和水分。新分数用于总分、优先关注及原有边界推荐判断。后台新记录展示七项独立分数,历史记录不重算;旧tone字段仅为兼容。详见 [新版透亮感公式](../docs/透亮感改为三项独立最低分-2026-10-15.md)。
|
||
|
||
## 部署
|
||
|
||
需要 Node ≥ 18 运行 `server.js`(负责静态与 API 中转),**不能用纯静态托管**(密钥不能进前端)。
|
||
|
||
```bash
|
||
PORT=8080 node server.js # 建议 pm2 / systemd 常驻
|
||
```
|
||
|
||
### 线上部署(juzi.xiangsilian.com)
|
||
|
||
- Node 只绑回环(`HOST` 默认 `127.0.0.1`,公网入口仅 nginx;需局域网调试才 `HOST=0.0.0.0`),nginx 反代 + certbot HTTPS,见 `/etc/nginx/sites-available/juzi`。
|
||
- **后台 Basic Auth**:nginx 对 `admin.html`、`css/admin.css`、`js/admin.js`、`/api/admin/` 做 `auth_basic`(账密 `/etc/nginx/.htpasswd-juzi`),`/data/`(照片/原始 JSON/记录库)由 nginx 404 + server.js 静态层 403 双重封锁,照片只能走鉴权后的 `/api/admin/photos`。**上线正式版前应替换为文档要求的后台账号体系。**
|
||
- 服务常驻建议 systemd:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=juzi skin h5
|
||
After=network.target
|
||
[Service]
|
||
WorkingDirectory=/home/ubuntu/juzi/h5
|
||
ExecStart=/usr/bin/node server.js
|
||
Restart=always
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
### 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
|
||
server {
|
||
listen 443 ssl;
|
||
server_name example.com;
|
||
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8080;
|
||
proxy_set_header Host $host;
|
||
client_max_body_size 20m; # 与 server.js 请求上限一致
|
||
}
|
||
}
|
||
```
|
||
|
||
> **HTTPS 必需**:getUserMedia 相机权限仅在安全上下文可用(localhost 除外)。正式域名必须上 HTTPS,否则前端自动降级为系统相机拍照。
|
||
|
||
## 已知占位清单
|
||
|
||
- 产品图(prod-1.jpg)为设计稿素材;检测图已替换为用户实拍。
|
||
- 语音 14 条已由 `tools/gen_voice.py` 生成;改文案后重跑脚本并同步 `manifest.json`。
|
||
- 「了解更多」为开发中占位(Toast 提示);拍照页右下按钮为切换前后镜头(系统相机降级模式下提示在系统相机内切换)。
|
||
- 产品推荐卡(COREⅡ 双仓精华液)为设计稿固定素材,不随精华推荐档位(25+/35+/50+)变化,待产品提供三档对应素材。
|
||
- **姿态与距离参数已用模拟摄像头校准**(2026-09-27):以 C3 三视图(+45° 侧脸)生成 Y4M 模拟摄像头视频,闭环修正了 yaw 增益(2.2→3.8)、pitch 基线(0.32→0.45)、check() 双重镜像符号 bug、距离阈值(0.35/0.8→0.26/0.58;2026-10 预览缩小 15% 后按比例换算为 0.30/0.68)、检测参数(320/0.4→416/0.3)与漏检 3 帧容忍;自动三连拍在无头浏览器全程通过(e2e E7)。**真机仍建议复核一次**(尤其 2026-10 新增的居中/亮度阈值):不同机型前置镜像策略/取景比例可能微调体验(校准接口:调用方 `mirrored` 标志与 `HeadPose.config`)。
|
||
|
||
## 技术说明
|
||
|
||
- 尺寸换算:设计稿 1080px 基准 ÷ 2.88 = 375px 落地基准。
|
||
- 图片性能:首屏背景 preload;首屏外 `loading="lazy"`。
|
||
- 快门防连点/双流程令牌(camSeq/camSeqCam)防竞态;拍照页取景框外纯黑(对齐 Figma),白字直接落在黑色上。
|
||
- face-api.js 0.22.2 与模型全部本地化(`vendor/faceapi/`,约 1.4MB),运行时零外部请求,微信 webview / iOS Safari 弱网与内网部署可用;许可证(MIT / Apache-2.0)见 `vendor/faceapi/README.md`。
|