Compare commits

...
16 Commits
159 changed files with 25677 additions and 2757 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

+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,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.
+66 -41
View File
@@ -1,69 +1,94 @@
{
"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": "k",
"8": "admin.js",
"9": "__awaiter",
"10": "extractWeightEntryFactory",
"11": "FaceProcessor",
"10": "mn",
"11": "lib/accounts.js",
"12": "server.js",
"13": "files",
"14": "extractAllFacesAndComputeResults",
"15": ".runAndExtendWithFaceDetection",
"14": ".draw",
"15": "extractFaces",
"16": "巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范",
"17": "extractFaces",
"17": "FaceLandmarks68",
"18": "1'me 在线肌肤测试 H5",
"19": "head-pose.js",
"20": "build_fixtures.py",
"21": ".forwardInput",
"22": ".loadFromDisk",
"21": "AgeGenderNet",
"22": "extractAllFacesAndComputeResults",
"23": "facepp-skinanalyze(旷视皮肤分析专业版调用技能)",
"24": "巨子测肤 · 1'me 语音播报内容设计文档 V1.0",
"25": "extractParamsFromWeigthMap$1",
"25": "mobile-select.iife.min.js",
"26": "wi",
"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交互界面调整",
"40": "extractFaceTensors",
"41": "NeuralNetwork",
"42": "export_psd_assets.py",
"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",
"71": ".toString",
"72": "build_report_copy.py",
"73": "camera-geometry.js",
"74": "report-copy.js",
"75": "prepare_h5_assets.py",
"76": ".detect",
"77": ".runAndExtendWithFaceDetections",
"78": "t",
"79": "登录反馈与加载优化(2026-10-12)",
"80": "线上微信登录部署记录",
"81": "wechat-production.mjs",
"82": "wechat-browser.mjs",
"83": "10.09 新版 H5 实施与验收",
"84": "微信登录联调:2026-10-12",
"86": "LabeledFaceDescriptors",
"87": "产品推荐规则:肤龄基准与边界调整",
"89": "傅艳NEW与项思炼的会话 2026年10月12日.md",
"90": "customer-feedback.mjs",
"92": "客户反馈修改(2026-10-12)",
"97": "PredictSingleAgeAndGenderWithFaceAlignmentTask",
"98": "PredictSingleFaceExpressionsWithFaceAlignmentTask"
}
+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": "00c97d478a8f90eb", "1": "cb6ea50bd4e4a5ca", "2": "00705ebb76871f0f", "3": "1f737ca8edb978f2", "4": "56bf12952b0de42b", "5": "93c69860c6f6563c", "6": "5a9014c4a7f87699", "7": "188dd5e33354e35d", "8": "1ecf47531e1d507b", "9": "64a6935321c48bb6", "10": "9ac0935bc0d2c1fd", "11": "ed0d13cfa8571d24", "12": "53594b653ad48743", "13": "7e3b558a587a4ebe", "14": "bf75bfc99d4f9e34", "15": "72d3264e772df1cb", "16": "95add6a975c0cb72", "17": "15606ce5d8eec23a", "18": "38bb0b185ceed116", "19": "0a373ad2197fd1a6", "20": "aecf2fce44ba49c5", "21": "224bdcd6528fddbf", "22": "e42f384dfedcff89", "23": "90fac00055070ce2", "24": "f85b5b1634224c86", "25": "46e8db3e6e3f37ea", "26": "83b0fd3b14363e4d", "27": "eab427ce9a5219e9", "28": "9c6f14efe476acf4", "29": "c608f1c6492e1cb1", "30": "73c13793fca09f56", "31": "fbbcfc059751cc6a", "32": "f6bcf56c4589316b", "33": "75f750c5b7d9f63d", "34": "f024ff7942935690", "35": "c4fa292c3dfb0d0e", "36": "a7d375041779bad4", "37": "cb8f26249485324a", "38": "9a25b1230b5fe649", "39": "fdd95375b4a5ddb3", "40": "c6e2efd58425d13d", "41": "d96493a5f110bd4c", "42": "ffd960136fde00ad", "43": "5b04375ec4594cb1", "44": "852c1bb2446edd9c", "45": "92ab3e5134ed8685", "46": "65718282b3c32109", "47": "e594ecaa8e7d1538", "48": "1e2c13775a52fdea", "49": "1f976711cda158dc", "50": "bbb2b57d72922b16", "51": "eb6a182204867275", "52": "2ec3c8c088568ba9", "53": "68c45056378341a7", "54": "c58d41e8e580fdc6", "55": "54d7329fed675057", "56": "8c9a3fc5edff7e44", "57": "f2a40b5f1b29f669", "58": "d0d4f9872c8bcc83", "59": "891f59cda3708718", "60": "a505b7de6257a4e8", "61": "2d008ec4a8df7849", "62": "1366ae24c7e5897d", "63": "c256b43f75d7809e", "64": "ce0e68dab611f022", "65": "74ae017de0ffd36b", "66": "e042d7fe7351bcb9", "67": "3e126d8deaf6e200", "68": "6b91f3679541de79", "69": "e511846eeba03076", "70": "880a74c6d255e5cc", "71": "cd6f168abba94fea", "72": "c84f196a7bd35861", "73": "2b887bf601ccfb9f", "74": "ae2a154311e1f53a", "75": "69fdd23278518884", "76": "68250611c66f576f", "77": "429cd62f9137881c", "78": "f4aae1bece83f9e0", "79": "c6cfdfe181232ecc", "80": "315325cde1352e98", "81": "37c559741825be74", "82": "2b037735e1f5b0f6", "83": "1284897282eb47a3", "84": "d8bb296741d3d05c", "86": "f8b38aeaaab11ec4", "87": "e5fa7717989eda80", "89": "9e3363b80eeceeea", "90": "dd1c246e19a2d032", "92": "fced244d93677d90", "97": "01f68dfc34a29d32", "98": "8c471c7baeaaca54"}
+270 -164
View File
@@ -1,190 +1,204 @@
# Graph Report - juzi (2026-09-28)
# Graph Report - juzi (2026-10-12)
## Corpus Check
- 31 files · ~1,845,638 words
- 77 files · ~4,389,750 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)
- 1280 nodes · 2677 edges · 92 communities (75 shown, 17 thin omitted)
- Extraction: 93% EXTRACTED · 7% INFERRED · 0% AMBIGUOUS · INFERRED: 181 edges (avg confidence: 0.53)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `4e3d915b`
- Built from commit: `fe9623a1`
- 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
- k
- admin.js
- __awaiter
- extractWeightEntryFactory
- FaceProcessor
- mn
- lib/accounts.js
- server.js
- files
- extractAllFacesAndComputeResults
- .runAndExtendWithFaceDetection
- 巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范
- .draw
- extractFaces
- 巨子测肤 · 1'me 在线肌肤测试 — UI 设计规范
- FaceLandmarks68
- 1'me 在线肌肤测试 H5
- head-pose.js
- build_fixtures.py
- .forwardInput
- .loadFromDisk
- AgeGenderNet
- extractAllFacesAndComputeResults
- facepp-skinanalyze(旷视皮肤分析专业版调用技能)
- 巨子测肤 · 1'me 语音播报内容设计文档 V1.0
- extractParamsFromWeigthMap$1
- mobile-select.iife.min.js
- wi
- 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交互界面调整
- extractFaceTensors
- NeuralNetwork
- export_psd_assets.py
- 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
- .toString
- build_report_copy.py
- .detect
- .runAndExtendWithFaceDetections
- t
- 登录反馈与加载优化(2026-10-12)
- 线上微信登录部署记录
- wechat-production.mjs
- wechat-browser.mjs
- 10.09 新版 H5 实施与验收
- 微信登录联调:2026-10-12
- LabeledFaceDescriptors
- 产品推荐规则:肤龄基准与边界调整
- 傅艳NEW与项思炼的会话 2026年10月12日.md
- 客户反馈修改(2026-10-12)
- PredictSingleAgeAndGenderWithFaceAlignmentTask
- PredictSingleFaceExpressionsWithFaceAlignmentTask
## 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)
- `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]
- `vizCompose()` --indirect_call--> `scale()` [INFERRED]
h5/js/app.js → h5/vendor/faceapi/face-api.js
## Import Cycles
- None detected.
## Communities (67 total, 21 thin omitted)
## Communities (92 total, 17 thin omitted)
### Community 0 - "face-api.js"
Cohesion: 0.02
Nodes (38): bn(), ca(), cd(), ce(), dn(), ee(), er(), et() (+30 more)
Nodes (43): ce(), cn(), computeReshapedDimensions(), createBrowserEnv(), createFileSystem(), createNodejsEnv(), Dimensions(), dr() (+35 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 (81): applyMirror(), armNudge(), beginDetect(), bindBackdropClose(), busyWithFlow(), captureFromVideo(), captureHaptic(), checkStill() (+73 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.17
Nodes (39): a(), ae(), be(), bf(), cf(), d(), de(), drawContour() (+31 more)
### Community 6 - ".runAndExtendWithFaceDetection"
Cohesion: 0.33
Nodes (5): extractorsFactory$3(), extractParamsFromWeigthMap$4(), isTensor(), isTensor1D(), isTensor2D()
Nodes (5): allFacesMtcnn(), allFacesSsdMobilenetv1(), allFacesTinyYolov2(), detectAllFaces(), DetectSingleFaceTask()
### Community 5 - ".toBatchTensor"
Cohesion: 0.08
Nodes (30): conv(), conv$1(), convDown(), convLayer(), convLayer$1(), convNoRelu(), convWithBatchNorm(), denseBlock3() (+22 more)
### Community 7 - "k"
Cohesion: 0.18
Nodes (11): b(), gn(), k(), kn(), ne(), tt(), wo(), Yt() (+3 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 (13): __awaiter(), bufferToImage(), ComposableTask(), fetchImage(), fetchJson(), fetchNetWeights(), fetchOrThrow(), __generator() (+5 more)
### Community 10 - "extractWeightEntryFactory"
Cohesion: 0.53
Nodes (6): extractorsFactory$7(), extractWeightEntryFactory(), loadConvParamsFactory(), loadParamsFactory(), loadParamsFactory$1(), loadSeparableConvParamsFactory()
### Community 10 - "mn"
Cohesion: 0.17
Nodes (11): Lu(), mn(), Point(), Pu(), Uu(), vn(), Vu(), wl() (+3 more)
### Community 11 - "FaceProcessor"
Cohesion: 0.24
Nodes (3): extractParams$1(), FaceLandmark68TinyNet(), FaceProcessor()
### Community 11 - "lib/accounts.js"
Cohesion: 0.07
Nodes (34): atomic(), body(), cookie(), cookies(), crypto, fs, json(), login() (+26 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.21
Nodes (17): awaitMediaLoaded(), createCanvas(), createCanvasFromMedia(), extractFaces(), extractImagePatches(), getContext2dOrThrow(), getEnv(), getMediaDimensions() (+9 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 - "FaceLandmarks68"
Cohesion: 0.19
Nodes (3): FaceLandmarks5(), FaceLandmarks68(), getCenterPoint()
### 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 +208,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 - "AgeGenderNet"
Cohesion: 0.05
Nodes (28): AgeGenderNet(), disposeUnusedWeightTensors(), extractorsFactory$3(), extractorsFactory$5(), extractorsFactory$7(), extractorsFactory$9(), extractParamsFromWeigthMap(), extractParamsFromWeigthMap$1() (+20 more)
### Community 22 - ".loadFromDisk"
Cohesion: 0.19
Nodes (5): ComposableTask(), getModelUris(), Gf(), loadWeightMap(), TinyFaceDetector()
### Community 22 - "extractAllFacesAndComputeResults"
Cohesion: 0.09
Nodes (12): ComputeAllFaceDescriptorsTask(), ComputeSingleFaceDescriptorTask(), extendWithAge(), extendWithFaceDescriptor(), extendWithFaceExpressions(), extendWithGender(), extractAllFacesAndComputeResults(), extractSingleFaceAndComputeResult() (+4 more)
### Community 23 - "facepp-skinanalyze(旷视皮肤分析专业版调用技能)"
Cohesion: 0.20
@@ -210,9 +224,9 @@ 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
@@ -222,89 +236,181 @@ Nodes (9): Ci(), Ei(), Fi(), Ii(), ki(), pe(), ri(), wi() (+1 more)
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.10
Nodes (22): ao(), Bo(), C(), cd(), co(), Do(), eo(), fo() (+14 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.11
Nodes (17): alternate, assert, crop, details, excellent, fs, model, modelBefore (+9 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 40 - "extractFaceTensors"
Cohesion: 0.08
Nodes (10): bgrToRgbTensor(), Box(), DetectAllFaceLandmarksTask(), DetectSingleFaceLandmarksTask(), extendWithFaceLandmarks(), extractFaceTensors(), FaceLandmarks(), isTensor3D() (+2 more)
### Community 49 - "Yo"
Cohesion: 0.67
Nodes (3): qo(), Xo(), Yo()
### Community 42 - "export_psd_assets.py"
Cohesion: 0.70
Nodes (4): bounds(), layer_at(), main(), save_image()
### Community 43 - "normalize"
Cohesion: 0.07
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.25
Nodes (7): 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.08
Nodes (31): ba(), bn(), ca(), cl(), da(), dn(), ee(), et() (+23 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)
Cohesion: 0.21
Nodes (9): FaceRecognitionNet(), getSizesForScale(), nonMaxSuppression(), normalize$1(), rescaleAndNormalize(), scale(), stage1(), stage2() (+1 more)
### Community 61 - ".extractWeights"
Cohesion: 0.08
Nodes (25): createFaceDetectionNet(), createFaceRecognitionNet(), createMtcnn(), createSsdMobilenetv1(), createTinyFaceDetector(), createTinyYolov2(), extractConvParamsFactory(), extractFCParamsFactory() (+17 more)
Cohesion: 0.09
Nodes (26): createFaceDetectionNet(), createFaceRecognitionNet(), createMtcnn(), createSsdMobilenetv1(), createTinyFaceDetector(), createTinyYolov2(), extractConvParamsFactory(), extractFCParamsFactory() (+18 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 71 - ".toString"
Cohesion: 0.33
Nodes (5): FaceMatch(), la(), pr(), pt(), vt()
### Community 76 - ".detect"
Cohesion: 0.15
Nodes (6): iou(), NetInput(), nonMaxSuppression$1(), SsdMobilenetv1(), TinyYolov2(), TinyYolov2Base()
### Community 77 - ".runAndExtendWithFaceDetections"
Cohesion: 0.15
Nodes (11): DetectAllFacesTask(), DrawBox(), drawDetections(), drawFaceExpressions(), DrawFaceLandmarks(), extendWithFaceDetection(), FaceExpressions(), isWithFaceDetection() (+3 more)
### Community 78 - "t"
Cohesion: 0.14
Nodes (14): bi(), Ef(), er(), ia(), Ie(), j(), Kt(), Mf() (+6 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 87 - "产品推荐规则:肤龄基准与边界调整"
Cohesion: 0.33
Nodes (6): 产品推荐规则:肤龄基准与边界调整, 决策顺序, 后台与前端一致性, 实施与验证范围, 已明确的要求, 已确认的三个条件
### Community 92 - "客户反馈修改(2026-10-12)"
Cohesion: 0.33
Nodes (5): 公网部署与复测, 客户反馈修改(2026-10-12), 皱纹数据说明, 逐项处理, 验证
## Knowledge Gaps
- **63 isolated node(s):** `step1-front`, `step2-rightface`, `step3-leftface`, `hint-noface`, `hint-near` (+58 more)
- **236 isolated node(s):** `source`, `document_size`, `pages`, `assets`, `layers` (+231 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.
- **17 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 `r()` connect `n` to `face-api.js`, `.toString`, `mn`, `normalize`, `server.js`, `t`, `i`, `C`?**
_High betweenness centrality (0.050) - this node is a cross-community bridge._
- **Why does `k()` connect `k` to `face-api.js`, `app.js`, `n`, `lib/accounts.js`, `t`, `i`?**
_High betweenness centrality (0.043) - this node is a cross-community bridge._
- **Why does `renderReport()` connect `app.js` to `t`, `k`?**
_High betweenness centrality (0.041) - 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?**
_236 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because one or more lines are too long
+12829 -2117
View File
File diff suppressed because it is too large Load Diff
+896 -16
View File
@@ -40,23 +40,23 @@
"semantic_hash": ""
},
"h5/js/app.js": {
"mtime": 1790531707.8223634,
"ast_hash": "7c3652449c905d9f19ca37a66089766f",
"mtime": 1791786351.8390813,
"ast_hash": "7bfa025f6ac5b97e62075cd762a1eee0",
"semantic_hash": ""
},
"h5/js/head-pose.js": {
"mtime": 1790528422.8265557,
"ast_hash": "165cc2ae73d0a9526db0755ec62ffaac",
"mtime": 1791559485.790093,
"ast_hash": "cdfc5962793a4d6e567ffe00be7653e8",
"semantic_hash": ""
},
"h5/js/skin-score.js": {
"mtime": 1790528158.6665542,
"ast_hash": "61d27784c13fc9fd5ea3b4e2f6187756",
"mtime": 1791735501.357809,
"ast_hash": "783b6c9abbe2bf1832e8eafe5aead7e3",
"semantic_hash": ""
},
"h5/server.js": {
"mtime": 1790528158.6705542,
"ast_hash": "e23a541e2ce85e0815616071820b46ef",
"mtime": 1791773587.8349628,
"ast_hash": "4895534b06f0c2d29ada96d9b52b4693",
"semantic_hash": ""
},
"h5/test/build_fixtures.py": {
@@ -80,8 +80,8 @@
"semantic_hash": ""
},
"h5/test/regression.js": {
"mtime": 1790528158.6705542,
"ast_hash": "d83fc0a378d1117fc2203569d325ca24",
"mtime": 1791735501.3654072,
"ast_hash": "4a712e295c115b6051726cd028e67be2",
"semantic_hash": ""
},
"h5/vendor/faceapi/face-api.js": {
@@ -135,13 +135,13 @@
"semantic_hash": ""
},
"h5/README.md": {
"mtime": 1790531597.4743752,
"ast_hash": "26db3c846af2efc0a4bfb442985e2922",
"mtime": 1791646538.376033,
"ast_hash": "1f8bb2cad20e467a93e1120dc7cf3924",
"semantic_hash": ""
},
"h5/index.html": {
"mtime": 1790531666.4743714,
"ast_hash": "66cb0b0c306fcc9adb4dbed358651d13",
"mtime": 1791786653.9070883,
"ast_hash": "39e41fec33bce3bf22520b9a3128b2ec",
"semantic_hash": ""
},
"h5/vendor/faceapi/README.md": {
@@ -770,8 +770,8 @@
"semantic_hash": ""
},
"h5/test/e2e-browser.mjs": {
"mtime": 1790531366.4343376,
"ast_hash": "3ae597baedac698f02f27c5662784348",
"mtime": 1791773638.010959,
"ast_hash": "9327216df7c2e80f2615cb7f800b7f1a",
"semantic_hash": ""
},
"h5/assets/svg/prod-badge.svg": {
@@ -808,5 +808,885 @@
"mtime": 1790528422.8225558,
"ast_hash": "9f7ec9320aad81e86fbb1677228e6483",
"semantic_hash": ""
},
"design/ui-20261009/manifest.json": {
"mtime": 1791642120.005687,
"ast_hash": "30a5ef4a5abcd6c6c4294e8202a4bad4",
"semantic_hash": ""
},
"h5/js/admin.js": {
"mtime": 1791735707.6717124,
"ast_hash": "0de3075fb017762401fd2d27eb26abcb",
"semantic_hash": ""
},
"h5/js/region-data.js": {
"mtime": 1791559485.790093,
"ast_hash": "9ede70cb3ed9777a62559451e1969b9d",
"semantic_hash": ""
},
"h5/start-local.sh": {
"mtime": 1791559485.790093,
"ast_hash": "f71b1c8e2ada83f6ee607ae4577d0b9a",
"semantic_hash": ""
},
"h5/vendor/mobile-select/mobile-select.iife.min.js": {
"mtime": 1791559485.790093,
"ast_hash": "52578c3b95933f96493d3b879d353421",
"semantic_hash": ""
},
"tools/export_psd_assets.py": {
"mtime": 1791642102.9273047,
"ast_hash": "7cee68c3db2cd4a52b58b8e380e2fe8d",
"semantic_hash": ""
},
"MP_verify_kpx234XimQGtFPo6.txt": {
"mtime": 1791639433.9595106,
"ast_hash": "3acc5e996d0ddc5b2b225b56536d2bf3",
"semantic_hash": ""
},
"design/ui-20261009/README.md": {
"mtime": 1791710791.707398,
"ast_hash": "097628df23775e8d518f9ec83593e211",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988.md": {
"mtime": 1791556943.7718663,
"ast_hash": "d99151ccfc4d73a421f49546feef45fa",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u7b2c\u4e8c\u8f6e2\u53cd\u9988.md": {
"mtime": 1791639433.9595106,
"ast_hash": "7c20a160d9b965ff019445cae51c5628",
"semantic_hash": ""
},
"h5/admin.html": {
"mtime": 1791735369.9312572,
"ast_hash": "ddfe588d49452e3973e21b080e61dbd7",
"semantic_hash": ""
},
"h5/vendor/mobile-select/README.md": {
"mtime": 1791559485.790093,
"ast_hash": "fe3287899b96f598c60f9bc030a1368e",
"semantic_hash": ""
},
"design/ui-20261009/assets-overview.jpg": {
"mtime": 1791642120.005687,
"ast_hash": "d9475b775e202ae0e0e329b48625529f",
"semantic_hash": ""
},
"design/ui-20261009/pages-overview.jpg": {
"mtime": 1791642119.9744806,
"ast_hash": "1231c4badaf76d859d02c71beb67b5d3",
"semantic_hash": ""
},
"design/ui-20261009/pages/01-\u542f\u52a8\u9875.png": {
"mtime": 1791641925.2282495,
"ast_hash": "9e0086ed5831752589a87e5a4bf41a32",
"semantic_hash": ""
},
"design/ui-20261009/pages/02-\u542f\u52a8\u9875\u5f39\u7a97.png": {
"mtime": 1791641927.5536723,
"ast_hash": "b69f8d3b109472bcffb08dc861bae7ec",
"semantic_hash": ""
},
"design/ui-20261009/pages/03-\u6211\u7684\u62a5\u544a.png": {
"mtime": 1791641931.276532,
"ast_hash": "28b96724cba921c1b996bccf5921fcc4",
"semantic_hash": ""
},
"design/ui-20261009/pages/04-\u6863\u6848\u586b\u5199.png": {
"mtime": 1791641935.9516084,
"ast_hash": "7c702fa5005b0009998da437678d643a",
"semantic_hash": ""
},
"design/ui-20261009/pages/05-\u62cd\u7167\u5f39\u7a97.png": {
"mtime": 1791641938.6036084,
"ast_hash": "949fecb7cf12227e2afc0223977f0774",
"semantic_hash": ""
},
"design/ui-20261009/pages/06-\u6b63\u9762.png": {
"mtime": 1791641940.6992414,
"ast_hash": "9cc466bf5e1e34852a672570eaf075c4",
"semantic_hash": ""
},
"design/ui-20261009/pages/07-\u5de6\u9762.png": {
"mtime": 1791641942.8299313,
"ast_hash": "09f1a2059586cd430e79f21d2abe262f",
"semantic_hash": ""
},
"design/ui-20261009/pages/08-\u53f3\u9762.png": {
"mtime": 1791641945.0379312,
"ast_hash": "1394a72d8fdeb223d2ccab4c7121f542",
"semantic_hash": ""
},
"design/ui-20261009/pages/09-\u62a5\u544a\u9875.png": {
"mtime": 1791641991.6536736,
"ast_hash": "7593a81623b7f56aec5272bf3435132b",
"semantic_hash": ""
},
"design/ui-20261009/pages/10-\u767b\u5f55.png": {
"mtime": 1791641995.750234,
"ast_hash": "193866f312d2652e3bfad47b9cffb41c",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 1.png": {
"mtime": 1791556943.775871,
"ast_hash": "26f81b5937eeb366495cb1a8cd6a83a7",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 2.png": {
"mtime": 1791556943.7798755,
"ast_hash": "66c1339d62b9ff4cc7e0437625575650",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 3.png": {
"mtime": 1791556943.7798755,
"ast_hash": "5f3a5fab25af9f28a73681afcd8163b5",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 4.png": {
"mtime": 1791556943.7838802,
"ast_hash": "d6e125783fdd17d64104479ecec6b178",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 5.png": {
"mtime": 1791556943.7918894,
"ast_hash": "9ae5ae786e478c26c2cd6a4cd31e8548",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 6.png": {
"mtime": 1791556943.7918894,
"ast_hash": "26f81b5937eeb366495cb1a8cd6a83a7",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image 7.png": {
"mtime": 1791556943.7998989,
"ast_hash": "ff17d0fb064a446a66c80e16c442d1bc",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/image.png": {
"mtime": 1791556943.7998989,
"ast_hash": "4231c15316cf0172bc8a61e3608e4a8b",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0215v_ae6e553b-8196-4962-9529-abfd20b33d3g.jpg": {
"mtime": 1791556943.7998989,
"ast_hash": "80e37fba538c321c72f1e07b1cc01fdc",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0215v_de81e319-a8c7-4ec8-bd78-ae4a127ef30g.jpg": {
"mtime": 1791556943.7998989,
"ast_hash": "2cd1dd131c8e8e6d644c8261b4c48f55",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_45a139b6-f58c-4bab-8ca1-1c29fcde18fg.png": {
"mtime": 1791556943.8039033,
"ast_hash": "68e53d7530b6fc82219497a9c3a4da67",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_5d95bb41-92ae-4fa4-82a5-74f274e58cbg.png": {
"mtime": 1791556943.8159173,
"ast_hash": "4647889d0de806bce9061e475450ca0e",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_71bbe9b7-3add-49ac-a482-294ab995bf8g.png": {
"mtime": 1791556943.819922,
"ast_hash": "de799cb297b18ad7af5dcbedcb7a3af7",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_9c98146e-8996-4191-b21f-077c525ea5eg 1.png": {
"mtime": 1791556943.8239264,
"ast_hash": "2100794ade2e248b74fb725b40772549",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_9c98146e-8996-4191-b21f-077c525ea5eg.png": {
"mtime": 1791556943.8239264,
"ast_hash": "2100794ade2e248b74fb725b40772549",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_d853ee3b-5310-4c90-b34d-ab43b9acff4g.png": {
"mtime": 1791556943.8239264,
"ast_hash": "4be961746f76e410175b22c6cb4a297e",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_d9062dad-94c8-49ca-8f2a-02525fcf6bbg.png": {
"mtime": 1791556943.8279312,
"ast_hash": "6bd46dfccfa2c9eef496406494c28805",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/img_v3_0216a_df6ae147-0add-4863-879f-38f2dc94220g.png": {
"mtime": 1791556943.8279312,
"ast_hash": "0cee77873e56c1d57caf3c30739e4e77",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u6211\u7684\u62a5\u544a.jpg": {
"mtime": 1791556943.8279312,
"ast_hash": "a5cb508204efbcc0aa814607b5171b9e",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 13.38.51.png": {
"mtime": 1791556943.8359404,
"ast_hash": "3c7b6fe631e0002929548a3c50733f8b",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 13.43.25.png": {
"mtime": 1791556943.839945,
"ast_hash": "f54f57c72a1f3ad50fc3ce20f2f72900",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 13.44.10.png": {
"mtime": 1791556943.8479543,
"ast_hash": "e7208ae3baa0a3e4677176242f089d7f",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 13.45.46.png": {
"mtime": 1791556943.851959,
"ast_hash": "f0590a59b792b30fcbf8e402e12b701c",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 13.56.32.png": {
"mtime": 1791556943.8559637,
"ast_hash": "ca8c8ba2e8ddb457a47cd811a1b1e525",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u622a\u5c4f2026-09-28 14.13.35.png": {
"mtime": 1791556943.8599682,
"ast_hash": "7b90c45f235da04d0bbbb937728e1e1e",
"semantic_hash": ""
},
"feedback/1meAI\u6d4b\u80a4\u65f7\u89c6UI\u95ee\u9898\u53cd\u9988/\u56fe\u7247\u548c\u9644\u4ef6/\u6863\u6848\u586b\u5199.png": {
"mtime": 1791556943.863973,
"ast_hash": "dd7e776e075a90325e826e6af5a3dc06",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-history.jpg": {
"mtime": 1791642110.8726454,
"ast_hash": "b40384ccab84c686ca7e95f06ef402d1",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-login.jpg": {
"mtime": 1791642107.6766243,
"ast_hash": "7d9428b9c02024b7ba0b9e3845c0811e",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-profile.jpg": {
"mtime": 1791642106.283487,
"ast_hash": "7d9428b9c02024b7ba0b9e3845c0811e",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-report.jpg": {
"mtime": 1791642109.7565744,
"ast_hash": "7d72639b3df259555a0db509555c6e92",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-splash.jpg": {
"mtime": 1791642105.7254517,
"ast_hash": "4d1ef1156513a372f62383d15a0542f0",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/brand-login.png": {
"mtime": 1791642110.9038641,
"ast_hash": "b4a8630878423bd42c9a2ab0178637b3",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/brand-logo.png": {
"mtime": 1791642110.892157,
"ast_hash": "6d3045d44d9fc90850216c6e1291837f",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-mask-front.png": {
"mtime": 1791642116.7875311,
"ast_hash": "de2962a8f6f9851c7076c6e0935a225a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-mask-left.png": {
"mtime": 1791642117.4116583,
"ast_hash": "0192103bb8e372166a6cd271e8eae9a2",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-mask-right.png": {
"mtime": 1791642117.0878923,
"ast_hash": "40b770583c47ba0f539661cb9a466eee",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-album.png": {
"mtime": 1791642110.9116688,
"ast_hash": "2d819e63db048d874cbc429d3131037d",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-back.png": {
"mtime": 1791642110.9077663,
"ast_hash": "410c4d4e6d363644e9e073fd1cdf5391",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-close.png": {
"mtime": 1791642110.9077663,
"ast_hash": "6dd0c1f1555d55f54ec80b8231089e4d",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-dropdown.png": {
"mtime": 1791642110.9077663,
"ast_hash": "20cb370372a6a3796274703c83a2790a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-guest.png": {
"mtime": 1791642110.9194734,
"ast_hash": "3f04d7abdf607cfc4c1a737e00a18ba3",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-shutter.png": {
"mtime": 1791642110.935083,
"ast_hash": "6cb6892af9f6ad8828bc65d01fa42a36",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-switch-camera.png": {
"mtime": 1791642110.915571,
"ast_hash": "6197c8000c21156ffecdf77f9c5ed5ba",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/icon-wechat.png": {
"mtime": 1791642110.9194734,
"ast_hash": "42dd3e96cdd78ae90796627321fa9287",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/product-25.png": {
"mtime": 1791642111.5399463,
"ast_hash": "5380d9e7b154bc65e44bfd9816453abe",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/product-35.png": {
"mtime": 1791642112.0158572,
"ast_hash": "06b3d30cfa18f2d0412d5bd0b52d4281",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/product-50.png": {
"mtime": 1791642112.2265003,
"ast_hash": "6c7721402acb3de9a77b89a58bba8e72",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/product-badge.png": {
"mtime": 1791642112.2343018,
"ast_hash": "f345c695ccfa68eda6954f0e9c372566",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/sample-contour.png": {
"mtime": 1791642113.7790165,
"ast_hash": "564023f6104798a65ba176cd0d39fe68",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/sample-lines.png": {
"mtime": 1791642113.8102229,
"ast_hash": "620741bd7da835b9a7e7236bda0eca04",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/sample-redness.png": {
"mtime": 1791642116.4364598,
"ast_hash": "564023f6104798a65ba176cd0d39fe68",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/sample-tone.png": {
"mtime": 1791642114.0988817,
"ast_hash": "aed72b95748480cce62f6a2fb7ed7c24",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/score-ring-base.png": {
"mtime": 1791642112.2616074,
"ast_hash": "4286d187e29909a97aaa178c09fdce30",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/score-ring-value.png": {
"mtime": 1791642112.285012,
"ast_hash": "7677768b6dcc6531a73506f65b21548f",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/skin-0-dry.png": {
"mtime": 1791642110.9780085,
"ast_hash": "9c9ca68325293fd44605c9b03d658306",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/skin-1-mixeddry.png": {
"mtime": 1791642111.0248368,
"ast_hash": "1c282221809cda40e6b74fd98181bdfa",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/skin-2-neutral.png": {
"mtime": 1791642111.0677626,
"ast_hash": "5090258c61d6c52c6e16f77fe91fb86a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/skin-3-oily.png": {
"mtime": 1791642111.1106884,
"ast_hash": "9ffc7732de8c04507e8917a771f92c91",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/skin-4-mixedoily.png": {
"mtime": 1791642111.153614,
"ast_hash": "e76e01938d93d1a662d01e4713b54c94",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/evidence.json": {
"mtime": 1791642461.0152285,
"ast_hash": "1ea9200cc1dce15c6586edd965ddc0b4",
"semantic_hash": ""
},
"h5/js/account-client.js": {
"mtime": 1791774062.6429472,
"ast_hash": "3197b952f29f9e45176d4300082adaae",
"semantic_hash": ""
},
"h5/js/camera-geometry.js": {
"mtime": 1791773589.894928,
"ast_hash": "08048bc5e491ca47275afe067bedbc1c",
"semantic_hash": ""
},
"h5/js/report-copy.js": {
"mtime": 1791730702.7819743,
"ast_hash": "eedcef7cdd1a3f00fe7cb5dabcafb95c",
"semantic_hash": ""
},
"h5/js/report-ui.js": {
"mtime": 1791779513.7549675,
"ast_hash": "100c3438318d0f7a8048c74ef3455252",
"semantic_hash": ""
},
"h5/lib/accounts.js": {
"mtime": 1791770434.3399014,
"ast_hash": "af809199fbaae6318fb71fe602ddc102",
"semantic_hash": ""
},
"h5/test/accounts.js": {
"mtime": 1791770576.0104883,
"ast_hash": "d927f5d1486b56505ba4da49ec6a769c",
"semantic_hash": ""
},
"h5/test/report-persistence.mjs": {
"mtime": 1791773638.010959,
"ast_hash": "9876c659bb7c3513f871d576f0024e72",
"semantic_hash": ""
},
"h5/test/ui-20261009.mjs": {
"mtime": 1791773972.891023,
"ast_hash": "3a13f77513f40e5fc04aaeff06a906e8",
"semantic_hash": ""
},
"tools/build_report_copy.py": {
"mtime": 1791645357.4060252,
"ast_hash": "e4d2c1e2ab552c08cb271f0ec042668b",
"semantic_hash": ""
},
"tools/prepare_h5_assets.py": {
"mtime": 1791773528.470933,
"ast_hash": "60087af4505ea0fae11a6f3449c7e21e",
"semantic_hash": ""
},
"docs/10.09\u65b0\u7248H5\u5b9e\u65bd\u4e0e\u9a8c\u6536.md": {
"mtime": 1791735707.6755157,
"ast_hash": "6e83c0cd6df4b711c9c526a83f1c33a5",
"semantic_hash": ""
},
"docs/UI\u5bf9\u9f5010.09\u8bbe\u8ba1\u7a3f-\u8c03\u7814\u4e0e\u5b9e\u65bd\u8ba1\u5212.md": {
"mtime": 1791642695.148235,
"ast_hash": "1061787758ab6ac500ebd8617a211955",
"semantic_hash": ""
},
"docs/\u5fae\u4fe1\u767b\u5f55\u63a5\u5165\u4e0e\u516c\u7f51\u90e8\u7f72\u8bf4\u660e.md": {
"mtime": 1791774038.1869662,
"ast_hash": "61af7cde98a3299fcc4619e4776cce2a",
"semantic_hash": ""
},
"h5/assets/fonts/misans/README.md": {
"mtime": 1791785802.8870926,
"ast_hash": "7d94c6ffc6ff797d644b2765243c9a59",
"semantic_hash": ""
},
"reference/AI\u6d4b\u80a4\u62a5\u544a\u9875\u8bdd\u672f\u4fee\u6539.md": {
"mtime": 1791730674.7447748,
"ast_hash": "7e6f8ef4ae4a1c010f3344ccdc088cf6",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/01-splash.png": {
"mtime": 1791642440.1249583,
"ast_hash": "3da7aac3c729a22044e0bc38a2d0a5e8",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/02-privacy.png": {
"mtime": 1791642440.7098482,
"ast_hash": "b7ad52ea336102a0239497176799243c",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/03-profile.png": {
"mtime": 1791642441.2944968,
"ast_hash": "2ac2495f969b0662fbb0276b9e6f2a80",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/04-guide.png": {
"mtime": 1791642445.54505,
"ast_hash": "4ceb830ca14aa9ee40cc342753012159",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/05-camera-front.png": {
"mtime": 1791642446.0783443,
"ast_hash": "38bce9558d5a92675a34db1921ad02a3",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/06-camera-right.png": {
"mtime": 1791642450.9363437,
"ast_hash": "5b157765b11f784a825058bbf83f0fbc",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/07-camera-left.png": {
"mtime": 1791642454.9502327,
"ast_hash": "a1bad1a71d685011e276bcb11a32bfb5",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/08-report-top.png": {
"mtime": 1791642456.0373278,
"ast_hash": "cc03d71114e67a70be1da597be3a1004",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/09-report-tab-0.png": {
"mtime": 1791642456.5868485,
"ast_hash": "fffafbf39ef5034fd482f7a8913e77a3",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/09-report-tab-1.png": {
"mtime": 1791642457.1358962,
"ast_hash": "a9711819779892e12e4822adea670327",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/09-report-tab-2.png": {
"mtime": 1791642457.6528962,
"ast_hash": "98f3345d71f386432a0abdc4101372c1",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/10-report-full.png": {
"mtime": 1791642457.8252294,
"ast_hash": "75079ede2b0ec48e2a61bf880c3df379",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/comparison-flow.jpg": {
"mtime": 1791642519.1911554,
"ast_hash": "fe2bfa1ccdfa0009a33dea1eb640aa9b",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/comparison-report.jpg": {
"mtime": 1791642519.3780205,
"ast_hash": "c00044e21709055f246aeedaff0e2e1b",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/splash-375x844.png": {
"mtime": 1791642458.8775625,
"ast_hash": "72f3e6d936f46bab4abfc331598c0bc0",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/splash-390x844.png": {
"mtime": 1791642459.9445622,
"ast_hash": "343b642a20aad07ee2998cbe0ff5897b",
"semantic_hash": ""
},
"design/ui-20261009/audit-current/splash-414x844.png": {
"mtime": 1791642461.0152285,
"ast_hash": "a930db67507523cbe3dda1676fd2827e",
"semantic_hash": ""
},
"design/ui-20261009/decisions/01-header.jpg": {
"mtime": 1791643908.9231389,
"ast_hash": "96222db0eafd143c991e3d25ff2b4f83",
"semantic_hash": ""
},
"design/ui-20261009/decisions/02-login.jpg": {
"mtime": 1791643909.0195472,
"ast_hash": "e700914fc28d01fb76f801bbd9bc0b45",
"semantic_hash": ""
},
"design/ui-20261009/decisions/03-history.jpg": {
"mtime": 1791643909.0426853,
"ast_hash": "a3509a51b2487038f4f45e9948286073",
"semantic_hash": ""
},
"design/ui-20261009/decisions/04-score.jpg": {
"mtime": 1791643909.1622317,
"ast_hash": "6794174cba816fd3800f32c1987e6c4c",
"semantic_hash": ""
},
"design/ui-20261009/decisions/05-tabs.jpg": {
"mtime": 1791643909.270209,
"ast_hash": "026049c527dc312bcc569bc3bfd40901",
"semantic_hash": ""
},
"design/ui-20261009/decisions/06-content.jpg": {
"mtime": 1791643909.3781865,
"ast_hash": "ea8f898ddb9edd5ce9f1029078937eb2",
"semantic_hash": ""
},
"design/ui-20261009/decisions/07-products.jpg": {
"mtime": 1791643909.4938765,
"ast_hash": "751604b9f1bbcd77158e7a69e1b46054",
"semantic_hash": ""
},
"design/ui-20261009/decisions/08-camera.jpg": {
"mtime": 1791643909.520871,
"ast_hash": "66e7c9baba319c310828093f6a380646",
"semantic_hash": ""
},
"design/ui-20261009/decisions/09-font.jpg": {
"mtime": 1791643909.6172793,
"ast_hash": "12881243d056472816871ccf0d028b1c",
"semantic_hash": ""
},
"design/ui-20261009/decisions/10-proofreading.jpg": {
"mtime": 1791643909.6674116,
"ast_hash": "927a5d7e5a1460c961a447ac48f184c4",
"semantic_hash": ""
},
"design/ui-20261009/verification/01-login.png": {
"mtime": 1791780103.439073,
"ast_hash": "32f142e3c11d1c7768477e37b06e2209",
"semantic_hash": ""
},
"design/ui-20261009/verification/02-home.png": {
"mtime": 1791780106.015078,
"ast_hash": "52b5c983e23e89638f6f13e4b805bc33",
"semantic_hash": ""
},
"design/ui-20261009/verification/03-history-empty.png": {
"mtime": 1791780106.5190792,
"ast_hash": "6d94cf3a87064fe79bb8a3cb7e227228",
"semantic_hash": ""
},
"design/ui-20261009/verification/04-privacy.png": {
"mtime": 1791780107.4590812,
"ast_hash": "7765fe3123d067a5211190f7a08e90c5",
"semantic_hash": ""
},
"design/ui-20261009/verification/05-profile.png": {
"mtime": 1791780108.0350823,
"ast_hash": "0a74071e471e65a16cf5ed22f27b7803",
"semantic_hash": ""
},
"design/ui-20261009/verification/06-report-top.png": {
"mtime": 1791780109.9670866,
"ast_hash": "4f47a34364b02e9ef6189d7ac0ef6660",
"semantic_hash": ""
},
"design/ui-20261009/verification/07-report-full.png": {
"mtime": 1791780110.071087,
"ast_hash": "0be8c9110241f7305598415d6c861f5c",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-history-h5.jpg": {
"mtime": 1791773589.4149413,
"ast_hash": "1dca9bdb3e19bdf837fc317e8dc3d65a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-login-h5.jpg": {
"mtime": 1791773588.8749564,
"ast_hash": "bbc72a85580326d228e92c0efd0293aa",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-profile-h5.jpg": {
"mtime": 1791773589.1589484,
"ast_hash": "bbc72a85580326d228e92c0efd0293aa",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-report-h5.jpg": {
"mtime": 1791773589.5629373,
"ast_hash": "b49c61b68a714da65fdb706060e1a4f0",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-splash-h5.jpg": {
"mtime": 1791773588.2109632,
"ast_hash": "0ab40c14387e86508639e733fea6d168",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-guide-front.png": {
"mtime": 1791642118.1918173,
"ast_hash": "0e487819610ac8aeb58b728f629910df",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-guide-left.png": {
"mtime": 1791642119.9198694,
"ast_hash": "ef6f454709623d7eae301b3d4be52054",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/camera-guide-right.png": {
"mtime": 1791642119.049992,
"ast_hash": "02af0bb38e2630bcdd00d8349f3c0d44",
"semantic_hash": ""
},
"design/ui-20261009/verification/08-camera-front-layout.png": {
"mtime": 1791780108.4710832,
"ast_hash": "b9d04eed0983f1f49fed6c663070ecc1",
"semantic_hash": ""
},
"design/ui-20261009/verification/08-camera-left-layout.png": {
"mtime": 1791780108.5350835,
"ast_hash": "2cb973828c1d18bfbcb5c0cb8ad06d41",
"semantic_hash": ""
},
"design/ui-20261009/verification/08-camera-right-layout.png": {
"mtime": 1791780108.5030832,
"ast_hash": "4b299e86432feb2697dd739dc77a825d",
"semantic_hash": ""
},
"h5/test/wechat-browser.mjs": {
"mtime": 1791773972.891023,
"ast_hash": "c27ca11dea3026358188f80affee2d6b",
"semantic_hash": ""
},
"docs/2026-10-11\u63d0\u4ea4\u524dReview.md": {
"mtime": 1791710850.0228443,
"ast_hash": "93bae80d0b6998a1d31616c512261e68",
"semantic_hash": ""
},
"h5/test/analyze-timeout.js": {
"mtime": 1791730674.763797,
"ast_hash": "c30bdb0ecb2de0b4c280335940e9d01f",
"semantic_hash": ""
},
"h5/test/production-loop.mjs": {
"mtime": 1791773638.010959,
"ast_hash": "0d0d46ec3ecfce9e767e7f31050befe2",
"semantic_hash": ""
},
"docs/\u7ebf\u4e0a\u5fae\u4fe1\u767b\u5f55\u90e8\u7f72\u8bb0\u5f55-2026-10-11.md": {
"mtime": 1791770706.5669558,
"ast_hash": "7a475de28d6a21509d18bf30a3b29080",
"semantic_hash": ""
},
"h5/test/browser-failures.mjs": {
"mtime": 1791773638.010959,
"ast_hash": "8cb2774864d3d59bd5a418ad61819129",
"semantic_hash": ""
},
"docs/\u7ebf\u4e0a\u95ed\u73af\u6d4b\u8bd5-2026-10-11.md": {
"mtime": 1791730745.132436,
"ast_hash": "04095d09dce4e81349b9256a0e8cb540",
"semantic_hash": ""
},
"h5/test/recommendation.js": {
"mtime": 1791735559.0619977,
"ast_hash": "589f7046d94485dfc1df20a6129640e0",
"semantic_hash": ""
},
"docs/\u4ea7\u54c1\u63a8\u8350\u89c4\u5219-\u80a4\u9f84\u7248.md": {
"mtime": 1791735707.6717124,
"ast_hash": "69c1278c6c8eea5866982940e20e421c",
"semantic_hash": ""
},
"docs/\u5fae\u4fe1\u767b\u5f55\u8054\u8c03-2026-10-12.md": {
"mtime": 1791774038.1869662,
"ast_hash": "14292d8da8570ff0f8af2aac08d895b8",
"semantic_hash": ""
},
"h5/test/wechat-production.mjs": {
"mtime": 1791770851.3389251,
"ast_hash": "ba457695243430e271cfd7927ef22541",
"semantic_hash": ""
},
"h5/test/login-performance.mjs": {
"mtime": 1791773812.003017,
"ast_hash": "1ba88edfa7b259fc319de7f845925582",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-history-h5.webp": {
"mtime": 1791773589.4829395,
"ast_hash": "9a886c304206c61fb50c4d8752bd8e7a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-login-h5.webp": {
"mtime": 1791773589.1229496,
"ast_hash": "bbd96b66102dd7af0f9f5b34b0c72cca",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-profile-h5.webp": {
"mtime": 1791773589.3989418,
"ast_hash": "bbd96b66102dd7af0f9f5b34b0c72cca",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-report-h5.webp": {
"mtime": 1791773589.8069305,
"ast_hash": "c7cbea632696ddb7703faef098f10f1a",
"semantic_hash": ""
},
"h5/assets/img/ui-20261009/bg-splash-h5.webp": {
"mtime": 1791773588.8429573,
"ast_hash": "9ba24f79e71ab8793eefd4e3271faa68",
"semantic_hash": ""
},
"docs/\u767b\u5f55\u53cd\u9988\u4e0e\u52a0\u8f7d\u4f18\u5316-2026-10-12.md": {
"mtime": 1791774227.194959,
"ast_hash": "e925c1ba7a57fdcb2321229b3eb32b66",
"semantic_hash": ""
},
"h5/test/customer-feedback.js": {
"mtime": 1791779743.1310055,
"ast_hash": "a01d5f9ec4424a2f21f8239451f576f9",
"semantic_hash": ""
},
"h5/test/customer-feedback.mjs": {
"mtime": 1791780010.8710139,
"ast_hash": "a8332f17ef693582bc81bff83950fedb",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5.md": {
"mtime": 1791778827.0,
"ast_hash": "09b009c9cdb63cf90710b1b45fc6cee0",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_40a9e343-5d09-4760-aa80-6d13cc9ac8eg.png": {
"mtime": 1791778827.0,
"ast_hash": "7a58ab24cec7edaaac8667f37f4e8e5a",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_718c2f13-c978-4e07-bf72-21b24236bd6g.png": {
"mtime": 1791778827.0,
"ast_hash": "96c0f8befaa850e8cb6eab3c54c4ad04",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_92abffb2-da3a-42d4-bd42-6d1b6a864e3g.png": {
"mtime": 1791778827.0,
"ast_hash": "9c902ad750767d96b98b20c713c36c14",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_d5d64a81-acad-4db9-970c-2dd1398b532g.png": {
"mtime": 1791778827.0,
"ast_hash": "12c940d1d339ff8a7a5bcb4e9292f1ff",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_ee1903ba-96ef-4778-9891-027588093c7g.jpeg": {
"mtime": 1791778827.0,
"ast_hash": "12251b6acdea7aaba393c3544832572e",
"semantic_hash": ""
},
"feedback/\u5085\u8273NEW\u4e0e\u9879\u601d\u70bc\u7684\u4f1a\u8bdd 2026\u5e7410\u670812\u65e5/\u56fe\u7247\u548c\u9644\u4ef6/exported_img_v3_0216d_eec8b07b-be07-4d37-9a9f-4057c5afc56g.png": {
"mtime": 1791778827.0,
"ast_hash": "c7a7e7a16688e354c0010a278fcf9124",
"semantic_hash": ""
},
"docs/\u5ba2\u6237\u53cd\u9988\u4fee\u6539-2026-10-12.md": {
"mtime": 1791782301.2510374,
"ast_hash": "2602a202e227a624108b7749f43175be",
"semantic_hash": ""
},
"design/ui-20261009/verification/02-login-success.png": {
"mtime": 1791780103.7670736,
"ast_hash": "6e06a46e74f50f9f9a4ee94bb34bc3de",
"semantic_hash": ""
},
"h5/test/customer-feedback-second.js": {
"mtime": 1791786351.8390813,
"ast_hash": "ca63c07bb8ee8eb77267b9d08e5cb3d8",
"semantic_hash": ""
},
"h5/test/customer-feedback-second.mjs": {
"mtime": 1791787113.9230983,
"ast_hash": "2ee0f030571c4a0f6b897a8f9bd4b15d",
"semantic_hash": ""
},
"docs/\u5ba2\u6237\u53cd\u9988\u7b2c\u4e8c\u6279\u4fee\u6539-2026-10-12.md": {
"mtime": 1791786653.9070883,
"ast_hash": "0cb17a2e1c909816642fa31d3a416f1e",
"semantic_hash": ""
}
}
+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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

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