Files
soul-yongping/soul-api/internal/handler/match_records.go

191 lines
5.5 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"
"strconv"
"time"
"soul-api/internal/database"
"soul-api/internal/model"
"github.com/gin-gonic/gin"
)
// DBMatchRecordsList GET /api/db/match-records 管理端-匹配记录列表(分页、按类型筛选)
// 当 ?stats=true 时返回汇总统计(总匹配数、今日匹配、按类型分布、独立用户数)
func DBMatchRecordsList(c *gin.Context) {
db := database.DB()
if c.Query("stats") == "true" {
var totalMatches int64
db.Raw("SELECT COUNT(*) FROM match_records").Scan(&totalMatches)
var todayMatches int64
db.Raw("SELECT COUNT(*) FROM match_records WHERE created_at >= CURDATE()").Scan(&todayMatches)
type TypeCount struct {
MatchType string `json:"matchType" gorm:"column:match_type"`
Count int64 `json:"count" gorm:"column:count"`
}
var byType []TypeCount
db.Raw("SELECT match_type, COUNT(*) as count FROM match_records GROUP BY match_type").Scan(&byType)
var uniqueUsers int64
db.Raw("SELECT COUNT(DISTINCT user_id) FROM match_records WHERE user_id IS NOT NULL AND user_id != ''").Scan(&uniqueUsers)
// 匹配收益product_type=match 且 status=paid 的订单金额总和
var matchRevenue float64
db.Model(&model.Order{}).Where("product_type = ? AND status = ?", "match", "paid").
Select("COALESCE(SUM(amount), 0)").Scan(&matchRevenue)
var paidMatchCount int64
db.Model(&model.Order{}).Where("product_type = ? AND status = ?", "match", "paid").Count(&paidMatchCount)
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"totalMatches": totalMatches,
"todayMatches": todayMatches,
"byType": byType,
"uniqueUsers": uniqueUsers,
"matchRevenue": matchRevenue,
"paidMatchCount": paidMatchCount,
},
})
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
matchType := c.Query("matchType")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
q := db.Model(&model.MatchRecord{})
if matchType != "" {
q = q.Where("match_type = ?", matchType)
}
var total int64
q.Count(&total)
var records []model.MatchRecord
if err := q.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&records).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error(), "records": []interface{}{}})
return
}
userIDs := make(map[string]bool)
for _, r := range records {
userIDs[r.UserID] = true
userIDs[r.MatchedUserID] = true
}
ids := make([]string, 0, len(userIDs))
for id := range userIDs {
ids = append(ids, id)
}
var users []model.User
if len(ids) > 0 {
database.DB().Where("id IN ?", ids).Find(&users)
}
userMap := make(map[string]*model.User)
for i := range users {
userMap[users[i].ID] = &users[i]
}
getStr := func(s *string) string {
if s == nil {
return ""
}
return *s
}
safeNickname := func(u *model.User) string {
if u == nil || u.Nickname == nil { return "" }
return *u.Nickname
}
safeAvatar := func(u *model.User) string {
if u == nil || u.Avatar == nil { return "" }
return *u.Avatar
}
out := make([]gin.H, 0, len(records))
for _, r := range records {
u := userMap[r.UserID]
mu := userMap[r.MatchedUserID]
out = append(out, gin.H{
"id": r.ID, "userId": r.UserID, "matchedUserId": r.MatchedUserID,
"matchType": r.MatchType, "phone": getStr(r.Phone), "wechatId": getStr(r.WechatID),
"userNickname": safeNickname(u),
"matchedNickname": safeNickname(mu),
"userAvatar": safeAvatar(u),
"matchedUserAvatar": safeAvatar(mu),
"matchScore": r.MatchScore,
"createdAt": r.CreatedAt,
})
}
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++
}
c.JSON(http.StatusOK, gin.H{
"success": true, "records": out,
"total": total, "page": page, "pageSize": pageSize, "totalPages": totalPages,
})
}
// DBMatchPoolCounts GET /api/db/match-pool-counts 返回各匹配池的用户人数
func DBMatchPoolCounts(c *gin.Context) {
db := database.DB()
var vipCount int64
db.Model(&model.User{}).Where("is_vip = 1 AND vip_expire_date > NOW()").Count(&vipCount)
var completeCount int64
db.Model(&model.User{}).Where(
"(phone IS NOT NULL AND phone != '') AND (nickname IS NOT NULL AND nickname != '' AND nickname != '微信用户') AND (avatar IS NOT NULL AND avatar != '')",
).Count(&completeCount)
var allCount int64
db.Model(&model.User{}).Where(
"((wechat_id IS NOT NULL AND wechat_id != '') OR (phone IS NOT NULL AND phone != ''))",
).Count(&allCount)
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"vip": vipCount,
"complete": completeCount,
"all": allCount,
},
})
}
// DBMatchRecordInsertTest POST /api/db/match-records/test 插入测试匹配记录
func DBMatchRecordInsertTest(c *gin.Context) {
var body struct {
MatchType string `json:"matchType"`
Phone string `json:"phone"`
WechatID string `json:"wechatId"`
}
_ = c.ShouldBindJSON(&body)
if body.MatchType == "" {
body.MatchType = "team"
}
if body.Phone == "" {
body.Phone = "13800000000"
}
db := database.DB()
rec := model.MatchRecord{
ID: fmt.Sprintf("mr_test_%d", time.Now().UnixNano()),
UserID: "admin_test",
MatchType: body.MatchType,
Phone: &body.Phone,
}
if body.WechatID != "" {
rec.WechatID = &body.WechatID
}
if err := db.Create(&rec).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "message": "测试记录已插入", "id": rec.ID})
}