Compare commits

...
18 Commits
Author SHA1 Message Date
xsl e53c809522 docs(deploy): record third feedback production verification 2026-10-12 16:38:36 +08:00
xsl c7b085e795 feat(h5): align third feedback with latest PSD and scanning screen 2026-10-12 16:33:10 +08:00
xsl f3e7eac909 test(deploy): verify second feedback release on public site 2026-10-12 14:40:25 +08:00
xsl fe9623a1d7 feat(h5): apply second customer UI feedback and homepage refinements 2026-10-12 14:35:39 +08:00
xsl abb1c4c1de docs(deploy): record customer feedback release verification 2026-10-12 13:18:21 +08:00
xsl d7ae748e08 feat(h5): apply October 12 customer report feedback 2026-10-12 13:15:09 +08:00
xsl 3193a4b683 docs(auth): record deployed login performance verification 2026-10-12 11:03:47 +08:00
xsl d2931a41e4 feat(auth): clarify login identity and speed up mobile entry 2026-10-12 11:01:49 +08:00
xsl 85790ef985 docs(auth): record live preflight and portable log checks 2026-10-12 10:11:44 +08:00
xsl d68f841f0e fix(auth): harden WeChat login and explicit identity switching 2026-10-12 10:07:54 +08:00
xsl 0ec5e5dc32 fix(report): recommend by measured skin age and align admin results 2026-10-12 00:23:57 +08:00
xsl 7518dc2307 fix(h5): sync latest report copy and deployment verification 2026-10-11 22:59:21 +08:00
Ubuntu 3dda4ec7ff 话术有修改 2026-10-11 22:52:15 +08:00
xsl 55b3a45282 添加参考图 psd 2026-10-11 17:51:48 +08:00
xsl ce8d0ff1f7 feat(h5): align October UI and add WeChat auth with saved reports 2026-10-11 17:28:11 +08:00
xsl ee744b6b5f save 2026-10-10 14:24:32 +08:00
root 652c977cec 第二版修改 2026-10-10 09:46:17 +08:00
root 4b26288098 save 2026-10-09 23:41:04 +08:00
177 changed files with 28053 additions and 2766 deletions
+1
View File
@@ -0,0 +1 @@
kpx234XimQGtFPo6
+42
View File
@@ -0,0 +1,42 @@
# UI 外发版 10.09 图片资源
来源:用户提供的 `UI外发版10.09.psd`。按 Photoshop 图层原样导出,未重新生成或重绘素材。
## 文件位置
- `pages/`:10 个画板的原尺寸 PNG,宽 1080px。包含登录、启动、授权弹窗、我的报告、档案填写、拍照弹窗、三视图拍照及长报告。
- `pages-overview.jpg`:全部页面缩略总览,按 PSD 原始画板顺序排列。
- `assets-overview.jpg`:独立切图总览。
- `manifest.json`:页面与切图的尺寸、文件名、图层索引、边界、可见状态及 SHA-256;同时保存完整图层树及文字,便于后续还原。
- `../../h5/assets/img/ui-20261009/`:新版页面使用的独立图片,与旧版素材分开存放。H5 使用去除原生顶栏的 `*-h5.jpg` 衍生背景。
## 前端素材对应
| 文件 | 用途 |
| --- | --- |
| `bg-splash.jpg` | 启动页原始照片,不含独立标题、Logo 与按钮 |
| `bg-profile.jpg` / `bg-login.jpg` | 档案页 / 登录页背景,按画板裁剪,保留设计里的叠色 |
| `bg-report.jpg` / `bg-history.jpg` | 长报告 / 我的报告页纯背景,按画板裁剪 |
| `skin-0-dry.png` … `skin-4-mixedoily.png` | 五档肤质插画;也导出了隐藏状态中的变体 |
| `product-25.png` / `product-35.png` / `product-50.png` | 三档产品图,分别取对应产品组中的主视觉图层 |
| `brand-*.png` / `product-badge.png` | 品牌标志与产品标志 |
| `icon-*.png` | 关闭、返回、下拉、相册、切换相机、快门、微信、游客图标 |
| `score-ring-*.png` | 设计稿中的评分环参考素材;实际评分仍应动态绘制 |
| `camera-mask-front.png` / `camera-mask-right.png` / `camera-mask-left.png` | 三步拍照轮廓蒙版,保留画板坐标与透明区域 |
| `camera-guide-front.png` / `camera-guide-right.png` / `camera-guide-left.png` | 对应三步的眼睛与鼻梁虚线定位叠层,与蒙版使用相同画板坐标 |
| `sample-*.png` | 设计稿内检测示意图,仅供视觉参考,不代替用户真实检测图 |
照片使用高质量渐进 JPG;其他素材使用 PNG 保留透明通道。切图按原图层尺寸输出,不强行放大,不将文字与按钮烘焙进背景。整页预览保留设计稿原有系统状态栏和重复/隐藏图层状态。
`sample-lines.png` 来自隐藏的备选示意图层,设计文件内各分类存在重复示意图,文件名不表示图片已经过该项目评分引擎的真实检测。产品图按主视觉独立导出,卡片圆角和裁剪需由前端容器实现。
注意:PSD 的画板名「左面」实际文案为第 2 步「右脸出镜」,「右面」实际文案为第 3 步「左脸出镜」。切图按文案中的用户脸部方向命名,清单保存了对应图层路径。
## 重新导出
```bash
python -m pip install psd-tools Pillow aggdraw
python tools/export_psd_assets.py '/absolute/path/UI外发版10.09.psd'
```
重新导出会更新本版本目录内的导出文件,不会覆盖 `h5/assets/img/` 下原有同名线上图片。
Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

@@ -0,0 +1,780 @@
{
"viewport": {
"width": 375,
"height": 726
},
"pages": {
"01-splash": {
"screen": "s-splash",
"text": "Life in Full\n为更好的生命表现\n\n肌肤状态检测\n开始检测",
"overlays": [],
"rects": [
{
"class": "splash-slogan",
"rect": {
"x": 0,
"y": 77.671875,
"width": 375,
"height": 63.46875,
"top": 77.671875,
"right": 375,
"bottom": 141.140625,
"left": 0
}
},
{
"class": "splash-bottom",
"rect": {
"x": 0,
"y": 551.984375,
"width": 375,
"height": 84,
"top": 551.984375,
"right": 375,
"bottom": 635.984375,
"left": 0
}
}
]
},
"02-privacy": {
"screen": "s-splash",
"text": "Life in Full\n为更好的生命表现\n\n肌肤状态检测\n开始检测",
"overlays": [
{
"id": "p-privacy",
"text": "欢迎体验1'me\n肌肤状态评估服务!\n\n我们将采集您的面部影像,为您生成专属肌肤分析与状态管理方案。所有数据严格保密,仅用于本次检测分析。\n\n我已阅读并同意相关隐私协议\n继续"
}
],
"rects": [
{
"class": "splash-slogan",
"rect": {
"x": 0,
"y": 77.671875,
"width": 375,
"height": 63.46875,
"top": 77.671875,
"right": 375,
"bottom": 141.140625,
"left": 0
}
},
{
"class": "splash-bottom",
"rect": {
"x": 0,
"y": 551.984375,
"width": 375,
"height": 84,
"top": 551.984375,
"right": 375,
"bottom": 635.984375,
"left": 0
}
}
]
},
"03-profile": {
"screen": "s-profile",
"text": "您的肌肤状态\n您的出生年月\n您的性别\n您的所在地区\n您的日常肤质\n偏干\n偏油\n水油平衡\n干性肌肤\n\n全脸干燥,容易紧绷、缺乏光泽\n\n混合偏干肌肤\n\n两颊偏干,T区油脂较少或偶尔出油\n\n中性肌肤\n\n水油平衡,肤感舒适,无明显干燥出油\n\n油性肌肤\n\n全脸易出油,容易泛油光、毛孔明显\n\n混合偏油肌肤\n\nT区易出油,两颊相对清爽或轻微干燥\n\n提交",
"overlays": [],
"rects": [
{
"class": "profile-body",
"rect": {
"x": 0,
"y": 0,
"width": 375,
"height": 726,
"top": 0,
"right": 375,
"bottom": 726,
"left": 0
}
},
{
"class": "page-title",
"rect": {
"x": 64,
"y": 34,
"width": 247,
"height": 31,
"top": 34,
"right": 311,
"bottom": 65,
"left": 64
}
}
]
},
"04-guide": {
"screen": "s-profile",
"text": "您的肌肤状态\n您的出生年月\n1996年1月\n您的性别\n女\n您的所在地区\n北京市\n您的日常肤质\n偏干\n偏油\n水油平衡\n干性肌肤\n\n全脸干燥,容易紧绷、缺乏光泽\n\n混合偏干肌肤\n\n两颊偏干,T区油脂较少或偶尔出油\n\n中性肌肤\n\n水油平衡,肤感舒适,无明显干燥出油\n\n油性肌肤\n\n全脸易出油,容易泛油光、毛孔明显\n\n混合偏油肌肤\n\nT区易出油,两颊相对清爽或轻微干燥\n\n提交",
"overlays": [
{
"id": "p-photoguide",
"text": "Life in Full\n为更好的生命表现\n\n请拍摄或上传\n正面及左右侧面照片\n开始"
}
],
"rects": [
{
"class": "profile-body",
"rect": {
"x": 0,
"y": 0,
"width": 375,
"height": 726,
"top": 0,
"right": 375,
"bottom": 726,
"left": 0
}
},
{
"class": "page-title",
"rect": {
"x": 64,
"y": 34,
"width": 247,
"height": 31,
"top": 34,
"right": 311,
"bottom": 65,
"left": 64
}
}
]
},
"05-camera-front": {
"screen": "s-cam",
"text": "1/3\n请正对屏幕\n\n检测引擎加载中…\n\n识别不到?请正对镜头,并到光线充足处\n\n正脸拍摄完成\n准备拍摄下一张\n正脸居中\n取下眼镜\n光线充足",
"overlays": [],
"rects": [
{
"class": "cam-step",
"rect": {
"x": 0,
"y": 51.53125,
"width": 375,
"height": 64.5,
"top": 51.53125,
"right": 375,
"bottom": 116.03125,
"left": 0
}
},
{
"class": "cam-guide",
"rect": {
"x": 0,
"y": 542.390625,
"width": 375,
"height": 21,
"top": 542.390625,
"right": 375,
"bottom": 563.390625,
"left": 0
}
},
{
"class": "cam-controls",
"rect": {
"x": 0,
"y": 626.25,
"width": 375,
"height": 62,
"top": 626.25,
"right": 375,
"bottom": 688.25,
"left": 0
}
}
]
},
"06-camera-right": {
"screen": "s-cam",
"text": "1/3\n请正对屏幕\n\n检测引擎加载中…\n\n识别不到?请正对镜头,并到光线充足处\n\n正脸拍摄完成\n准备拍摄下一张\n正脸居中\n取下眼镜\n光线充足",
"overlays": [],
"rects": [
{
"class": "cam-step",
"rect": {
"x": 0,
"y": 51.53125,
"width": 375,
"height": 64.5,
"top": 51.53125,
"right": 375,
"bottom": 116.03125,
"left": 0
}
},
{
"class": "cam-guide",
"rect": {
"x": 0,
"y": 542.390625,
"width": 375,
"height": 21,
"top": 542.390625,
"right": 375,
"bottom": 563.390625,
"left": 0
}
},
{
"class": "cam-controls",
"rect": {
"x": 0,
"y": 626.25,
"width": 375,
"height": 62,
"top": 626.25,
"right": 375,
"bottom": 688.25,
"left": 0
}
}
]
},
"07-camera-left": {
"screen": "s-cam",
"text": "2/3\n请右脸出镜\n\n检测引擎加载中…\n\n识别不到?请正对镜头,并到光线充足处\n\n正脸拍摄完成\n下一张:右脸出镜\n请向左转头\n右脸居中\n取下眼镜\n光线充足",
"overlays": [],
"rects": [
{
"class": "cam-step",
"rect": {
"x": 0,
"y": 51.53125,
"width": 375,
"height": 64.5,
"top": 51.53125,
"right": 375,
"bottom": 116.03125,
"left": 0
}
},
{
"class": "cam-guide",
"rect": {
"x": 0,
"y": 542.390625,
"width": 375,
"height": 21,
"top": 542.390625,
"right": 375,
"bottom": 563.390625,
"left": 0
}
},
{
"class": "cam-controls",
"rect": {
"x": 0,
"y": 626.25,
"width": 375,
"height": 62,
"top": 626.25,
"right": 375,
"bottom": 688.25,
"left": 0
}
}
]
},
"08-report-top": {
"screen": "s-report",
"text": "您的肌肤状态表现\n70分\n综合评分\n肌肤年龄\n27岁\n比实际年龄年轻1岁\n紧塑力\n7.7\n优先关注\n活力感\n8.7\n稳定力\n9.1\n近期优先关注:紧塑力\n紧塑提拉力,轮廓上扬不松垮。\n紧塑力\n活力感\n稳定力\n轮廓\n\n下颌线角度左104.54°/右103.75°(标准区间116–120°,双侧均偏离标准,提示下颌缘支撑力下降);眼袋1级、黑眼圈1级;中面部支撑与轮廓清晰度处于预警水平。建议加强轮廓紧致护理:重点针对下颌线与下颌缘区域使用紧致类精华,配合由下向上的提拉按摩手法,同时加强眼周支撑护理,将紧致防线前置。\n\n皱纹\n\n全脸皱纹159条、细纹150条,最差区域为左嘴角纹(分区分77,最高严重度3级),部分区域已现静态纹趋势。建议针对眼周、法令纹等高发区使用视黄醇/胜肽类淡纹精华,遏制静态纹转化。\n\n*您的肌肤状态会随年龄、环境、作息和生活节奏动态变化。\n*本次检测仅代表当前肌肤状态参考。\n\n护理建议\n\n1、你的日常肤质为干性肌肤:全脸皮脂分泌偏少,角质层保水能力弱,易紧绷起屑。护理以高效保湿与封闭锁水为核心,洁面选用温和低泡产品,避免频繁去角质,面霜可选择含神经酰胺、角鲨烷等封闭性成分的配方。\n\n2、加强纹路分级管理,重点针对眼周、法令纹等动态纹高发区域,使用视黄醇/胜肽类淡纹精华并配合点按手法,遏制静态纹转化。\n\n3、加强细腻度管理,避免过度清洁破坏角质屏障,重点关注毛孔显著区域,配合低浓度酸类(如水杨酸)周期护理与柔润精华。\n\n4、建议搭配使用35+支撑精华:专为轻熟肌打造,提升弹韧表现、维持面部轮廓清晰度,改善疲态并延缓衰老速度。\n\n5、保持侧卧改仰卧的睡姿习惯,减少枕巾摩擦;控制屏幕时间,缓解眼周疲劳\n\n好状态,是内外同调的长期节奏。\n\n精华推荐\n\n35+支撑精华\n\n推荐依据:皱纹得分偏低\n\n感受高光时刻\n从日常的好状态开始\n以我自在有势挺括\n双仓精华液\n\n参透皮下根源 28天校准皮肤状态\n\n了解更多",
"overlays": [],
"rects": [
{
"class": "report-title",
"rect": {
"x": 18,
"y": 0,
"width": 339,
"height": 86,
"top": 0,
"right": 357,
"bottom": 86,
"left": 18
}
},
{
"class": "card card-summary",
"rect": {
"x": 18,
"y": 86,
"width": 339,
"height": 140,
"top": 86,
"right": 357,
"bottom": 226,
"left": 18
}
},
{
"class": "card card-dims",
"rect": {
"x": 18,
"y": 238,
"width": 339,
"height": 201.1875,
"top": 238,
"right": 357,
"bottom": 439.1875,
"left": 18
}
},
{
"class": "card card-detail",
"rect": {
"x": 18,
"y": 451.1875,
"width": 339,
"height": 334.96875,
"top": 451.1875,
"right": 357,
"bottom": 786.15625,
"left": 18
}
},
{
"class": "card card-care",
"rect": {
"x": 18,
"y": 852.15625,
"width": 339,
"height": 510,
"top": 852.15625,
"right": 357,
"bottom": 1362.15625,
"left": 18
}
},
{
"class": "card card-essence",
"rect": {
"x": 18,
"y": 1374.15625,
"width": 339,
"height": 119.5,
"top": 1374.15625,
"right": 357,
"bottom": 1493.65625,
"left": 18
}
},
{
"class": "card card-product",
"rect": {
"x": 18,
"y": 1608.34375,
"width": 339,
"height": 215.671875,
"top": 1608.34375,
"right": 357,
"bottom": 1824.015625,
"left": 18
}
}
]
},
"09-report-tab-0": {
"screen": "s-report",
"text": "您的肌肤状态表现\n70分\n综合评分\n肌肤年龄\n27岁\n比实际年龄年轻1岁\n紧塑力\n7.7\n优先关注\n活力感\n8.7\n稳定力\n9.1\n近期优先关注:紧塑力\n紧塑提拉力,轮廓上扬不松垮。\n紧塑力\n活力感\n稳定力\n轮廓\n\n下颌线角度左104.54°/右103.75°(标准区间116–120°,双侧均偏离标准,提示下颌缘支撑力下降);眼袋1级、黑眼圈1级;中面部支撑与轮廓清晰度处于预警水平。建议加强轮廓紧致护理:重点针对下颌线与下颌缘区域使用紧致类精华,配合由下向上的提拉按摩手法,同时加强眼周支撑护理,将紧致防线前置。\n\n皱纹\n\n全脸皱纹159条、细纹150条,最差区域为左嘴角纹(分区分77,最高严重度3级),部分区域已现静态纹趋势。建议针对眼周、法令纹等高发区使用视黄醇/胜肽类淡纹精华,遏制静态纹转化。\n\n*您的肌肤状态会随年龄、环境、作息和生活节奏动态变化。\n*本次检测仅代表当前肌肤状态参考。\n\n护理建议\n\n1、你的日常肤质为干性肌肤:全脸皮脂分泌偏少,角质层保水能力弱,易紧绷起屑。护理以高效保湿与封闭锁水为核心,洁面选用温和低泡产品,避免频繁去角质,面霜可选择含神经酰胺、角鲨烷等封闭性成分的配方。\n\n2、加强纹路分级管理,重点针对眼周、法令纹等动态纹高发区域,使用视黄醇/胜肽类淡纹精华并配合点按手法,遏制静态纹转化。\n\n3、加强细腻度管理,避免过度清洁破坏角质屏障,重点关注毛孔显著区域,配合低浓度酸类(如水杨酸)周期护理与柔润精华。\n\n4、建议搭配使用35+支撑精华:专为轻熟肌打造,提升弹韧表现、维持面部轮廓清晰度,改善疲态并延缓衰老速度。\n\n5、保持侧卧改仰卧的睡姿习惯,减少枕巾摩擦;控制屏幕时间,缓解眼周疲劳\n\n好状态,是内外同调的长期节奏。\n\n精华推荐\n\n35+支撑精华\n\n推荐依据:皱纹得分偏低\n\n感受高光时刻\n从日常的好状态开始\n以我自在有势挺括\n双仓精华液\n\n参透皮下根源 28天校准皮肤状态\n\n了解更多",
"overlays": [],
"rects": [
{
"class": "report-title",
"rect": {
"x": 18,
"y": -60,
"width": 339,
"height": 86,
"top": -60,
"right": 357,
"bottom": 26,
"left": 18
}
},
{
"class": "card card-summary",
"rect": {
"x": 18,
"y": 26,
"width": 339,
"height": 140,
"top": 26,
"right": 357,
"bottom": 166,
"left": 18
}
},
{
"class": "card card-dims",
"rect": {
"x": 18,
"y": 178,
"width": 339,
"height": 201.1875,
"top": 178,
"right": 357,
"bottom": 379.1875,
"left": 18
}
},
{
"class": "card card-detail",
"rect": {
"x": 18,
"y": 391.1875,
"width": 339,
"height": 334.96875,
"top": 391.1875,
"right": 357,
"bottom": 726.15625,
"left": 18
}
},
{
"class": "card card-care",
"rect": {
"x": 18,
"y": 792.15625,
"width": 339,
"height": 510,
"top": 792.15625,
"right": 357,
"bottom": 1302.15625,
"left": 18
}
},
{
"class": "card card-essence",
"rect": {
"x": 18,
"y": 1314.15625,
"width": 339,
"height": 119.5,
"top": 1314.15625,
"right": 357,
"bottom": 1433.65625,
"left": 18
}
},
{
"class": "card card-product",
"rect": {
"x": 18,
"y": 1548.34375,
"width": 339,
"height": 215.671875,
"top": 1548.34375,
"right": 357,
"bottom": 1764.015625,
"left": 18
}
}
]
},
"09-report-tab-1": {
"screen": "s-report",
"text": "您的肌肤状态表现\n70分\n综合评分\n肌肤年龄\n27岁\n比实际年龄年轻1岁\n紧塑力\n7.7\n优先关注\n活力感\n8.7\n稳定力\n9.1\n近期优先关注:紧塑力\n紧塑提拉力,轮廓上扬不松垮。\n紧塑力\n活力感\n稳定力\n光滑\n\n皮肤表面平整度较低,最差毛孔区位于额头(分区毛孔分32),粗大毛孔1041颗、黑头113颗,T区出油面积57%、左右脸颊20%/2%,纹理分仅82,整体细腻度不足。建议避免过度清洁,重点关注额头,配合低浓度酸类周期护理与柔润精华。\n\n肤色水润\n\n肤色ITA 58.6(极白,HA 56.2),色素分92,整体稳定但存在局部暗沉倾向(未见明显色斑标记,检出褐色斑37处);水分分78,缺水面积19.8%、严重度22。建议加强抗氧化与光泽管理,做好基础防晒与补水。\n\n*您的肌肤状态会随年龄、环境、作息和生活节奏动态变化。\n*本次检测仅代表当前肌肤状态参考。\n\n护理建议\n\n1、你的日常肤质为干性肌肤:全脸皮脂分泌偏少,角质层保水能力弱,易紧绷起屑。护理以高效保湿与封闭锁水为核心,洁面选用温和低泡产品,避免频繁去角质,面霜可选择含神经酰胺、角鲨烷等封闭性成分的配方。\n\n2、加强纹路分级管理,重点针对眼周、法令纹等动态纹高发区域,使用视黄醇/胜肽类淡纹精华并配合点按手法,遏制静态纹转化。\n\n3、加强细腻度管理,避免过度清洁破坏角质屏障,重点关注毛孔显著区域,配合低浓度酸类(如水杨酸)周期护理与柔润精华。\n\n4、建议搭配使用35+支撑精华:专为轻熟肌打造,提升弹韧表现、维持面部轮廓清晰度,改善疲态并延缓衰老速度。\n\n5、保持侧卧改仰卧的睡姿习惯,减少枕巾摩擦;控制屏幕时间,缓解眼周疲劳\n\n好状态,是内外同调的长期节奏。\n\n精华推荐\n\n35+支撑精华\n\n推荐依据:皱纹得分偏低\n\n感受高光时刻\n从日常的好状态开始\n以我自在有势挺括\n双仓精华液\n\n参透皮下根源 28天校准皮肤状态\n\n了解更多",
"overlays": [],
"rects": [
{
"class": "report-title",
"rect": {
"x": 18,
"y": -60,
"width": 339,
"height": 86,
"top": -60,
"right": 357,
"bottom": 26,
"left": 18
}
},
{
"class": "card card-summary",
"rect": {
"x": 18,
"y": 26,
"width": 339,
"height": 140,
"top": 26,
"right": 357,
"bottom": 166,
"left": 18
}
},
{
"class": "card card-dims",
"rect": {
"x": 18,
"y": 178,
"width": 339,
"height": 201.1875,
"top": 178,
"right": 357,
"bottom": 379.1875,
"left": 18
}
},
{
"class": "card card-detail",
"rect": {
"x": 18,
"y": 391.1875,
"width": 339,
"height": 306.53125,
"top": 391.1875,
"right": 357,
"bottom": 697.71875,
"left": 18
}
},
{
"class": "card card-care",
"rect": {
"x": 18,
"y": 763.71875,
"width": 339,
"height": 510,
"top": 763.71875,
"right": 357,
"bottom": 1273.71875,
"left": 18
}
},
{
"class": "card card-essence",
"rect": {
"x": 18,
"y": 1285.71875,
"width": 339,
"height": 119.5,
"top": 1285.71875,
"right": 357,
"bottom": 1405.21875,
"left": 18
}
},
{
"class": "card card-product",
"rect": {
"x": 18,
"y": 1519.90625,
"width": 339,
"height": 215.671875,
"top": 1519.90625,
"right": 357,
"bottom": 1735.578125,
"left": 18
}
}
]
},
"09-report-tab-2": {
"screen": "s-report",
"text": "您的肌肤状态表现\n70分\n综合评分\n肌肤年龄\n27岁\n比实际年龄年轻1岁\n紧塑力\n7.7\n优先关注\n活力感\n8.7\n稳定力\n9.1\n近期优先关注:紧塑力\n紧塑提拉力,轮廓上扬不松垮。\n紧塑力\n活力感\n稳定力\n敏感\n\n泛红面积0.6%,红区分99,屏障状态稳定,未见明显敏感信号。建议维持现有温和护理,避免过度去角质。\n\n痘痘\n\n检出9处皮损(闭口粉刺6处、丘疹3处)。建议尽快进行针对性抗痘护理,避免挤压与刺激,必要时寻求皮肤科专业处理。\n\n*您的肌肤状态会随年龄、环境、作息和生活节奏动态变化。\n*本次检测仅代表当前肌肤状态参考。\n\n护理建议\n\n1、你的日常肤质为干性肌肤:全脸皮脂分泌偏少,角质层保水能力弱,易紧绷起屑。护理以高效保湿与封闭锁水为核心,洁面选用温和低泡产品,避免频繁去角质,面霜可选择含神经酰胺、角鲨烷等封闭性成分的配方。\n\n2、加强纹路分级管理,重点针对眼周、法令纹等动态纹高发区域,使用视黄醇/胜肽类淡纹精华并配合点按手法,遏制静态纹转化。\n\n3、加强细腻度管理,避免过度清洁破坏角质屏障,重点关注毛孔显著区域,配合低浓度酸类(如水杨酸)周期护理与柔润精华。\n\n4、建议搭配使用35+支撑精华:专为轻熟肌打造,提升弹韧表现、维持面部轮廓清晰度,改善疲态并延缓衰老速度。\n\n5、保持侧卧改仰卧的睡姿习惯,减少枕巾摩擦;控制屏幕时间,缓解眼周疲劳\n\n好状态,是内外同调的长期节奏。\n\n精华推荐\n\n35+支撑精华\n\n推荐依据:皱纹得分偏低\n\n感受高光时刻\n从日常的好状态开始\n以我自在有势挺括\n双仓精华液\n\n参透皮下根源 28天校准皮肤状态\n\n了解更多",
"overlays": [],
"rects": [
{
"class": "report-title",
"rect": {
"x": 18,
"y": -60,
"width": 339,
"height": 86,
"top": -60,
"right": 357,
"bottom": 26,
"left": 18
}
},
{
"class": "card card-summary",
"rect": {
"x": 18,
"y": 26,
"width": 339,
"height": 140,
"top": 26,
"right": 357,
"bottom": 166,
"left": 18
}
},
{
"class": "card card-dims",
"rect": {
"x": 18,
"y": 178,
"width": 339,
"height": 201.1875,
"top": 178,
"right": 357,
"bottom": 379.1875,
"left": 18
}
},
{
"class": "card card-detail",
"rect": {
"x": 18,
"y": 391.1875,
"width": 339,
"height": 303,
"top": 391.1875,
"right": 357,
"bottom": 694.1875,
"left": 18
}
},
{
"class": "card card-care",
"rect": {
"x": 18,
"y": 760.1875,
"width": 339,
"height": 510,
"top": 760.1875,
"right": 357,
"bottom": 1270.1875,
"left": 18
}
},
{
"class": "card card-essence",
"rect": {
"x": 18,
"y": 1282.1875,
"width": 339,
"height": 119.5,
"top": 1282.1875,
"right": 357,
"bottom": 1401.6875,
"left": 18
}
},
{
"class": "card card-product",
"rect": {
"x": 18,
"y": 1516.375,
"width": 339,
"height": 215.671875,
"top": 1516.375,
"right": 357,
"bottom": 1732.046875,
"left": 18
}
}
]
},
"splash-375x844": {
"screen": "s-splash",
"text": "Life in Full\n为更好的生命表现\n\n肌肤状态检测\n开始检测",
"overlays": [],
"rects": [
{
"class": "splash-slogan",
"rect": {
"x": 0,
"y": 90.296875,
"width": 375,
"height": 63.46875,
"top": 90.296875,
"right": 375,
"bottom": 153.765625,
"left": 0
}
},
{
"class": "splash-bottom",
"rect": {
"x": 0,
"y": 655.359375,
"width": 375,
"height": 84,
"top": 655.359375,
"right": 375,
"bottom": 739.359375,
"left": 0
}
}
]
},
"splash-390x844": {
"screen": "s-splash",
"text": "Life in Full\n为更好的生命表现\n\n肌肤状态检测\n开始检测",
"overlays": [],
"rects": [
{
"class": "splash-slogan",
"rect": {
"x": 0,
"y": 90.296875,
"width": 390,
"height": 66,
"top": 90.296875,
"right": 390,
"bottom": 156.296875,
"left": 0
}
},
{
"class": "splash-bottom",
"rect": {
"x": 0,
"y": 652.265625,
"width": 390,
"height": 87.09375,
"top": 652.265625,
"right": 390,
"bottom": 739.359375,
"left": 0
}
}
]
},
"splash-414x844": {
"screen": "s-splash",
"text": "Life in Full\n为更好的生命表现\n\n肌肤状态检测\n开始检测",
"overlays": [],
"rects": [
{
"class": "splash-slogan",
"rect": {
"x": 0,
"y": 90.296875,
"width": 414,
"height": 70.0625,
"top": 90.296875,
"right": 414,
"bottom": 160.359375,
"left": 0
}
},
{
"class": "splash-bottom",
"rect": {
"x": 0,
"y": 645.859375,
"width": 414,
"height": 93.5,
"top": 645.859375,
"right": 414,
"bottom": 739.359375,
"left": 0
}
}
]
}
},
"errors": []
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+501
View File
@@ -0,0 +1,501 @@
{
"source": "UI外发版10.09.psd",
"source_sha256": "c7698f5900adc7476286818450d928e8fa2ce2e58cda09375ff2b861629fd58f",
"document_size": [
7330,
16411
],
"native_header_px": 248,
"design_scale": 2.88,
"assets": [
{
"file": "h5/assets/img/ui-20261012/splash-slogan.png",
"source_layers": [
"0/5",
"0/4"
],
"bbox": [
1415,
391,
2133,
608
],
"size": [
718,
217
],
"bytes": 12781,
"sha256": "c9dee693eb3c1d566bb08d3770e7dd2f0c22e7d4e0b81bbe5e10ad8219b26044"
},
{
"file": "h5/assets/img/ui-20261012/brand-lockup.png",
"source_layers": [
"0/1"
],
"bbox": [
1515,
1861,
2032,
1920
],
"size": [
517,
59
],
"bytes": 7013,
"sha256": "3cb697d6c5542562a9cea20554b0772f2c0137b9c3977ff258908024d5a8e9ed"
},
{
"file": "h5/assets/img/ui-20261012/button-start-label.png",
"source_layers": [
"0/3/1"
],
"bbox": [
1696,
2005,
1857,
2045
],
"size": [
161,
40
],
"bytes": 2163,
"sha256": "fd88b174f120845d8045a78a36bc7665516d91011f7ea0ebc7fe200271e17738"
},
{
"file": "h5/assets/img/ui-20261012/button-history-label.png",
"source_layers": [
"0/2/2/2"
],
"bbox": [
1696,
2252,
1859,
2291
],
"size": [
163,
39
],
"bytes": 2024,
"sha256": "0253d761f63bda89301c1eec9df84352a2f5dd1aa97884e27ebf91092c0816d8"
},
{
"file": "h5/assets/img/ui-20261012/product-25.webp",
"source_layers": [
"8/1/0"
],
"bbox": [
1765,
14677,
2269,
15180
],
"size": [
504,
503
],
"bytes": 276778,
"sha256": "c18827342d23bbff2de04c3a2ecbeeae9b16eb96f3a9f7e30f2a66cdc21beddc"
},
{
"file": "h5/assets/img/ui-20261012/product-35.webp",
"source_layers": [
"8/1/1"
],
"bbox": [
1765,
15228,
2269,
15731
],
"size": [
504,
503
],
"bytes": 267094,
"sha256": "c1dc455c8072da17ac06cee386248b358947da65bd190b714f2ccb25360afea4"
},
{
"file": "h5/assets/img/ui-20261012/product-50.webp",
"source_layers": [
"8/1/2"
],
"bbox": [
1765,
15778,
2269,
16281
],
"size": [
504,
503
],
"bytes": 331924,
"sha256": "86647c8882caf62c43eafb1efe495bad3289126cab73a88c8963b7ad64ffa084"
},
{
"file": "h5/assets/img/ui-20261012/scan-waiting.webp",
"source_layers": [
"9"
],
"bbox": [
6250,
5500,
7330,
7591
],
"size": [
1080,
2091
],
"bytes": 11690,
"sha256": "6b661f3d454e9b696b70ca8846fed11243d3f53b95506c2d78b11b6b1a6d9f77"
}
],
"text_styles": [
{
"path": "0/4",
"text": "以我为始 自有表现",
"bbox": [
1502,
545,
2045,
608
],
"transform": [
0.7386429422796229,
0.0,
-1.0090361144648896e-16,
0.7386429422796231,
1772.6682649161658,
602.0113961671652
],
"runs": [
{
"font": "MiSans-Demibold",
"font_size": 90.25561,
"leading": 104.16666
},
{
"font": "MiSans-Demibold",
"font_size": 90.25561,
"leading": 104.16666
}
]
},
{
"path": "0/5",
"text": " ONE ME,\rFROM THE INSIDE OUT",
"bbox": [
1415,
391,
2133,
520
],
"transform": [
0.7386429422796229,
0.0,
-1.0090361144648896e-16,
0.7386429422796231,
1771.9296219738862,
442.37366417300353
],
"runs": [
{
"font": "MiSans-Regular",
"font_size": 90.25561,
"leading": 104.16666
},
{
"font": "MiSans-Regular",
"font_size": 90.25561,
"leading": 104.16666
}
]
},
{
"path": "0/1/1",
"text": "肌肤状态检测",
"bbox": [
1664,
1862,
2032,
1920
],
"transform": [
1.0,
0.0,
0.0,
0.9999999999999999,
1662.281376176642,
1868.3475610977566
],
"runs": [
{
"font": "MiSans-Demibold",
"font_size": 62.5,
"leading": null
},
{
"font": "MiSans-Demibold",
"font_size": 62.5,
"leading": null
}
]
},
{
"path": "1/3/0",
"text": "欢迎体验1'me\r肌肤状态评估服务!",
"bbox": [
2664,
832,
3218,
979
],
"transform": [
1.0,
0.0,
0.0,
0.9999999999999999,
2662.281376176642,
840.3475610977557
],
"runs": [
{
"font": "MiSans-Demibold",
"font_size": 66.66666,
"leading": 83.33334
},
{
"font": "MiSans-Demibold",
"font_size": 66.66666,
"leading": 83.33334
}
]
},
{
"path": "1/3/1",
"text": "我们将采集您的面部影像,为您生成专属肌肤分析与状态管理方案。所有数据严格保密,仅用于本次检测分析。",
"bbox": [
2663,
1045,
3361,
1177
],
"transform": [
0.9999999999999999,
0.0,
0.0,
1.0,
2662.281376176642,
1049.3475610977548
],
"runs": [
{
"font": "MiSans-Regular",
"font_size": 33.33333,
"leading": 50.0
},
{
"font": "MiSans-Regular",
"font_size": 33.33333,
"leading": 50.0
}
]
},
{
"path": "4/7",
"text": "请拍摄或上传\r正面及左右侧面照片",
"bbox": [
1442,
6160,
2036,
6307
],
"transform": [
1.0,
0.0,
0.0,
0.9999999999999999,
1439.281376176642,
6167.347561097756
],
"runs": [
{
"font": "MiSans-Demibold",
"font_size": 66.66666,
"leading": 83.33334
},
{
"font": "MiSans-Demibold",
"font_size": 66.66666,
"leading": 83.33334
}
]
},
{
"path": "4/8",
"text": "为使测试结果更准确,请关闭美颜/滤镜、并确保光线充足均匀。",
"bbox": [
1443,
6372,
2119,
6454
],
"transform": [
0.9999999999999999,
0.0,
0.0,
1.0,
1442.281376176642,
6376.347561097755
],
"runs": [
{
"font": "MiSans-Regular",
"font_size": 33.33333,
"leading": 50.0
},
{
"font": "MiSans-Regular",
"font_size": 33.33333,
"leading": 50.0
}
]
},
{
"path": "8/1/0/5",
"text": "以我本然有光蓄活\r双仓精华液",
"bbox": [
1362,
14850,
1627,
14932
],
"transform": [
1.7691030436028,
0.0,
0.0,
1.769103043602818,
1361.2813761766417,
14854.052217587123
],
"runs": [
{
"font": "MiSans-Semibold",
"font_size": 18.84194,
"leading": 28.26291
},
{
"font": "MiSans-Semibold",
"font_size": 18.84194,
"leading": 28.26291
}
]
},
{
"path": "8/1/0/6",
"text": "1'me 主场精华 I",
"bbox": [
1363,
14779,
1653,
14820
],
"transform": [
1.5358216667040816,
0.0,
0.0,
1.5358216667040878,
1361.2813761766417,
14784.058678621632
],
"runs": [
{
"font": "MiSans-Semibold",
"font_size": 27.12989,
"leading": 28.26291
},
{
"font": "MiSans-Semibold",
"font_size": 27.12989,
"leading": 28.26291
}
]
}
],
"shape_metrics": [
{
"path": "0/3/0",
"bbox": [
1516,
1971,
2038,
2083
],
"corner_radius_psd_px": 20.000065
},
{
"path": "0/2/0",
"bbox": [
1516,
2104,
2038,
2216
],
"corner_radius_psd_px": 20.000065
},
{
"path": "1/2/0",
"bbox": [
2753,
1410,
3275,
1522
],
"corner_radius_psd_px": 20.000065
},
{
"path": "4/2/0",
"bbox": [
1517,
6604,
2039,
6716
],
"corner_radius_psd_px": 20.000065
},
{
"path": "8/1/0/0",
"bbox": [
1285,
14677,
2269,
15180
],
"corner_radius_psd_px": 15.000158
},
{
"path": "1/1",
"bbox": [
2571,
719,
3458,
1621
],
"corner_radius_psd_px": 24.999972
},
{
"path": "4/1",
"bbox": [
1334,
5971,
2221,
6873
],
"corner_radius_psd_px": 24.999972
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

+56
View File
@@ -0,0 +1,56 @@
# 10.09 新版 H5 实施与验收
更新:2026-10-12。
## 按确认项实现
- 保持 H5,无额外网页导航栏,不仿造系统/微信顶部栏。
- 登录在首页之前;复用现有协议弹层。微信网页授权链路已实现,当前内网使用本机浏览器身份,公网限制与部署见《微信登录接入与公网部署说明》。
- 首页更新口号、背景和品牌图,增加「我的报告」。修正重复入口、出镜错字。
- 我的报告独立页面,数量、日期、查看详情、空/失败状态;读取当前账号最近 5 份后端报告。
- 档案页换用新版背景和五类肤质插画,保留原选择器和校验。
- 相机轮廓与眼鼻定位虚线从 PSD 素材提取,前→右→左流程、自动抓拍、降级、语音和前后摄像头逻辑保持。
- 汇总卡合并,名称为「生物可视年龄」「紧塑力」「透亮感」「稳韧力」,不照抄设计样例中的数值。
- 七项视图分成三个独立 Tab 组:轮廓/皱纹;肤色/平滑度/水分;泛红/痘痘。使用真实检测图层或用户照片,不把设计里的模特检测图当成检测结果。
- 根据 `reference/AI测肤报告页话术修改.md` 生成新结论和护理建议。无效字段不冒充 0,无图层明确说明。
- 只显示原算法选中的一款主场精华 I / II / III,不可点击,移除「了解更多」占位入口。
- MiSans 官方原始 Web 字体本地提供,未改造字体文件;「我的报告」底部注明字体使用。
## 文档未定义部分的实施口径
话术文档没有新增计分公式或阈值,因此原六分类/三维度评分、肌龄计算不变。2026-10-12 经用户确认,精华推荐另按[肤龄基准与边界调整](产品推荐规则-肤龄版.md)实施,替代原三重判据;原始肌龄和展示肌龄在后台同时展示,历史快照不重算。旧分档「预警/改善/良好」按低/中/高顺序映射为新文案的「改善/良好/优秀」,后台同步此口径。肤色和水分拆分展示,但继续共享原 `tone` 分类的分档,**不是新增独立评分算法**。
痘痘话术按真实脓包/结节计数决定是否输出中重度皮损说明;没有计数不宣称「未检出」。当真实计数与高分模板冲突时,展示层选择可反映计数的文案,不改变原分数。
护理建议按原六分类得分选取最需要关注的两项,拼接文档对应的「护理 + 生活方式」;若命中肤色水润,分别展示肤色与水分建议,可能为三段。设计里的示例两段不作为截断真实文本的硬限制。
历史详情读取保存时的 `vm`、`presentation`、照片、图层,不用当前评分/话术重新生成历史数据。图像合成有渲染版本防护,切换报告时不会串用上一份图片。
## 验证命令
```bash
node h5/js/skin-score.js
node h5/test/regression.js
node h5/test/recommendation.js
node h5/test/accounts.js
node h5/test/report-persistence.mjs
node h5/test/wechat-browser.mjs
node h5/test/ui-20261009.mjs
node h5/test/e2e-browser.mjs
```
浏览器测试需要全局 Playwright 与 Chromium,沿用项目既有环境。`ui-20261009.mjs` 默认访问本地 8080,可用 `H5_BASE_URL` 覆盖;`report-persistence.mjs` 单独启用 18089 临时服务和临时数据目录,不写入日常报告,不调用收费 Face++。
本轮结果:评分模块自测 83 条、真值回归 86 条通过;账号隔离、恢复登录、重启持久化、最近 5 条、OAuth state 防重放/子路径检查通过;模拟上游的完整 HTTP 链路验证七张图片及文案刷新回看完全一致;三连拍与语音端到端检查通过;320/390/480px 宽度无横向溢出,浏览器无脚本异常。
截图位于 `design/ui-20261009/verification/`。这些是本地 Chromium/样本数据检查,不等于真实手机摄像头或微信公网账号验收。本轮没有触发真实 Face++ 收费请求。
## 素材再生成
```bash
python tools/export_psd_assets.py /你的路径/UI外发版10.09.psd
python tools/prepare_h5_assets.py
python tools/build_report_copy.py
```
PSD 导出依赖 psd-tools/Pillow/aggdraw;H5 裁剪与相机轮廓提取还使用 numpy/opencv-python。原始导出保留,`*-h5.jpg` 是去除原生顶栏后的衍生背景;相机几何写入 `h5/js/camera-geometry.js`。
+25
View File
@@ -0,0 +1,25 @@
# 提交前 Review(2026-10-11)
范围:10.09 H5 UI、浏览器/微信身份、后端报告快照、部署说明;重点检查公网调试前的访问控制和回调路径。
## 已发现并修复
1. **P1:静态敏感路径可被重复/编码斜杠绕过。** 原先先按原始路径检查 `/data`、`/lib`、`/server.js`,文件读取时才规范化;`/%2fserver.js` 实测返回 200。现统一斜杠并规范化后再授权,额外禁止访问 `JUZI_DATA_ROOT`。新增原始 HTTP 路径测试,覆盖双斜杠、编码斜杠、反斜杠和父目录段;修复后源码路径返回 404,数据目录返回拒绝响应。
2. **P2:nginx 后的用户共用登录限流。** 原限流只看 TCP 对端,代理后全站共用回环地址的每分钟 60 次额度。新增显式 `TRUST_LOOPBACK_PROXY=1`,仅信任回环代理覆盖的合法 `X-Real-IP`;文档同步 nginx 配置。已测试不同 IP 额度独立,以及未启用信任时伪造请求头不能绕过限流。
## 验证
- 评分模块自测:83 通过;真值回归:86 通过。
- 账号/报告:权限隔离、最近 5 份、重启恢复、浏览器凭据恢复、会话退出、OAuth state 绑定和重放检查通过。
- 完整检测 API→保存→回看:使用模拟上游,不调用收费 Face++;七张图片和话术刷新后完全一致。
- UI 与三连拍:协议校验、登录回退、移动端布局、三视角流程、语音均通过。
- `wechat-browser.mjs`:使用临时 HTTPS 证书和 `/state/` 反代路径,验证前端微信按钮→模拟授权站点→服务端回调→Secure/HttpOnly/SameSite Cookie→首页/报告。两个独立浏览器上下文关联同一模拟 OpenID。此测试需要本地 OpenSSL 和 Playwright。
## 仍需公网/业务确认
- 未使用真实微信 AppID/AppSecret 联调,公众号权限、网页授权域名及真实手机回调需在公网验收。
- Node 管理接口仍依赖 nginx Basic Auth,公网只能反代访问,不能暴露 Node 端口。文件存储仅支持单进程。
- 浏览器与微信账号不自动合并;先用本机身份产生的报告不会自动出现在微信账号中。
- 已有协议是 Face++ 隐私政策,品牌方账号关联、报告保留/删除范围仍需业务确认;不应将当前版本视作已完成正式发布合规审核。
提交范围不含真实密钥、`h5/data`、PSD 原稿及 Windows Zone.Identifier 元数据。代码、运行素材、文案来源、测试和部署说明一起提交,便于服务器复现。
@@ -0,0 +1,195 @@
# UI 对齐 10.09 设计稿:调研与实施计划
调研日期:2026-10-10。阶段:调研与计划,尚未修改业务页面。
## 1. 结论和依据
这次更新包括三个层面:既有页面的视觉对齐、报告页的结构重做、新增登录与报告列表流程。仅替换图片不能达到设计稿效果。
依据:
- 最新 PSD:`UI外发版10.09.psd`,10 个画板;采用 `design/ui-20261009/manifest.json` 的图层与文本信息,不再把旧 `design/设计规范.md` 当作新版尺寸依据。
- 实际运行页面:`http://127.0.0.1:8080/?mock=1`。在独立 Chromium 上下文采集启动、授权、档案、拍照引导、三个拍照步骤和报告三组 Tab;另检查 375/390/414px 宽启动页。
- 实测记录:`design/ui-20261009/audit-current/evidence.json`,本轮 15 张截图,未捕获到 JS 页面异常。拍照截图使用 mock/模拟摄像头,不能据此认定手机真实摄像头已通过验证。
- 源码:`h5/index.html`、`h5/css/style.css`、`h5/js/app.js`、`h5/js/skin-score.js`、`h5/server.js` 和浏览器回归脚本。
- 视觉对比:`design/ui-20261009/audit-current/comparison-flow.jpg` 与 `comparison-report.jpg`。每组左侧当前页面、右侧新设计;报告采用不同示例数据,只比较结构与样式,不比较数值与照片内容。
当前技术栈是原生 HTML/CSS/JS + Node 服务。继续沿用即可,没有必要为此次还原引入前端框架。
## 2. 统一比较口径
PSD 标准画板为 1080×2339,报告长页为 1080×7765。375px 宽页面的换算系数为 2.88。
设计顶部约 248px 是系统状态栏与微信标题/胶囊区域。当前 H5 将这块交给浏览器容器,业务页面没有顶部导航。因此本次常规页面对比裁掉该区域,统一为约 375×726 的业务视口;报告顶部按其自身画板标题栏边界约 253px 处理。
建议以「H5 业务内容还原」验收,不在网页里重复绘制电量、时间、信号和小程序胶囊。是否增加网页自己的标题/返回栏需要单独确定;新增一条导航栏会改变所有页面的纵向坐标,不能等页面完成后再决定。
设计实测尺寸(375px 宽基准):
| 元素 | PSD 尺寸 | 换算后尺寸 | 更新要点 |
| --- | --- | --- | --- |
| 首页主/次按钮 | 522×112 | 181.3×38.9 | 当前主按钮高 37px,需统一到新版 |
| 弹窗 | 887×902 | 308.0×313.2 | 统一位置、内边距与文案行距 |
| 弹窗关闭图标 | 51×50 | 17.7×17.4 | 图形按设计大小,触摸区域单独扩大 |
| 档案单列输入框 | 325×112 | 112.8×38.9 | 对齐双列间距、标签间距 |
| 肤质选择卡 | 713×808 | 247.6×280.6 | 配合新插画和文字宽度 |
| 肤质插画 | 316×316 | 109.7×109.7 | 五种状态均已导出 |
| 报告汇总卡 | 982×696 | 341.0×241.7 | 合并当前两张卡 |
| 报告分类卡 | 983×1177 | 341.3×408.7 | 改为大图在上、正文在下 |
| 分类展示图片框 | 619×802 | 214.9×278.5 | 真实照片按比例呈现,标注坐标保持一致 |
| 产品卡 | 984×503 | 341.7×174.7 | 三张卡统一规格 |
这些是基准几何,不代表真实数据页面一律固定高度。长护理建议、缺数据提示等内容应自然撑高,避免截断。
PSD 字体包含 MiSans Regular / Semibold / Demibold;当前 CSS 使用 Noto Sans SC / PingFang / 系统字体,项目未发现配套字体文件。字体会影响换行、数字宽度和字重。实施时优先准备可用于 Web 的对应字体文件,或明确接受系统字体近似。PSD 文本字号还要结合图层变换矩阵计算,不能只读原始 FontSize 就直接换算。
## 3. 逐页差异和调整内容
| 页面 | 当前实现 | 最新设计 | 具体工作 |
| --- | --- | --- | --- |
| 登录 | 无登录页面、无身份状态 | 品牌标志、微信登录、游客登录、协议勾选 | 新增页面与入口状态;游客通路和真实微信认证分别验收 |
| 启动 | 旧口号、单个「开始检测」按钮 | 英文两行 + 中文口号;新增白色「我的报告」按钮 | 更新文案、背景构图、Logo/按钮间距,增加历史入口 |
| 隐私授权 | 直接覆盖当前首页,仍露出首页口号和按钮 | 遮罩底图没有首页交互内容 | 采用独立背景层;校准弹窗位置、字号、关闭图标和勾选行 |
| 档案填写 | 结构基本完整,旧肤质插画;表单整体偏下,背景构图不同 | 相同字段与轮播,但几何、字重、背景不同 | 复用选择器与字段校验,重新标定间距、卡片、五种插画和底部按钮 |
| 拍照引导 | 旧口号;标题居中;没有环境要求说明 | 新口号;标题左对齐;补充关闭美颜/滤镜和均匀光线说明 | 更新背景、标题与说明,校准按钮位置 |
| 三步拍照 | 旧脸型轮廓,无眼鼻虚线;首步「请正对屏幕」 | 新脸型、眼鼻虚线;首步「请正脸出境」 | 更新蒙版及定位线,文案建议纠正为「出镜」;复核前后镜头方向 |
| 我的报告 | 存在隐藏历史弹层逻辑,没有可点击入口 | 独立页面,数量卡、日期列表、查看详情 | 将历史展示升级为页面,接通首页入口、返回和详情打开;补空列表状态 |
| 报告汇总 | 总分/肌龄一张卡,三维度另一张;额外肌龄差、关注说明 | 总分、年龄、三维度在同一张卡 | 合并 DOM 与间距;显示名改为紧塑力/透亮感/稳韧力,处理设计未显示的说明 |
| 报告详情 | 一个全局三维度 Tab,每页两个小图+侧边文字 | 三个纵向区块,每块自有分类 Tab,大图+下方文字 | 重构为独立区块及 Tab 状态,增加肤色/水分两个展示视图 |
| 护理建议 | 五段动态文案和额外结尾句 | 设计只示意两段 | 对齐卡片和排版,保留动态文案;内容长度不能照示例硬截成两段 |
| 产品推荐 | 额外精华推荐卡;固定一张 CORE II 产品图;「了解更多」占位按钮 | 主场精华 I/II/III 三张产品卡 | 三档素材/商品文案配置化;保留真实推荐逻辑,收敛重复模块 |
实测的报告上半部分:当前总分卡 339×140,三维度卡约 339×201,中间有 12px 间隔;设计是一张约 341×242 的卡。这是明显的结构差异,靠缩字体和调颜色无法解决。
当前首页和档案页的背景使用同一张图加 CSS 叠色;新版已导出独立背景,但不能把带叠色的 JPG 再叠一遍旧渐变,否则会过暗。背景和相机蒙版还需根据业务视口裁剪,不能直接把全画板 2339px 高图片拉伸到去掉顶部栏后的 2091px 区域。
## 4. 报告数据如何接到新 UI
建议增加展示适配层,例如 `h5/js/report-ui.js`。评分模块继续产出已有计算结果,适配层负责新版名称、分类视图、图片和商品展示,不把显示名写回数据库或评分公式。
| 新 UI | 已有数据 | 展示适配 |
| --- | --- | --- |
| 紧塑力 | `vm.dims.firm` | 保留该维度评分 |
| 透亮感 | `vm.dims.vita` | 将「活力感」映射为新显示名,包括相关关注提示 |
| 稳韧力 | `vm.dims.stab` | 将「稳定力」映射为新显示名 |
| 轮廓 | `vm.cats.contour` + 正脸照 | 大图及该分类文案 |
| 皱纹 | `vm.cats.wrinkles` + `texture_enhanced_lines` | 保留增强线与原照片合成 |
| 肤色 | `vm.cats.tone` 中肤色相关数据 + `brown_area` | 从合并分类中独立展示,须明确专属文案来源 |
| 平滑度 | `vm.cats.smooth` + 已有标记/图层 | 改名并复用图像合成 |
| 水分 | `vm.cats.tone` 中水分相关数据 + `water_area` | 独立图像/说明,不能声称新增了一项独立评分 |
| 泛红 | `vm.cats.sens` + `red_area` | 显示名与红区合成图更新 |
| 痘痘 | `vm.cats.acne` + 痤疮标记 | 复用坐标叠加 |
当前服务端已经请求并保留 `brown_area` 和 `water_area`。当前 UI 的 `tone` 只展示水分图,因此图库接口不是主要阻碍;需要增加图片容器、展示配置及缺失图层状态。
现有 `tone` 评分合并肤色与水分,话术也是合并话术。新版的七个图片视图,不应自动变成七分类评分。肤色和水分的独立文字应由确定的字段/模板产生;不能把同一段合并文案复制到两个 Tab 后视为完成。
产品映射建议使用明确的配置表:已有「25+束紧精华 / 35+支撑精华 / 50+重塑精华」对应主场精华 I / II / III 的名称、图片和说明。实际推荐由已有多项判据决定,不能只按年龄选择。三张卡可以按设计全部展示;是否强调推荐卡应另定,避免擅自在设计上增加标签。
重构图片异步加载时增加报告/视图版本标记,避免快速切 Tab 或历史报告后,较早的图片合成回调覆盖新页面。
## 5. 登录与“我的报告”的真实能力缺口
目前只有静态页面、检测 API、管理 API 与健康检查,没有面向用户的登录、会话或个人报告接口。管理后台记录不等于个人报告库,不能直接将后台查询接口接到“我的报告”。
本机历史:`juzi.reports.v1`,最多 5 条,存储三张照片与结果,主动删除 `face_maps` 以减少容量。代码可打开旧报告,但图层类图片回看会降级。因而“恢复入口”可以复用较多代码,“跨设备完整回看”则需要新增服务端能力。
建议分两个可验收阶段:
1. **游客闭环**:登录页游客入口 → 首页 → 检测 / 我的报告 → 详情。本地报告列表如实表达当前保留数量,补空态、损坏记录、存储失败和返回行为。图层不全时明确缺失,不能用设计示例人脸补齐。
2. **账号闭环**:明确微信公众号/H5 实际运行容器及可用配置后接入真实登录;建立会话、报告 owner 归属、用户范围内列表/详情/图片读取、退出及游客数据衔接。设计稿自身不能提供这些接口契约。
第二阶段尚未落地前,不能把微信按钮接成“点击即登录成功”。希望完整实现设计中的微信登录功能,需要纳入后端和账号配置工作,而不只是 UI 工期。
若要求游客也能完整回看,需要另选客户端持久化渲染缩略图/IndexedDB,或以受限凭据查询服务端结果;在明确存储策略前,不把大幅 base64 图层直接塞回现有 localStorage。
## 6. 原稿中需要整理的内容
以下来自对 PSD 可见图层及代码契约的核对,不能无条件当成正式需求:
- 首页底部存在重复的「我的报告」文字图层,建议只保留一枚有效按钮。
- 首步「请正脸出境」建议修正为「请正脸出镜」;报告标题多余的右括号、历史数量文案中的重复字也需校正。
- 画板名「左面」文案是第 2 步右脸,「右面」文案是第 3 步左脸。以用户脸部语义和送检字段为准,不按画板名机械替换。
- PSD 范例里有 5.1 分,但现行 S = 7 + 3×P/100 的正常 0–100 输入对应 7–10 分。5.1 只是设计示例;若它代表新评分规则,需独立修改业务契约。
- 设计复用了检测示意图与描述,不能据此让所有真实分类显示同一张图或同一段话。
- 两段护理建议与三张商品卡属于展示示例,需保留真实文案和推荐判据。
- 新登录页列出用户协议、隐私政策、儿童保护政策;当前只有已有隐私弹层内容,缺少新入口各自明确的文本对应。实施时须确定已有文本的覆盖范围和最终链接。
## 7. 实施顺序与交付物
### P0:冻结视觉和数据基准
- 确定 H5 顶栏口径、流程入口、字体,以及上面的设计示例处理方式。
- 按画板重新整理 `--u`、卡片宽、间距、圆角、按钮高、字号和色值。
- 复核 36 张切图:背景裁剪、透明边缘、产品剪贴边界;拍照眼鼻定位线应保留原稿虚线效果,不能只依赖可能近似的 PSD 矢量栅格结果。
- 建立固定的视觉测试数据和「缺图/空态」样例,与真实评分数据分开。
- 交付:新版尺寸表、素材映射、显示字段映射、确定的页面路由。
### P1:公共样式与既有入口流程
- 修改 `h5/css/style.css` 公共组件与背景策略。
- 修改 `h5/index.html` 首页/授权/档案/拍照引导结构,接入新版素材。
- 修改 `h5/js/app.js` 文案、肤质图片配置和相应入口,保留档案选择器校验。
- 交付:首页、授权、档案和引导弹窗的并排截图与可操作流程。
### P2:报告页重构(视觉改动最大)
- 合并汇总卡;显示层统一三个新维度名称。
- 将一个三维度 Tab 改成三个纵向区块,各自包含 2 / 3 / 2 个分类 Tab。
- 大图、分隔线、正文、免责声明、护理建议按新版排版。
- 接入 `brown_area`/`water_area` 视图及独立文案,处理加载/缺失状态。
- 三档产品列表配置化;按定稿移除旧布局的多余块与无实际动作的按钮。
- 主要文件:`index.html`、`style.css`、`app.js`,新增展示适配模块;不因换名改评分引擎。
- 交付:完整长报告、七个视图、三种商品结果和缺数据状态。
### P3:拍照页视觉校准
- 更新三套轮廓与眼鼻定位线,统一使用同一套 viewBox/坐标系。
- 同步裁剪范围、前后镜头镜像、扫描区域、轮廓成功提示;继续使用现有脸部检测和自动抓拍流程。
- 保留实时自动模式和系统相机/相册降级模式;不能因为设计隐藏快门就删除降级模式的快门。
- 交付:三步静态对照、真实摄像头全流程和 HTTP 降级验证。
### P4:登录外观与本机历史闭环
- 新增 `s-login` 与 `s-history`;统一返回栈和刷新行为。
- 游客入口接入首页,“我的报告”显示真实保留数量、日期与详情。
- 为新/旧记录定义兼容读取及缺图策略;退出相机、关闭放大图、回到列表都要返回正确位置。
- 登录图上无法确定授权后跳转、重复授权等状态,按明确的流程方案补齐。
- 交付:游客用户可完整走通;微信入口状态与真实后端能力一致。
### P5:真实账号能力(依赖账号与后端范围确定)
- 登录接入、会话、记录归属及用户级报告 API;补齐跨设备历史和对应权限验证。
- 同步处理退出、授权取消、会话失效和游客报告迁移策略。
- 交付:真实微信登录及本人报告查询。完成此阶段才能把所有设计功能视为完成。
### P6:联合验收与部署
- 固定 375px 基准截图,对照去除原生顶栏的 PSD;再检查 390/414px 及短屏、长屏、底部安全区。
- 静态结构完成后核对字体、图片裁剪、Tab 下划线、卡片边缘和垂直节奏。
- 更新现有 `h5/test/e2e-browser.mjs`:新版入口、独立 Tab、列表详情返回、七图、三商品、无空引用和无 JS 异常。
- 跑 `h5/test/regression.js`;同一测试数据的现有评分/护理建议/推荐结果应保持一致。
- 实机验证微信内 H5、Android 浏览器、iOS 浏览器;现有局域网 HTTP 只能验证降级拍照,实时自动相机需在可用的 HTTPS 环境验证。
- 交付:最终对照图、回归结果、可访问页面地址及尚未覆盖的设备范围。
## 8. 验收定义
1. **结构**:10 个设计画板对应的页面/弹层均有明确落点;新增登录与报告列表不能只是无法操作的截图。
2. **视觉**:375px 固定视口下,静态主要边界目标误差不超过约 2 CSS px;同字体、同文本、同图片才能做局部像素差比较。不同真实数据不得按整页像素误差判断。
3. **响应式**:375/390/414px 下无横向溢出、文字截断、底部按钮被安全区遮挡;长报告允许内容自然增长。
4. **交互**:三块报告 Tab 相互独立,图片点开/返回正常,历史列表与详情可往返,返回不重新启动摄像头。
5. **数据**:七个展示视图与六分类计算关系清晰;姓名/日期/数量/分数来自真实状态;不会加载上一个报告的图片。
6. **拍照**:正 → 用户右脸 → 用户左脸,API 仍按 front/left/right 正确送检;自动、拒绝权限、相册、系统相机路径分别检查。
7. **历史**:列表计数与保留策略一致,缺图或记录损坏有正确提示;若接账号,必须只读本人记录。
8. **性能**:整页 PNG 与设计示意人脸仅用于验收,业务页面按需加载独立素材;产品图懒加载,避免把 445MB PSD 或大幅设计预览带进页面。
## 9. 实施前需要定下的四个选择
本轮计划不依赖立即答复;进入实现前按以下顺序确定即可:
1. **容器**:沿用当前 H5、去除原生顶栏,还是补一条网页标题/返回栏?建议沿用 H5 口径。
2. **账号范围**:先完成游客及本机历史、再接微信,还是把真实登录和跨设备报告纳入同一批交付?两者的后端工作量明显不同。
3. **业务口径**:是否同意三维度仅改显示名、六分类算法保持,肤色/水分拆成展示视图?建议如此,独立话术另行定稿。
4. **原稿整理**:是否按设计保留三商品卡,并去掉旧版额外卡片/占位按钮,同时纠正原稿重复字和错字?建议保留真实推荐信息于护理建议中,清理重复展示。
推荐先完成 P0–P3 的视觉主体,再交付 P4 游客闭环;P5 随账号条件落地。报告结构和新增身份/历史能力是主要工作量,字体和相机坐标是还原精度的主要风险。
+67
View File
@@ -0,0 +1,67 @@
# 产品推荐规则:肤龄基准与边界调整
状态:2026-10-12 用户已确认三个边界条件;已实现。规则版本:`2026-10-12-skin-age-v1`。
## 已明确的要求
- 基准只取 Face++ 返回的原始 `skin_age`,不取实际年龄或修正后的展示肤龄。
- 基础分组:肤龄小于 35 岁为 25+(主场精华 I),35 至不足 50 岁为 35+(II),50 岁及以上为 50+(III)。24 岁等不足 25 岁的检测结果也归 I。
- 肤龄小于 32 岁,固定推荐 I,即使轮廓或皱纹得分低,也不触发升级。
- 删除原来的实际年龄组、六分类均分、最差分类三重判据。
- 边界附近结合三维度评分调整:紧塑力为必要条件,另外一个达标维度可为透亮感或稳韧力;不能仅因为另外两个维度高而降档。
- 37 岁肌龄、仅存在暗沉问题的案例,应允许推荐 I。
- 每份新报告仍只推荐一款;评分公式、检测流程和已有报告快照不因本次规则调整而改写。
- 原始肤龄缺失或无效时,不回退实际年龄推测产品;需要明确显示本次无法推荐。
## 已确认的三个条件
1. 只在 35±3 岁及 50±3 岁范围内调整,范围外只按肤龄。
2. 50 岁边界与 35 岁同样处理:47~不足50 岁紧塑力低于 8 分升至 III,50~53 岁紧塑力至少 9 分且另一维度至少 9 分降至 II。
3. 「7+最低档」为低于 8 分;「9+」为大于等于 9 分,恰好 8 分不升档,恰好 9 分可以满足降档条件。
## 决策顺序
年龄支持小数,区间严格按下表执行:
| 原始肤龄 | 默认产品 | 可调整条件 |
|---|---|---|
| 小于 32 | I | 固定,不升档 |
| 32~不足35 | I | 紧塑力低于8,升为II |
| 35~38(含38) | II | 紧塑力至少9,且透亮感/稳韧力任一至少9,降为I |
| 大于38~不足47 | II | 按肤龄,不调整 |
| 47~不足50 | II | 紧塑力低于8,升为III |
| 50~53(含53) | III | 紧塑力至少9,且透亮感/稳韧力任一至少9,降为II |
| 大于53 | III | 按肤龄,不调整 |
调整最多一档。没有足够的必要维度数据证明满足调整条件时,保持肤龄基础档;不把缺失值当成低分。
比较分数沿用评分引擎的三维度原始 `S=7+3×P/100`,不先进行前端一位小数或 VM 两位小数舍入。例如 7.99999 小于 8,8.99999 小于 9。原六分类和三维度评分公式、低/中/高档位阈值均不变;推荐阈值与档位文案阈值是不同概念。
## 后台与前端一致性
- 后台列表新增「肌龄(原始 / 展示)」;档案里的年龄明确为实际年龄。
- 名称统一为紧塑力、透亮感、稳韧力;列表显示与 H5 相同的一位小数分数。内部旧档位 `预警/改善/良好` 映射成 `改善/良好/优秀`,筛选参数仍传原值,避免改写旧数据或破坏统计。
- 新记录同时保存 `skin_age_display`、`recommendation` 和前端 `presentation`。后台详情展示基础档、最终档、未舍入的判定分、推荐原因与规则版本;七项结论和护理建议直接使用同一份前端快照。
- 旧记录没有新规则元数据时,明确显示「旧版规则(历史记录)」,不按新规则重新推荐。后台详情可读取原账户已保存的前端展示快照;无快照则标注旧版文案。旧记录未存展示肌龄时,按原有实际年龄±3规则只读补全展示值。
- CSV 同步提供两种肌龄、统一的新档位名、推荐版本和原因。
## 实施与验证范围
- `h5/js/skin-score.js`:推荐决策、产品组别、推荐原因及相关综合文案;保留原评分算法。
- `h5/js/report-ui.js`:前后台共用名称、档位、三款产品映射;H5 无有效肤龄时隐藏具体产品并说明原因。
- 更新引擎自测和 Excel 回归中的产品相关断言;原始 Excel 保留为旧算法参照,不改写评分真值来掩盖差异。
- 新增边界测试:24岁且轮廓/皱纹低分;31.99/32/34.99/35/38/38.01;46.99/47/49.99/50/53/53.01;紧塑力7.99/8/8.99/9;另两个维度分别达标;必要条件不满足;实际年龄改变不影响推荐;原始肤龄和显示肤龄不同;维度或肤龄缺失。
- 验证新报告保存与重开后产品一致,已有报告仍读取原有快照。
- 本次不调整展示肤龄的实际年龄±3岁修正规则,后台并列展示两者,推荐只使用原始肤龄。
验证命令:
```bash
node h5/test/recommendation.js
node h5/js/skin-score.js
node h5/test/regression.js
node h5/test/accounts.js
node h5/test/report-persistence.mjs
```
已通过 126 个推荐用例、83 条评分引擎自测、86 条真值回归。隔离 HTTP + Chromium 验证前后台原始/展示肌龄、三维度评分、推荐产品、七项结论、护理建议、筛选与 CSV 一致;验证保存重开、旧报告快照不改写、无肤龄时不残留上一款产品。全部使用样本上游,不调用收费 Face++。
+67
View File
@@ -0,0 +1,67 @@
# 客户反馈修改(2026-10-12)
依据:`feedback/傅艳NEW与项思炼的会话 2026年10月12日/傅艳NEW与项思炼的会话 2026年10月12日.md` 及其 6 张截图。本文仅处理本次客户反馈,不修改评分公式、肤龄显示规则或产品推荐规则。
## 逐项处理
| 客户反馈 | 处理 |
| --- | --- |
| 报告 Tab 同时支持滑动、点击 | 三组维度卡分别支持横向手势及点击;纵向滚动不切换,边界不循环。保留键盘方向键、Home/End 操作。 |
| 检测图脸部放大,同时保留点开放大 | 根据接口原始人脸矩形统一裁切照片、增强图和标记的展示区域,保留额头、下巴余量;七张检测图均处理,点开放大保留。无人脸矩形或矩形无效时展示原图。上传原图、检测输入、存档照片不裁切。 |
| 皱纹分区详细说明 | 展示 12 个真实分区的 9 项原始指标,缺失显示「未获取」,0 保留为 0。抬头纹和额头纹在现有数据中同属 `forehead_wrinkle_info`,合并为「抬头纹(额头纹)」,不虚构第 13 区。左右以本人为准。 |
| 平滑度图有框,但文字说无毛孔、黑头 | 原图错误使用色斑、痣、痤疮框。改为真实的毛孔、黑头增强图,并说明颜色及其不代表严重程度。缺图不绘制其他问题框。总体评分优秀但局部计数大于 0 时明确列出计数,不再写「无明显」。评分与档位本身不变。 |
| 黄色按钮色号不一致 | 最新 PSD 按钮矢量填色与导出像素均为 `#F5F078`,当前按钮也使用该色;正常按钮没有额外透明度/滤镜。傅艳已确认暂按 PSD 的 `#F5F078`,保持现有色值。屏幕色彩模式可能影响观感,不能据截图猜测新色。 |
| 产品名固定断句 | 三款副标题第一行分别为「以我本然有光蓄活」「以我自在有势挺括」「以我从容有态构塑」,第二行统一「双仓精华液」,独立块且不自动换行。 |
| 链接卡顿、拍摄提示不符 | 卡顿已由上一轮首屏延迟加载与静态缓存优化处理,本次复测无回退。拍摄提示按最新 PSD 改为「为使测试结果更准确,请关闭美颜/滤镜、并确保光线充足均匀。」 |
| 字号不随手机宽度缩放 | 原来按 `--u` 缩放的字号改为 375 宽设计基准的固定 CSS px;布局间距和图片仍适应屏宽。既有文字自动放大控制保留。不承诺不同屏幕的物理毫米大小或系统强制辅助缩放完全相同。 |
## 皱纹数据说明
12 区:抬头纹(额头纹)、左右法令纹、左右鱼尾纹、左右眼部细纹、左右嘴角纹、眉间纹、左右脸颊纹。
9 项:皱纹分、严重等级、深纹条数、浅纹条数、标准化长度、标准化深度、像素密度、面积比例、深纹比例。
数值来自接口原始字段,最多显示四位小数;比例保留接口原值,不擅自转成百分比或补单位。分区深浅纹条数仅在对应指标缺失时使用接口分区计数回退,其余缺失不推算。不对原始数据及评分模型作任何写入。
新生成报告由后端保存新版文案快照,后台与前端读取同一份内容。已有历史报告继续保留当时的文案和推荐快照,不批量重算、不修改历史记录;展示图片可依据已保存原图与矩形使用新的裁切方式。验收新增皱纹话术请生成新报告,不以旧快照作为新版话术验证依据。
## 验证
```bash
node h5/test/customer-feedback.js
H5_ARTIFACT_DIR=/tmp/juzi-feedback-customer-20261012 node h5/test/customer-feedback.mjs
node h5/test/recommendation.js
node h5/test/regression.js
node h5/test/accounts.js
node h5/test/report-persistence.mjs
node h5/test/ui-20261009.mjs
node h5/test/login-performance.mjs
node h5/test/wechat-browser.mjs
node h5/test/analyze-timeout.js
node h5/test/browser-failures.mjs
node h5/test/e2e-browser.mjs
```
专项验证包括真实浏览器触摸事件、三组独立切换、纵滚不误触、横滑不误开放大图、点开放大仍正常、七图裁切及边界容错、12 区真实指标和缺失值、部分增强图缺失、三款产品在 320/375/390/414/480 五种宽度下固定两行且无溢出。专项截图见上述临时目录。
登录性能复测:150 ms 网络延迟、约 2 Mbps 下载、CPU 四倍降速,冷登录可操作约 1636 ms、静态资源编码后 262618 字节;登录仍不加载人脸引擎、模型或完整字体。该结果是开发机模拟观测,不保证所有手机耗时。微信回归使用模拟授权上游,实际 HTTPS Cookie 和跨设备身份隔离均验证,不代表重新完成一次真人微信授权。
上述测试全部通过,包含 126 个产品推荐案例、86 条评分真值断言;UI 回归同时刷新了 `design/ui-20261009/verification/` 内的验收截图。已执行 `graphify update .` 更新代码关系图。
测试使用样例、隔离数据目录或只读演示页,不调用收费 Face++,不修改生产账号或历史报告。确认本地没有近 180 秒进行中的检测后,已重启本地 H5 服务以加载后端新版话术,健康检查正常。黄色按钮色值已确认,无其他待确认项。
提交仅包含代码、修改说明、代码关系图及验收截图,不包含客户聊天原文和附件。公网部署沿用既有 Git bundle 快进同步方式,不推送远程仓库,不迁移或覆盖 `h5/data`,不修改微信凭据、nginx 或后台访问保护。
## 公网部署与复测
2026-10-12 已将功能提交 `d7ae748` 从本机同步至 `ubuntu@1me.xajuzi.com:/home/ubuntu/juzi`,从 `3193a4b` 快进更新。确认近 180 秒无进行中的检测后重启 `juzi-h5.service`;Node 服务与 nginx 均正常,健康检查成功,服务无自动重启。服务器上重新运行专项纯函数、126 个推荐案例、账号测试全部通过。
通过 `https://1me.xajuzi.com/state/` 完成以下复测:
- 公网浏览器专项测试全部通过:真实触摸切换、纵向滚动不误切、点开放大、七张人脸裁切图、12 区皱纹文案、三款产品在五种屏宽下固定两行、增强图缺失提示及无脚本异常。截图目录 `/tmp/juzi-online-feedback-d7ae748`。使用只读样例页,不发起收费检测。
- HTML、应用脚本、报告文案脚本和两份样式的公网内容与本地逐字节一致,gzip、ETag/304 校验通过。功能版本的全部跟踪文件哈希清单在本地和服务器均为 `a45146c325004eecf5448d394d85ccc507e0e8f60798637829917a42b7c3a519`。
- 微信配置仍启用,匿名会话无用户,私有会话接口保持 `no-store`。匿名报告、后台页面和后台接口返回 401;数据、源码和测试目录返回 404。没有发起新的真人微信授权,也未改动账号身份。
- 部署前后检测记录均为 105 条,近 180 秒进行中的检测均为 0;不创建生产测试账号、报告,不清理历史数据。
- 公网冷登录编码资源为 262640 字节,未提前加载字体、人脸引擎或模型。并发专项测试期间一次可操作耗时 5341 ms;专项完成后,三个全新浏览器上下文分别为 384、1185、299 ms,HTML 首字节分别为 245、737、220 ms。存在网络波动,不将模拟或单次观测作为手机耗时保证。
本记录随后以文档提交同步到服务器,不需要再次重启应用。远程仓库未推送,仍由傅艳自行 push。
@@ -0,0 +1,89 @@
# 第三轮反馈:PSD 对齐、验收及上线记录
日期:2026-10-12。依据 `new_feedback/第三轮反馈20261012` 的客户会话、四张批注及新版 `UI外发版10.09.psd`。本轮已完成本地开发、Git 提交及生产部署;按此前约定未推送 origin,由用户自行推送。
## 已完成
| 范围 | 实现 |
| --- | --- |
| 首页/引导背景 | 直接导出 PSD 中三行口号、Logo 与中文组合,保留原始字形、字重及可见高度比例;首页两个按钮的静态文字也从 PSD 导出,不再依赖手机系统字体。 |
| 介绍弹窗 | 完全采用用户确认的 PSD 原文,按原稿校准弹窗位置、标题、正文、勾选区及按钮;服务端保存和回看保持不变。 |
| 拍照引导 | 固定设计稿的两行断句、字号及文本栏;弹窗固定约 308×313 CSS px,320px 窄屏也能完整展示,不机械缩小文字。 |
| 产品卡 | 使用 Semibold 原字体,标题约 14.47px、名称约 11.57px;校准左侧约 26.74px 留白、品牌标识及纵向位置。右图按 PSD 白卡的实际可见区域导出,不再二次居中裁切。 |
| 生成报告等待 | 三张照片拍完后显示 PSD 原始黑底、白色人脸、黄色扫描区和「检测中…」。相册/翻转图标仅是图片内容,不可点击、不可聚焦;保留明确取消、返回、失败恢复及请求完成后的报告跳转。 |
产品仍只展示算法推荐的一款;没有修改评分、肤龄、推荐规则、三视角顺序或自动拍摄阈值。历史报告保留原推荐和原文案,仅按已有产品档位更新展示图片的裁切,不回写历史数据。
用户确认的弹窗原文:
> 我们将采集您的面部影像,为您生成专属肌肤分析与状态管理方案。所有数据严格保密,仅用于本次检测分析。
引导正文的 PSD 断点(包括「确/保」之间的换行):
```text
为使测试结果更准确,请关闭美颜/滤镜、并确
保光线充足均匀。
```
## 原稿与导出可追溯性
- 新 PSD 为 7330×16411、11 个顶层分组,新增第 9 号「扫描」,原「登录」变为第 10 号。旧导出器改为按唯一名称定位「登录」,拒绝缺失或重名,避免切图串页。
- 本轮选择性导出脚本:`tools/export_third_feedback_assets.py`。运行方式:
```bash
python3 tools/export_third_feedback_assets.py 'new_feedback/第三轮反馈20261012/UI外发版10.09.psd'
```
- 原 PSD SHA-256:`c7698f5900adc7476286818450d928e8fa2ce2e58cda09375ff2b861629fd58f`。
- `design/ui-20261012/manifest.json` 记录源文件校验、图层路径、原始坐标、字体及变换矩阵、输出尺寸和逐资源 SHA-256。测量包含文字图层的缩放,不直接把原始 FontSize 当最终字号。
- 另读取矢量路径确定原稿圆角:弹窗 25 个设计像素、按钮 20px、产品卡 15px(375px 基准分别约 8.68/6.94/5.21 CSS px),不是继续沿用旧版估计值;产品卡宽高按 984×503 的原稿比例校准。
- 八份前端资源位于 `h5/assets/img/ui-20261012/`;PNG/无损 WebP 均来自 PSD,不使用生成式替代图、不改人物或产品包装。原 10.09 资源和字体文件保留不动。
- 扫描页须整体合成,保留跨图层剪贴依赖,再裁掉 248px 微信原生顶栏;单独导出图标组会漏掉人脸。导出器及浏览器测试都检查白色人脸、黄色下半脸像素。
- 原稿预览保存在 `design/ui-20261012/page-{0,1,4,9}.webp`,仅用于核对,不将整个交互页面替换为背景截图。微信原生导航由宿主提供,不叠加进 H5。
- 等待画面保持原始长宽比,异形屏用黑色留白适配;额外的「取消」为保留已有功能的必要入口。不新增虚构百分比、扫描动画或固定长时间等待,沿用原有至少 1.2 秒防闪及 150 秒请求预算。
- 扫描图未加载或加载失败时仍显示「检测中…」文字并可取消,不留下无提示黑屏;正常加载后仅展示原稿文字,不重复叠字。
- 静态品牌图规避系统字体差异;其余动态文字使用完整原始 MiSans,保留 `font-display: swap`。小字号开启几何精度渲染,减少浏览器将小数像素字宽取整造成的断行偏差;不同系统的文字抗锯齿仍可能有细微区别。
## 本地验证
已通过以下检查,未调用收费 Face++ 或改动线上账号、照片、报告:
- 第三轮专项:源资源校验/PSD 字体与原文、320/375/390/414/480px 弹窗和产品排版,三款推荐产品、固定断行、字体失败降级、短视口及等待页六种尺寸;实际三张上传推进、装饰图标、键盘焦点、取消/迟到响应、失败重试、成功进入报告。
- 第二轮专项:全屏相机、三种圆角轮廓、五官辅助线、震动安全降级、450ms 自动抓拍条件、协议及报告实际字体、首屏无大字体/模型请求。
- 原反馈回归:报告图放大、三个维度独立滑动切换、上下滚动、七张真实数据可视化及皱纹/毛孔文案。
- 推荐规则 126 个用例、原算法 86 条断言,以及 150 秒超时/迟到回调单测。
- 隔离服务器的报告持久化:后台/H5/CSV 一致、最新五份报告、用户隔离、历史快照不回写,包含旧图片路径升级展示的验证。
- 登录及模拟微信:隔离 HTTPS `/state/` 回调、Secure cookie、账号切换、跨设备同账号和不同账号隔离;不是对真实手机授权的替代验收。
- 登录性能回归:150ms 延迟、约 2Mbps 下载及 4 倍 CPU 降速下冷启动约 **1.64 秒**、资源 **264610 字节**;新增扫描图约 11.7KB,在进入档案/相机后才加载,不竞争登录首屏。
- 切图映射 3 个单测、JS/Python 语法及 diff 空白检查。按项目约定执行 `graphify update .`(AST 更新,无付费语义提取)。
主要复验命令:
```bash
python3 tools/test_export_psd_assets.py
H5_ARTIFACT_DIR=/tmp/juzi-third-final node h5/test/customer-feedback-third.mjs
node h5/test/customer-feedback-second.js
node h5/test/customer-feedback-second.mjs
node h5/test/customer-feedback.js
node h5/test/customer-feedback.mjs
node h5/test/recommendation.js
node h5/test/regression.js
node h5/test/analyze-timeout.js
node h5/test/report-persistence.mjs
node h5/test/accounts.js
node h5/test/login-performance.mjs
node h5/test/wechat-browser.mjs
```
## 生产部署及只读验收
- 功能提交:`c7b085e795f21565a9802896041b447970f44ab9`(`feat(h5): align third feedback with latest PSD and scanning screen`)。
- 部署目标:`ubuntu@1me.xajuzi.com:/home/ubuntu/juzi`;页面为 <https://1me.xajuzi.com/state/>。部署前确认服务器工作区干净,与本地基线 `f3e7eac` 一致,通过 Git bundle 快进更新,不覆盖服务器私有配置或用户数据。
- 因后端启动时加载 `report-ui.js`,同步代码后重启 `juzi-h5.service`,保证前后端使用同一版本。服务 `active`、自动重启次数 `NRestarts=0`,健康接口返回 `ok:true`;检查时最近 15 分钟没有 systemd 错误级日志。
- 线上首页、两个 JS 模块、新 CSS 及八份新素材均返回 200,与本地逐文件 SHA-256 一致,并带有 `Cache-Control: no-cache`。
- 线上第三轮浏览器专项全部通过:五种宽度弹窗/断行/产品排版、三款产品、三张照片推进至扫描页、白色人脸和黄色扫描区、六种视口、装饰图标、取消/迟到响应、失败重试和成功报告、字体及扫描图失败降级;没有页面 JS 异常。检查了线上首页、等待画面和产品卡截图。
- 线上第二轮浏览器回归也通过:首屏不请求完整字体/人脸引擎、协议字体实际加载、全屏相机和三种圆角轮廓/五官辅助线、报告数字及标题的真实 PSD 字体、五种宽度无溢出;没有页面 JS 异常。
- 访问控制检查:未登录报告接口、后台页面和统计接口均返回 401;私有身份文件和 `server.js` 返回 404;微信登录配置仍启用。
- 生产浏览器回归使用内存中的登录/分析响应及本地示例图片,不提交真实检测、不调用收费 Face++、不创建或改动线上账号和报告;不能替代真实手机微信授权及摄像头验收。
仍需用户在真实手机微信中做最终视觉/相机验收。客户反馈原件及 PSD 未修改、未纳入提交;服务器凭据、环境配置、已有照片和报告保持不动。
@@ -0,0 +1,82 @@
# 12 日第二批:明确项修改
依据用户提供的 `12 日第二批.docx`(正文与 6 张截图)、随后补充的首页高清批注图及最新 `UI外发版10.09.psd`。原第 3 张首页截图仅 73×160 像素,未猜测;收到高清图后补齐下面两项。
## 已修改
### 首页高清批注
- 将三行口号中的中文「以我为始 自有表现」独立为 700 字重,英文继续使用 400 字重,保留三行断句与位置。PSD 对应中文 `0/4` 为 Demibold、英文 `0/5` 为 Regular,确实有粗细区别。
- 放大底部「肌肤状态检测」到 24 CSS px、700 字重,与原始 Logo 约 26 CSS px 的可见高度对齐;Logo 保持原图宽高比。Logo 与文字均固定 CSS px,不再因屏宽缩放 Logo 而让二者比例失衡。
- 首页继续使用系统字体表达字重区别,不提前下载约 5 MB 的完整 MiSans 字体;不改动或子集化字体。系统字体字形的细微差别仍依手机系统而定。
- 在 320、375、390、414、480 五种屏宽验证:品牌行不溢出,口号仍三行,登录状态不遮挡口号,首屏不新增字体或检测引擎请求。
### 拍照展示
- 去掉人脸外轮廓的白色描边,以及原来无脸呼吸/合格变绿的轮廓描边;保留明暗镂空、五官虚线、扫描提示和「保持不动」反馈。
- 视频由 85% 缩小预览恢复为铺满视口,避免外侧矩形取景边界;镜像仍保留。屏幕人脸中心坐标的缩放系数同步为 1,距离、姿态、光线判定阈值不变。
- 对 PSD 导出的折线轮廓做最大 2 CSS px 的局部圆角化,使用二次曲线、不过冲、不改变轮廓坐标系。镂空与扫描裁切使用同一份平滑轮廓。原始 `CameraGeometry` 不修改。
- 原始视频抓帧、上传图片、报告图片处理与评分/推荐逻辑不变。
### 准备捕捉提醒
原来仅在补光抓拍瞬间请求 30 ms 震动,改为首次满足检测条件、开始「保持不动」时请求 `[70, 40, 70]` 两次短震动。连续合格 450 ms 后仍按原流程自动抓拍,不增加倒计时。抖动重试时最多每两秒请求一次,每步重置。
实时相机的手动快门模式仍在点击抓拍时请求提醒;自动模式不会在抓拍瞬间再重复震动。
API 不存在、返回 false 或抛出异常时安全跳过,不能因震动失败卡住拍摄。此处没有接入未经验证的微信私有接口,也不声称所有 iPhone/微信环境均可震动。振动接口存在浏览器、用户激活和系统设置限制,实效必须在手机微信上测试;参见 [MDN Navigator.vibrate](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate)。保留原有文字、语音和补光反馈。
### 字重
直接读取 PSD 字体元数据,明确区分而非将所有文字一起加粗:
| 位置 | PSD 图层 | 原始字体 |
| --- | --- | --- |
| 协议弹窗标题 | `1/3/0` | MiSans-Demibold |
| 拍照引导弹窗标题 | `4/7` | MiSans-Demibold |
| 正/右/左拍照步骤 | `5/3`、`6/2`、`7/2` | MiSans-Demibold |
| 综合评分、肌龄 | `8/12/2`、`8/12/3` | MiSans-Demibold |
| 三维度数字 | `8/12/7/2`、`8/12/7/9`、`8/12/7/13` | MiSans-Demibold |
| 报告标题、三维度大标题 | `8/13`、`8/9/17`、`8/10/20`、`8/11/17` | MiSans-Semibold |
| 护理建议、产品区大标题 | `8/2/0/1/0`、`8/1/3` | MiSans-Semibold |
项目原来只有 Regular、Semibold,数字和部分提示还使用 Regular 字重。现从[小米官方字体包](https://hyperos.mi.com/font/zh/download/)增加完整的原始 Demibold 文件(4994364 字节),未转换、未子集化、未修改字形;SHA-256 为 `7afe3737efaf6a137db0ef4857fbe4033a8802756485ebc7f008e96a4bdedcf2`。保留字体归属声明。
Demibold 使用独立 CSS 字体族,仅应用于本轮明确需要它的位置,避免影响原有正文、Tab 和其他 Semibold 元素。报告大标题按原始 Semibold 字形显示,不能承诺所有原本已经正确加载 Semibold 的标题都会额外变粗。
字体样式注册提前到打开协议/拍照引导弹窗时,解决首次协议弹窗尚未注册项目字体的问题。继续 `font-display: swap`,不阻塞交互;登录、首页和账号恢复仍使用系统字体,不在首屏请求完整字体或模型。新增完整字体约 5 MB,在需要的页面下载并缓存;弱网下载完成前仍可能短暂显示回退字体。固定字号与产品两行断句保持不变。
## 验证与状态
```bash
node h5/test/customer-feedback-second.js
H5_ARTIFACT_DIR=/tmp/juzi-feedback-second-final-20261012 node h5/test/customer-feedback-second.mjs
node h5/test/customer-feedback.js
H5_ARTIFACT_DIR=/tmp/juzi-feedback-second-regression node h5/test/customer-feedback.mjs
node h5/test/recommendation.js
node h5/test/analyze-timeout.js
node h5/test/login-performance.mjs
node h5/test/e2e-browser.mjs
H5_ARTIFACT_DIR=/tmp/juzi-feedback-second-ui-20261012 node h5/test/ui-20261009.mjs
```
专项测试覆盖三种轮廓的有界曲线和无原始数据修改、异常输入回退、震动缺失/拒绝/抛错容错、抓拍前触发及频率限制、原有 450 ms 自动抓拍时机;浏览器核验实际渲染字体(不仅是 CSS 数值)、五种屏宽的全屏预览与固定字号、三种状态均无轮廓描边、五官引导保留、登录首屏无大字体/模型请求。专项浏览器测试拦截会话并使用本地合成视频流,不写入后端账号、报告,也不调用收费检测。
已完成本地开发及验证,首页高清图的两处批注也已补齐,并按用户要求提交 Git、部署公网。未推送 origin,由用户自行推送。真实手机震动实效仍待确认。
## 公网部署与复核(2026-10-12,北京时间)
- 功能提交:`fe9623a`;服务器 `/home/ubuntu/juzi` 通过 Git bundle 从 `abb1c4c` 快进部署到此版本。随后同步本节部署记录及公网字体检查的测试时序修正。
- 地址:`https://1me.xajuzi.com/state/`。本轮仅变更前端资源,服务按请求读取磁盘文件,因此无需重启 Node 或 nginx;`juzi-h5.service` 持续 active,进程 PID `3481826` 未变化。
- 公网 HTML、三份 CSS、`app.js`、新增 Demibold 字体均与本地 SHA-256 一致,资源保留 `Cache-Control: no-cache`,刷新会重新校验版本。
- 健康接口 200/ok,微信配置 `wechat: true`;匿名报告接口和后台页面/统计接口均 401,源码与私有账号数据路径均 404。未修改微信私有配置、真实账号、照片和报告,也未调用收费检测。
- 专项浏览器测试分别在本地及公网通过,确认五种屏宽首页字重与视觉高度、协议/报告实际渲染字体、全屏取景/无轮廓描边/圆角与五官引导、无 JS 错误。公网测试仅拦截会话、使用浏览器本地合成视频和模拟报告,不向后端写账号或报告。
- 发现并修正测试本身的时序问题:`<link>` 插入 DOM 不代表字体声明已加载;测试先等待 `document.fonts` 注册后再检查字体。未因此更改页面行为。
- 登录性能回归通过:模拟 150 ms 延迟、约 2 Mbps 下载、4 倍 CPU 降速,冷启动约 1.66 秒、资源约 264 KB(测试环境数据,不代表所有手机实测);登录及首页没有新增字体/模型下载。
- 126 项推荐规则测试、抓拍提醒/容错测试、原反馈及超时测试通过;隔离 HTTPS 环境的模拟微信授权、跨设备报告/账号隔离和切换登录回归通过。本轮未替代真实手机微信授权或震动验收。
公网专项验证命令:
```bash
H5_BASE_URL=https://1me.xajuzi.com/state/ H5_ARTIFACT_DIR=/tmp/juzi-second-deploy-online-20261012 node h5/test/customer-feedback-second.mjs
```
@@ -0,0 +1,109 @@
# 微信登录接入与公网部署说明
更新:2026-10-12。当前环境为 Windows 宿主机 + 内网 WSL。
线上状态补充:`https://1me.xajuzi.com/state/` 已部署并配置真实微信凭据,服务器检查记录见 `线上微信登录部署记录-2026-10-11.md`。2026-10-12 用户已确认手机微信登录成功,服务端亦记录成功回调。下文的内网缺配置说明针对本机开发环境。
2026-10-12 白名单复测已通过,基础凭据有效。最新修正和真机验证步骤见[微信登录联调记录](微信登录联调-2026-10-12.md)。已有本机用户可从「我的报告」→「切换登录方式」进入微信登录;微信会话失效不会自动恢复为本机账号,两个账号的报告不自动合并。
## 本机开发环境限制(不代表线上状态)
本机未配置生产用 `WECHAT_APP_ID`、`WECHAT_APP_SECRET`、`PUBLIC_BASE_URL`。内网 `http://192.168.3.25:8080/` 不能作为本次正式授权部署地址。因此本地启用「本机浏览器登录」,不会模拟微信登录成功。线上真实登录已通过,详见联调记录。
官方网页授权适用于具有相应权限的认证服务号等账号;需要在公众号后台配置网页授权域名。普通小程序 AppID、开放平台网站应用 AppID 不能直接替代本方案的服务号 AppID。本实现采用 `snsapi_base` 获取 OpenID 做账号关联,不请求头像、昵称。
依据:[微信官方 H5 网页授权文档](https://developers.weixin.qq.com/doc/service/guide/h5/auth.html)(2026-10-10 核对)。实际账号权限和后台配置还需由公众号管理员确认。
## 已完成的链路
1. 登录页先勾选现有协议,再选择微信或本机浏览器登录。
2. 服务端构造授权地址;回调校验一次性 state、浏览器绑定和 10 分钟有效期。
3. 服务端用 code 换取 OpenID,AppSecret 不发送至前端、不写入报告。
4. 同一 AppID + OpenID 对应同一个服务端账号,换手机登录后读取该账号最近 5 份报告。
5. 会话采用 HttpOnly / SameSite=Lax Cookie;公网 HTTPS 配置下附带 Secure。
6. 未配置微信、未在微信浏览器内打开、授权失败时提供明确提示,可选择本机浏览器登录。
自动化测试使用模拟的微信换码响应,已覆盖 state 缺失、绑定错误、重复回调、子路径回调、同一 OpenID 返回同一账号;这些测试不等同于微信真机验收。
## 公网服务器配置
部署整个 `h5/`,包括 `lib/`、`js/report-copy.js`、`js/report-ui.js`、`js/account-client.js`、`js/camera-geometry.js`、新版 CSS、图片和字体。需要 Node 18+,建议沿用当前 Node 22。不能仅部署静态文件。
通过服务管理器的私有环境文件设置以下内容,不放入前端,不提交 Git:
```ini
HOST=127.0.0.1
PORT=8080
# 仅限 Node 绑定回环、nginx 覆盖 X-Real-IP 的部署方式
TRUST_LOOPBACK_PROXY=1
PUBLIC_BASE_URL=https://你的域名/state/
WECHAT_APP_ID=你的认证服务号AppID
WECHAT_APP_SECRET=你的认证服务号AppSecret
SKIN_API_KEY=你的Face++Key
SKIN_API_SECRET=你的Face++Secret
# 可选:独立持久化目录。未设置则使用 h5/data。
JUZI_DATA_ROOT=/var/lib/juzi-h5
```
环境文件权限设为仅服务账号可读,systemd 中使用 `EnvironmentFile=`。重启服务后访问 `.../api/auth/config`,`wechat: true` 只代表必需配置齐备,不代表平台授权已经验收通过。
`PUBLIC_BASE_URL` 必须是用户实际访问的 HTTPS 地址,包含真实子路径。示例回调为:`https://你的域名/state/api/auth/wechat/callback`。公众号后台填写的是域名,不是完整 URL;按后台要求部署域名验证文件到域名根目录。不要向聊天或代码库粘贴 AppSecret。
## 反向代理与后台保护
如果使用 `/state/` 子路径,反向代理要剥离该前缀。以下片段加入已经配置证书的 HTTPS server 中;实际部署前先核对现有 nginx 配置,不要直接覆盖。
```nginx
location = /state { return 301 /state/; }
# 管理后台与用户账号接口相互独立;必须继续保护后台。
location ~ ^/state/(admin\.html|css/admin\.css|js/admin\.js|api/admin/.*)$ {
auth_basic "Juzi Admin";
auth_basic_user_file /etc/nginx/.htpasswd-juzi;
rewrite ^/state/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 20m;
proxy_read_timeout 150s;
}
location /state/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 20m;
proxy_read_timeout 150s;
}
```
根路径部署则设置 `PUBLIC_BASE_URL=https://你的域名/` 并相应去掉上述 `/state/`。不要遗漏 `admin.html` 和 `/api/admin/` 的鉴权,也不要把 Node 8080 直接暴露到公网。现有 Node 管理接口本身不使用新 C 端账号鉴权;公网安全仍依赖后台鉴权和回环监听。
`TRUST_LOOPBACK_PROXY=1` 用于按真实客户端 IP 限制登录频率;只信任来自回环代理且格式合法的 `X-Real-IP`。nginx 必须按示例覆盖该头,不能透传浏览器传入值。本地直接访问时不要启用。未启用时按 TCP 对端限流,在反代后会共享同一计数。
当前 WSL 使用 `HOST=0.0.0.0` 方便局域网测试,局域网内管理接口也可达,请只在可信网络使用。手机 HTTP 访问不是安全相机上下文,会沿用现有系统相机/上传降级;自动实时抓拍需 HTTPS,或在本机 localhost 测试。这与微信授权是否配置是两个不同限制。
## 数据、迁移与备份
- `h5/data/accounts/identities.json`:账号、随机身份凭据的哈希、会话、最近报告索引。
- `h5/data/accounts/reports/<user-id>/<report-id>.json`:完整报告快照,含三张照片、检测图层、评分、话术和推荐产品。
- 原有 `data/records.jsonl`、`data/photos`、`data/results` 继续供后台复核。
- 本机浏览器 localStorage 只保存随机身份凭据,不再存照片与报告。更换设备/浏览器/站点域名,或清除全部站点数据后,不能靠本机身份找回原账号。
- 微信与本机账号不自动合并;旧版 localStorage 报告未自动认领迁移,避免错误关联。旧数据不删除。
- 前台只列出并允许回看最近 5 份,超出 5 份的快照/后台记录暂保留,不代表已物理删除。正式上线需业务方确认保留和删除周期。
- 迁移时停止写入,备份并迁移**完整数据目录**,保留访问权限。只同步代码不会带上报告。禁止将数据目录提交 Git 或由 nginx 作为静态目录公开。
- 当前为单 Node 进程的文件存储,不可对同一目录开多个 worker/多实例;未来扩容需迁移数据库和对象存储。
## 公网真机验收清单
- 微信打开 HTTPS 页面,协议可查看、未勾选不能登录。
- 首次微信登录→首页→三视角检测→我的报告→详情。
- 同一微信账号换另一台手机,能看到原报告;另一微信账号看不到。
- 报告七项检测图与初次生成一致,文案、评分、推荐商品不因回看重算。
- 取消/失败授权后可重新登录或选择本机浏览器登录。
- 重启服务后报告与账号不丢失。
- 检查 HTTPS 摄像头权限、三视角自动抓拍、前后镜头、后台鉴权、数据目录不可访问。
协议处理:登录和检测授权复用 `h5/index.html` 中已有的「Face++ 个人信息与隐私保护政策」(包含儿童相关章节)。本次未发现独立的品牌方用户协议文件,未凭空编写法律条款;正式发布前请业务/法务确认现有政策是否覆盖品牌方的账号关联、报告存储、保存期限和删除渠道。
+60
View File
@@ -0,0 +1,60 @@
# 微信登录联调:2026-10-12
站点:`https://1me.xajuzi.com/state/`;服务器:`ubuntu@1me.xajuzi.com`。
## 后续真机确认与体验优化
- 2026-10-12 用户已在手机微信内通过「切换登录方式」完成真实微信登录;生产服务 10:40:31 记录 `login_success`。下文“待真机验证”是此前预检时点的记录,不再代表当前登录状态。跨手机读取仍只有隔离测试覆盖,未声称用户已做跨手机实测。
- 新增「登录成功」弹框,说明当前使用微信或本机浏览器;首页右上角、我的报告顶部持续展示登录方式,并可点击切换。恢复有效会话不重复弹框;错误回调、丢失 Cookie 不显示成功。
- 登录加载慢的实测原因、优化及验证命令见[登录反馈与加载优化](登录反馈与加载优化-2026-10-12.md)。
## 白名单与凭据
- 用户再次确认已将服务器出口 IP `49.233.222.47` 加入公众号 IP 白名单。
- 从生产服务正在运行的进程环境读取凭据,在服务器调用微信 `cgi-bin/stable_token`,使用 `force_refresh:false`,未强制刷新已有令牌。
- 返回有效 `access_token`,未返回 `40164`;复测剩余有效时间为 7168 秒。已确认该配置的 AppID 与 `wx645d9a7573f1fe9a` 一致,AppID/AppSecret 校验通过。
- 密钥继续留在 root 私有文件 `/etc/juzi/wechat.env`(600);服务监听 `127.0.0.1:8080`,站点地址包含 `/state/`。没有将密钥或令牌写入代码、日志、文档或前端。
- 基础接口令牌成功不等于真实用户网页授权成功;后者仍要验证公众号网页授权权限、网页授权域名以及真实微信客户端回调。
## 本轮修正
1. 「我的报告」增加「切换登录方式」,复用现有登录页。已有本机用户可以进入微信登录;重新勾选协议后才发起授权。只切换所选账号,不自动合并、迁移或删除报告;返回可继续原会话。
2. 本地只记录最近一次有效登录的方式(`juzi.auth.kind.v1`),不存 OpenID 或微信令牌。微信会话失效后,不能用遗留本机随机凭据自动登录另一个账号;需显式重新选择登录方式。同一微信重新登录仍对应同一服务端账号。
3. 微信返回错误或返回后 Cookie 未保留时,保持登录页并说明原因,不允许用本机自动恢复掩盖失败。处理后移除 URL 的一次性登录结果参数,避免刷新后反复显示旧错误。
4. 服务端拒绝微信响应中的无效 OpenID、异常 HTTP 状态等错误响应;网络失败、平台失败、state 校验失败均不签发会话。
5. 增加 `[auth/wechat]` 运行事件:`callback_rejected`、`exchange_failed`、`login_success`。仅记录错误种类、微信数值错误码、HTTP 状态和是否新账号;不记录 code/state、Cookie、OpenID、密钥或令牌。
## 自动验证
```bash
node h5/test/accounts.js
node h5/test/wechat-browser.mjs
node h5/test/report-persistence.mjs
node h5/test/recommendation.js
node h5/test/analyze-timeout.js
H5_BASE_URL=https://1me.xajuzi.com/state/ node h5/test/wechat-production.mjs
```
- 账号测试覆盖协议、同源校验、state 绑定/一次性使用、跨账号隔离、账号持久化;新增无效 code、空响应、非字符串 OpenID、HTTP 500 和网络错误均拒绝登录的验证。
- 真实 HTTPS + `/state/` 浏览器测试仅模拟微信上游,覆盖本机→微信→本机显式切换、原报告保留、同一微信跨两个独立浏览器读取同一报告、另一微信无法读取、Cookie 丢失不串号、失败提示不被本机自动恢复掩盖。
- 报告保存与回看、前后台一致性、126 个推荐用例及检测超时回归通过;不调用收费 Face++。
- 公网预检脚本只使用刻意无效的 code,验证授权 URL/绑定 Cookie、真实微信拒绝无效换码、state 防重放、同源与协议校验、匿名报告拒绝及后台保护;不创建微信身份,不表示真人授权成功。
- 部署后公网预检复测通过,新版 HTML/脚本已实际提供;本地和线上代码校验一致,服务与 nginx 均正常。生产日志中的 `40029` 来自上述刻意无效 code 的负向测试,并非一次真人登录失败;尚未收到本轮真人授权成功的回调。
## 真实微信验收与排查
目前尚不能将模拟授权和基础凭据通过视为真人登录已完成。服务器与无头浏览器不能替用户产生真实微信授权 code,也不在生产环境伪造微信身份。
1. 手机微信内打开 `https://1me.xajuzi.com/state/`,不要选择「在浏览器打开」。
2. 若已有本机登录,进入「我的报告」→「切换登录方式」;勾选协议,点击「微信登录」。
3. 登录后进入「我的报告」,身份提示应为「同一微信账号可跨手机查看」。首次使用微信身份时列表可能为空:本机账号历史报告不会自动合并。
4. 同一微信重新登录或换另一台手机查看,账号和已保存微信报告应一致;无需为了验证登录再次调用收费检测。
5. 如微信自身页面提示域名或 scope 权限错误,请核对对应 AppID 公众号的「网页授权域名」是否为 `1me.xajuzi.com`、是否有网页授权权限;这些设置不能由域名校验文件存在或基础接口令牌通过来推断。
服务端排查命令:
```bash
sudo journalctl -u juzi-h5.service --since '10 minutes ago' --no-pager | grep -F '[auth/wechat]'
```
只需反馈页面是否进入首页/报告列表,或错误页面截图。不要传授权 code、Cookie、OpenID、AppSecret 或接口令牌。
@@ -0,0 +1,46 @@
# 登录反馈与加载优化(2026-10-12)
## 体验变化
- 显式本机登录、微信授权成功且会话确认后,显示「登录成功」弹框,说明登录方式和报告访问范围。
- 首页右上角和「我的报告」顶部显示「微信已登录」或「本机浏览器已登录」;标识可点击切换。依据服务端会话的 `user.kind`,不根据浏览器是否处于微信来猜测。
- 刷新后恢复有效会话不重复弹框。授权失败、Cookie 丢失不会显示成功;不合并账号、不删除缓存身份或历史报告。
- 弹框支持确认按钮、Esc、键盘焦点约束,关闭后恢复页面交互。
## 慢的原因与处理
优化前,在开发机浏览器对公网页面的一次无缓存测量:HTML 首字节约 244 ms,登录约 5510 ms 可操作;同步排在应用前的 face-api.js 853487 字节、下载约 4319 ms。两份完整字体分别 4858624 和 5034212 字节,下载约 14.5/15.6 秒。额外还预加载了已不使用的旧首页背景(约 590 KB)。认证状态接口独立 curl 测量约 431 ms,不能将本次首屏等待主要归因为微信接口;手机网络、DNS/TLS 和真实授权往返仍会影响实际时间。
处理:
1. 人脸引擎改为进入拍照、实际可用相机或系统相机降级时才请求;加载失败可重试,20 秒超时后保留手动兜底。模型/自动抓拍算法不变。
2. 登录、首页、报告列表使用系统字体;进入档案/拍照/报告时再注册原始 MiSans 字体。字体原文件保持不变。官方许可不允许改编或二次开发,因此没有对子集化作授权推断。[官方许可](https://hyperos.mi.com/font/zh/download/)。
3. 保留 PSD 原导出和 JPEG,新增 WebP 分发背景(同分辨率),移除旧背景预加载,优先加载登录背景。生成方式:`python tools/prepare_h5_assets.py`。
4. Node 静态资源增加 gzip 和 ETag/304,继续 `Cache-Control: no-cache` 及时校验新版本;仅压缩静态公共文本,不缓存/压缩账号、报告等私有 API 响应。未改 nginx 后台保护配置。
5. 微信登录去掉多余的配置查询往返,直接请求受协议及同源校验保护的授权接口;禁用环境仍由服务端明确拒绝。仅登录鉴权请求增加 12 秒超时,失败后不清除身份凭据;不缩短含照片报告的下载等待时间。
## 验证
```bash
H5_ARTIFACT_DIR=/tmp/juzi-login-20261012 node h5/test/login-performance.mjs
node h5/test/wechat-browser.mjs
node h5/test/accounts.js
node h5/test/report-persistence.mjs
node h5/test/recommendation.js
node h5/test/analyze-timeout.js
H5_ARTIFACT_DIR=/tmp/juzi-login-ui-20261012 node h5/test/ui-20261009.mjs
node h5/test/browser-failures.mjs
node h5/test/e2e-browser.mjs
```
隔离性能测试:150 ms 网络延迟、约 2 Mbps 下载、CPU 四倍降速,冷登录可操作约 1598 ms,静态资源编码后共 259547 字节;登录、首页不请求完整字体、人脸引擎或模型。另验证 gzip 解码一致、304、拒绝 `gzip;q=0`、私有 API `no-store`、超时保留身份、320/375/390/480 宽度标识不遮挡文案。拍照时引擎失败重试后,真实模型能返回 68 点人脸特征。
以上是可重复的开发机模拟结果,不等于每部手机的耗时保证,也不能直接与未限速的公网测量作同条件性能比例比较。测试不调用收费 Face++。
## 公网部署后复测
代码 `d2931a4` 已在本机和生产同步,生产 `juzi-h5` 与 nginx 均正常。部署前确认近 180 秒无进行中的检测;仅更新跟踪代码,未迁移、清理或覆盖 `h5/data`,没有改变微信凭据或域名设置。
同一开发机、无浏览器缓存的公网复测:HTML 首字节约 222 ms,登录可操作约 311 ms,资源编码后共 259678 字节;未请求完整字体或人脸引擎。随后刷新页面,资源网络传输约 7523 字节(其余走校验缓存)。这是一次观测值,不能保证所有手机达到该时间。
线上 gzip 与 ETag/304 已验证;健康、登录配置接口正常,匿名报告与后台均拒绝访问,私有数据目录不可公开读取。复测只读,不创建生产测试账号、报告,也不发起新的微信授权或收费检测。
@@ -0,0 +1,68 @@
# 线上微信登录部署记录
时间:2026-10-11(北京时间)。服务器:`ubuntu@1me.xajuzi.com`,页面:`https://1me.xajuzi.com/state/`。
2026-10-12 最新状态:白名单已生效,生产服务器成功获取微信基础接口令牌,AppID/AppSecret 校验通过;新增账号切换、会话失效防串号和回调诊断,详见[本轮联调记录](微信登录联调-2026-10-12.md)。下文 `40164` 是历史结果,真实微信客户端授权仍待验证。
## 后续更新:已配置真实凭据
用户提供凭据后,已将其写入 `/etc/juzi/wechat.env` 并重启服务。该文件仅 root 可读写(600),目录为 700;临时凭据文件已移除,未写入代码库或前端。
- 公网健康检查正常;`/state/api/auth/config` 返回 `wechat: true`。
- 真实登录入口返回微信授权 URL;AppID 与配置匹配,作用域为 `snsapi_base`,回调为 `https://1me.xajuzi.com/state/api/auth/wechat/callback`。
- OAuth 绑定 Cookie 确认为 HttpOnly、Secure、SameSite=Lax,路径 `/state/`;回调无效 code 后返回登录错误,重复 state 被拒绝,未错误发放登录会话。
- 微信网页授权换码接口使用诊断用无效 code 返回 `40029 invalid code`,未返回 IP 白名单错误。这只验证端点连通和错误处理,不能证明 AppSecret 正确或真实授权成功(无效 Secret 对照也返回相同错误)。
- 微信基础接口 `/cgi-bin/stable_token` 返回 `40164`,服务器出口 IP `49.233.222.47` 不在公众号 IP 白名单。须由公众号管理员加入后,才能完成这项基础凭据校验;不能直接把此错误认定为网页授权登录必然失败。
- 自动浏览器能够从真实页面发起微信跳转,但无法取得真实微信客户端授权 code,因此没有伪造生产微信身份或宣称真人登录已通过。
剩余验证:公众号管理员添加基础接口 IP 白名单;在真实微信内完成一次授权,确认权限与网页授权域名,并检查首页和报告列表。非登录功能的真实闭环结果见 `线上闭环测试-2026-10-11.md`。
### 用户确认加入白名单后的复测
2026-10-11 14:50 UTC,从同一生产服务器间隔复测:`stable_token` 和标准 `cgi-bin/token` 均仍返回 `40164`,提示 `49.233.222.47` 不在白名单。已确认服务器读取的 AppID 与用户提供的 `wx645d9a7573f1fe9a` 一致;未输出密钥或令牌。仍需核对是在该 AppID 对应账号的 **IP 白名单** 中添加并保存,而非网页授权域名/业务域名或其他微信应用的白名单。
网页授权端点返回的 HTML 仅做微信客户端环境判断,并未提供账号权限/域名是否合规的诊断结果;伪造 User-Agent 无法完成真实授权。换码负向测试仍正常,因此上述基础接口错误不能替代真实微信登录的验收结果。
## 已执行
- 确认服务器仓库为 `55b3a45`,包含 `ce8d0ff` 登录和 UI 更新;未覆盖服务器仓库文件。
- 原 Node 进程仍运行旧版本,重启 `juzi-h5.service` 后新账号接口生效。
- 新增 `/etc/systemd/system/juzi-h5.service.d/wechat.conf`,设置实际 HTTPS 站点地址、回环监听、代理信任和私有环境文件加载。
- 初次准备 `/etc/juzi/wechat.env` 时两个微信变量为空;随后已配置真实凭据。目录权限 700、文件 600,均为 root 所有。
- 修改 `/etc/nginx/sites-available/juzi`:为 `/state/api/admin/` 增加 Basic Auth(原先只有 `/api/admin/` 受保护);兼容旧路径,覆盖真实 IP 请求头;请求体上限调整为 20 MB,读取超时调整为 150 秒。
- `nginx -t` 通过后平滑 reload,原证书、管理员密码文件和域名验证文件未改动。
- Node 确认仅监听 `127.0.0.1:8080`。
备份:`/etc/juzi-deploy-backup.3ipdsA/`,含修改前 nginx 配置和 systemd 主服务文件。新增 systemd drop-in 为独立文件,可单独回退;数据目录未移动或删除。
## 初次部署的线上检查(历史结果)
| 检查 | 结果 |
|---|---|
| `/state/`、`/state/api/health` | 200 |
| `/state/api/auth/config` | 200,wechat=false,明确缺配置 |
| 匿名读取报告 | 401 |
| `/state/api/admin/stats`、`/api/admin/stats`、后台页面 | 401 |
| 数据目录、编码斜杠源码路径 | 404 |
| 同站但未同意协议的登录请求 | 400 |
| 跨站登录请求 | 403 |
| 微信登录请求(尚无凭据) | 503,未伪装登录成功 |
| 无效微信回调 | 302,返回 `/state/?login_error=state` |
| 根目录微信验证文件 | 200 |
## 初次部署时的凭据查找记录(现已由用户提供)
查找到的是 `MP_verify_kpx234XimQGtFPo6.txt`,只有 16 字符校验串;本地与服务器相关目录均未找到同名 `.md`。它用于域名验证,不包含 AppID/AppSecret,也不能用于换取微信用户身份。
在服务器运行 `sudoedit /etc/juzi/wechat.env`,填写对应公众号凭据:
```ini
WECHAT_APP_ID=公众号AppID
WECHAT_APP_SECRET=公众号AppSecret
```
保存后执行 `sudo systemctl restart juzi-h5.service`。无需在聊天中发送 AppSecret,也不要将该私有文件加入 Git。然后重新检查 `/state/api/auth/config`,并在微信内访问站点完成真实授权测试。
同时须确认该公众号具备网页授权权限,公众号后台的「网页授权域名」是 `1me.xajuzi.com`。目前仅确认校验文件可访问,尚无法确认公众号后台实际设置。回调 URL 已准备为 `https://1me.xajuzi.com/state/api/auth/wechat/callback`。
初次部署阶段没有触发收费 Face++ 检测,没有更改既有报告或真实用户账号。后续闭环测试使用独立测试身份调用一次真实 Face++,详见闭环测试记录。真实微信账号联调仍待完成,不能将接口部署成功视为微信授权成功。
+69
View File
@@ -0,0 +1,69 @@
# 线上闭环测试(不含真实微信授权)
测试时间:2026-10-11;站点:`https://1me.xajuzi.com/state/`。
## 结论
真实线上测肤、完整报告存储与回看已跑通。测试使用项目自带的 C3 三视角示例照片和新建的独立浏览器身份,没有修改或删除既有用户报告。除明确标注的故障注入/演示测试外,真实闭环经过公网 HTTPS、nginx、生产 Node 服务和真实 Face++ 接口。
## 发现并修复
前端原先 45 秒取消测肤请求;后端两次 60 秒请求加 4 秒重试间隔,最长约 124 秒,导致网络慢或重试时前端提前失败。前端调整为 150 秒,与已部署的 nginx 150 秒超时配合,并新增请求成功、超时、过期响应丢弃及计时器清理回归测试。
线上已同步 `h5/js/app.js`,原文件备份为 `/home/ubuntu/juzi-app-before-closedloop-20261011.js`。静态 JS 响应 `Cache-Control: no-cache`。该超时修复已包含在提交 `3dda4ec` 中;本次将新增测试和记录同步纳入 Git,统一本地和线上版本。
## 话术及仓库同步
以线上提交 `3dda4ec` 中的最新 `reference/AI测肤报告页话术修改.md` 为准,其 SHA-256 与用户附件完全一致(`c871de9ac3ddd40410653da904cb0b800e24bdf550df53d60602645bab2aafbb`)。本地拉取时先备份并恢复了原有测试、文档及知识图谱改动,未丢弃未提交工作。
运行 `python3 tools/build_report_copy.py` 重新生成 `h5/js/report-copy.js`,将水分「改善」结论中的「水分分 {water_score}」同步到页面实际使用的话术。评分和推荐算法不变。部署后重启服务,让服务端新报告使用更新文案;已保存的历史报告继续读取原有快照。
## 已通过的检查
| 场景 | 验证方式与结果 |
|---|---|
| 核心评分 | 服务器运行 83 项引擎自测、86 项回归断言,全部通过 |
| 账号和报告 | 服务器隔离测试验证授权同意、CSRF、账号隔离、完整快照、最近 5 份、重启持久化、身份恢复及 OAuth 状态绑定/防重放 |
| 公网页面 | Chromium 经真实 HTTPS 访问登录、首页、隐私协议、档案、报告列表和报告;320/390/480 宽度无横向溢出 |
| 三连拍与语音 | 模拟摄像头完整正脸→右脸→左脸流程;14 条音频可获取、引导顺序正确、三次完成播报、无语音截断 |
| 档案保护 | 未同意隐私协议不能继续;未填写档案不能启动拍摄 |
| 相机降级 | 注入相机拒绝及模型不可用,系统相机文件输入仍可完成三视角流程;坏图提示错误后可重新选择 |
| 真实测肤 | 三视角样例经页面压缩上传,真实 Face++ 返回成功,记录已完成,服务记录耗时 1281 ms,综合分 74 |
| 完整报告 | 七张检测图加载、七个标签切换、图片放大及浏览器返回通过;后端保存三张照片、完整可视化数据与文案 |
| 历史恢复 | 清除会话 Cookie 后由浏览器本地身份恢复;从服务端再次打开报告,七张图片与文案和首次展示逐项一致 |
| 历史异常 | 浏览器侧注入列表 503 错误,错误提示与刷新重试恢复正常 |
| 检测异常 | 浏览器侧分别注入业务失败、断网和超时,均关闭分析弹层、保留档案并允许重拍;不产生虚假报告 |
| 人脸模型 | 公网真实模型加载正常,示例图能实际识别人脸并输出 68 个关键点 |
| 权限隔离 | 匿名读报告 401,另一个已登录身份读测试报告 404;跨站检测请求 403;缺图请求 400 |
| 文件/后台保护 | 后台接口、后台页面需认证;数据、源码、编码斜杠访问被拒绝 |
| 服务状态 | nginx 配置检查通过;nginx 和 juzi-h5 均 active;Node 仅监听 127.0.0.1:8080 |
真实调用记录:`R20261011-193351-PD7P`。本次只有一笔真实 Face++ 检测请求;故障测试不调用收费接口。测试报告保留在独立测试身份下,未清理或改写其他数据。
## 可复现命令
```bash
node h5/test/accounts.js
node h5/js/skin-score.js
node h5/test/regression.js
node h5/test/analyze-timeout.js
node h5/test/report-persistence.mjs
node h5/test/wechat-browser.mjs
BASE_URL=https://1me.xajuzi.com/state node h5/test/e2e-browser.mjs
H5_BASE_URL=https://1me.xajuzi.com/state/ H5_ARTIFACT_DIR=/tmp/juzi-production-ui-20261011 node h5/test/ui-20261009.mjs
H5_BASE_URL=https://1me.xajuzi.com/state/ node h5/test/browser-failures.mjs
# 显式开启:会新增独立测试身份和报告,并实际消耗一次 Face++ 调用。
RUN_LIVE_ANALYZE=1 H5_BASE_URL=https://1me.xajuzi.com/state/ node h5/test/production-loop.mjs
```
浏览器测试依赖全局 Playwright 和 Chromium。`wechat-browser.mjs` 使用隔离 HTTPS 及模拟微信响应,只验证我们的跳转、会话和身份流程,不代表真实微信授权通过。
本机截图/结果:`/tmp/juzi-production-loop-LqVf32/`、`/tmp/juzi-production-ui-20261011/`、`/tmp/juzi-e2e-report.png`。不记录身份凭证或密钥,不将报告快照/照片加入 Git。
## 验证边界
- 真实微信授权尚未完成:后续用户提供 AppID/AppSecret 后已配置并启用;基础接口凭据校验受到 IP 白名单限制,网页授权换码负向测试正常,真实微信授权仍需客户端完成。详见微信部署记录。
- 自动化不等同于真实手机相机验收。微信内置浏览器的权限、前后镜头、实际光线/姿态下自动抓拍和系统相机交互,需要最终真机验证。
- 这次是功能闭环和异常恢复检查,不是高并发压力测试或完整安全审计。
@@ -0,0 +1,2 @@
1 拍摄的时候,拍摄完前一张图片的时候,需要有一停顿提示用户 拍摄下一张。 这样用户有心理预期。
2 报告页面的 "紧塑力 活力感 稳定力" 这个部分还是改回原来的布局,保留点击照片放大的功能就行。
Binary file not shown.
+74 -43
View File
@@ -1,69 +1,100 @@
{
"0": "face-api.js",
"1": "n",
"1": "线上闭环测试(不含真实微信授权)",
"2": "app.js",
"3": "skin-score.js",
"4": "isTensor",
"5": ".toBatchTensor",
"6": "getEnv",
"7": "FaceLandmarks",
"8": "disposeUnusedWeightTensors",
"4": "customer-feedback-second.js",
"5": "n",
"6": ".runAndExtendWithFaceDetection",
"7": ".toString",
"8": "admin.js",
"9": "__awaiter",
"10": "extractWeightEntryFactory",
"11": "FaceProcessor",
"10": "FaceLandmarks",
"11": "lib/accounts.js",
"12": "server.js",
"13": "files",
"14": "extractAllFacesAndComputeResults",
"15": ".runAndExtendWithFaceDetection",
"14": ".draw",
"15": "extractFaces",
"16": "巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范",
"17": "extractFaces",
"17": "k",
"18": "1'me 在线肌肤测试 H5",
"19": "head-pose.js",
"20": "build_fixtures.py",
"21": ".forwardInput",
"22": ".loadFromDisk",
"21": "disposeUnusedWeightTensors",
"22": ".predictAgeAndGender",
"23": "facepp-skinanalyze(旷视皮肤分析专业版调用技能)",
"24": "巨子测肤 · 1'me 语音播报内容设计文档 V1.0",
"25": "extractParamsFromWeigthMap$1",
"26": "wi",
"25": "mobile-select.iife.min.js",
"26": ".extractWeights",
"27": "gen_voice.py",
"28": "initialize",
"28": "C",
"29": "face-api.js 本地化依赖(h5/vendor/faceapi/)",
"30": "regression.js",
"31": "isValidNumber",
"30": "ui-20261009/manifest.json",
"31": "browser-failures.mjs",
"32": "FaceMatcher",
"33": "build-fixture.py",
"34": "smoke-test.js",
"35": "DetectSingleFaceLandmarksTask",
"36": "so",
"37": "tt",
"38": "TinyYolov2",
"39": "cn",
"40": ".computeFaceDescriptor",
"41": "SsdMobilenetv1",
"42": "dr",
"43": "FaceLandmark68Net",
"44": "rescaleAndNormalize",
"35": "customer-feedback.js",
"36": "actualFont",
"37": "UI 外发版 10.09 图片资源",
"38": "FaceLandmark68Net",
"39": "UI交互界面调整",
"41": "NeuralNetwork",
"42": "layer_at",
"43": "normalize",
"44": "start-local.sh",
"45": "PredictAllAgeAndGenderWithFaceAlignmentTask",
"46": "PredictAllFaceExpressionsWithFaceAlignmentTask",
"47": "PredictSingleAgeAndGenderWithFaceAlignmentTask",
"48": "PredictSingleFaceExpressionsWithFaceAlignmentTask",
"49": "Yo",
"47": "recommendation.js",
"48": "已修改",
"49": "mobile-select/README.md",
"50": "call_skinanalyze_3view.sh",
"51": "TinyXception",
"52": "Vc",
"53": "FaceDetection",
"54": "ff",
"55": "ge",
"56": "sa",
"57": "po",
"51": "第二轮2反馈.md",
"52": "region-data.js",
"53": "护理建议(全量文本,按需求摘抄)",
"54": "UI 对齐 10.09 设计稿:调研与实施计划",
"55": "i",
"56": "report-ui.js",
"57": "regression.js",
"58": ".dispose",
"59": "call_skinanalyze.sh",
"60": "report-fixture.js",
"61": ".extractWeights",
"62": "NeuralNetwork",
"62": "提交前 Review(2026-10-11)",
"63": "e2e-browser.mjs",
"64": "AgeGenderNet",
"66": "FaceExpressionNet",
"71": "PredictSingleFaceExpressionsTask"
"64": "account-client.js",
"65": "微信登录接入与公网部署说明",
"66": "report-persistence.mjs",
"67": "ui-20261009.mjs",
"68": "analyze-timeout.js",
"69": "misans/README.md",
"70": ".forwardInput",
"72": "build_report_copy.py",
"73": "camera-geometry.js",
"74": "report-copy.js",
"75": "prepare_h5_assets.py",
"76": "TinyYolov2Base",
"77": "drawFaceExpressions",
"78": "t",
"79": "登录反馈与加载优化(2026-10-12)",
"80": "线上微信登录部署记录",
"81": "wechat-production.mjs",
"82": "wechat-browser.mjs",
"83": "10.09 新版 H5 实施与验收",
"84": "微信登录联调:2026-10-12",
"85": "test/accounts.js",
"87": "产品推荐规则:肤龄基准与边界调整",
"88": "ui-20261012/manifest.json",
"89": "傅艳NEW与项思炼的会话 2026年10月12日.md",
"90": "customer-feedback.mjs",
"91": "wi",
"92": "客户反馈修改(2026-10-12)",
"93": ".runAndExtendWithFaceDetections",
"94": "第三轮反馈:PSD 对齐及本地验收",
"95": "login-performance.mjs",
"96": "production-loop.mjs",
"97": "PredictSingleAgeAndGenderWithFaceAlignmentTask",
"98": "PredictSingleFaceExpressionsWithFaceAlignmentTask",
"99": "DetectSingleFaceLandmarksTask",
"100": "extractAllFacesAndComputeResults",
"106": "第三轮反馈20261012/傅艳NEW与项思炼的会话 2026年10月12日.md"
}
+1 -1
View File
@@ -1 +1 @@
{"0": "084975bb68797cf4", "1": "6c6ce24229e04595", "2": "64f07a78d61df933", "3": "8ac814cdb72a1a7d", "4": "c18a4a8fab272cc6", "5": "7f62d3cc90a92195", "6": "865611558d18274c", "7": "e7b6c52cb9523ba1", "8": "934b465b2e687d3d", "9": "31a3a505422c0528", "10": "f943c63a37873e9e", "11": "9ed244ba3453a0c1", "12": "8d1af962d46ec19f", "13": "7e3b558a587a4ebe", "14": "2681c1b3f4f3539e", "15": "8bd61518dd267641", "16": "95add6a975c0cb72", "17": "d5ee72467704e6f0", "18": "fd0841b607871dc7", "19": "0a373ad2197fd1a6", "20": "aecf2fce44ba49c5", "21": "880a74c6d255e5cc", "22": "1c67ad3d97e5c622", "23": "90fac00055070ce2", "24": "f85b5b1634224c86", "25": "79d2843a716971c8", "26": "83b0fd3b14363e4d", "27": "eab427ce9a5219e9", "28": "eb3733e409dc7411", "29": "c608f1c6492e1cb1", "30": "f2a40b5f1b29f669", "31": "8f647d3ce08ecd2b", "32": "f6bcf56c4589316b", "33": "75f750c5b7d9f63d", "34": "f024ff7942935690", "35": "23f1d406f6c779ae", "36": "c55f872b3e16ab6f", "37": "3088ae48043ee39d", "38": "c96276581a8c98d4", "39": "932f010dc6115fb4", "40": "444ef0744645f5ac", "41": "386a184c6bd5a40e", "42": "2bfc96d4090afd84", "43": "9a25b1230b5fe649", "44": "8d5f4e992bac9095", "45": "92ab3e5134ed8685", "46": "65718282b3c32109", "47": "01f68dfc34a29d32", "48": "8c471c7baeaaca54", "49": "a54008a71fdd44fa", "50": "bbb2b57d72922b16", "51": "0985ce7473fdd021", "52": "c5a22f1501fe85b7", "53": "287d60115d9acda8", "54": "1331494302f6c136", "55": "f42d242a020b2f05", "56": "2537fe1309d3ab9a", "57": "7862e3ee5a214160", "58": "440ae90e84499b6f", "59": "891f59cda3708718", "60": "a505b7de6257a4e8", "61": "151209871c761759", "62": "d96493a5f110bd4c", "63": "c256b43f75d7809e", "64": "512a2535c88c4284", "66": "e858d75e995c92e5", "71": "cb5ac3401283c378"}
{"0": "ea3f8d8f67782fdc", "1": "cb6ea50bd4e4a5ca", "2": "485914b81d357062", "3": "1f737ca8edb978f2", "4": "56bf12952b0de42b", "5": "ecc33b1dfcb6c0a4", "6": "8bd61518dd267641", "7": "cd6f168abba94fea", "8": "1ecf47531e1d507b", "9": "8abf7998360e0ccc", "10": "7fb7f7cc828b286c", "11": "4a490abd5e503318", "12": "53594b653ad48743", "13": "7e3b558a587a4ebe", "14": "bf75bfc99d4f9e34", "15": "fb002c2979ef551d", "16": "95add6a975c0cb72", "17": "09246ceabd90e01c", "18": "38bb0b185ceed116", "19": "0a373ad2197fd1a6", "20": "aecf2fce44ba49c5", "21": "254ec4cab6c4ac57", "22": "a2b13160223b5a98", "23": "90fac00055070ce2", "24": "f85b5b1634224c86", "25": "46e8db3e6e3f37ea", "26": "487edffbbcedb8cb", "27": "eab427ce9a5219e9", "28": "121c45cc1e571288", "29": "c608f1c6492e1cb1", "30": "73c13793fca09f56", "31": "fbbcfc059751cc6a", "32": "f6bcf56c4589316b", "33": "75f750c5b7d9f63d", "34": "f024ff7942935690", "35": "5a17f8810dae4ff2", "36": "a7d375041779bad4", "37": "cb8f26249485324a", "38": "9a25b1230b5fe649", "39": "fdd95375b4a5ddb3", "41": "d96493a5f110bd4c", "42": "916170b9f5d7346b", "43": "e48804ba2a6c4e86", "44": "852c1bb2446edd9c", "45": "92ab3e5134ed8685", "46": "65718282b3c32109", "47": "e594ecaa8e7d1538", "48": "cf2f2424bc00436f", "49": "1f976711cda158dc", "50": "bbb2b57d72922b16", "51": "eb6a182204867275", "52": "2ec3c8c088568ba9", "53": "68c45056378341a7", "54": "c58d41e8e580fdc6", "55": "b9ba0a5c51b155b1", "56": "8c9a3fc5edff7e44", "57": "f2a40b5f1b29f669", "58": "3ba0641876109aff", "59": "891f59cda3708718", "60": "a505b7de6257a4e8", "62": "1366ae24c7e5897d", "63": "c256b43f75d7809e", "64": "ce0e68dab611f022", "65": "74ae017de0ffd36b", "66": "e042d7fe7351bcb9", "67": "3e126d8deaf6e200", "68": "6b91f3679541de79", "69": "e511846eeba03076", "70": "880a74c6d255e5cc", "72": "c84f196a7bd35861", "73": "2b887bf601ccfb9f", "74": "ae2a154311e1f53a", "75": "69fdd23278518884", "76": "04c475f726d2508f", "77": "979dcc63399620ad", "78": "bf1217db5748fa4e", "79": "c6cfdfe181232ecc", "80": "315325cde1352e98", "81": "37c559741825be74", "82": "2b037735e1f5b0f6", "83": "1284897282eb47a3", "84": "d8bb296741d3d05c", "85": "4bc370be8afd7db1", "87": "e5fa7717989eda80", "88": "8b5ab5232cef3457", "89": "9e3363b80eeceeea", "90": "dd1c246e19a2d032", "91": "83b0fd3b14363e4d", "92": "fced244d93677d90", "93": "435ca504c453b5ca", "94": "70616dc5d4c69039", "95": "59c31f858c7b6970", "96": "4195478d5c528371", "97": "01f68dfc34a29d32", "98": "8c471c7baeaaca54", "99": "da489b69f7f553c4", "100": "adaed54e9cb4adfc", "106": "8bc72c179793e1c2"}
+295 -171
View File
@@ -1,190 +1,210 @@
# Graph Report - juzi (2026-09-28)
# Graph Report - juzi (2026-10-12)
## Corpus Check
- 31 files · ~1,845,638 words
- 83 files · ~4,627,050 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 900 nodes · 2117 edges · 67 communities (46 shown, 21 thin omitted)
- Extraction: 92% EXTRACTED · 8% INFERRED · 0% AMBIGUOUS · INFERRED: 168 edges (avg confidence: 0.52)
- 1326 nodes · 2732 edges · 98 communities (78 shown, 20 thin omitted)
- Extraction: 93% EXTRACTED · 7% INFERRED · 0% AMBIGUOUS · INFERRED: 183 edges (avg confidence: 0.54)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `4e3d915b`
- Built from commit: `f3e7eac9`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- face-api.js
- n
- 线上闭环测试(不含真实微信授权)
- app.js
- skin-score.js
- isTensor
- .toBatchTensor
- getEnv
- FaceLandmarks
- disposeUnusedWeightTensors
- customer-feedback-second.js
- n
- .runAndExtendWithFaceDetection
- .toString
- admin.js
- __awaiter
- extractWeightEntryFactory
- FaceProcessor
- FaceLandmarks
- lib/accounts.js
- server.js
- files
- extractAllFacesAndComputeResults
- .runAndExtendWithFaceDetection
- 巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范
- .draw
- extractFaces
- 巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范
- k
- 1'me 在线肌肤测试 H5
- head-pose.js
- build_fixtures.py
- .forwardInput
- .loadFromDisk
- disposeUnusedWeightTensors
- .predictAgeAndGender
- facepp-skinanalyze(旷视皮肤分析专业版调用技能)
- 巨子测肤 · 1'me 语音播报内容设计文档 V1.0
- extractParamsFromWeigthMap$1
- wi
- mobile-select.iife.min.js
- .extractWeights
- gen_voice.py
- initialize
- C
- face-api.js 本地化依赖(h5/vendor/faceapi/)
- regression.js
- isValidNumber
- ui-20261009/manifest.json
- browser-failures.mjs
- FaceMatcher
- build-fixture.py
- DetectSingleFaceLandmarksTask
- so
- tt
- TinyYolov2
- cn
- .computeFaceDescriptor
- SsdMobilenetv1
- dr
- customer-feedback.js
- UI 外发版 10.09 图片资源
- FaceLandmark68Net
- rescaleAndNormalize
- UI交互界面调整
- NeuralNetwork
- layer_at
- normalize
- start-local.sh
- PredictAllAgeAndGenderWithFaceAlignmentTask
- PredictAllFaceExpressionsWithFaceAlignmentTask
- PredictSingleAgeAndGenderWithFaceAlignmentTask
- PredictSingleFaceExpressionsWithFaceAlignmentTask
- Yo
- recommendation.js
- 已修改
- mobile-select/README.md
- call_skinanalyze_3view.sh
- TinyXception
- Vc
- FaceDetection
- ff
- ge
- sa
- po
- 护理建议(全量文本,按需求摘抄)
- UI 对齐 10.09 设计稿:调研与实施计划
- i
- report-ui.js
- regression.js
- .dispose
- call_skinanalyze.sh
- .extractWeights
- NeuralNetwork
- 提交前 Review(2026-10-11)
- e2e-browser.mjs
- AgeGenderNet
- FaceExpressionNet
- PredictSingleFaceExpressionsTask
- account-client.js
- 微信登录接入与公网部署说明
- report-persistence.mjs
- ui-20261009.mjs
- analyze-timeout.js
- misans/README.md
- .forwardInput
- build_report_copy.py
- TinyYolov2Base
- drawFaceExpressions
- t
- 登录反馈与加载优化(2026-10-12)
- 线上微信登录部署记录
- wechat-production.mjs
- wechat-browser.mjs
- 10.09 新版 H5 实施与验收
- 微信登录联调:2026-10-12
- test/accounts.js
- 产品推荐规则:肤龄基准与边界调整
- ui-20261012/manifest.json
- 傅艳NEW与项思炼的会话 2026年10月12日.md
- wi
- 客户反馈修改(2026-10-12)
- .runAndExtendWithFaceDetections
- 第三轮反馈:PSD 对齐及本地验收
- login-performance.mjs
- production-loop.mjs
- PredictSingleAgeAndGenderWithFaceAlignmentTask
- PredictSingleFaceExpressionsWithFaceAlignmentTask
- DetectSingleFaceLandmarksTask
- extractAllFacesAndComputeResults
- 第三轮反馈20261012/傅艳NEW与项思炼的会话 2026年10月12日.md
## God Nodes (most connected - your core abstractions)
1. `__awaiter()` - 59 edges
2. `__generator()` - 59 edges
3. `n()` - 36 edges
4. `t()` - 33 edges
5. `i()` - 33 edges
6. `r()` - 32 edges
3. `n()` - 37 edges
4. `r()` - 35 edges
5. `t()` - 33 edges
6. `i()` - 33 edges
7. `C()` - 32 edges
8. `toNetInput()` - 27 edges
9. `e()` - 22 edges
10. `f()` - 20 edges
10. `loadRecords()` - 20 edges
## Surprising Connections (you probably didn't know these)
- `actualLines()` --indirect_call--> `el()` [INFERRED]
h5/test/customer-feedback-third.mjs → h5/js/admin.js
- `loadRecords()` --indirect_call--> `it()` [INFERRED]
h5/js/admin.js → h5/vendor/faceapi/face-api.js
- `bind()` --indirect_call--> `a()` [INFERRED]
h5/js/admin.js → h5/vendor/faceapi/face-api.js
- `detectFrame()` --indirect_call--> `d()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
- `vizCompose()` --indirect_call--> `h()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
- `vizCompose()` --indirect_call--> `rh()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
- `vizCompose()` --indirect_call--> `w()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
- `renderReport()` --indirect_call--> `t()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
## Import Cycles
- None detected.
## Communities (67 total, 21 thin omitted)
## Communities (98 total, 20 thin omitted)
### Community 0 - "face-api.js"
Cohesion: 0.02
Nodes (38): bn(), ca(), cd(), ce(), dn(), ee(), er(), et() (+30 more)
Nodes (37): bgrToRgbTensor(), cn(), computeReshapedDimensions(), createBrowserEnv(), createFileSystem(), createNodejsEnv(), Dimensions(), dr() (+29 more)
### Community 1 - "n"
Cohesion: 0.07
Nodes (85): a(), ae(), ao(), b(), ba(), be(), bf(), bi() (+77 more)
### Community 1 - "线上闭环测试(不含真实微信授权)"
Cohesion: 0.25
Nodes (7): 发现并修复, 可复现命令, 已通过的检查, 线上闭环测试(不含真实微信授权), 结论, 话术及仓库同步, 验证边界
### Community 2 - "app.js"
Cohesion: 0.06
Nodes (71): afterCurrent(), applyMirror(), armNudge(), beginCountdown(), beginDetect(), bindBackdropClose(), busyWithFlow(), cancelCountdown() (+63 more)
Cohesion: 0.05
Nodes (82): applyMirror(), armNudge(), beginDetect(), bindBackdropClose(), busyWithFlow(), captureFromVideo(), captureHaptic(), checkStill() (+74 more)
### Community 3 - "skin-score.js"
Cohesion: 0.12
Nodes (44): acneGrade(), buildViewModel(), calcAcne(), calcContour(), calcSens(), calcSmooth(), calcTone(), calcWrinkles() (+36 more)
Nodes (45): acneGrade(), buildViewModel(), calcAcne(), calcContour(), calcSens(), calcSmooth(), calcTone(), calcWrinkles() (+37 more)
### Community 4 - "isTensor"
### Community 4 - "customer-feedback-second.js"
Cohesion: 0.17
Nodes (10): assert, context, face, fs, geometry, original, path, source (+2 more)
### Community 5 - "n"
Cohesion: 0.15
Nodes (36): a(), ae(), bf(), cf(), d(), drawContour(), dt(), ea() (+28 more)
### Community 6 - ".runAndExtendWithFaceDetection"
Cohesion: 0.23
Nodes (6): allFacesMtcnn(), allFacesSsdMobilenetv1(), allFacesTinyYolov2(), DetectAllFaceLandmarksTask(), detectAllFaces(), DetectSingleFaceTask()
### Community 7 - ".toString"
Cohesion: 0.33
Nodes (5): extractorsFactory$3(), extractParamsFromWeigthMap$4(), isTensor(), isTensor1D(), isTensor2D()
Nodes (5): FaceMatch(), la(), pr(), pt(), vt()
### Community 5 - ".toBatchTensor"
Cohesion: 0.08
Nodes (30): conv(), conv$1(), convDown(), convLayer(), convLayer$1(), convNoRelu(), convWithBatchNorm(), denseBlock3() (+22 more)
### Community 6 - "getEnv"
Cohesion: 0.09
Nodes (26): awaitMediaLoaded(), bufferToImage(), createCanvas(), createCanvasFromMedia(), DetectAllFacesTask(), DrawBox(), drawDetections(), drawFaceExpressions() (+18 more)
### Community 7 - "FaceLandmarks"
Cohesion: 0.06
Nodes (16): bgrToRgbTensor(), extendWithFaceLandmarks(), FaceLandmark68NetBase(), FaceLandmarks(), FaceLandmarks5(), FaceLandmarks68(), getCenterPoint(), isDimensions() (+8 more)
### Community 8 - "disposeUnusedWeightTensors"
Cohesion: 0.22
Nodes (6): disposeUnusedWeightTensors(), extractorsFactory$9(), extractParamsFromWeigthMap(), extractParamsFromWeigthMap$6(), extractParamsFromWeigthMap$7(), extractParamsFromWeigthMapTiny()
### Community 8 - "admin.js"
Cohesion: 0.21
Nodes (28): ageText(), api(), bind(), closeDetail(), collectFilters(), dimsHtml(), doRetry(), dSec() (+20 more)
### Community 9 - "__awaiter"
Cohesion: 0.22
Nodes (8): __awaiter(), fetchImage(), fetchJson(), fetchNetWeights(), fetchOrThrow(), __generator(), Mtcnn(), toNetInput()
Cohesion: 0.14
Nodes (12): __awaiter(), ComposableTask(), fetchImage(), fetchJson(), fetchNetWeights(), fetchOrThrow(), __generator(), getModelUris() (+4 more)
### Community 10 - "extractWeightEntryFactory"
Cohesion: 0.53
Nodes (6): extractorsFactory$7(), extractWeightEntryFactory(), loadConvParamsFactory(), loadParamsFactory(), loadParamsFactory$1(), loadSeparableConvParamsFactory()
### Community 10 - "FaceLandmarks"
Cohesion: 0.09
Nodes (8): FaceLandmark68NetBase(), FaceLandmarks(), FaceLandmarks5(), FaceLandmarks68(), getCenterPoint(), minBbox(), Point(), wl()
### Community 11 - "FaceProcessor"
Cohesion: 0.24
Nodes (3): extractParams$1(), FaceLandmark68TinyNet(), FaceProcessor()
### Community 11 - "lib/accounts.js"
Cohesion: 0.17
Nodes (20): atomic(), body(), cookie(), cookies(), crypto, fs, json(), login() (+12 more)
### Community 12 - "server.js"
Cohesion: 0.14
Nodes (16): callFacepp(), CRED, DATA_DIR, ERR_MSG, fs, handleAnalyze(), http, isUsableB64() (+8 more)
Cohesion: 0.08
Nodes (51): accounts, appendRecord(), callFacepp(), CRED, crypto, csvCell(), DATA_DIR, displaySkinAge() (+43 more)
### Community 13 - "files"
Cohesion: 0.12
Nodes (15): files, analyze-fail, analyzing, countdown-321, hint-far, hint-front, hint-hold, hint-near (+7 more)
### Community 14 - "extractAllFacesAndComputeResults"
Cohesion: 0.15
Nodes (8): extendWithAge(), extendWithFaceExpressions(), extendWithGender(), extractAllFacesAndComputeResults(), extractSingleFaceAndComputeResult(), PredictAllAgeAndGenderTask(), PredictAllFaceExpressionsTask(), PredictSingleAgeAndGenderTask()
### Community 15 - ".runAndExtendWithFaceDetection"
Cohesion: 0.23
Nodes (6): allFacesMtcnn(), allFacesSsdMobilenetv1(), allFacesTinyYolov2(), DetectAllFaceLandmarksTask(), detectAllFaces(), DetectSingleFaceTask()
### Community 15 - "extractFaces"
Cohesion: 0.12
Nodes (22): awaitMediaLoaded(), Box(), bufferToImage(), createCanvas(), createCanvasFromMedia(), extractFaces(), extractFaceTensors(), extractImagePatches() (+14 more)
### Community 16 - "巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范"
Cohesion: 0.15
Nodes (12): 1. 基准与尺寸换算, 2. 色彩规范, 3. 字体规范, 4. 组件切图(assets/svg/), 5. 图片素材(assets/images/,共 15 张), 6. 页面清单(design/ 下有整屏 PNG 预览), 7. 目录结构, 8. 还原建议 (+4 more)
### Community 17 - "extractFaces"
Cohesion: 0.22
Nodes (6): Box(), extractFaces(), extractFaceTensors(), imageTensorToCanvas(), isTensor3D(), isTensor4D()
### Community 17 - "k"
Cohesion: 0.11
Nodes (21): b(), ba(), be(), ca(), da(), fa(), jt(), k() (+13 more)
### Community 18 - "1'me 在线肌肤测试 H5"
Cohesion: 0.18
Nodes (10): 1'me 在线肌肤测试 H5, Windows 本机部署(提测用), 密钥配置, 已知占位清单, 技术说明, 拍照交互(引导式自动三连拍), 本地运行, 检测流程与评分渲染 (+2 more)
Cohesion: 0.13
Nodes (15): 10.09 更新, 1'me 在线肌肤测试 H5, Admin API(统一返回 `{ok:false,message}` 错误), Windows 本机部署(提测用), 后台查看(admin.html), 密钥配置, 已知占位清单, 技术说明 (+7 more)
### Community 19 - "head-pose.js"
Cohesion: 0.33
@@ -194,13 +214,13 @@ Nodes (8): assertPoints(), check(), clamp(), dist(), estimate(), makeFrontal(),
Cohesion: 0.40
Nodes (9): build_case1(), build_case2(), build_truth(), col(), dump(), main(), parse_value(), set_path() (+1 more)
### Community 21 - ".forwardInput"
Cohesion: 0.22
Nodes (9): boxPredictionLayer(), decodeBoxesLayer(), depthwiseConvLayer(), getCenterCoordinatesAndSizesLayer(), getStridesForLayerIdx(), mobileNetV1(), outputLayer(), pointwiseConvLayer() (+1 more)
### Community 21 - "disposeUnusedWeightTensors"
Cohesion: 0.08
Nodes (24): disposeUnusedWeightTensors(), extractorsFactory$3(), extractorsFactory$5(), extractorsFactory$7(), extractorsFactory$9(), extractParamsFromWeigthMap(), extractParamsFromWeigthMap$1(), extractParamsFromWeigthMap$2() (+16 more)
### Community 22 - ".loadFromDisk"
Cohesion: 0.19
Nodes (5): ComposableTask(), getModelUris(), Gf(), loadWeightMap(), TinyFaceDetector()
### Community 22 - ".predictAgeAndGender"
Cohesion: 0.28
Nodes (4): extendWithAge(), extendWithGender(), PredictAllAgeAndGenderTask(), PredictSingleAgeAndGenderTask()
### Community 23 - "facepp-skinanalyze(旷视皮肤分析专业版调用技能)"
Cohesion: 0.20
@@ -210,101 +230,205 @@ Nodes (9): 1. 凭据(统一管理), 2. 标准调用模板(全可选参数
Cohesion: 0.22
Nodes (8): 1. 音频格式约定(已定), 2. 播报清单(14 条), 3. 播放策略(前端已实现侧约定), 4. 生成验收, A. 步骤引导(进入每个拍照步骤时播,与屏幕步骤文案一致), B. 姿态提示(实时检测不通过时播,同一提示 4 秒内不重复), C. 拍摄与流程, 巨子测肤 · 1'me 语音播报内容设计文档 V1.0
### Community 25 - "extractParamsFromWeigthMap$1"
Cohesion: 0.40
Nodes (3): extractParamsFromWeigthMap$1(), extractParamsFromWeigthMap$3(), seperateWeightMaps()
### Community 25 - "mobile-select.iife.min.js"
Cohesion: 0.20
Nodes (16): _arrayLikeToArray(), _arrayWithHoles(), _arrayWithoutHoles(), _classCallCheck(), _createClass(), _defineProperties(), h(), _iterableToArray() (+8 more)
### Community 26 - "wi"
Cohesion: 0.28
Nodes (9): Ci(), Ei(), Fi(), Ii(), ki(), pe(), ri(), wi() (+1 more)
### Community 26 - ".extractWeights"
Cohesion: 0.05
Nodes (29): AgeGenderNet(), createFaceDetectionNet(), createFaceRecognitionNet(), createMtcnn(), createSsdMobilenetv1(), createTinyFaceDetector(), createTinyYolov2(), extractConvParamsFactory() (+21 more)
### Community 27 - "gen_voice.py"
Cohesion: 0.30
Nodes (11): ndarray, Path, gen_countdown(), main(), normalize_clip(), 倒计时单独按字生成再拼接,避免「三、二、一」整句被读成播报腔、与提示音气质漂移。 与提示音共用同一 speaker + instruct;句间留约…, 裁掉过长首尾静音,保留 ≤150ms 边沿,避免提示显迟钝。, 响度归一 −16 LUFS + 转 MP3 48k CBR mono 22050Hz。 (+3 more)
### Community 28 - "initialize"
Cohesion: 0.29
Nodes (7): createBrowserEnv(), createFileSystem(), createNodejsEnv(), initialize(), isBrowser(), monkeyPatch(), setEnv()
### Community 28 - "C"
Cohesion: 0.09
Nodes (31): ao(), C(), cd(), co(), Do(), eo(), fo(), Ft() (+23 more)
### Community 29 - "face-api.js 本地化依赖(h5/vendor/faceapi/)"
Cohesion: 0.29
Nodes (6): face-api.js 本地化依赖(h5/vendor/faceapi/), 为什么本地化, 文件清单与体积, 来源与版本, 用法, 许可证
### Community 30 - "regression.js"
Cohesion: 0.60
Nodes (5): diffCtx(), loadJSON(), main(), okNum(), okStr()
### Community 31 - "isValidNumber"
Cohesion: 0.50
Nodes (4): Dimensions(), isValidNumber(), isValidProbablitiy(), isWithGender()
### Community 30 - "ui-20261009/manifest.json"
Cohesion: 0.33
Nodes (5): assets, document_size, layers, pages, source
### Community 33 - "build-fixture.py"
Cohesion: 0.60
Nodes (4): main(), 递归缩放 result 内全部坐标:{left,top,width,height} 矩形与 {x,y} 点,与原数据同为整数。, scale_coords(), to_data_url()
### Community 36 - "so"
Cohesion: 0.50
Nodes (4): Do(), io(), so(), To()
### Community 35 - "customer-feedback.js"
Cohesion: 0.06
Nodes (26): alternate, assert, crop, details, excellent, fs, model, modelBefore (+18 more)
### Community 37 - "tt"
Cohesion: 0.50
Nodes (4): kn(), tt(), wo(), z()
### Community 39 - "cn"
Cohesion: 0.67
Nodes (3): cn(), en(), yn()
### Community 40 - ".computeFaceDescriptor"
Cohesion: 0.18
Nodes (4): ComputeAllFaceDescriptorsTask(), ComputeSingleFaceDescriptorTask(), extendWithFaceDescriptor(), FaceRecognitionNet()
### Community 41 - "SsdMobilenetv1"
### Community 37 - "UI 外发版 10.09 图片资源"
Cohesion: 0.40
Nodes (3): extractorsFactory$5(), extractParamsFromWeigthMap$5(), SsdMobilenetv1()
Nodes (4): UI 外发版 10.09 图片资源, 前端素材对应, 文件位置, 重新导出
### Community 42 - "dr"
Cohesion: 0.67
Nodes (3): dr(), na(), ta()
### Community 39 - "UI交互界面调整"
Cohesion: 0.40
Nodes (4): 1meAI测肤旷视UI问题反馈, UI交互界面调整, 交互, **设计**
### Community 44 - "rescaleAndNormalize"
Cohesion: 0.67
Nodes (3): getSizesForScale(), normalize$1(), rescaleAndNormalize()
### Community 42 - "layer_at"
Cohesion: 0.18
Nodes (11): list, bounds(), layer_at(), main(), save_image(), composite(), main(), sha256() (+3 more)
### Community 49 - "Yo"
Cohesion: 0.67
Nodes (3): qo(), Xo(), Yo()
### Community 43 - "normalize"
Cohesion: 0.09
Nodes (27): conv(), conv$1(), convDown(), convLayer(), convLayer$1(), convNoRelu(), convWithBatchNorm(), denseBlock3() (+19 more)
### Community 44 - "start-local.sh"
Cohesion: 0.50
Nodes (3): HOST, PORT, start-local.sh script
### Community 47 - "recommendation.js"
Cohesion: 0.38
Nodes (6): assert, check(), missing, raw, SS, UI
### Community 48 - "已修改"
Cohesion: 0.22
Nodes (8): 12 日第二批:明确项修改, 公网部署与复核(2026-10-12,北京时间), 准备捕捉提醒, 字重, 已修改, 拍照展示, 首页高清批注, 验证与状态
### Community 53 - "护理建议(全量文本,按需求摘抄)"
Cohesion: 0.10
Nodes (19): 5\.1 轮廓(紧塑力), 5\.2 皱纹(紧塑力), 5\.3 肤色(透亮感), 5\.4 平滑度(透亮感), 5\.5 水分(透亮感), 5\.6 泛红(稳韧力), 5\.7 痘痘(稳韧力), 6\.1 轮廓 (+11 more)
### Community 54 - "UI 对齐 10.09 设计稿:调研与实施计划"
Cohesion: 0.11
Nodes (17): 1. 结论和依据, 2. 统一比较口径, 3. 逐页差异和调整内容, 4. 报告数据如何接到新 UI, 5. 登录与“我的报告”的真实能力缺口, 6. 原稿中需要整理的内容, 7. 实施顺序与交付物, 8. 验收定义 (+9 more)
### Community 55 - "i"
Cohesion: 0.11
Nodes (20): bn(), dn(), ee(), et(), fn(), i(), je(), ln() (+12 more)
### Community 56 - "report-ui.js"
Cohesion: 0.50
Nodes (8): build(), count(), fill(), metricText(), numeric(), scalar(), wrinkleDetails(), wrinkleText()
### Community 57 - "regression.js"
Cohesion: 0.60
Nodes (5): diffCtx(), loadJSON(), main(), okNum(), okStr()
### Community 58 - ".dispose"
Cohesion: 0.14
Nodes (13): computeReshapedDimensions(), extractBoundingBoxes(), iou(), isEven(), nonMaxSuppression(), nonMaxSuppression$1(), pyramidDown(), scale() (+5 more)
Nodes (13): extendWithFaceLandmarks(), getSizesForScale(), iou(), nonMaxSuppression(), nonMaxSuppression$1(), normalize$1(), ObjectDetection(), rescaleAndNormalize() (+5 more)
### Community 61 - ".extractWeights"
Cohesion: 0.08
Nodes (25): createFaceDetectionNet(), createFaceRecognitionNet(), createMtcnn(), createSsdMobilenetv1(), createTinyFaceDetector(), createTinyYolov2(), extractConvParamsFactory(), extractFCParamsFactory() (+17 more)
### Community 62 - "提交前 Review(2026-10-11)"
Cohesion: 0.40
Nodes (4): 仍需公网/业务确认, 已发现并修复, 提交前 Review(2026-10-11), 验证
### Community 63 - "e2e-browser.mjs"
Cohesion: 0.29
Nodes (4): EXPECTED, fails, pageErrors, pwRoot
### Community 64 - "account-client.js"
Cohesion: 0.80
Nodes (5): browser(), init(), read(), rememberKind(), request()
### Community 65 - "微信登录接入与公网部署说明"
Cohesion: 0.22
Nodes (7): 公网服务器配置, 公网真机验收清单, 反向代理与后台保护, 已完成的链路, 微信登录接入与公网部署说明, 数据、迁移与备份, 本机开发环境限制(不代表线上状态)
### Community 66 - "report-persistence.mjs"
Cohesion: 0.29
Nodes (6): child, dataRoot, fx, h5, ReportUI, require
### Community 68 - "analyze-timeout.js"
Cohesion: 0.20
Nodes (7): assert, end, fs, path, source, start, vm
### Community 70 - ".forwardInput"
Cohesion: 0.22
Nodes (9): boxPredictionLayer(), decodeBoxesLayer(), depthwiseConvLayer(), getCenterCoordinatesAndSizesLayer(), getStridesForLayerIdx(), mobileNetV1(), outputLayer(), pointwiseConvLayer() (+1 more)
### Community 77 - "drawFaceExpressions"
Cohesion: 0.22
Nodes (6): DrawBox(), drawDetections(), drawFaceExpressions(), DrawFaceLandmarks(), FaceExpressions(), isWithFaceExpressions()
### Community 78 - "t"
Cohesion: 0.10
Nodes (26): bi(), Bo(), ce(), cl(), de(), e(), Ef(), er() (+18 more)
### Community 79 - "登录反馈与加载优化(2026-10-12)"
Cohesion: 0.25
Nodes (5): 体验变化, 公网部署后复测, 慢的原因与处理, 登录反馈与加载优化(2026-10-12), 验证
### Community 80 - "线上微信登录部署记录"
Cohesion: 0.33
Nodes (6): 初次部署时的凭据查找记录(现已由用户提供), 初次部署的线上检查(历史结果), 后续更新:已配置真实凭据, 已执行, 用户确认加入白名单后的复测, 线上微信登录部署记录
### Community 81 - "wechat-production.mjs"
Cohesion: 0.60
Nodes (4): api(), base, callback(), start()
### Community 82 - "wechat-browser.mjs"
Cohesion: 0.22
Nodes (6): createAccounts, h5, ReportUI, require, root, SkinScore
### Community 83 - "10.09 新版 H5 实施与验收"
Cohesion: 0.29
Nodes (5): 10.09 新版 H5 实施与验收, 按确认项实现, 文档未定义部分的实施口径, 素材再生成, 验证命令
### Community 84 - "微信登录联调:2026-10-12"
Cohesion: 0.33
Nodes (6): 后续真机确认与体验优化, 微信登录联调:2026-10-12, 本轮修正, 白名单与凭据, 真实微信验收与排查, 自动验证
### Community 85 - "test/accounts.js"
Cohesion: 0.22
Nodes (9): assert, create, fs, http, os, path, ReportUI, SkinScore (+1 more)
### Community 87 - "产品推荐规则:肤龄基准与边界调整"
Cohesion: 0.33
Nodes (6): 产品推荐规则:肤龄基准与边界调整, 决策顺序, 后台与前端一致性, 实施与验证范围, 已明确的要求, 已确认的三个条件
### Community 88 - "ui-20261012/manifest.json"
Cohesion: 0.22
Nodes (8): assets, design_scale, document_size, native_header_px, shape_metrics, source, source_sha256, text_styles
### Community 91 - "wi"
Cohesion: 0.28
Nodes (9): Ci(), Ei(), Fi(), Ii(), ki(), pe(), ri(), wi() (+1 more)
### Community 92 - "客户反馈修改(2026-10-12)"
Cohesion: 0.33
Nodes (5): 公网部署与复测, 客户反馈修改(2026-10-12), 皱纹数据说明, 逐项处理, 验证
### Community 93 - ".runAndExtendWithFaceDetections"
Cohesion: 0.33
Nodes (5): DetectAllFacesTask(), extendWithFaceDetection(), isWithFaceDetection(), isWithFaceLandmarks(), resizeResults()
### Community 94 - "第三轮反馈:PSD 对齐及本地验收"
Cohesion: 0.40
Nodes (4): 原稿与导出可追溯性, 已完成, 本地验证, 第三轮反馈:PSD 对齐及本地验收
### Community 96 - "production-loop.mjs"
Cohesion: 0.50
Nodes (3): artifacts, errors, samples
### Community 100 - "extractAllFacesAndComputeResults"
Cohesion: 0.11
Nodes (9): ComputeAllFaceDescriptorsTask(), ComputeSingleFaceDescriptorTask(), extendWithFaceDescriptor(), extendWithFaceExpressions(), extractAllFacesAndComputeResults(), extractSingleFaceAndComputeResult(), FaceRecognitionNet(), PredictAllFaceExpressionsTask() (+1 more)
## Knowledge Gaps
- **63 isolated node(s):** `step1-front`, `step2-rightface`, `step3-leftface`, `hint-noface`, `hint-near` (+58 more)
- **256 isolated node(s):** `source`, `document_size`, `pages`, `assets`, `layers` (+251 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **21 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **20 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `val()` connect `skin-score.js` to `extractFaces`?**
_High betweenness centrality (0.083) - this node is a cross-community bridge._
- **Why does `vizCompose()` connect `app.js` to `n`?**
_High betweenness centrality (0.058) - this node is a cross-community bridge._
- **Why does `Box()` connect `extractFaces` to `face-api.js`, `.dispose`, `isValidNumber`, `FaceLandmarks`?**
_High betweenness centrality (0.058) - this node is a cross-community bridge._
- **Are the 23 inferred relationships involving `n()` (e.g. with `ae()` and `be()`) actually correct?**
_`n()` has 23 INFERRED edges - model-reasoned connections that need verification._
- **Why does `k()` connect `k` to `face-api.js`, `app.js`, `n`, `lib/accounts.js`, `t`, `C`?**
_High betweenness centrality (0.061) - this node is a cross-community bridge._
- **Why does `renderReport()` connect `app.js` to `k`, `t`?**
_High betweenness centrality (0.048) - this node is a cross-community bridge._
- **Why does `rate()` connect `lib/accounts.js` to `k`?**
_High betweenness centrality (0.048) - this node is a cross-community bridge._
- **Are the 24 inferred relationships involving `n()` (e.g. with `newRecordId()` and `ae()`) actually correct?**
_`n()` has 24 INFERRED edges - model-reasoned connections that need verification._
- **Are the 27 inferred relationships involving `r()` (e.g. with `handleExport()` and `handleRecordsQuery()`) actually correct?**
_`r()` has 27 INFERRED edges - model-reasoned connections that need verification._
- **Are the 7 inferred relationships involving `t()` (e.g. with `renderReport()` and `e()`) actually correct?**
_`t()` has 7 INFERRED edges - model-reasoned connections that need verification._
- **Are the 9 inferred relationships involving `i()` (e.g. with `da()` and `f()`) actually correct?**
_`i()` has 9 INFERRED edges - model-reasoned connections that need verification._
- **What connects `step1-front`, `step2-rightface`, `step3-leftface` to the rest of the system?**
_63 weakly-connected nodes found - possible documentation gaps or missing edges._
- **What connects `source`, `document_size`, `pages` to the rest of the system?**
_256 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because one or more lines are too long
+13966 -2109
View File
File diff suppressed because it is too large Load Diff
+1006 -16
View File
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -65,6 +65,7 @@ node server.js # 默认 8080,可用 PORT=xxx 修改
- **预览缩小 15%**:`#cam-video` 按 `scale(.85)` 显示,手机离脸近一些也能把脸装进轮廓;距离阈值随之按 1/0.85 换算(人脸框高/视频高 <0.30 提示「请靠近一点」、>0.68 提示「请远一点」)。
- **动态提示**(一次一条,优先级:无人脸 > 距离 > 居中 > 角度 > 光线):「未检测到人脸」「请靠近一点 / 请远一点」「请将脸部移入取景框」(人脸中心偏离中线 >18% 屏宽)、角度类「请向左转头 / 请向右转头 / 请略微抬头 / 请略微低头」(正脸阈值 |yaw|≤15°、|pitch|≤18°,超出时按超出比例更大的一项给方向;侧脸 yaw 28°–75°、|pitch|≤20°)、光线类「请到光线充足处 / 光线过强,请避开强光」(脸部平均亮度 <65 或 >235)。
- **合格即拍**:上述条件全部合格并连续维持 450ms(滤掉一闪而过的合格帧,防运动模糊)立即白屏补光抓拍,**无倒计时、无手动快门**。
- **两张之间停顿**:正脸、右脸拍完后中央弹出停顿卡约 2s(「正脸拍摄完成 / 下一张:右脸出镜,请向左转头」),停顿期间不检测,结束后再切到下一步并播下一步引导,让用户对下一张有心理预期。
- **实时检测**:face-api 从本地 `vendor/faceapi/` 加载(`tinyFaceDetector` + `faceLandmark68Net`),进入拍照页开始加载(状态提示「检测引擎加载中…」)。检测循环 ~150ms 一帧,特征点取自原始帧(预览 CSS 镜像不影响),个别前置帧已镜像的机型由侧脸自校准翻转 `sideYawFlip`;连续漏检 ≥3 帧才报「未检测到人脸」。
- **镜像策略**:前置镜头预览镜像(照镜子习惯);后置预览不镜像,侧脸轮廓随之翻转(`.cam-mask.flip`)。**抓帧一律不镜像**,三张照片都是摄像头原始视角。
- **白屏补光**:全屏纯白覆盖层瞬间点亮,~300ms 后抓全分辨率帧(原始视角 + 最长边 1280 JPEG),保持 ~300ms 再淡出,同步 `navigator.vibrate(30)`。
@@ -77,7 +78,7 @@ node server.js # 默认 8080,可用 PORT=xxx 修改
## 档案页与报告页
- **档案页**:出生年月 / 性别 / 所在地区三个滚轮选择器同一风格(`vendor/mobile-select`,MIT),出生年月自动换算年龄;地区为省–市两级联动,数据为 `js/region-data.js`(由 china-division 2.7.0 `pc.json` 生成,另含港澳台与「海外」)。
- **报告页**:无返回按钮与页头标题;总分环形图 + 肌肤年龄与「比实际年龄年轻/大 N 岁」;三维度分数 + 进度条(最低分标红并挂「优先关注」);紧塑力/活力感/稳定力各一张卡,卡内子 Tab 切换;点检测图全屏放大,再点或按返回键回到报告。
- **报告页**:无返回按钮与页头标题;总分环形图 + 肌肤年龄与「比实际年龄年轻/大 N 岁」;三维度分数 + 进度条(最低分标红并挂「优先关注」);维度详情为原布局——一张卡内紧塑力/活力感/稳定力三个 Tab,每个 Tab 两条「竖排标题 + 左图右文」;点检测图全屏放大,再点或按返回键回到报告。
## 检测流程与评分渲染
@@ -120,6 +121,12 @@ node server.js # 默认 8080,可用 PORT=xxx 修改
| `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)。下方旧功能说明以这两份更新文档为准。
## 部署
需要 Node ≥ 18 运行 `server.js`(负责静态与 API 中转),**不能用纯静态托管**(密钥不能进前端)。
+7 -5
View File
@@ -44,7 +44,7 @@
<option>NETWORK_ERROR</option>
</select>
</label>
<label>年龄 <input type="number" id="f-agemin" min="1" max="120" placeholder="最小"> ~ <input type="number" id="f-agemax" min="1" max="120" placeholder="最大"></label>
<label>实际年龄 <input type="number" id="f-agemin" min="1" max="120" placeholder="最小"> ~ <input type="number" id="f-agemax" min="1" max="120" placeholder="最大"></label>
<label>产品组别
<select id="f-group">
<option value="">全部</option>
@@ -55,12 +55,12 @@
<select id="f-dim">
<option value="">不限</option>
<option value="firm">紧塑力</option>
<option value="vita">活力感</option>
<option value="stab">稳定力</option>
<option value="vita">透亮感</option>
<option value="stab">稳韧力</option>
</select>
<select id="f-dimlevel">
<option value="">不限档位</option>
<option>预警</option><option>改善</option><option>良好</option>
<option value="预警">改善</option><option value="改善">良好</option><option value="良好">优秀</option>
</select>
</label>
<label>记录ID <input type="text" id="f-q" placeholder="精确/前缀匹配"></label>
@@ -72,7 +72,7 @@
<thead>
<tr>
<th>照片</th><th>记录ID</th><th>年龄/性别/地区/肤质</th><th>拍摄状态</th><th>上传状态</th>
<th>检测状态</th><th>总分</th><th>三维度档位</th><th>推荐精华</th><th>耗时</th><th>创建时间</th>
<th>检测状态</th><th>总分</th><th>肌龄(原始 / 展示)</th><th>三维度评分 / 档位</th><th>推荐精华</th><th>耗时</th><th>创建时间</th>
</tr>
</thead>
<tbody></tbody>
@@ -149,6 +149,8 @@
<div id="toast" class="toast hidden"></div>
<script src="js/report-copy.js"></script>
<script src="js/report-ui.js"></script>
<script src="js/admin.js"></script>
</body>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
# MiSans 字体
本应用使用 MiSans 字体(小米科技有限责任公司)。
- 官方来源:https://hyperos.mi.com/font/zh/download/
- 下载包:https://hyperos.mi.com/font-download/MiSans.zip
- 下载日期:2026-10-10
- 文件:官方原始 MiSans-Regular.woff2、MiSans-Semibold.woff2,未修改、未子集化。
- 2026-10-12 按最新 PSD 补充官方原始 MiSans-Demibold.woff2,未修改、未子集化;报告主标题使用 Semibold,拍照/协议弹窗标题及报告数字使用 Demibold。保留按需加载,不用于登录首屏。
- 许可协议:上述官方下载页的「许可协议」;此处字体随本应用使用,不作为独立字库产品分发。
页面「我的报告」底部包含字体使用声明。请保持声明,单独再分发或改造字体前需另行确认授权。
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Some files were not shown because too many files have changed in this diff Show More