optimize ui loading and dashboard performance

This commit is contained in:
hectorzhao
2026-08-27 15:17:47 +08:00
parent 7ff019e829
commit ecb567c0da
28 changed files with 1307 additions and 132 deletions
+2 -2
View File
@@ -202,10 +202,10 @@ export class PrismaCdrsRepository implements CdrsRepository {
async list(query: CdrQuery): Promise<{ items: CdrListItem[]; meta: CdrPageMeta }> {
const where = this.where(query);
const [items, total] = await this.prisma.$transaction([
const [items, total] = await Promise.all([
this.prisma.rawCdr.findMany({
where,
orderBy: [{ startedAt: 'desc' }],
orderBy: [{ startedAt: 'desc' }, { id: 'desc' }],
take: query.take,
skip: query.skip,
include: this.includeCdr()
@@ -75,6 +75,15 @@ class MemoryCdrsRepository implements CdrsRepository {
}
describe('CDR service', () => {
it('uses a 25-row default page to keep the first list response small', async () => {
const repository = new MemoryCdrsRepository();
const service = new CdrsService(repository);
await service.list({});
expect(repository.lastQuery).toMatchObject({ take: 25, skip: 0 });
});
it('normalizes filters for city, carrier and time range queries', async () => {
const repository = new MemoryCdrsRepository();
const service = new CdrsService(repository);
+1 -1
View File
@@ -15,7 +15,7 @@ export class CdrsService {
carrier: rawQuery.carrier === undefined ? undefined : this.carrier(rawQuery.carrier),
startedFrom: this.optionalDate(rawQuery.startedFrom ?? rawQuery.startTime ?? rawQuery.from),
startedTo: this.optionalDate(rawQuery.startedTo ?? rawQuery.endTime ?? rawQuery.to),
take: this.int(rawQuery.take, 100, 1, 500),
take: this.int(rawQuery.take, 25, 1, 500),
skip: this.int(rawQuery.skip, 0, 0, 1_000_000)
};
if (query.startedFrom && query.startedTo && query.startedFrom > query.startedTo) {
@@ -14,6 +14,12 @@ export class DashboardController {
return this.dashboardService.summary();
}
@Get('overview')
@RequirePermissions('dashboard.view')
overview(@Query() query: { hours?: string; bucketMinutes?: string }) {
return this.dashboardService.overview(query);
}
@Get('trends')
@RequirePermissions('dashboard.view')
trends(@Query() query: { hours?: string; bucketMinutes?: string }) {
@@ -4,6 +4,22 @@ import { buildTrendBuckets, callMetrics, DashboardService, startOfShanghaiDayUtc
import type { DashboardRepository } from './dashboard.repository.js';
describe('dashboard service', () => {
it('loads summary and trends together for the overview response', async () => {
const repository = {
summaryWindow: async () => ({
cdrs: [], ratedCdrs: [], failureCodes: [], abnormalGateways: [],
activeCustomers: 0, activeCustomerGateways: 0, activeVendorGateways: 0, pendingQuality: 0
}),
trendWindow: async () => ({ cdrs: [], ratedCdrs: [] })
} as unknown as DashboardRepository;
const service = new DashboardService(repository);
const result = await service.overview({ hours: 1, bucketMinutes: 60 });
expect(result.summary).toHaveProperty('calls.totalCalls', 0);
expect(result.trends.buckets).toHaveLength(1);
});
it('uses Asia/Shanghai day boundary for today summary', async () => {
const calls: Array<{ start: Date; end: Date }> = [];
const repository = {
@@ -36,6 +36,14 @@ export interface DashboardTrendBucket {
export class DashboardService {
constructor(@Inject(DASHBOARD_REPOSITORY) private readonly dashboard: DashboardRepository) {}
async overview(query: { hours?: unknown; bucketMinutes?: unknown } = {}) {
const [summary, trends] = await Promise.all([
this.summary(),
this.trends(query)
]);
return { summary, trends };
}
async summary(now = new Date()) {
const start = startOfShanghaiDayUtc(now);
const snapshot = await this.dashboard.summaryWindow(start, now);