refactor(statistics): 删除认种统计页面不相关的mock功能

删除以下mock数据和功能:
- 榴莲树认种数量趋势图表
- 龙虎榜与排名统计
- 区域认种数据统计
- 省/市公司运营统计
- 收益明细与来源

保留核心认种统计:
- 榴莲树认种总量(含积分)
- 今日认种数量(含积分)
- 本月认种数量(含积分)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-06 10:00:50 -08:00
parent 36a83397a8
commit 24bcc45d5a
2 changed files with 105 additions and 375 deletions

View File

@ -0,0 +1,52 @@
-- ============================================
-- 系统账户转出订单表
-- 用于记录从系统账户(S开头/区域账户)转出到用户账户的订单
-- ============================================
CREATE TABLE IF NOT EXISTS "system_withdrawal_orders" (
"order_id" BIGSERIAL PRIMARY KEY,
"order_no" VARCHAR(50) NOT NULL UNIQUE,
-- 转出方(系统账户)
"from_account_sequence" VARCHAR(20) NOT NULL, -- 系统账户序列号 (S0000000001, 9440000, 8440100 等)
"from_account_name" VARCHAR(100) NOT NULL, -- 系统账户名称 (总部账户, 广东省区域 等)
-- 接收方(用户账户)
"to_account_sequence" VARCHAR(20) NOT NULL, -- 用户充值ID (D...)
"to_user_id" BIGINT NOT NULL, -- 用户ID
"to_user_name" VARCHAR(100), -- 用户姓名
"to_address" VARCHAR(100) NOT NULL, -- 用户区块链地址
-- 金额
"amount" DECIMAL(20, 8) NOT NULL, -- 转出金额
"chain_type" VARCHAR(20) NOT NULL DEFAULT 'KAVA', -- 链类型
-- 交易信息
"tx_hash" VARCHAR(100), -- 链上交易哈希
-- 状态: PENDING -> FROZEN -> BROADCASTED -> CONFIRMED / FAILED
"status" VARCHAR(20) NOT NULL DEFAULT 'PENDING',
"error_message" VARCHAR(500),
-- 操作者
"operator_id" VARCHAR(100) NOT NULL, -- 操作管理员ID
"operator_name" VARCHAR(100), -- 操作管理员姓名
-- 备注
"memo" TEXT,
-- 时间戳
"frozen_at" TIMESTAMP,
"broadcasted_at" TIMESTAMP,
"confirmed_at" TIMESTAMP,
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX "idx_system_withdrawal_from_account" ON "system_withdrawal_orders" ("from_account_sequence");
CREATE INDEX "idx_system_withdrawal_to_account" ON "system_withdrawal_orders" ("to_account_sequence");
CREATE INDEX "idx_system_withdrawal_to_user" ON "system_withdrawal_orders" ("to_user_id");
CREATE INDEX "idx_system_withdrawal_status" ON "system_withdrawal_orders" ("status");
CREATE INDEX "idx_system_withdrawal_tx_hash" ON "system_withdrawal_orders" ("tx_hash");
CREATE INDEX "idx_system_withdrawal_operator" ON "system_withdrawal_orders" ("operator_id");
CREATE INDEX "idx_system_withdrawal_created" ON "system_withdrawal_orders" ("created_at");

View File

@ -1,7 +1,7 @@
/**
*
* [2026-01-04] Tab
* [2026-01-06]
* [2026-01-06] mock功能
* SystemAccountsTab mainTab state
*/
'use client';
@ -15,93 +15,6 @@ import { dashboardService } from '@/services/dashboardService';
import type { PlantingGlobalStats } from '@/types';
import styles from './statistics.module.scss';
/**
*
*/
interface RankingItem {
rank: number;
account: string;
count: number;
province: string;
city: string;
}
/**
*
*/
interface RegionItem {
region: string;
period: string;
count: number;
ratio: string;
}
/**
*
*/
interface OperationItem {
type: string;
name: string;
month: string;
hashrate: string;
mining: string;
commission: string;
}
/**
*
*/
interface RevenueItem {
time: string;
account: string;
source: string;
amount: string;
address: string;
txId: string;
}
// 排名数据
const rankingData: RankingItem[] = [
{ rank: 1, account: 'user_001', count: 58, province: '广东', city: '深圳' },
{ rank: 2, account: 'user_002', count: 55, province: '广东', city: '广州' },
{ rank: 3, account: 'user_003', count: 49, province: '湖南', city: '长沙' },
{ rank: 4, account: 'user_004', count: 42, province: '浙江', city: '杭州' },
{ rank: 5, account: 'user_005', count: 38, province: '江苏', city: '南京' },
];
// 区域数据
const regionData: RegionItem[] = [
{ region: '广东省', period: '2023-10', count: 2345, ratio: '25.8%' },
{ region: '湖南省', period: '2023-10', count: 1890, ratio: '20.8%' },
{ region: '浙江省', period: '2023-10', count: 1560, ratio: '17.1%' },
{ region: '江苏省', period: '2023-10', count: 1230, ratio: '13.5%' },
];
// 运营数据
const operationData: OperationItem[] = [
{ type: '省公司', name: '广东省公司', month: '2023-10', hashrate: '150 TH/s', mining: '0.8 BTC', commission: '¥2,500' },
{ type: '市公司', name: '深圳市公司', month: '2023-10', hashrate: '80 TH/s', mining: '0.45 BTC', commission: '¥1,800' },
{ type: '市公司', name: '广州市公司', month: '2023-10', hashrate: '70 TH/s', mining: '0.35 BTC', commission: '¥1,500' },
];
// 收益数据
const revenueData: RevenueItem[] = [
{ time: '2023-10-26 10:30', account: 'user_001', source: '挖矿收益', amount: '+ ¥50.00', address: 'bc1...xyz', txId: 'TXN1234567890' },
{ time: '2023-10-26 09:15', account: 'user_002', source: '认种提成', amount: '+ ¥120.00', address: 'bc1...abc', txId: 'TXN0987654321' },
{ time: '2023-10-25 18:00', account: 'user_003', source: '分润', amount: '+ ¥35.50', address: 'bc1...def', txId: 'TXN5432109876' },
];
// 运营指标数据
const metricsData = [
{ label: '每月算力', value: '1,200\nTH/s' },
{ label: '累计算力', value: '15,000\nTH/s' },
{ label: '每月挖矿量', value: '5.6 BTC' },
{ label: '累计挖矿量', value: '67.8 BTC' },
{ label: '每月佣金', value: '¥12,345' },
{ label: '累计佣金', value: '¥150,000' },
{ label: '每月累计认种\n提成', value: '¥8,900' },
];
/**
*
*/
@ -120,19 +33,12 @@ function formatAmount(amount: string): string {
/**
*
* UIPro Figma
* [2026-01-04] Tab
* [2026-01-06]
*/
export default function StatisticsPage() {
// [2026-01-04] 新增主Tab切换 - 数据统计 vs 系统账户
const [mainTab, setMainTab] = useState<'statistics' | 'system-accounts'>('statistics');
// 趋势图时间维度
const [trendPeriod, setTrendPeriod] = useState<'day' | 'week' | 'month' | 'quarter' | 'year'>('day');
// 龙虎榜时间维度
const [rankingTab, setRankingTab] = useState<'daily' | 'weekly' | 'monthly'>('daily');
// 区域统计维度
const [regionType, setRegionType] = useState<'province' | 'city'>('province');
// [2026-01-06] 新增:认种统计数据状态
const [plantingStats, setPlantingStats] = useState<PlantingGlobalStats | null>(null);
@ -186,291 +92,63 @@ export default function StatisticsPage() {
{/* [2026-01-04] 新增系统账户报表Tab内容 */}
{mainTab === 'system-accounts' && <SystemAccountsTab />}
{/* 原有统计内容 - 仅在 statistics tab 显示 */}
{/* 认种统计内容 - 仅在 statistics tab 显示 */}
{mainTab === 'statistics' && (
<>
{/* 统计概览卡片 - [2026-01-06] 改为真实数据 */}
<section className={styles.statistics__overview}>
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.totalTreeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.totalAmount ?? '0')}
{/* 统计概览卡片 - [2026-01-06] 改为真实数据 */}
<section className={styles.statistics__overview}>
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.totalTreeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.totalAmount ?? '0')}
</div>
</>
)}
</div>
</>
)}
</div>
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.todayStats?.treeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.todayStats?.amount ?? '0')}
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.todayStats?.treeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.todayStats?.amount ?? '0')}
</div>
</>
)}
</div>
</>
)}
</div>
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.monthStats?.treeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.monthStats?.amount ?? '0')}
<div className={styles.statistics__overviewCard}>
<div className={styles.statistics__overviewLabel}></div>
{statsLoading ? (
<h1 className={styles.statistics__overviewValue}>...</h1>
) : statsError ? (
<h1 className={styles.statistics__overviewValue}>--</h1>
) : (
<>
<h1 className={styles.statistics__overviewValue}>
{formatNumber(plantingStats?.monthStats?.treeCount ?? 0)}
</h1>
<div className={styles.statistics__overviewSubValue}>
: {formatAmount(plantingStats?.monthStats?.amount ?? '0')}
</div>
</>
)}
</div>
</>
)}
</div>
</section>
{/* 榴莲树认种数量趋势 */}
<section className={styles.statistics__card}>
<div className={styles.statistics__cardHeader}>
<b className={styles.statistics__cardTitle}></b>
<div className={styles.statistics__periodTabs}>
{(['day', 'week', 'month', 'quarter', 'year'] as const).map((period) => (
<button
key={period}
className={cn(styles.statistics__periodTab, trendPeriod === period && styles['statistics__periodTab--active'])}
onClick={() => setTrendPeriod(period)}
>
{period === 'day' ? '日' : period === 'week' ? '周' : period === 'month' ? '月' : period === 'quarter' ? '季度' : '年度'}
</button>
))}
</div>
</div>
<div className={styles.statistics__chartArea}>Chart Placeholder</div>
</section>
{/* 龙虎榜与排名统计 */}
<section>
<h3 className={styles.statistics__sectionTitle}></h3>
<div className={styles.statistics__tabs}>
<button
className={cn(styles.statistics__tab, rankingTab === 'daily' && styles['statistics__tab--active'])}
onClick={() => setRankingTab('daily')}
>
</button>
<button
className={cn(styles.statistics__tab, rankingTab === 'weekly' && styles['statistics__tab--active'])}
onClick={() => setRankingTab('weekly')}
>
</button>
<button
className={cn(styles.statistics__tab, rankingTab === 'monthly' && styles['statistics__tab--active'])}
onClick={() => setRankingTab('monthly')}
>
</button>
</div>
<div className={styles.statistics__rankingSection}>
{/* 排名表格 */}
<div className={styles.statistics__rankingTable}>
<div className={styles.statistics__table}>
<div className={styles.statistics__tableHeader}>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__tableCell--rank'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__tableCell--account'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__tableCell--count'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__tableCell--province'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__tableCell--city'])}></div>
</div>
{rankingData.map((item) => (
<div key={item.rank} className={styles.statistics__tableRow}>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--rank'])}>{item.rank}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--account'])}>{item.account}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--count'])}>{item.count}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--province'])}>{item.province}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--city'])}>{item.city}</div>
</div>
))}
</div>
</div>
{/* 授权公司第一名 */}
<div className={styles.statistics__highlightCards}>
<div className={styles.statistics__highlightCard}>
<b className={styles.statistics__highlightTitle}></b>
<h3 className={styles.statistics__highlightName}>广</h3>
<div className={styles.statistics__highlightData}>完成数据: 2,345</div>
</div>
<div className={styles.statistics__highlightCard}>
<b className={styles.statistics__highlightTitle}></b>
<h3 className={styles.statistics__highlightName}></h3>
<div className={styles.statistics__highlightData}>完成数据: 1,120</div>
</div>
</div>
</div>
</section>
{/* 区域认种数据统计 */}
<section className={styles.statistics__regionCard}>
<div className={styles.statistics__regionHeader}>
<h3 className={styles.statistics__regionTitle}></h3>
<div className={styles.statistics__regionTabs}>
<button
className={cn(styles.statistics__regionTab, regionType === 'province' && styles['statistics__regionTab--active'])}
onClick={() => setRegionType('province')}
>
</button>
<button
className={cn(styles.statistics__regionTab, regionType === 'city' && styles['statistics__regionTab--active'])}
onClick={() => setRegionType('city')}
>
</button>
</div>
</div>
<div className={styles.statistics__regionContent}>
<div className={styles.statistics__regionChart}>
<b className={styles.statistics__regionChartTitle}></b>
<div className={styles.statistics__regionChartArea}>Bar Chart Placeholder</div>
</div>
<div className={styles.statistics__regionTableWrapper}>
<div className={styles.statistics__table} style={{ width: '100%' }}>
<div className={styles.statistics__tableHeader} style={{ minWidth: '341px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__regionCell--region'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__regionCell--period'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__regionCell--count'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__regionCell--ratio'])}></div>
</div>
{regionData.map((item, index) => (
<div key={item.region} className={cn(styles.statistics__tableRow, index === regionData.length - 1 && styles['statistics__tableRow--bordered'])} style={{ minWidth: '341px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__regionCell--region'])}>{item.region}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__regionCell--period'])}>{item.period}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__regionCell--count'])}>{item.count.toLocaleString()}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__regionCell--ratio'])}>{item.ratio}</div>
</div>
))}
</div>
</div>
</div>
<div className={styles.statistics__viewDetail}>
<button className={styles.statistics__viewDetailLink}></button>
</div>
</section>
{/* 省/市公司运营统计 */}
<section className={styles.statistics__operationCard}>
<div className={styles.statistics__operationHeader}>
<h3 className={styles.statistics__operationTitle}> / </h3>
<button className={styles.statistics__exportBtn}>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
<span></span>
</button>
</div>
<div className={styles.statistics__metricsGrid}>
{metricsData.map((metric) => (
<div key={metric.label} className={styles.statistics__metricCard}>
<div className={styles.statistics__metricLabel} style={{ whiteSpace: 'pre-wrap' }}>{metric.label}</div>
<b className={styles.statistics__metricValue} style={{ whiteSpace: 'pre-wrap' }}>{metric.value}</b>
</div>
))}
</div>
<div className={styles.statistics__table} style={{ minWidth: '736px' }}>
<div className={styles.statistics__tableHeader} style={{ minWidth: '736px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--type'])}> / </div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--name'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--month'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--hashrate'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--mining'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__operationCell--commission'])}></div>
</div>
{operationData.map((item, index) => (
<div key={`${item.name}-${index}`} className={styles.statistics__tableRow} style={{ minWidth: '736px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--type'])}>{item.type}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--name'])}>{item.name}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--month'])}>{item.month}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--hashrate'])}>{item.hashrate}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--mining'])}>{item.mining}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__operationCell--commission'])}>{item.commission}</div>
</div>
))}
</div>
</section>
{/* 收益明细与来源 */}
<section className={styles.statistics__revenueCard}>
<h3 className={styles.statistics__revenueTitle}></h3>
<div className={styles.statistics__filters}>
<div className={styles.statistics__filterGroup}>
<label className={styles.statistics__filterLabel}></label>
<select className={styles.statistics__filterSelect} aria-label="收益来源">
<option value=""></option>
<option value="mining"></option>
<option value="adoption"></option>
<option value="profit"></option>
</select>
</div>
<div className={styles.statistics__filterGroup}>
<label className={styles.statistics__filterLabel}> / </label>
<div className={styles.statistics__filterInputWrapper}>
<div className={styles.statistics__filterIcon}>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.35-4.35" />
</svg>
</div>
<input
className={styles.statistics__filterInput}
type="text"
placeholder="按地址 / 交易流水号筛选"
aria-label="按地址 / 交易流水号筛选"
/>
</div>
</div>
</div>
<div className={styles.statistics__table} style={{ minWidth: '762px' }}>
<div className={styles.statistics__tableHeader} style={{ minWidth: '762px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--time'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--account'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--source'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--amount'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--address'])}></div>
<div className={cn(styles.statistics__tableCell, styles['statistics__tableCell--header'], styles['statistics__revenueCell--txId'])}></div>
</div>
{revenueData.map((item, index) => (
<div key={item.txId} className={cn(styles.statistics__tableRow, index === revenueData.length - 1 && styles['statistics__tableRow--bordered'])} style={{ minWidth: '762px' }}>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--time'])}>{item.time}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--account'])}>{item.account}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--source'])}>{item.source}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--amount'])}>{item.amount}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--address'])}>{item.address}</div>
<div className={cn(styles.statistics__tableCell, styles['statistics__revenueCell--txId'])}>{item.txId}</div>
</div>
))}
</div>
<div className={styles.statistics__timelineLink}>
<button className={styles.statistics__viewDetailLink}></button>
</div>
</section>
</section>
</>
)}
</div>