diff --git a/backend/services/planting-service/src/api/dto/response/planting-stats.response.ts b/backend/services/planting-service/src/api/dto/response/planting-stats.response.ts index 8361db84..0d7cc1bf 100644 --- a/backend/services/planting-service/src/api/dto/response/planting-stats.response.ts +++ b/backend/services/planting-service/src/api/dto/response/planting-stats.response.ts @@ -34,8 +34,24 @@ export class TodayStatsDto { amount: string; } +/** + * 本月统计 DTO + * [2026-01-06] 新增 + */ +export class MonthStatsDto { + @ApiProperty({ description: '本月认种棵数', example: 150 }) + treeCount: number; + + @ApiProperty({ description: '本月订单数', example: 50 }) + orderCount: number; + + @ApiProperty({ description: '本月认种金额', example: '15000.00' }) + amount: string; +} + /** * 全局统计响应 DTO + * [2026-01-06] 更新:添加本月统计 */ export class GlobalStatsResponseDto { @ApiProperty({ description: '总认种棵数(PAID及之后状态)', example: 760 }) @@ -53,6 +69,9 @@ export class GlobalStatsResponseDto { @ApiProperty({ description: '今日统计', type: TodayStatsDto }) todayStats: TodayStatsDto; + @ApiProperty({ description: '本月统计', type: MonthStatsDto }) + monthStats: MonthStatsDto; + @ApiProperty({ description: '统计计算时间', example: '2026-01-04T12:00:00.000Z' }) calculatedAt: string; } diff --git a/backend/services/planting-service/src/application/services/planting-application.service.ts b/backend/services/planting-service/src/application/services/planting-application.service.ts index 041b56c4..f087dc3f 100644 --- a/backend/services/planting-service/src/application/services/planting-application.service.ts +++ b/backend/services/planting-service/src/application/services/planting-application.service.ts @@ -513,13 +513,15 @@ export class PlantingApplicationService { /** * 获取全局认种统计数据 * 直接从订单表聚合查询,确保数据可靠性 + * [2026-01-06] 更新:添加本月统计 */ async getGlobalStats(): Promise { this.logger.log('Getting global planting stats from database'); - const [globalStats, todayStats, statusDistribution] = await Promise.all([ + const [globalStats, todayStats, monthStats, statusDistribution] = await Promise.all([ this.orderRepository.getGlobalStats(), this.orderRepository.getTodayStats(), + this.orderRepository.getMonthStats(), this.orderRepository.getStatusDistribution(), ]); @@ -529,6 +531,7 @@ export class PlantingApplicationService { totalAmount: globalStats.totalAmount, statusDistribution, todayStats, + monthStats, calculatedAt: new Date().toISOString(), }; } @@ -556,6 +559,12 @@ export interface GlobalStatsResult { orderCount: number; amount: string; }; + /** 本月统计 [2026-01-06] 新增 */ + monthStats: { + treeCount: number; + orderCount: number; + amount: string; + }; /** 统计时间 */ calculatedAt: string; } diff --git a/backend/services/planting-service/src/domain/repositories/planting-order.repository.interface.ts b/backend/services/planting-service/src/domain/repositories/planting-order.repository.interface.ts index 873a6f7c..630ef2a9 100644 --- a/backend/services/planting-service/src/domain/repositories/planting-order.repository.interface.ts +++ b/backend/services/planting-service/src/domain/repositories/planting-order.repository.interface.ts @@ -35,6 +35,12 @@ export interface IPlantingOrderRepository { */ getTodayStats(): Promise; + /** + * 获取本月统计数据 + * [2026-01-06] 新增 + */ + getMonthStats(): Promise; + /** * 按状态获取订单分布统计 */ diff --git a/backend/services/planting-service/src/infrastructure/persistence/repositories/planting-order.repository.impl.ts b/backend/services/planting-service/src/infrastructure/persistence/repositories/planting-order.repository.impl.ts index 8f6e844e..8bc65684 100644 --- a/backend/services/planting-service/src/infrastructure/persistence/repositories/planting-order.repository.impl.ts +++ b/backend/services/planting-service/src/infrastructure/persistence/repositories/planting-order.repository.impl.ts @@ -305,6 +305,47 @@ export class PlantingOrderRepositoryImpl implements IPlantingOrderRepository { }; } + /** + * 获取本月统计数据 + * [2026-01-06] 新增 + */ + async getMonthStats(): Promise { + const now = new Date(); + const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); + const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 1); + + const paidStatuses = [ + PlantingOrderStatus.PAID, + PlantingOrderStatus.FUND_ALLOCATED, + PlantingOrderStatus.POOL_SCHEDULED, + PlantingOrderStatus.POOL_INJECTED, + PlantingOrderStatus.MINING_ENABLED, + ]; + + const result = await this.prisma.plantingOrder.aggregate({ + where: { + status: { in: paidStatuses }, + paidAt: { + gte: monthStart, + lt: monthEnd, + }, + }, + _sum: { + treeCount: true, + totalAmount: true, + }, + _count: { + id: true, + }, + }); + + return { + treeCount: result._sum.treeCount || 0, + orderCount: result._count.id || 0, + amount: result._sum.totalAmount?.toString() || '0', + }; + } + /** * 按状态获取订单分布统计 */ diff --git a/frontend/admin-web/src/app/(dashboard)/statistics/page.tsx b/frontend/admin-web/src/app/(dashboard)/statistics/page.tsx index 79c2b79e..2e448b2e 100644 --- a/frontend/admin-web/src/app/(dashboard)/statistics/page.tsx +++ b/frontend/admin-web/src/app/(dashboard)/statistics/page.tsx @@ -1,15 +1,18 @@ /** * 数据统计页面 * [2026-01-04] 更新:新增系统账户报表Tab + * [2026-01-06] 更新:认种统计改为真实数据 * 回滚方式:删除 SystemAccountsTab 导入及相关 mainTab state 和切换逻辑 */ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { PageContainer } from '@/components/layout'; import { cn } from '@/utils/helpers'; // [2026-01-04] 新增:系统账户报表Tab import { SystemAccountsTab } from '@/components/features/system-account-report'; +import { dashboardService } from '@/services/dashboardService'; +import type { PlantingGlobalStats } from '@/types'; import styles from './statistics.module.scss'; /** @@ -99,10 +102,27 @@ const metricsData = [ { label: '每月累计认种\n提成', value: '¥8,900' }, ]; +/** + * 格式化数字显示 + */ +function formatNumber(num: number): string { + return num.toLocaleString('zh-CN'); +} + +/** + * 格式化金额显示(积分) + */ +function formatAmount(amount: string): string { + const num = parseFloat(amount); + if (isNaN(num)) return '0'; + return num.toLocaleString('zh-CN', { maximumFractionDigits: 2 }); +} + /** * 数据统计页面 * 基于 UIPro Figma 设计实现 * [2026-01-04] 更新:新增系统账户报表Tab + * [2026-01-06] 更新:认种统计改为真实数据 */ export default function StatisticsPage() { // [2026-01-04] 新增:主Tab切换 - 数据统计 vs 系统账户 @@ -114,6 +134,33 @@ export default function StatisticsPage() { // 区域统计维度 const [regionType, setRegionType] = useState<'province' | 'city'>('province'); + // [2026-01-06] 新增:认种统计数据状态 + const [plantingStats, setPlantingStats] = useState(null); + const [statsLoading, setStatsLoading] = useState(true); + const [statsError, setStatsError] = useState(null); + + // [2026-01-06] 新增:获取认种统计数据 + useEffect(() => { + async function fetchPlantingStats() { + try { + setStatsLoading(true); + setStatsError(null); + const response = await dashboardService.getPlantingStats(); + if (response.code === 0 && response.data) { + setPlantingStats(response.data); + } else { + setStatsError(response.message || '获取统计数据失败'); + } + } catch (error) { + console.error('获取认种统计失败:', error); + setStatsError('获取统计数据失败'); + } finally { + setStatsLoading(false); + } + } + fetchPlantingStats(); + }, []); + return (
@@ -142,19 +189,58 @@ export default function StatisticsPage() { {/* 原有统计内容 - 仅在 statistics tab 显示 */} {mainTab === 'statistics' && ( <> - {/* 统计概览卡片 */} + {/* 统计概览卡片 - [2026-01-06] 改为真实数据 */}
榴莲树认种总量
-

12,345

+ {statsLoading ? ( +

加载中...

+ ) : statsError ? ( +

--

+ ) : ( + <> +

+ {formatNumber(plantingStats?.totalTreeCount ?? 0)} +

+
+ 积分: {formatAmount(plantingStats?.totalAmount ?? '0')} +
+ + )}
今日认种数量
-

123

+ {statsLoading ? ( +

加载中...

+ ) : statsError ? ( +

--

+ ) : ( + <> +

+ {formatNumber(plantingStats?.todayStats?.treeCount ?? 0)} +

+
+ 积分: {formatAmount(plantingStats?.todayStats?.amount ?? '0')} +
+ + )}
本月认种数量
-

1,456

+ {statsLoading ? ( +

加载中...

+ ) : statsError ? ( +

--

+ ) : ( + <> +

+ {formatNumber(plantingStats?.monthStats?.treeCount ?? 0)} +

+
+ 积分: {formatAmount(plantingStats?.monthStats?.amount ?? '0')} +
+ + )}
diff --git a/frontend/admin-web/src/app/(dashboard)/statistics/statistics.module.scss b/frontend/admin-web/src/app/(dashboard)/statistics/statistics.module.scss index c35ad198..0446a8ea 100644 --- a/frontend/admin-web/src/app/(dashboard)/statistics/statistics.module.scss +++ b/frontend/admin-web/src/app/(dashboard)/statistics/statistics.module.scss @@ -98,6 +98,15 @@ color: #0f172a; } +/* [2026-01-06] 新增:积分副标题样式 */ +.statistics__overviewSubValue { + align-self: stretch; + font-size: 14px; + line-height: 20px; + color: #9ca3af; + margin-top: 4px; +} + /* 通用卡片样式 */ .statistics__card { align-self: stretch; diff --git a/frontend/admin-web/src/types/dashboard.types.ts b/frontend/admin-web/src/types/dashboard.types.ts index 6f70bdbd..928cb28d 100644 --- a/frontend/admin-web/src/types/dashboard.types.ts +++ b/frontend/admin-web/src/types/dashboard.types.ts @@ -88,9 +88,31 @@ export interface DashboardActivitiesParams { limit?: number; } -/** 认种全局统计数据(来自 planting-service) */ +/** 认种周期统计数据 */ +export interface PlantingPeriodStats { + treeCount: number; + orderCount: number; + amount: string; +} + +/** 状态分布统计 */ +export interface PlantingStatusDistribution { + paid: number; + fundAllocated: number; + poolScheduled: number; + poolInjected: number; + miningEnabled: number; +} + +/** 认种全局统计数据(来自 planting-service) + * [2026-01-06] 更新:添加今日、本月统计和积分信息 + */ export interface PlantingGlobalStats { totalTreeCount: number; totalOrderCount: number; totalAmount: string; + statusDistribution: PlantingStatusDistribution; + todayStats: PlantingPeriodStats; + monthStats: PlantingPeriodStats; + calculatedAt: string; }