feat(report): unify display grades and add contour side photos

This commit is contained in:
xsl
2026-10-15 17:14:05 +08:00
parent 60f0965c5a
commit 2d80f6dfd0
21 changed files with 557 additions and 49 deletions
+25 -8
View File
@@ -281,10 +281,10 @@ function fillSuccess(rec, fpJson, persist = true) {
rec.skin_age_display = vm.skinAgeDisplay;
rec.recommendation = vm.recommendation;
rec.presentation = ReportUI.build(vm, fpJson.result);
rec.grade_rule = ReportUI.gradeRule;
rec.vita_score_rule = vm.vitaScoreRule;
rec.sub_scores = Object.fromEntries(rec.presentation.views.map(view => [view.key, {
P: view.P, S: view.P == null ? null : Math.round((Math.max(7, Math.min(10, 7 + 3 * view.P / 100)) + 1e-9) * 100) / 100,
level: view.level === '改善' ? '预警' : view.level === '良好' ? '改善' : view.level === '优秀' ? '良好' : null
P: view.P, S: view.S, level: ReportUI.internalLevel(view.S)
}]));
rec.total_score = vm.total;
rec.total_score_rule = vm.totalRule;
@@ -299,7 +299,7 @@ function fillSuccess(rec, fpJson, persist = true) {
rec.copy_6[k] = vm.cats[k].text;
}
rec.dims = {};
for (const k of Object.keys(vm.dims)) rec.dims[k] = { P: vm.dims[k].P, S: vm.dims[k].S, level: vm.dims[k].level };
for (const k of Object.keys(vm.dims)) rec.dims[k] = { P: vm.dims[k].P, S: vm.dims[k].S, level: ReportUI.internalLevel(vm.dims[k].S) };
rec.avg_s = vm.avgS;
rec.group = vm.group;
rec.copy_summary = vm.summary;
@@ -530,6 +530,10 @@ function displaySkinAge(rec) {
function displayTotalScore(rec) {
return SkinScore.weightedTotal(rec.dims);
}
function displayScoreLevels(scores) {
return scores ? Object.fromEntries(Object.entries(scores).map(([key, value]) => [key,
Object.assign({}, value, { level: ReportUI.internalLevel(value && value.S) })])) : scores;
}
function listItem(rec) {
return {
record_id: rec.record_id,
@@ -541,7 +545,7 @@ function listItem(rec) {
fail_code: rec.fail_code, fail_msg: rec.fail_msg,
total_score: displayTotalScore(rec), total_score_rule: 'dimensions-433-v1', skin_age: rec.skin_age,
skin_age_display: displaySkinAge(rec), recommendation: rec.recommendation || null,
dims: rec.dims, group: rec.group,
dims: displayScoreLevels(rec.dims), grade_rule: ReportUI.gradeRule, group: rec.group,
essence: rec.essence ? rec.essence.name : null,
cost_ms: rec.cost_ms,
retry_of: rec.retry_of, retry_count: rec.retry_count || 0,
@@ -566,7 +570,7 @@ function matchQuery(rec, q) {
if (q.age_max != null) { if (rec.real_age == null || rec.real_age > q.age_max) return false; }
if (q.dim && q.dim_level) {
const d = rec.dims && rec.dims[q.dim];
if (!d || d.level !== q.dim_level) return false;
if (!d || ReportUI.internalLevel(d.S) !== q.dim_level) return false;
}
if (q.only_fail === '1' && rec.detect_status !== '失败') return false;
return true;
@@ -626,10 +630,23 @@ function handleRecordDetail(req, res, id) {
const parent = rec.retry_of ? (all.find(r => r.record_id === rec.retry_of) || null) : null;
const children = all.filter(r => r.retry_of === rec.record_id)
.map(r => ({ record_id: r.record_id, detect_status: r.detect_status, created_at: r.created_at }));
let presentation = rec.presentation || accounts.reportPresentation(rec.owner_id, rec.record_id);
if (presentation && (presentation.gradeRule !== ReportUI.gradeRule || presentation.copyRule !== ReportUI.copyRule) && rawJson && rawJson.result) {
// Recover text variables from raw data, but retain the saved scores,
// recommendation and care order. No database/snapshot mutation.
const displayVm = SkinScore.buildViewModel(rawJson.result, {
left: rawJson.left_side_result || (rawJson.side_result && rawJson.side_result.left_side_result),
right: rawJson.right_side_result || (rawJson.side_result && rawJson.side_result.right_side_result)
}, scoreProfile(rec));
for (const key of Object.keys(rec.cats || {})) Object.assign(displayVm.cats[key], rec.cats[key]);
displayVm.dims = rec.dims || displayVm.dims;
if (rec.sub_scores && rec.sub_scores.color) displayVm.cats.tone.vars.color_score = rec.sub_scores.color.P;
presentation = ReportUI.forDisplay(displayVm, rawJson.result, presentation);
}
sendJson(res, 200, {
ok: true,
record: Object.assign({}, rec, { total_score: displayTotalScore(rec), total_score_rule: 'dimensions-433-v1', skin_age_display: displaySkinAge(rec),
presentation: rec.presentation || accounts.reportPresentation(rec.owner_id, rec.record_id) }),
dims: displayScoreLevels(rec.dims), sub_scores: displayScoreLevels(rec.sub_scores), grade_rule: ReportUI.gradeRule, presentation }),
raw_json: rawJson,
photos: photoUrls(rec),
versions: {
@@ -691,7 +708,7 @@ function handleStats(req, res) {
const group_dist = {};
for (const r of succeeded) {
for (const d of ['firm', 'vita', 'stab']) {
const lv = r.dims && r.dims[d] && r.dims[d].level;
const lv = r.dims && r.dims[d] && ReportUI.internalLevel(r.dims[d].S);
if (lv && dim_dist[d][lv] != null) dim_dist[d][lv] += 1;
}
const g = r.group || '未分组';
@@ -787,7 +804,7 @@ 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]) ? ReportUI.levelMap[r.dims[d].level] : null;
const dimLv = (r, d) => (r.dims && r.dims[d]) ? ReportUI.levelForScore(r.dims[d].S) : null;
const header = ['记录ID', '创建时间(北京时间)', '年龄', '性别', '地区', '日常肤质', '拍摄状态', '上传状态', '检测状态',
'失败环节', '失败码', '失败信息', '综合得分(4:3:3)', '原始肌龄(推荐依据)', '前端展示肌龄',
'紧塑力P', '紧塑力S', '紧塑力档', '透亮感P', '透亮感S', '透亮感档', '稳韧力P', '稳韧力S', '稳韧力档',