fix(report): recommend by measured skin age and align admin results

This commit is contained in:
xsl
2026-10-12 00:23:57 +08:00
parent 7518dc2307
commit 0ec5e5dc32
21 changed files with 3010 additions and 1875 deletions
+22 -8
View File
@@ -257,6 +257,9 @@ function fillSuccess(rec, fpJson) {
rec.detect_status = '成功';
rec.fail_stage = null; rec.fail_code = null; rec.fail_msg = null;
rec.skin_age = vm.skinAgeRaw;
rec.skin_age_display = vm.skinAgeDisplay;
rec.recommendation = vm.recommendation;
rec.presentation = ReportUI.build(vm, fpJson.result);
rec.total_score = vm.total;
rec.jawline_angle_l = jawlineAngle(fpJson.left_side_result, 'left');
rec.jawline_angle_r = jawlineAngle(fpJson.right_side_result, 'right');
@@ -433,7 +436,7 @@ async function handleAnalyze(req, res) {
let reportSaved = false, presentation = null, snapshotVm = null;
try {
snapshotVm = SkinScore.buildViewModel(result, payload.side, scoreProfile(rec));
presentation = ReportUI.build(snapshotVm, result);
presentation = rec.presentation || ReportUI.build(snapshotVm, result);
accounts.storeReport(owner.id, { id: recordId, created_at: rec.created_at,
version: '2026-10-09', profile, vm: snapshotVm, presentation, payload,
photos: [front, left, right].map(b64 => b64 ? toDataUrl(b64) : null) });
@@ -482,6 +485,12 @@ function photoUrls(rec) {
}
/* 列表用的字段子集(不含 raw_json 与文案全文) */
function displaySkinAge(rec) {
if (Object.prototype.hasOwnProperty.call(rec, 'skin_age_display')) return rec.skin_age_display;
// 旧记录没有展示值,沿用生成时的规则,不重算推荐或改写文件。
if (rec.skin_age == null || rec.real_age == null) return rec.skin_age;
return Math.min(Math.max(rec.skin_age, rec.real_age - 3), rec.real_age + 3);
}
function listItem(rec) {
return {
record_id: rec.record_id,
@@ -490,6 +499,7 @@ function listItem(rec) {
shoot_status: rec.shoot_status, upload_status: rec.upload_status, detect_status: rec.detect_status,
fail_code: rec.fail_code, fail_msg: rec.fail_msg,
total_score: rec.total_score, skin_age: rec.skin_age,
skin_age_display: displaySkinAge(rec), recommendation: rec.recommendation || null,
dims: rec.dims, group: rec.group,
essence: rec.essence ? rec.essence.name : null,
cost_ms: rec.cost_ms,
@@ -576,7 +586,8 @@ function handleRecordDetail(req, res, id) {
.map(r => ({ record_id: r.record_id, detect_status: r.detect_status, created_at: r.created_at }));
sendJson(res, 200, {
ok: true,
record: rec,
record: Object.assign({}, rec, { skin_age_display: displaySkinAge(rec),
presentation: rec.presentation || accounts.reportPresentation(rec.owner_id, rec.record_id) }),
raw_json: rawJson,
photos: photoUrls(rec),
versions: {
@@ -733,21 +744,24 @@ function handleExport(req, res, url) {
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1));
const dimP = (r, d) => (r.dims && r.dims[d]) ? r.dims[d].P : null;
const dimS = (r, d) => (r.dims && r.dims[d]) ? r.dims[d].S : null;
const dimLv = (r, d) => (r.dims && r.dims[d]) ? r.dims[d].level : null;
const dimLv = (r, d) => (r.dims && r.dims[d]) ? ReportUI.levelMap[r.dims[d].level] : null;
const header = ['记录ID', '创建时间', '年龄', '性别', '地区', '日常肤质', '拍摄状态', '上传状态', '检测状态',
'失败环节', '失败码', '失败信息', '总分', '肌肤年龄',
'紧塑力P', '紧塑力S', '紧塑力档', '活力感P', '活力感S', '活力感档', '稳定力P', '稳定力S', '稳定力档',
'均分S', '产品组别', '推荐精华', '触发判据', '耗时ms'];
'失败环节', '失败码', '失败信息', '总分', '原始肌龄(推荐依据)', '前端展示肌龄',
'紧塑力P', '紧塑力S', '紧塑力档', '透亮感P', '透亮感S', '透亮感档', '稳韧力P', '稳韧力S', '稳韧力档',
'均分S', '产品组别', '推荐精华', '触发判据', '耗时ms', '推荐规则版本', '推荐原因'];
const lines = [header.join(',')];
for (const r of filtered) {
lines.push([
r.record_id, r.created_at, r.real_age, r.gender, r.region, r.daily_skin_type,
r.shoot_status, r.upload_status, r.detect_status,
r.fail_stage, r.fail_code, r.fail_msg, r.total_score, r.skin_age,
r.fail_stage, r.fail_code, r.fail_msg, r.total_score, r.skin_age, displaySkinAge(r),
dimP(r, 'firm'), dimS(r, 'firm'), dimLv(r, 'firm'),
dimP(r, 'vita'), dimS(r, 'vita'), dimLv(r, 'vita'),
dimP(r, 'stab'), dimS(r, 'stab'), dimLv(r, 'stab'),
r.avg_s, r.group, r.essence ? r.essence.name : null, r.trigger, r.cost_ms
r.avg_s, r.group, r.essence ? ((ReportUI.products.find(p => p.match === r.essence.name) || {}).title || r.essence.name) : null,
r.trigger, r.cost_ms,
r.recommendation ? r.recommendation.version : '旧版规则(历史记录)',
r.recommendation ? r.recommendation.reason : null
].map(csvCell).join(','));
}
const fromTag = (q.from || 'all').replace(/[^\d-]/g, '');