2025-09-06 10:01:52 +09:00
|
|
|
import { Injectable, Inject } from "@nestjs/common";
|
|
|
|
|
import { PrismaService } from "../common/prisma/prisma.service";
|
|
|
|
|
import { Logger } from "nestjs-pino";
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class SimUsageStoreService {
|
|
|
|
|
constructor(
|
|
|
|
|
private readonly prisma: PrismaService,
|
2025-09-09 15:45:03 +09:00
|
|
|
@Inject(Logger) private readonly logger: Logger
|
2025-09-06 10:01:52 +09:00
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
private normalizeDate(date?: Date): Date {
|
|
|
|
|
const d = date ? new Date(date) : new Date();
|
|
|
|
|
// strip time to YYYY-MM-DD
|
2025-09-09 15:45:03 +09:00
|
|
|
const iso = d.toISOString().split("T")[0];
|
|
|
|
|
return new Date(iso + "T00:00:00.000Z");
|
2025-09-06 10:01:52 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async upsertToday(account: string, usageMb: number, date?: Date): Promise<void> {
|
|
|
|
|
const day = this.normalizeDate(date);
|
|
|
|
|
try {
|
2025-09-09 16:07:48 +09:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
2025-09-09 15:59:30 +09:00
|
|
|
await this.prisma.simUsageDaily.upsert({
|
|
|
|
|
// @ts-expect-error composite unique input type depends on Prisma schema
|
|
|
|
|
where: { account_date: { account, date: day } as unknown },
|
2025-09-06 10:01:52 +09:00
|
|
|
update: { usageMb },
|
|
|
|
|
create: { account, date: day, usageMb },
|
|
|
|
|
});
|
2025-09-09 15:59:30 +09:00
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const message = e instanceof Error ? e.message : String(e);
|
|
|
|
|
this.logger.error("Failed to upsert daily usage", { account, error: message });
|
2025-09-06 10:01:52 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 15:45:03 +09:00
|
|
|
async getLastNDays(
|
|
|
|
|
account: string,
|
|
|
|
|
days = 30
|
|
|
|
|
): Promise<Array<{ date: string; usageMb: number }>> {
|
2025-09-06 10:01:52 +09:00
|
|
|
const end = this.normalizeDate();
|
|
|
|
|
const start = new Date(end);
|
|
|
|
|
start.setUTCDate(end.getUTCDate() - (days - 1));
|
2025-09-09 16:07:48 +09:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
2025-09-09 15:59:30 +09:00
|
|
|
const rows = (await this.prisma.simUsageDaily.findMany({
|
2025-09-06 10:01:52 +09:00
|
|
|
where: { account, date: { gte: start, lte: end } },
|
2025-09-09 15:45:03 +09:00
|
|
|
orderBy: { date: "desc" },
|
|
|
|
|
})) as Array<{ date: Date; usageMb: number }>;
|
|
|
|
|
return rows.map(r => ({ date: r.date.toISOString().split("T")[0], usageMb: r.usageMb }));
|
2025-09-06 10:01:52 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async cleanupPreviousMonths(): Promise<number> {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const firstOfMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
2025-09-09 16:07:48 +09:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
|
|
|
const result = (await this.prisma.simUsageDaily.deleteMany({
|
2025-09-09 15:45:03 +09:00
|
|
|
where: { date: { lt: firstOfMonth } },
|
2025-09-09 16:07:48 +09:00
|
|
|
})) as unknown as { count: number };
|
2025-09-06 10:01:52 +09:00
|
|
|
return result.count;
|
|
|
|
|
}
|
|
|
|
|
}
|