Cloud Functions 도메인 함수 — POP 원장 · 퀘스트 · 이클립스 · 행성 성장 · 캠페인 · 결제 · 알림 · 브리핑 · INSIDE · 추천 · 번역 · 서버 테스트
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { Timestamp, type DocumentReference } from 'firebase-admin/firestore';
|
||||
import { dayKey } from '../core/time.js';
|
||||
|
||||
/**
|
||||
* 2026-09-16 EC02·EC05: 누락된 집계를 0으로 채우지 않는다.
|
||||
* 원장 C-26 `target = max(1, ceil(avgDau × rate))` · C-27 생성 후 완료 집계일 ≤ [windowDays] 일 평균 ·
|
||||
* C-21 집계 기간은 운영값(기본 7일).
|
||||
*/
|
||||
export async function eclipseTarget(
|
||||
fandom: DocumentReference,
|
||||
createdAt: unknown,
|
||||
day: string,
|
||||
rate = 0.05,
|
||||
windowDays = 7,
|
||||
): Promise<
|
||||
{ target: number; averageDau: number | null; dauDays: string[] }
|
||||
| { result: 'cancelled_error'; cancelReason: string }
|
||||
> {
|
||||
const invalid = (cancelReason: string) => ({ result: 'cancelled_error' as const, cancelReason });
|
||||
if (!Number.isFinite(rate) || rate <= 0 || rate > 1) return invalid('INVALID_TARGET_RATE');
|
||||
if (!Number.isSafeInteger(windowDays) || windowDays < 1) return invalid('INVALID_DAU_WINDOW');
|
||||
if (!(createdAt instanceof Timestamp)) return invalid('MISSING_FANDOM_CREATED_AT');
|
||||
// 기존 scheduler/dayKey와 같은 날짜 키로 완료된 일별 집계를 조회한다.
|
||||
const createdDay = dayKey(createdAt.toDate());
|
||||
if (createdDay > day) return invalid('FUTURE_FANDOM_CREATED_AT');
|
||||
if (createdDay === day) return { target: 1, averageDau: null, dauDays: [] };
|
||||
const days: string[] = [];
|
||||
for (let i = 1; i <= windowDays; i++) {
|
||||
const previous = new Date(`${day}T00:00:00Z`);
|
||||
previous.setUTCDate(previous.getUTCDate() - i);
|
||||
const key = previous.toISOString().slice(0, 10);
|
||||
if (key < createdDay) break;
|
||||
days.push(key);
|
||||
}
|
||||
const docs = await fandom.firestore.getAll(
|
||||
...days.map((key) => fandom.collection('fandomDailyStats').doc(key)),
|
||||
);
|
||||
if (docs.some((doc) => !doc.exists || !Number.isSafeInteger(doc.get('dau')) || doc.get('dau') < 0)) {
|
||||
return invalid('MISSING_OR_INVALID_DAU');
|
||||
}
|
||||
const sum = docs.reduce((total, doc) => total + (doc.get('dau') as number), 0);
|
||||
if (!Number.isSafeInteger(sum)) return invalid('INVALID_DAU_TOTAL');
|
||||
const averageDau = sum / days.length;
|
||||
return { target: Math.max(1, Math.ceil(averageDau * rate)), averageDau, dauDays: days };
|
||||
}
|
||||
Reference in New Issue
Block a user