Skip to content

Commit ab7b1b6

Browse files
committed
새로운 메트릭 추가 - 유저 가입 경로, 디바이스 플랫폼
1 parent 26660d7 commit ab7b1b6

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

src/main/java/apu/saerok_admin/infra/stat/StatMetric.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ public enum StatMetric {
9696
false,
9797
Map.of(),
9898
false
99+
),
100+
USER_SIGNUP_SOURCE_TOTAL(
101+
"가입 경로별 누적 사용자",
102+
"가입 경로(소셜 로그인 종류)별 누적 가입자 수",
103+
MetricUnit.COUNT,
104+
true,
105+
Map.of(),
106+
false
107+
),
108+
USER_DEVICE_PLATFORM_TOTAL(
109+
"기기 플랫폼별 누적 사용자",
110+
"기기 플랫폼(iOS/Android/Web 등)별 누적 사용자 수",
111+
MetricUnit.COUNT,
112+
true,
113+
Map.of(),
114+
false
99115
);
100116

101117
private final String label;

src/main/resources/static/js/service-insight.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
USER_DAU: 'USER_DAU',
7474
USER_WAU: 'USER_WAU',
7575
USER_MAU: 'USER_MAU',
76+
USER_SIGNUP_SOURCE_TOTAL: 'USER_SIGNUP_SOURCE_TOTAL',
77+
USER_DEVICE_PLATFORM_TOTAL: 'USER_DEVICE_PLATFORM_TOTAL',
7678
};
7779

7880
// (아래부터는 기존 로직 그대로입니다. 색상/그룹/플롯/툴팁/박스플롯 렌더링 등 전체 원본 유지)
@@ -167,7 +169,8 @@
167169
{ key:'collection', name:'새록', metrics:[METRICS.TOTAL_COUNT, METRICS.PRIVATE_RATIO] },
168170
{ key:'user', name:'유저', metrics:[
169171
METRICS.USER_COMPLETED_TOTAL, METRICS.USER_SIGNUP_DAILY, METRICS.USER_WITHDRAWAL_DAILY,
170-
METRICS.USER_DAU, METRICS.USER_WAU, METRICS.USER_MAU
172+
METRICS.USER_DAU, METRICS.USER_WAU, METRICS.USER_MAU,
173+
METRICS.USER_SIGNUP_SOURCE_TOTAL, METRICS.USER_DEVICE_PLATFORM_TOTAL
171174
] },
172175
{ key:'id', name:'동정 요청', metrics:[METRICS.PENDING_COUNT, METRICS.RESOLVED_COUNT, METRICS.RESOLUTION_STATS] },
173176
];
@@ -683,6 +686,50 @@
683686
groupSet.add(id);
684687
changed = true;
685688
}
689+
} else if (opt.multiSeries) {
690+
// 일반 다중 시리즈: 컴포넌트별 라인 렌더링
691+
const series = seriesMap.get(metric);
692+
const comps = Array.isArray(series?.components) ? series.components : [];
693+
const labelsMap = (componentLabels[metric] && typeof componentLabels[metric] === 'object') ? componentLabels[metric] : {};
694+
const unit = String(opt.unit || '').toUpperCase();
695+
const yAxis = axisByUnit(unit);
696+
697+
comps.forEach(comp => {
698+
if (!comp || !comp.key) return;
699+
const compKey = comp.key;
700+
const id = datasetIdFor(metric, compKey);
701+
if (p.datasets.has(id)) return;
702+
703+
const compColor = colorForMetric(id);
704+
const compLabel = labelsMap[compKey] || compKey;
705+
const points = (Array.isArray(comp.points) ? comp.points : [])
706+
.map(pt => {
707+
const x = toDateKST(pt.date);
708+
const y = normalizeValue(pt.value, unit);
709+
return (x && y != null) ? { x, y } : null;
710+
})
711+
.filter(Boolean);
712+
713+
const ds = {
714+
_id: id,
715+
label: `${opt.label || metric} (${compLabel})`,
716+
data: points,
717+
parsing: { xAxisKey: 'x', yAxisKey: 'y' },
718+
borderColor: compColor,
719+
backgroundColor: compColor,
720+
tension: 0.25,
721+
pointRadius: 0,
722+
pointHoverRadius: 4,
723+
fill: false,
724+
spanGaps: true,
725+
yAxisID: yAxis,
726+
_saUnit: unit
727+
};
728+
p.chart.data.datasets.push(ds);
729+
p.datasets.add(id);
730+
groupSet.add(id);
731+
changed = true;
732+
});
686733
} else {
687734
const unit = String(optionMap.get(metric)?.unit || '').toUpperCase();
688735
const s = seriesMap.get(metric);

0 commit comments

Comments
 (0)