161 lines
4.8 KiB
Go
161 lines
4.8 KiB
Go
package admin
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"bindbox-game/internal/code"
|
|
"bindbox-game/internal/pkg/core"
|
|
"bindbox-game/internal/pkg/validation"
|
|
)
|
|
|
|
type activityRankingsRequest struct {
|
|
SortBy string `form:"sort_by"`
|
|
Page int `form:"page"`
|
|
PageSize int `form:"page_size"`
|
|
}
|
|
|
|
type activityRankingItem struct {
|
|
Rank int64 `json:"rank"`
|
|
UserID int64 `json:"user_id"`
|
|
Nickname string `json:"nickname"`
|
|
Avatar string `json:"avatar"`
|
|
TotalAmount int64 `json:"total_amount"`
|
|
OrderCount int64 `json:"order_count"`
|
|
}
|
|
|
|
type activityRankingsResponse struct {
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
Total int64 `json:"total"`
|
|
List []activityRankingItem `json:"list"`
|
|
}
|
|
|
|
// GetActivityRankings 获取活动内用户消费/订单排行榜
|
|
// @Summary 活动排行榜
|
|
// @Description 按活动维度统计用户消费总额与订单数排行榜
|
|
// @Tags 管理端.活动
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param activity_id path int true "活动ID"
|
|
// @Param sort_by query string false "排序字段: amount|orders"
|
|
// @Param page query int false "页码" default(1)
|
|
// @Param page_size query int false "每页条数(最大100)" default(20)
|
|
// @Success 200 {object} activityRankingsResponse
|
|
// @Failure 400 {object} code.Failure
|
|
// @Router /api/admin/activities/{activity_id}/rankings [get]
|
|
// @Security LoginVerifyToken
|
|
func (h *handler) GetActivityRankings() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
activityID, err := strconv.ParseInt(ctx.Param("activity_id"), 10, 64)
|
|
if err != nil || activityID <= 0 {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "无效的活动ID"))
|
|
return
|
|
}
|
|
|
|
req := new(activityRankingsRequest)
|
|
if err := ctx.ShouldBindForm(req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
|
|
if req.Page <= 0 {
|
|
req.Page = 1
|
|
}
|
|
if req.PageSize <= 0 {
|
|
req.PageSize = 20
|
|
}
|
|
if req.PageSize > 100 {
|
|
req.PageSize = 100
|
|
}
|
|
if req.SortBy != "orders" {
|
|
req.SortBy = "amount"
|
|
}
|
|
|
|
baseQuery := h.repo.GetDbR().WithContext(ctx.RequestContext()).
|
|
Table("orders").
|
|
Joins(`
|
|
JOIN (
|
|
SELECT DISTINCT activity_draw_logs.order_id, activity_issues.activity_id
|
|
FROM activity_draw_logs
|
|
JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id
|
|
) AS order_activities ON order_activities.order_id = orders.id
|
|
`).
|
|
Where("order_activities.activity_id = ?", activityID).
|
|
Where("orders.status = ?", 2)
|
|
|
|
userSubQuery := baseQuery.
|
|
Select("orders.user_id").
|
|
Group("orders.user_id")
|
|
|
|
var total int64
|
|
if err := h.repo.GetDbR().WithContext(ctx.RequestContext()).
|
|
Table("(?) AS ranked_users", userSubQuery).
|
|
Count(&total).Error; err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
|
|
return
|
|
}
|
|
|
|
type rankingRow struct {
|
|
UserID int64
|
|
Nickname string
|
|
Avatar string
|
|
TotalAmount int64
|
|
OrderCount int64
|
|
}
|
|
var rows []rankingRow
|
|
|
|
listQuery := h.repo.GetDbR().WithContext(ctx.RequestContext()).
|
|
Table("orders").
|
|
Joins(`
|
|
JOIN (
|
|
SELECT DISTINCT activity_draw_logs.order_id, activity_issues.activity_id
|
|
FROM activity_draw_logs
|
|
JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id
|
|
) AS order_activities ON order_activities.order_id = orders.id
|
|
`).
|
|
Joins("LEFT JOIN users ON users.id = orders.user_id").
|
|
Where("order_activities.activity_id = ?", activityID).
|
|
Where("orders.status = ?", 2).
|
|
Select(`
|
|
orders.user_id,
|
|
COALESCE(users.nickname, '') AS nickname,
|
|
COALESCE(users.avatar, '') AS avatar,
|
|
CAST(SUM(orders.actual_amount + orders.discount_amount) AS SIGNED) AS total_amount,
|
|
COUNT(DISTINCT orders.id) AS order_count
|
|
`).
|
|
Group("orders.user_id, users.nickname, users.avatar")
|
|
|
|
if req.SortBy == "orders" {
|
|
listQuery = listQuery.Order("order_count DESC").Order("total_amount DESC").Order("orders.user_id ASC")
|
|
} else {
|
|
listQuery = listQuery.Order("total_amount DESC").Order("order_count DESC").Order("orders.user_id ASC")
|
|
}
|
|
|
|
offset := (req.Page - 1) * req.PageSize
|
|
if err := listQuery.Offset(offset).Limit(req.PageSize).Scan(&rows).Error; err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
|
|
return
|
|
}
|
|
|
|
res := activityRankingsResponse{
|
|
Page: req.Page,
|
|
PageSize: req.PageSize,
|
|
Total: total,
|
|
List: make([]activityRankingItem, 0, len(rows)),
|
|
}
|
|
for i, row := range rows {
|
|
res.List = append(res.List, activityRankingItem{
|
|
Rank: int64(offset + i + 1),
|
|
UserID: row.UserID,
|
|
Nickname: row.Nickname,
|
|
Avatar: row.Avatar,
|
|
TotalAmount: row.TotalAmount,
|
|
OrderCount: row.OrderCount,
|
|
})
|
|
}
|
|
|
|
ctx.Payload(res)
|
|
}
|
|
}
|