|
|
@@ -2,6 +2,9 @@ import { Component, OnInit } from '@angular/core';
|
|
|
import { CommonModule, DatePipe } from '@angular/common';
|
|
|
import { HttpClient } from '@angular/common/http';
|
|
|
import { Router } from '@angular/router';
|
|
|
+import { Observable, forkJoin } from 'rxjs'; // Import Observable, forkJoin
|
|
|
+import { finalize, catchError, map } from 'rxjs/operators'; // Import operators
|
|
|
+import { of } from 'rxjs'; // Import 'of' to create an Observable from a value
|
|
|
|
|
|
// NG-ZORRO 组件
|
|
|
import { NzCardModule } from 'ng-zorro-antd/card';
|
|
|
@@ -212,136 +215,125 @@ export class MessageDashboardComponent implements OnInit {
|
|
|
loadAllStatistics(): void {
|
|
|
this.isLoading = true;
|
|
|
|
|
|
- Promise.all([
|
|
|
- this.loadOverallStatistics(),
|
|
|
- this.loadStrategyStatistics(),
|
|
|
- this.loadTemplateStatistics(),
|
|
|
- this.loadDailyTrends(),
|
|
|
- this.loadAvgDeliveryTime(),
|
|
|
- ]).finally(() => {
|
|
|
- this.isLoading = false;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- loadOverallStatistics(): Promise<void> {
|
|
|
- return this.http
|
|
|
- .get('/api/message-records/statistics/overall')
|
|
|
- .toPromise()
|
|
|
- .then((data: any) => {
|
|
|
- this.overallStats = data?.data || null;
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- console.error('Failed to load overall statistics:', err);
|
|
|
- this.message.error('加载整体统计失败');
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- loadStrategyStatistics(): Promise<void> {
|
|
|
- return this.http
|
|
|
- .get('/api/message-records/statistics/by-strategy')
|
|
|
- .toPromise()
|
|
|
- .then((data: any) => {
|
|
|
- this.strategyStats = data?.data || [];
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- console.error('Failed to load strategy statistics:', err);
|
|
|
- this.message.error('加载策略统计失败');
|
|
|
- });
|
|
|
- }
|
|
|
+ // Use forkJoin to run all requests in parallel and wait for all to complete
|
|
|
+ forkJoin({
|
|
|
+ overall: this.http.get('/api/message-records/statistics/overall').pipe(
|
|
|
+ map((res: any) => res?.data || null),
|
|
|
+ catchError((err) => {
|
|
|
+ console.error('Failed to load overall statistics:', err);
|
|
|
+ this.message.error('加载整体统计失败');
|
|
|
+ return of(null); // Return a default value to prevent forkJoin from failing
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ strategies: this.http
|
|
|
+ .get('/api/message-records/statistics/by-strategy')
|
|
|
+ .pipe(
|
|
|
+ map((res: any) => res?.data || []),
|
|
|
+ catchError((err) => {
|
|
|
+ console.error('Failed to load strategy statistics:', err);
|
|
|
+ this.message.error('加载策略统计失败');
|
|
|
+ return of([]);
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ templates: this.http
|
|
|
+ .get('/api/message-records/statistics/by-template')
|
|
|
+ .pipe(
|
|
|
+ map((res: any) => res?.data || []),
|
|
|
+ catchError((err) => {
|
|
|
+ console.error('Failed to load template statistics:', err);
|
|
|
+ this.message.error('加载模板统计失败');
|
|
|
+ return of([]);
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ dailyTrends: this.http
|
|
|
+ .get('/api/message-records/statistics/daily-trends')
|
|
|
+ .pipe(
|
|
|
+ map((res: any) => res?.data || []),
|
|
|
+ catchError((err) => {
|
|
|
+ console.error('Failed to load daily trends:', err);
|
|
|
+ this.message.error('加载每日趋势失败');
|
|
|
+ return of([]);
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ avgTime: this.http
|
|
|
+ .get('/api/message-records/statistics/avg-delivery-time')
|
|
|
+ .pipe(
|
|
|
+ map((res: any) => res?.data || null),
|
|
|
+ catchError((err) => {
|
|
|
+ console.error('Failed to load average delivery time:', err);
|
|
|
+ this.message.error('加载平均送达时间失败');
|
|
|
+ return of(null);
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ })
|
|
|
+ .pipe(finalize(() => (this.isLoading = false)))
|
|
|
+ .subscribe((results: any) => {
|
|
|
+ // Assign data to component properties
|
|
|
+ this.overallStats = results.overall;
|
|
|
+ this.strategyStats = results.strategies;
|
|
|
+ this.templateStats = results.templates;
|
|
|
+ this.dailyTrends = results.dailyTrends;
|
|
|
+ this.avgDeliveryTime = results.avgTime;
|
|
|
|
|
|
- loadTemplateStatistics(): Promise<void> {
|
|
|
- return this.http
|
|
|
- .get('/api/message-records/statistics/by-template')
|
|
|
- .toPromise()
|
|
|
- .then((data: any) => {
|
|
|
- this.templateStats = data?.data || [];
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- console.error('Failed to load template statistics:', err);
|
|
|
- this.message.error('加载模板统计失败');
|
|
|
+ // Update charts based on the latest data
|
|
|
+ this.updateChartData();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- loadDailyTrends(): Promise<void> {
|
|
|
- return this.http
|
|
|
- .get('/api/message-records/statistics/daily-trends')
|
|
|
- .toPromise()
|
|
|
- .then((data: any) => {
|
|
|
- this.dailyTrends = data?.data || [];
|
|
|
-
|
|
|
- // 更新组合图表数据
|
|
|
- this.combinedChartData = {
|
|
|
- labels: this.dailyTrends.map((t) => this.formatDate(t.date)),
|
|
|
- datasets: [
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[0],
|
|
|
- data: this.dailyTrends.map((t) => t.totalRecords || 0),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[1],
|
|
|
- data: this.dailyTrends.map((t) => t.sent || 0),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[2],
|
|
|
- data: this.dailyTrends.map((t) => t.delivered || 0),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[3],
|
|
|
- data: this.dailyTrends.map((t) => t.opened || 0),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[4],
|
|
|
- data: this.dailyTrends.map((t) => t.displayCount || 0),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.combinedChartData.datasets[5],
|
|
|
- data: this.dailyTrends.map((t) => t.failed || 0),
|
|
|
- },
|
|
|
- ],
|
|
|
- };
|
|
|
-
|
|
|
- // 更新比率图表数据
|
|
|
- this.rateChartData = {
|
|
|
- labels: this.dailyTrends.map((t) => this.formatDate(t.date)),
|
|
|
- datasets: [
|
|
|
- {
|
|
|
- ...this.rateChartData.datasets[0],
|
|
|
- data: this.dailyTrends.map((t) => (t.sentSuccessRate || 0) * 100),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.rateChartData.datasets[1],
|
|
|
- data: this.dailyTrends.map((t) => (t.deliveredRate || 0) * 100),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.rateChartData.datasets[2],
|
|
|
- data: this.dailyTrends.map((t) => (t.displayRate || 0) * 100),
|
|
|
- },
|
|
|
- {
|
|
|
- ...this.rateChartData.datasets[3],
|
|
|
- data: this.dailyTrends.map(
|
|
|
- (t) => (t.clickThroughRate || 0) * 100
|
|
|
- ),
|
|
|
- },
|
|
|
- ],
|
|
|
- };
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- console.error('Failed to load daily trends:', err);
|
|
|
- this.message.error('加载每日趋势失败');
|
|
|
- });
|
|
|
- }
|
|
|
+ private updateChartData(): void {
|
|
|
+ // 更新组合图表数据
|
|
|
+ this.combinedChartData = {
|
|
|
+ labels: this.dailyTrends.map((t) => this.formatDate(t.date)),
|
|
|
+ datasets: [
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[0],
|
|
|
+ data: this.dailyTrends.map((t) => t.totalRecords || 0),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[1],
|
|
|
+ data: this.dailyTrends.map((t) => t.sent || 0),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[2],
|
|
|
+ data: this.dailyTrends.map((t) => t.delivered || 0),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[3],
|
|
|
+ data: this.dailyTrends.map((t) => t.opened || 0),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[4],
|
|
|
+ data: this.dailyTrends.map((t) => t.displayCount || 0),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.combinedChartData.datasets[5],
|
|
|
+ data: this.dailyTrends.map((t) => t.failed || 0),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
|
|
|
- loadAvgDeliveryTime(): Promise<void> {
|
|
|
- return this.http
|
|
|
- .get('/api/message-records/statistics/avg-delivery-time')
|
|
|
- .toPromise()
|
|
|
- .then((data: any) => {
|
|
|
- this.avgDeliveryTime = data?.data || null;
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- console.error('Failed to load average delivery time:', err);
|
|
|
- this.message.error('加载平均送达时间失败');
|
|
|
- });
|
|
|
+ // 更新比率图表数据
|
|
|
+ this.rateChartData = {
|
|
|
+ labels: this.dailyTrends.map((t) => this.formatDate(t.date)),
|
|
|
+ datasets: [
|
|
|
+ {
|
|
|
+ ...this.rateChartData.datasets[0],
|
|
|
+ data: this.dailyTrends.map((t) => (t.sentSuccessRate || 0) * 100),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.rateChartData.datasets[1],
|
|
|
+ data: this.dailyTrends.map((t) => (t.deliveredRate || 0) * 100),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.rateChartData.datasets[2],
|
|
|
+ data: this.dailyTrends.map((t) => (t.displayRate || 0) * 100),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ ...this.rateChartData.datasets[3],
|
|
|
+ data: this.dailyTrends.map((t) => (t.clickThroughRate || 0) * 100),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
refreshData(): void {
|