init: 初始化 AssetX 项目仓库

包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、
antdesign(管理后台)、landingpage(营销落地页)、
数据库 SQL 和配置文件。
This commit is contained in:
2026-03-27 11:26:43 +00:00
commit 2ee4553b71
634 changed files with 988255 additions and 0 deletions

205
webapp-back/models/asset.go Normal file
View File

@@ -0,0 +1,205 @@
package models
import (
"time"
)
// Asset represents the assets table
type Asset struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetCode string `gorm:"size:20;not null;unique;index" json:"asset_code"`
Name string `gorm:"size:255;not null" json:"name"`
Subtitle string `gorm:"type:text" json:"subtitle"`
Description string `gorm:"type:text" json:"description"`
TokenSymbol string `gorm:"size:20" json:"token_symbol"`
Decimals int `gorm:"default:18" json:"decimals"`
ChainID int `gorm:"default:97" json:"chain_id"`
ContractAddress string `gorm:"size:42" json:"contract_address"`
DeployBlock *uint64 `json:"deploy_block"`
Category string `gorm:"size:100" json:"category"`
CategoryColor string `gorm:"size:50" json:"category_color"`
IconURL string `gorm:"type:text" json:"icon_url"`
UnderlyingAssets string `gorm:"size:255" json:"underlying_assets"`
TargetAPY float64 `gorm:"type:decimal(10,2)" json:"target_apy"`
PoolCapUSD float64 `gorm:"type:decimal(30,2)" json:"pool_cap_usd"`
RiskLevel int `json:"risk_level"`
RiskLabel string `gorm:"size:50" json:"risk_label"`
TokenRole string `gorm:"size:20;default:'product'" json:"token_role"`
IsActive bool `gorm:"default:true" json:"is_active"`
IsFeatured bool `gorm:"default:false" json:"is_featured"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Asset) TableName() string {
return "assets"
}
// AssetPerformance represents the asset_performance table
type AssetPerformance struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index" json:"asset_id"`
CurrentAPY float64 `gorm:"type:decimal(10,2)" json:"current_apy"`
TVLUSD float64 `gorm:"type:decimal(30,2)" json:"tvl_usd"`
TotalInvestedUSD float64 `gorm:"type:decimal(30,2)" json:"total_invested_usd"`
InvestorCount int `gorm:"default:0" json:"investor_count"`
CumulativeYieldUSD float64 `gorm:"type:decimal(30,2)" json:"cumulative_yield_usd"`
Yield24hUSD float64 `gorm:"type:decimal(30,2)" json:"yield_24h_usd"`
PoolCapacityPercent float64 `gorm:"type:decimal(10,4)" json:"pool_capacity_percent"`
CirculatingSupply float64 `gorm:"type:decimal(30,18)" json:"circulating_supply"`
YTPrice float64 `gorm:"column:yt_price;type:decimal(30,18);default:0" json:"yt_price"`
Volume24hUSD float64 `gorm:"column:volume_24h_usd;type:decimal(30,2);default:0" json:"volume_24h_usd"`
SnapshotDate time.Time `gorm:"not null" json:"snapshot_date"`
CreatedAt time.Time `json:"created_at"`
}
func (AssetPerformance) TableName() string {
return "asset_performance"
}
// ProductResponse is the response format for fund market products
type ProductResponse struct {
ID int `json:"id"`
Name string `json:"name"`
TokenSymbol string `json:"tokenSymbol"`
Decimals int `json:"decimals"`
ContractAddress string `json:"contractAddress"`
ChainID int `json:"chainId"`
TokenRole string `json:"token_role"`
Category string `json:"category"`
CategoryColor string `json:"categoryColor"`
IconURL string `json:"iconUrl"`
YieldAPY string `json:"yieldAPY"`
PoolCap string `json:"poolCap"`
Risk string `json:"risk"`
RiskLevel int `json:"riskLevel"`
CirculatingSupply string `json:"circulatingSupply"`
PoolCapacityPercent float64 `json:"poolCapacityPercent"`
}
// StatsResponse is the response format for fund market stats
type StatsResponse struct {
Label string `json:"label"`
Value string `json:"value"`
Change string `json:"change"`
IsPositive bool `json:"isPositive"`
}
// AssetCustody represents the asset_custody table
type AssetCustody struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index" json:"asset_id"`
CustodianName string `gorm:"size:255" json:"custodian_name"`
CustodianAddress string `gorm:"type:text" json:"custodian_address"`
CustodianLicense string `gorm:"size:255" json:"custodian_license"`
CustodyType string `gorm:"size:100" json:"custody_type"`
CustodyLocation string `gorm:"size:255" json:"custody_location"`
AuditorName string `gorm:"size:255" json:"auditor_name"`
LastAuditDate NullTime `json:"last_audit_date"`
AuditReportURL string `gorm:"type:text" json:"audit_report_url"`
AdditionalInfo string `gorm:"type:json" json:"additional_info"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (AssetCustody) TableName() string {
return "asset_custody"
}
// AssetAuditReport represents the asset_audit_reports table
type AssetAuditReport struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index" json:"asset_id"`
ReportType string `gorm:"size:50;not null" json:"report_type"`
ReportTitle string `gorm:"size:255;not null" json:"report_title"`
ReportDate time.Time `gorm:"not null" json:"report_date"`
ReportURL string `gorm:"type:text" json:"report_url"`
AuditorName string `gorm:"size:255" json:"auditor_name"`
Summary string `gorm:"type:text" json:"summary"`
IsActive bool `gorm:"default:true" json:"is_active"`
DisplayOrder int `gorm:"default:0" json:"display_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (AssetAuditReport) TableName() string {
return "asset_audit_reports"
}
// ProductDetailResponse is the detailed response for a single product
type ProductDetailResponse struct {
// Basic Info
ID int `json:"id"`
AssetCode string `json:"assetCode"`
Name string `json:"name"`
Subtitle string `json:"subtitle"`
Description string `json:"description"`
TokenSymbol string `json:"tokenSymbol"`
Decimals int `json:"decimals"`
// Investment Parameters
UnderlyingAssets string `json:"underlyingAssets"`
PoolCapUSD float64 `json:"poolCapUsd"`
RiskLevel int `json:"riskLevel"`
RiskLabel string `json:"riskLabel"`
TargetAPY float64 `json:"targetApy"`
// Contract Info
ContractAddress string `json:"contractAddress"`
ChainID int `json:"chainId"`
// Display Info
Category string `json:"category"`
CategoryColor string `json:"categoryColor"`
IconURL string `json:"iconUrl"`
// Performance Data (from blockchain/database)
CurrentAPY float64 `json:"currentApy"`
TVLUSD float64 `json:"tvlUsd"`
Volume24hUSD float64 `json:"volume24hUsd"`
VolumeChangeVsAvg float64 `json:"volumeChangeVsAvg"`
CirculatingSupply float64 `json:"circulatingSupply"`
PoolCapacityPercent float64 `json:"poolCapacityPercent"`
CurrentPrice float64 `json:"currentPrice"` // e.g., 1.04 USDC per token
// Custody Info
Custody *CustodyInfo `json:"custody,omitempty"`
// Audit Reports
AuditReports []AuditReportInfo `json:"auditReports"`
// Product Links
ProductLinks []ProductLinkInfo `json:"productLinks"`
}
// ProductLinkInfo represents a single product link item
type ProductLinkInfo struct {
LinkText string `json:"linkText"`
LinkURL string `json:"linkUrl"`
Description string `json:"description"`
DisplayArea string `json:"displayArea"`
DisplayOrder int `json:"displayOrder"`
}
// CustodyInfo represents custody information
type CustodyInfo struct {
CustodianName string `json:"custodianName"`
CustodyType string `json:"custodyType"`
CustodyLocation string `json:"custodyLocation"`
AuditorName string `json:"auditorName"`
LastAuditDate string `json:"lastAuditDate"`
AuditReportURL string `json:"auditReportUrl,omitempty"`
AdditionalInfo map[string]interface{} `json:"additionalInfo,omitempty"`
}
// AuditReportInfo represents audit report information
type AuditReportInfo struct {
ReportType string `json:"reportType"`
ReportTitle string `json:"reportTitle"`
ReportDate string `json:"reportDate"`
AuditorName string `json:"auditorName"`
Summary string `json:"summary"`
ReportURL string `json:"reportUrl"`
}

View File

@@ -0,0 +1,22 @@
package models
import "time"
// CollateralBuyRecord records each collateral purchase made by the buyer bot
type CollateralBuyRecord struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
ChainID int `gorm:"index" json:"chain_id"`
TxHash string `gorm:"size:66;uniqueIndex" json:"tx_hash"`
BuyerAddr string `gorm:"size:42" json:"buyer_addr"`
AssetAddr string `gorm:"size:42;index" json:"asset_addr"`
AssetSymbol string `gorm:"size:50" json:"asset_symbol"`
PaidAmount string `gorm:"size:78" json:"paid_amount"` // USDC paid, base units
ReceivedAmount string `gorm:"size:78" json:"received_amount"` // collateral received, base units
GasUsed uint64 `json:"gas_used"`
BlockNumber uint64 `json:"block_number"`
Status string `gorm:"size:20;default:'success'" json:"status"` // success / failed
ErrorMessage string `gorm:"type:text" json:"error_message,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
func (CollateralBuyRecord) TableName() string { return "collateral_buy_records" }

View File

@@ -0,0 +1,217 @@
package models
import (
"time"
)
// ALPSnapshot records periodic ALP pool stats for APR calculation
type ALPSnapshot struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
PoolValue float64 `gorm:"type:decimal(30,18)" json:"pool_value"` // getAumInUsdy(true), 18 dec
UsdySupply float64 `gorm:"type:decimal(30,18)" json:"usdy_supply"` // USDY.totalSupply(), 18 dec
FeeSurplus float64 `gorm:"type:decimal(30,18)" json:"fee_surplus"` // poolValue - usdySupply
ALPPrice float64 `gorm:"type:decimal(30,18)" json:"alp_price"` // getPrice(false), 30 dec
SnapshotTime time.Time `gorm:"not null;index" json:"snapshot_time"`
CreatedAt time.Time `json:"created_at"`
}
func (ALPSnapshot) TableName() string {
return "alp_snapshots"
}
// APYSnapshot represents the apy_snapshots table
type APYSnapshot struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index:idx_asset_time" json:"asset_id"`
ChainID int `gorm:"not null" json:"chain_id"`
ContractAddress string `gorm:"size:42" json:"contract_address"`
APYValue float64 `gorm:"type:decimal(10,4)" json:"apy_value"`
SupplyAPY float64 `gorm:"type:decimal(20,4)" json:"supply_apy"`
BorrowAPY float64 `gorm:"type:decimal(20,4)" json:"borrow_apy"`
TotalAssets float64 `gorm:"type:decimal(30,18)" json:"total_assets"`
TotalSupply float64 `gorm:"type:decimal(30,18)" json:"total_supply"`
Price float64 `gorm:"type:decimal(30,18)" json:"price"`
SnapshotTime time.Time `gorm:"not null;index:idx_asset_time" json:"snapshot_time"`
CreatedAt time.Time `json:"created_at"`
}
func (APYSnapshot) TableName() string {
return "apy_snapshots"
}
// ProductLink represents the product_links table — per-asset links shown on the product detail page
type ProductLink struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index" json:"asset_id"`
LinkText string `gorm:"size:100;not null" json:"link_text"`
LinkURL string `gorm:"type:text;not null" json:"link_url"`
Description string `gorm:"size:500" json:"description"`
DisplayArea string `gorm:"size:20;default:protocol" json:"display_area"`
DisplayOrder int `gorm:"default:0" json:"display_order"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ProductLink) TableName() string {
return "product_links"
}
// KLineData represents the kline_data table
type KLineData struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
AssetID uint `gorm:"not null;index:idx_asset_time" json:"asset_id"`
ChainID int `gorm:"not null" json:"chain_id"`
ContractAddress string `gorm:"size:42" json:"contract_address"`
Timeframe string `gorm:"size:10;not null" json:"timeframe"`
OpenTime time.Time `gorm:"not null;index:idx_asset_time" json:"open_time"`
CloseTime time.Time `gorm:"not null" json:"close_time"`
OpenPrice float64 `gorm:"type:decimal(30,18);not null" json:"open_price"`
HighPrice float64 `gorm:"type:decimal(30,18);not null" json:"high_price"`
LowPrice float64 `gorm:"type:decimal(30,18);not null" json:"low_price"`
ClosePrice float64 `gorm:"type:decimal(30,18);not null" json:"close_price"`
Volume float64 `gorm:"type:decimal(30,18)" json:"volume"`
Trades int `json:"trades"`
CreatedAt time.Time `json:"created_at"`
}
func (KLineData) TableName() string {
return "kline_data"
}
// PointsRule represents the points_rules table
type PointsRule struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
RuleName string `gorm:"size:100;not null;unique" json:"rule_name"`
RuleType string `gorm:"size:50;not null" json:"rule_type"`
BasePoints int `gorm:"not null" json:"base_points"`
Multiplier float64 `gorm:"type:decimal(10,4);default:1.0000" json:"multiplier"`
Conditions string `gorm:"type:json" json:"conditions"`
Description string `gorm:"type:text" json:"description"`
ValidFrom NullTime `json:"valid_from"`
ValidUntil NullTime `json:"valid_until"`
IsActive bool `gorm:"default:true" json:"is_active"`
Priority int `gorm:"default:0" json:"priority"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PointsRule) TableName() string {
return "points_rules"
}
// Role represents the roles table
type Role struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
RoleName string `gorm:"size:50;not null;unique" json:"role_name"`
Description string `gorm:"type:text" json:"description"`
Permissions string `gorm:"type:json" json:"permissions"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Role) TableName() string {
return "roles"
}
// UserRole represents the user_roles table
type UserRole struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
UserID uint `gorm:"not null;uniqueIndex:uk_user_role" json:"user_id"`
RoleID uint `gorm:"not null;uniqueIndex:uk_user_role" json:"role_id"`
AssignedAt time.Time `gorm:"not null" json:"assigned_at"`
AssignedBy uint `json:"assigned_by"`
CreatedAt time.Time `json:"created_at"`
}
func (UserRole) TableName() string {
return "user_roles"
}
// Session represents the sessions table
type Session struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
SessionToken string `gorm:"size:255;not null;unique" json:"session_token"`
WalletAddress string `gorm:"size:42;not null" json:"wallet_address"`
SignMessage string `gorm:"type:text" json:"sign_message"`
Signature string `gorm:"size:132" json:"signature"`
SignatureHash string `gorm:"size:66" json:"signature_hash"`
IPAddress string `gorm:"size:45" json:"ip_address"`
UserAgent string `gorm:"type:text" json:"user_agent"`
ExpiresAt *time.Time `gorm:"index" json:"expires_at"`
LastActivityAt *time.Time `json:"last_activity_at"`
CreatedAt time.Time `json:"created_at"`
}
func (Session) TableName() string {
return "sessions"
}
// Transaction represents the transactions table
type Transaction struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
UserID uint `gorm:"not null;index:idx_user_time" json:"user_id"`
TxHash string `gorm:"size:66;not null;unique" json:"tx_hash"`
ChainID int `gorm:"not null" json:"chain_id"`
BlockNumber int64 `gorm:"not null" json:"block_number"`
TxType string `gorm:"size:50;not null" json:"tx_type"`
AssetID *uint `json:"asset_id"`
FromAddress string `gorm:"size:42;not null" json:"from_address"`
ToAddress string `gorm:"size:42;not null" json:"to_address"`
Amount float64 `gorm:"type:decimal(30,18)" json:"amount"`
TokenSymbol string `gorm:"size:20" json:"token_symbol"`
GasUsed int64 `json:"gas_used"`
GasPrice float64 `gorm:"type:decimal(30,18)" json:"gas_price"`
Status string `gorm:"size:20;not null" json:"status"`
ErrorMessage string `gorm:"type:text" json:"error_message"`
Metadata string `gorm:"type:json" json:"metadata"`
ConfirmedAt time.Time `gorm:"not null;index:idx_user_time" json:"confirmed_at"`
CreatedAt time.Time `json:"created_at"`
}
func (Transaction) TableName() string {
return "transactions"
}
// OperationLog represents the operation_logs table
type OperationLog struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
UserID *uint `gorm:"index" json:"user_id"`
OperationType string `gorm:"size:50;not null" json:"operation_type"`
TargetType string `gorm:"size:50" json:"target_type"`
TargetID *uint `json:"target_id"`
Action string `gorm:"size:100;not null" json:"action"`
Changes string `gorm:"type:json" json:"changes"`
IPAddress string `gorm:"size:45" json:"ip_address"`
UserAgent string `gorm:"type:text" json:"user_agent"`
Status string `gorm:"size:20;not null" json:"status"`
ErrorMessage string `gorm:"type:text" json:"error_message"`
CreatedAt time.Time `gorm:"index" json:"created_at"`
}
func (OperationLog) TableName() string {
return "operation_logs"
}
// UserActivity represents the user_activities table
type UserActivity struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
UserID uint `gorm:"not null;index:idx_user_time" json:"user_id"`
ActivityType string `gorm:"size:50;not null" json:"activity_type"`
ActivityData string `gorm:"type:json" json:"activity_data"`
AssetID *uint `json:"asset_id"`
Amount float64 `gorm:"type:decimal(30,18)" json:"amount"`
PointsEarned int `gorm:"default:0" json:"points_earned"`
ReferenceType string `gorm:"size:50" json:"reference_type"`
ReferenceID *uint `json:"reference_id"`
IPAddress string `gorm:"size:45" json:"ip_address"`
CreatedAt time.Time `gorm:"index:idx_user_time" json:"created_at"`
}
func (UserActivity) TableName() string {
return "user_activities"
}

View File

@@ -0,0 +1,43 @@
package models
import (
"time"
)
// HolderSnapshot represents token holders data from blockchain
type HolderSnapshot struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
HolderAddress string `gorm:"size:42;not null;index:idx_holder_token" json:"holder_address"`
TokenType string `gorm:"size:50;not null;index:idx_holder_token,idx_token_time" json:"token_type"`
TokenAddress string `gorm:"size:42;not null" json:"token_address"`
Balance string `gorm:"type:varchar(78);not null" json:"balance"`
ChainID int `gorm:"not null" json:"chain_id"`
FirstSeen int64 `gorm:"not null" json:"first_seen"`
LastUpdated int64 `gorm:"not null;index:idx_token_time" json:"last_updated"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (HolderSnapshot) TableName() string {
return "holder_snapshots"
}
// HolderStats represents aggregated statistics for each token type
type HolderStats struct {
TokenType string `json:"token_type"`
HolderCount int `json:"holder_count"`
TotalBalance string `json:"total_balance,omitempty"`
}
// ScannerState persists scanner progress so it can resume after restart
type ScannerState struct {
ID uint `gorm:"primaryKey;autoIncrement"`
ScannerType string `gorm:"column:scanner_type;not null;default:'holder';uniqueIndex:idx_chain_scanner"`
ChainID int `gorm:"column:chain_id;uniqueIndex:idx_chain_scanner;not null"`
LastScannedBlock uint64 `gorm:"column:last_scanned_block;not null;default:0"`
UpdatedAt time.Time `gorm:"column:updated_at"`
}
func (ScannerState) TableName() string {
return "scanner_state"
}

View File

@@ -0,0 +1,17 @@
package models
import "time"
// KnownBorrower stores every address that has ever interacted with the lending contract.
// The liquidation bot checks ALL known borrowers for isLiquidatable on every tick,
// so accounts whose health factor drops due to oracle price changes are still caught
// even if they haven't had a recent on-chain transaction.
type KnownBorrower struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
ChainID int `gorm:"uniqueIndex:idx_known_borrower" json:"chain_id"`
Address string `gorm:"size:42;uniqueIndex:idx_known_borrower" json:"address"`
FirstSeenAt time.Time `json:"first_seen_at"`
LastSeenAt time.Time `json:"last_seen_at"`
}
func (KnownBorrower) TableName() string { return "known_borrowers" }

View File

@@ -0,0 +1,22 @@
package models
import "time"
// LiquidationRecord stores each batch liquidation execution
type LiquidationRecord struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
ChainID int `gorm:"index" json:"chain_id"`
TxHash string `gorm:"size:66;uniqueIndex" json:"tx_hash"`
LiquidatorAddr string `gorm:"size:42" json:"liquidator_addr"`
AccountCount int `json:"account_count"`
Accounts string `gorm:"type:text" json:"accounts"` // JSON array of addresses
GasUsed uint64 `json:"gas_used"`
BlockNumber uint64 `json:"block_number"`
Status string `gorm:"size:20;default:'success'" json:"status"` // success / failed
ErrorMessage string `gorm:"type:text" json:"error_message,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
func (LiquidationRecord) TableName() string {
return "liquidation_records"
}

View File

@@ -0,0 +1,67 @@
package models
import (
"database/sql/driver"
"encoding/json"
"strings"
"time"
)
// NullTime wraps *time.Time with custom JSON and GORM handling:
// - JSON marshal: nil → "1970-01-01T00:00:00Z", non-nil → RFC3339
// - JSON unmarshal: null / "" / "1970-01-01..." → nil (stored as NULL in DB)
// - GORM Scan/Value: nil ↔ NULL column
type NullTime struct {
Time *time.Time
}
func (nt NullTime) MarshalJSON() ([]byte, error) {
if nt.Time == nil {
return []byte(`"1970-01-01T00:00:00Z"`), nil
}
return json.Marshal(nt.Time.Format(time.RFC3339))
}
func (nt *NullTime) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
nt.Time = nil
return nil
}
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
if s == "" || strings.HasPrefix(s, "1970-01-01") {
nt.Time = nil
return nil
}
t, err := time.Parse(time.RFC3339, s)
if err != nil {
return err
}
nt.Time = &t
return nil
}
// Value implements driver.Valuer so GORM writes NULL for nil.
func (nt NullTime) Value() (driver.Value, error) {
if nt.Time == nil {
return nil, nil
}
return *nt.Time, nil
}
// Scan implements sql.Scanner so GORM reads NULL as nil.
func (nt *NullTime) Scan(value interface{}) error {
if value == nil {
nt.Time = nil
return nil
}
t, ok := value.(time.Time)
if !ok {
nt.Time = nil
return nil
}
nt.Time = &t
return nil
}

View File

@@ -0,0 +1,262 @@
package models
import (
"time"
)
// Season represents the seasons table
type Season struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
SeasonNumber int `gorm:"not null;unique;index" json:"season_number"`
SeasonName string `gorm:"size:100;not null" json:"season_name"`
StartTime time.Time `gorm:"not null" json:"start_time"`
EndTime time.Time `gorm:"not null" json:"end_time"`
Status string `gorm:"size:20;default:'upcoming'" json:"status"` // upcoming, active, ended
TotalRewards float64 `gorm:"type:decimal(30,2)" json:"total_rewards"`
Multiplier float64 `gorm:"type:decimal(10,4);default:1.0000" json:"multiplier"`
Description string `gorm:"type:text" json:"description"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Season) TableName() string {
return "seasons"
}
// VIPTier represents the vip_tiers table
type VIPTier struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
TierName string `gorm:"size:50;not null" json:"tier_name"`
TierLevel int `gorm:"not null;unique;index" json:"tier_level"`
MinPoints int64 `gorm:"not null;index" json:"min_points"`
MaxPoints *int64 `json:"max_points"`
Multiplier float64 `gorm:"type:decimal(10,4);default:1.0000" json:"multiplier"`
Perks string `gorm:"type:json" json:"perks"`
Icon string `gorm:"size:255" json:"icon"`
Color string `gorm:"size:50" json:"color"`
DisplayOrder int `gorm:"default:0" json:"display_order"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (VIPTier) TableName() string {
return "vip_tiers"
}
// UserTeam represents the user_teams table
type UserTeam struct {
WalletAddress string `gorm:"primaryKey;size:42" json:"wallet_address"`
TeamTVLUSD float64 `gorm:"type:decimal(30,2);default:0.00" json:"team_tvl_usd"`
TeamTargetTVLUSD float64 `gorm:"type:decimal(30,2);default:10000000.00" json:"team_target_tvl_usd"`
TeamMembersCount int `gorm:"default:0" json:"team_members_count"`
WhalesCount int `gorm:"default:0" json:"whales_count"`
WhalesTarget int `gorm:"default:3" json:"whales_target"`
TradersCount int `gorm:"default:0" json:"traders_count"`
TradersTarget int `gorm:"default:3" json:"traders_target"`
UsersCount int `gorm:"default:0" json:"users_count"`
UsersTarget int `gorm:"default:3" json:"users_target"`
LastCalculatedAt *time.Time `json:"last_calculated_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (UserTeam) TableName() string {
return "user_teams"
}
// UserPointsSummary represents the user_points_summary table
type UserPointsSummary struct {
WalletAddress string `gorm:"primaryKey;size:42" json:"wallet_address"`
TotalPoints int64 `gorm:"default:0" json:"total_points"`
HoldingPoints int64 `gorm:"default:0" json:"holding_points"`
LpPoints int64 `gorm:"default:0" json:"lp_points"`
LendingPoints int64 `gorm:"default:0" json:"lending_points"`
TradingPoints int64 `gorm:"default:0" json:"trading_points"`
InvitationPoints int64 `gorm:"default:0" json:"invitation_points"`
BonusPoints int64 `gorm:"default:0" json:"bonus_points"`
GlobalRank *int `json:"global_rank"`
SeasonRank *int `json:"season_rank"`
CurrentSeason int `gorm:"default:1" json:"current_season"`
TotalTrades int `gorm:"default:0" json:"total_trades"`
TotalHoldingDays int `gorm:"default:0" json:"total_holding_days"`
TotalInvites int `gorm:"default:0" json:"total_invites"`
LastCalculatedAt *time.Time `json:"last_calculated_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (UserPointsSummary) TableName() string {
return "user_points_summary"
}
// UserPointsRecord represents the user_points_records table
type UserPointsRecord struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
WalletAddress string `gorm:"size:42;not null;index" json:"wallet_address"`
PointsChange int `gorm:"not null" json:"points_change"`
PointsBefore int `gorm:"not null" json:"points_before"`
PointsAfter int `gorm:"not null" json:"points_after"`
SourceType string `gorm:"size:50;not null" json:"source_type"`
MultiplierApplied float64 `gorm:"type:decimal(10,4);default:1.0000" json:"multiplier_applied"`
SourceID *uint `json:"source_id"`
RuleID *uint `json:"rule_id"`
Description string `gorm:"type:text" json:"description"`
Metadata string `gorm:"type:json" json:"metadata"`
CreatedAt time.Time `json:"created_at"`
}
func (UserPointsRecord) TableName() string {
return "user_points_records"
}
// Invitation represents the invitations table
type Invitation struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
ReferrerWallet string `gorm:"size:42;not null;index" json:"referrer_wallet"`
RefereeWallet string `gorm:"size:42;not null;unique;index" json:"referee_wallet"`
InviteCode string `gorm:"size:20;not null" json:"invite_code"`
BindSignature string `gorm:"type:text" json:"bind_signature"`
BindHash string `gorm:"size:66" json:"bind_hash"`
Status string `gorm:"size:50;default:'active'" json:"status"`
ReferrerRewardPoints int `gorm:"default:0" json:"referrer_reward_points"`
RefereeRewardPoints int `gorm:"default:0" json:"referee_reward_points"`
BoundAt time.Time `gorm:"not null" json:"bound_at"`
CreatedAt time.Time `json:"created_at"`
}
func (Invitation) TableName() string {
return "invitations"
}
// InviteCode represents the invite_codes table
type InviteCode struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
WalletAddress string `gorm:"size:42;not null;unique;index" json:"wallet_address"`
Code string `gorm:"size:20;not null;unique;index" json:"code"`
MaxUses int `gorm:"default:-1" json:"max_uses"` // -1 = unlimited
UsedCount int `gorm:"default:0" json:"used_count"`
ExpiresAt NullTime `json:"expires_at"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
}
func (InviteCode) TableName() string {
return "invite_codes"
}
// =====================
// Response DTOs
// =====================
// DashboardResponse is the response for GET /api/points/dashboard
type DashboardResponse struct {
TotalPoints int64 `json:"totalPoints"`
GlobalRank int `json:"globalRank"`
TopPercentage string `json:"topPercentage"`
MemberTier string `json:"memberTier"`
VIPLevel int `json:"vipLevel"`
PointsToNextTier int64 `json:"pointsToNextTier"`
NextTier string `json:"nextTier"`
Season SeasonInfo `json:"season"`
}
// SeasonInfo contains season information
type SeasonInfo struct {
SeasonNumber int `json:"seasonNumber"`
SeasonName string `json:"seasonName"`
IsLive bool `json:"isLive"`
EndTime time.Time `json:"endTime"`
DaysRemaining int `json:"daysRemaining"`
HoursRemaining int `json:"hoursRemaining"`
}
// LeaderboardResponse is the response for GET /api/points/leaderboard
type LeaderboardResponse struct {
TopUsers []LeaderboardUser `json:"topUsers"`
MyRank int `json:"myRank"`
MyPoints int64 `json:"myPoints"`
}
// LeaderboardUser represents a user in the leaderboard
type LeaderboardUser struct {
Rank int `json:"rank"`
WalletAddress string `json:"address"`
Points int64 `json:"points"`
}
// InviteCodeResponse is the response for GET /api/points/invite-code
type InviteCodeResponse struct {
Code string `json:"code"`
UsedCount int `json:"usedCount"`
MaxUses int `json:"maxUses"`
}
// TeamTVLResponse is the response for GET /api/points/team
type TeamTVLResponse struct {
CurrentTVL string `json:"currentTVL"`
TargetTVL string `json:"targetTVL"`
ProgressPercent float64 `json:"progressPercent"`
TotalMembers int `json:"totalMembers"`
Roles []RoleCount `json:"roles"`
}
// RoleCount represents count for each role
type RoleCount struct {
Icon string `json:"icon"`
Label string `json:"label"`
Current int `json:"current"`
Target int `json:"target"`
}
// ActivitiesResponse is the response for GET /api/points/activities
type ActivitiesResponse struct {
Activities []ActivityRecord `json:"activities"`
Pagination PaginationInfo `json:"pagination"`
}
// ActivityRecord represents a single activity
type ActivityRecord struct {
Type string `json:"type"`
UserAddress string `json:"userAddress"`
FriendAddress string `json:"friendAddress,omitempty"`
InviteCode string `json:"inviteCode,omitempty"`
Points int `json:"points"`
CreatedAt time.Time `json:"createdAt"`
}
// PaginationInfo contains pagination metadata
type PaginationInfo struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`
Total int `json:"total"`
TotalPage int `json:"totalPage"`
}
// BindInviteRequest is the request body for POST /api/points/bind-invite
type BindInviteRequest struct {
Code string `json:"code" binding:"required"`
Signature string `json:"signature" binding:"required"`
}
// HoldersSnapshot represents the holders_snapshots table
// 每小时快照:记录用户持有 YT/LP/Lending 代币的余额及积分计算结果
type HoldersSnapshot struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
WalletAddress string `gorm:"size:42;not null;index" json:"wallet_address"`
TokenType string `gorm:"size:20;not null" json:"token_type"`
TokenAddress string `gorm:"size:42;not null" json:"token_address"`
ChainID int `gorm:"not null" json:"chain_id"`
Balance float64 `gorm:"type:decimal(30,18);not null" json:"balance"`
BalanceUSD float64 `gorm:"type:decimal(30,2)" json:"balance_usd"`
HoldingDurationHours int `json:"holding_duration_hours"`
PointsMultiplier float64 `gorm:"type:decimal(10,4);default:1.0000" json:"points_multiplier"`
EarnedPoints int `json:"earned_points"`
SnapshotTime time.Time `gorm:"not null;index" json:"snapshot_time"`
SeasonID *uint `gorm:"index" json:"season_id"`
CreatedAt time.Time `json:"created_at"`
}
func (HoldersSnapshot) TableName() string {
return "holders_snapshots"
}

View File

@@ -0,0 +1,20 @@
package models
import "time"
// SystemContract is the central registry for all infrastructure contract addresses.
// One row per (name, chain_id) pair.
// Replaces the former alp_pools and lending_markets tables (which had only one row each).
type SystemContract struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
Name string `gorm:"size:100;not null;uniqueIndex:uk_name_chain" json:"name"`
ChainID int `gorm:"not null;uniqueIndex:uk_name_chain" json:"chain_id"`
Address string `gorm:"size:42" json:"address"`
DeployBlock *uint64 `json:"deploy_block"`
Description string `gorm:"type:text" json:"description"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (SystemContract) TableName() string { return "system_contracts" }

View File

@@ -0,0 +1,27 @@
package models
import (
"time"
)
// User represents the users table
// Web3 native: wallet_address is the primary key (no numeric id, no auth_user_id)
type User struct {
WalletAddress string `gorm:"primaryKey;size:42" json:"wallet_address"`
Nickname string `gorm:"size:100" json:"nickname"`
Avatar string `gorm:"type:text" json:"avatar"`
Bio string `gorm:"type:text" json:"bio"`
ReferrerWallet *string `gorm:"size:42;index" json:"referrer_wallet,omitempty"`
InviteCode string `gorm:"size:20;unique;index" json:"invite_code"`
DirectInvitesCount int `gorm:"default:0" json:"direct_invites_count"`
MemberTier string `gorm:"size:50;default:'Bronze'" json:"member_tier"`
VIPLevel int `gorm:"default:1" json:"vip_level"`
TotalPoints int64 `gorm:"default:0" json:"total_points"`
GlobalRank *int `gorm:"index" json:"global_rank,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (User) TableName() string {
return "users"
}

View File

@@ -0,0 +1,24 @@
package models
import "time"
// YTSwapRecord records each Swap event emitted by YT Vault contracts.
// Swap(address account, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, uint256 feeBasisPoints)
// account/tokenIn/tokenOut are indexed (topics), amountIn/amountOut are in data.
type YTSwapRecord struct {
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
TxHash string `gorm:"size:66;uniqueIndex:uk_tx_log" json:"tx_hash"`
LogIndex uint `gorm:"uniqueIndex:uk_tx_log" json:"log_index"`
ChainID int `gorm:"index" json:"chain_id"`
BlockNumber uint64 `json:"block_number"`
BlockTime time.Time `gorm:"index" json:"block_time"`
VaultAddr string `gorm:"size:42;index" json:"vault_addr"`
Account string `gorm:"size:42;index" json:"account"`
TokenIn string `gorm:"size:42" json:"token_in"`
TokenOut string `gorm:"size:42" json:"token_out"`
AmountIn string `gorm:"size:78" json:"amount_in"` // wei string, base units
AmountOut string `gorm:"size:78" json:"amount_out"` // wei string, base units
CreatedAt time.Time `json:"created_at"`
}
func (YTSwapRecord) TableName() string { return "yt_swap_records" }