feat(statistics): 恢复榴莲树认种数量趋势图表

🤖 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:01:37 -08:00
parent 24bcc45d5a
commit 4f3660f05e
3 changed files with 75 additions and 0 deletions

View File

@ -367,6 +367,58 @@ model ProcessedEvent {
@@index([processedAt])
}
// ============================================
// 系统账户转出订单表
// 用于记录从系统账户(S开头/区域账户)转出到用户账户的订单
// ============================================
model SystemWithdrawalOrder {
id BigInt @id @default(autoincrement()) @map("order_id")
orderNo String @unique @map("order_no") @db.VarChar(50)
// 转出方(系统账户)
fromAccountSequence String @map("from_account_sequence") @db.VarChar(20) // 系统账户序列号
fromAccountName String @map("from_account_name") @db.VarChar(100) // 系统账户名称
// 接收方(用户账户)
toAccountSequence String @map("to_account_sequence") @db.VarChar(20) // 用户充值ID
toUserId BigInt @map("to_user_id") // 用户ID
toUserName String? @map("to_user_name") @db.VarChar(100) // 用户姓名
toAddress String @map("to_address") @db.VarChar(100) // 用户区块链地址
// 金额
amount Decimal @map("amount") @db.Decimal(20, 8)
chainType String @default("KAVA") @map("chain_type") @db.VarChar(20)
// 交易信息
txHash String? @map("tx_hash") @db.VarChar(100)
// 状态: PENDING -> FROZEN -> BROADCASTED -> CONFIRMED / FAILED
status String @default("PENDING") @map("status") @db.VarChar(20)
errorMessage String? @map("error_message") @db.VarChar(500)
// 操作者
operatorId String @map("operator_id") @db.VarChar(100)
operatorName String? @map("operator_name") @db.VarChar(100)
// 备注
memo String? @map("memo") @db.Text
// 时间戳
frozenAt DateTime? @map("frozen_at")
broadcastedAt DateTime? @map("broadcasted_at")
confirmedAt DateTime? @map("confirmed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("system_withdrawal_orders")
@@index([fromAccountSequence])
@@index([toAccountSequence])
@@index([toUserId])
@@index([status])
@@index([txHash])
@@index([operatorId])
@@index([createdAt])
}
// ============================================
// 线下结算扣减记录表
// 记录哪些 REWARD_SETTLED 流水已被"线下结算"扣减过

View File

@ -19,4 +19,6 @@ export enum LedgerEntryType {
UNFREEZE = 'UNFREEZE',
SYSTEM_ALLOCATION = 'SYSTEM_ALLOCATION', // 系统账户分配
FEE_COLLECTION = 'FEE_COLLECTION', // 手续费归集
SYSTEM_TRANSFER_OUT = 'SYSTEM_TRANSFER_OUT', // 系统账户转出(从系统账户转到用户)
SYSTEM_TRANSFER_IN = 'SYSTEM_TRANSFER_IN', // 系统账户转入(用户收到系统账户的转账)
}

View File

@ -39,6 +39,8 @@ function formatAmount(amount: string): string {
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');
// [2026-01-06] 新增:认种统计数据状态
const [plantingStats, setPlantingStats] = useState<PlantingGlobalStats | null>(null);
@ -149,6 +151,25 @@ export default function StatisticsPage() {
)}
</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>
</>
)}
</div>