Files
soul-yongping/soul-api/internal/handler/admin_distribution.go
2026-03-07 22:58:43 +08:00

128 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
overview["conversionRate"] = formatPercent(float64(converted)/float64(visitTotal)*100)
}
// 今日独立访客数(按 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) + "%"
}