2026-03-07 22:58:43 +08:00
package handler
import (
"fmt"
"net/http"
"time"
"soul-api/internal/database"
"soul-api/internal/model"
"github.com/gin-gonic/gin"
)
// AdminDistributionOverview GET /api/admin/distribution/overview( 全部使用 GORM, 无 Raw SQL)
func AdminDistributionOverview ( c * gin . Context ) {
now := time . Now ( )
todayStart := time . Date ( now . Year ( ) , now . Month ( ) , now . Day ( ) , 0 , 0 , 0 , 0 , now . Location ( ) )
todayEnd := todayStart . Add ( 24 * time . Hour )
monthStart := time . Date ( now . Year ( ) , now . Month ( ) , 1 , 0 , 0 , 0 , 0 , now . Location ( ) )
db := database . DB ( )
overview := gin . H {
"todayClicks" : 0 , "todayBindings" : 0 , "todayConversions" : 0 , "todayEarnings" : 0 ,
"monthClicks" : 0 , "monthBindings" : 0 , "monthConversions" : 0 , "monthEarnings" : 0 ,
"totalClicks" : 0 , "totalBindings" : 0 , "totalConversions" : 0 , "totalEarnings" : 0 ,
"expiringBindings" : 0 , "pendingWithdrawals" : 0 , "pendingWithdrawAmount" : 0 ,
"conversionRate" : "0.00" , "totalDistributors" : 0 , "activeDistributors" : 0 ,
}
// 订单:仅用 Where + Count / Select(Sum) 参数化
var totalOrders int64
db . Model ( & model . Order { } ) . Where ( "status = ?" , "paid" ) . Count ( & totalOrders )
var totalAmount float64
db . Model ( & model . Order { } ) . Where ( "status = ?" , "paid" ) . Select ( "COALESCE(SUM(amount),0)" ) . Scan ( & totalAmount )
var todayOrders int64
db . Model ( & model . Order { } ) . Where ( "status = ? AND created_at >= ? AND created_at < ?" , "paid" , todayStart , todayEnd ) . Count ( & todayOrders )
var todayAmount float64
db . Model ( & model . Order { } ) . Where ( "status = ? AND created_at >= ? AND created_at < ?" , "paid" , todayStart , todayEnd ) . Select ( "COALESCE(SUM(amount),0)" ) . Scan ( & todayAmount )
var monthOrders int64
db . Model ( & model . Order { } ) . Where ( "status = ? AND created_at >= ?" , "paid" , monthStart ) . Count ( & monthOrders )
var monthAmount float64
db . Model ( & model . Order { } ) . Where ( "status = ? AND created_at >= ?" , "paid" , monthStart ) . Select ( "COALESCE(SUM(amount),0)" ) . Scan ( & monthAmount )
overview [ "totalEarnings" ] = totalAmount
overview [ "todayEarnings" ] = todayAmount
overview [ "monthEarnings" ] = monthAmount
// 绑定:全部 GORM Where
var totalBindings int64
db . Model ( & model . ReferralBinding { } ) . Count ( & totalBindings )
var converted int64
db . Model ( & model . ReferralBinding { } ) . Where ( "status = ?" , "converted" ) . Count ( & converted )
var todayBindings int64
db . Model ( & model . ReferralBinding { } ) . Where ( "binding_date >= ? AND binding_date < ?" , todayStart , todayEnd ) . Count ( & todayBindings )
var todayConv int64
db . Model ( & model . ReferralBinding { } ) . Where ( "status = ? AND binding_date >= ? AND binding_date < ?" , "converted" , todayStart , todayEnd ) . Count ( & todayConv )
var monthBindings int64
db . Model ( & model . ReferralBinding { } ) . Where ( "binding_date >= ?" , monthStart ) . Count ( & monthBindings )
var monthConv int64
db . Model ( & model . ReferralBinding { } ) . Where ( "status = ? AND binding_date >= ?" , "converted" , monthStart ) . Count ( & monthConv )
expiringEnd := now . Add ( 7 * 24 * time . Hour )
var expiring int64
db . Model ( & model . ReferralBinding { } ) . Where ( "status = ? AND expiry_date > ? AND expiry_date <= ?" , "active" , now , expiringEnd ) . Count ( & expiring )
overview [ "totalBindings" ] = totalBindings
overview [ "totalConversions" ] = converted
overview [ "todayBindings" ] = todayBindings
overview [ "todayConversions" ] = todayConv
overview [ "monthBindings" ] = monthBindings
overview [ "monthConversions" ] = monthConv
overview [ "expiringBindings" ] = expiring
// 访问数(点击量):总 / 今日 / 本月
var visitTotal int64
db . Model ( & model . ReferralVisit { } ) . Count ( & visitTotal )
var todayClicks int64
db . Model ( & model . ReferralVisit { } ) . Where ( "created_at >= ? AND created_at < ?" , todayStart , todayEnd ) . Count ( & todayClicks )
var monthClicks int64
db . Model ( & model . ReferralVisit { } ) . Where ( "created_at >= ?" , monthStart ) . Count ( & monthClicks )
overview [ "totalClicks" ] = visitTotal
overview [ "todayClicks" ] = todayClicks
overview [ "monthClicks" ] = monthClicks
if visitTotal > 0 && converted > 0 {
2026-03-22 08:34:28 +08:00
overview [ "conversionRate" ] = formatPercent ( float64 ( converted ) / float64 ( visitTotal ) * 100 )
2026-03-07 22:58:43 +08:00
}
// 今日独立访客数(按 visitor_id 或 visitor_openid 去重,空则用 id 占位)
var todayUniqueVisitors int64
db . Raw ( "SELECT COUNT(DISTINCT COALESCE(visitor_id, visitor_openid, CONCAT('_', id))) FROM referral_visits WHERE created_at >= ? AND created_at < ?" , todayStart , todayEnd ) . Scan ( & todayUniqueVisitors )
overview [ "todayUniqueVisitors" ] = todayUniqueVisitors
// 今日总文章点击率:今日总点击 / 今日独立访客(人均点击),无访客时为 0
todayClickRate := 0.0
if todayUniqueVisitors > 0 && todayClicks > 0 {
todayClickRate = float64 ( todayClicks ) / float64 ( todayUniqueVisitors )
}
overview [ "todayClickRate" ] = todayClickRate
// 每篇文章今日点击量(按 page 分组)
var pageClicks [ ] struct {
Page string ` gorm:"column:page" `
Clicks int64 ` gorm:"column:clicks" `
}
db . Raw ( "SELECT COALESCE(page, '') AS page, COUNT(*) AS clicks FROM referral_visits WHERE created_at >= ? AND created_at < ? GROUP BY COALESCE(page, '')" , todayStart , todayEnd ) . Scan ( & pageClicks )
byPage := make ( [ ] gin . H , 0 , len ( pageClicks ) )
for _ , r := range pageClicks {
byPage = append ( byPage , gin . H { "page" : r . Page , "clicks" : r . Clicks } )
}
overview [ "todayClicksByPage" ] = byPage
// 提现待处理
var pendCount int64
db . Model ( & model . Withdrawal { } ) . Where ( "status = ?" , "pending" ) . Count ( & pendCount )
var pendSum float64
db . Model ( & model . Withdrawal { } ) . Where ( "status = ?" , "pending" ) . Select ( "COALESCE(SUM(amount),0)" ) . Scan ( & pendSum )
overview [ "pendingWithdrawals" ] = pendCount
overview [ "pendingWithdrawAmount" ] = pendSum
// 分销商
var distTotal int64
db . Model ( & model . User { } ) . Where ( "referral_code IS NOT NULL AND referral_code != ?" , "" ) . Count ( & distTotal )
var distActive int64
db . Model ( & model . User { } ) . Where ( "referral_code IS NOT NULL AND referral_code != ? AND earnings > ?" , "" , 0 ) . Count ( & distActive )
overview [ "totalDistributors" ] = distTotal
overview [ "activeDistributors" ] = distActive
c . JSON ( http . StatusOK , gin . H { "success" : true , "overview" : overview } )
}
func formatPercent ( v float64 ) string {
return fmt . Sprintf ( "%.2f" , v ) + "%"
}