转盘抽奖修改

This commit is contained in:
hy001 2026-05-20 13:53:08 +08:00
parent 78bddf406d
commit c4d4f03ddf
6 changed files with 648 additions and 44 deletions

View File

@ -101,7 +101,7 @@ func registerWheelRoutes(engine *gin.Engine, cfg config.Config, javaClient authG
}) })
residentGroup.GET("/record/page", func(c *gin.Context) { residentGroup.GET("/record/page", func(c *gin.Context) {
cursor, limit := parseCursorLimit(c) cursor, limit := parseCursorLimit(c)
resp, err := wheelService.PageRecords( resp, err := wheelService.PageAdminRecords(
c.Request.Context(), c.Request.Context(),
c.Query("sysOrigin"), c.Query("sysOrigin"),
c.Query("category"), c.Query("category"),
@ -109,6 +109,8 @@ func registerWheelRoutes(engine *gin.Engine, cfg config.Config, javaClient authG
strings.TrimSpace(c.Query("status")), strings.TrimSpace(c.Query("status")),
cursor, cursor,
limit, limit,
c.Query("startTime"),
c.Query("endTime"),
) )
if err != nil { if err != nil {
writeError(c, err) writeError(c, err)
@ -217,11 +219,15 @@ func legacyRewardsHandler(wheelService *wheel.Service, category string) gin.Hand
items := make([]gin.H, 0, len(pool.Rewards)) items := make([]gin.H, 0, len(pool.Rewards))
for _, reward := range pool.Rewards { for _, reward := range pool.Rewards {
items = append(items, gin.H{ items = append(items, gin.H{
"id": reward.ID, "id": reward.ID,
"value": legacyRewardValue(reward), "animationUrl": reward.ResourceURL,
"resourceUrl": reward.ResourceURL,
"value": legacyRewardValue(reward),
"merchandise": gin.H{ "merchandise": gin.H{
"name": legacyRewardName(reward), "animationUrl": reward.ResourceURL,
"coverUrl": legacyRewardCover(reward), "coverUrl": legacyRewardCover(reward),
"name": legacyRewardName(reward),
"resourceUrl": reward.ResourceURL,
}, },
}) })
} }
@ -243,10 +249,12 @@ func legacyDrawHandler(wheelService *wheel.Service, category string) gin.Handler
rewards := make([]gin.H, 0, len(resp.Rewards)) rewards := make([]gin.H, 0, len(resp.Rewards))
for _, reward := range resp.Rewards { for _, reward := range resp.Rewards {
rewards = append(rewards, gin.H{ rewards = append(rewards, gin.H{
"coverUrl": legacyDrawRewardCover(reward), "animationUrl": reward.ResourceURL,
"count": reward.Count, "coverUrl": legacyDrawRewardCover(reward),
"name": legacyDrawRewardName(reward), "count": reward.Count,
"value": reward.Value, "name": legacyDrawRewardName(reward),
"resourceUrl": reward.ResourceURL,
"value": reward.Value,
}) })
} }
legacyOK(c, gin.H{ legacyOK(c, gin.H{
@ -276,10 +284,12 @@ func legacyHistoryHandler(wheelService *wheel.Service, category string) gin.Hand
"drawTimes": record.DrawTimes, "drawTimes": record.DrawTimes,
"rewardValue": legacyRecordValue(record), "rewardValue": legacyRecordValue(record),
"rewards": []gin.H{{ "rewards": []gin.H{{
"coverUrl": legacyRecordCover(record), "animationUrl": record.ResourceURL,
"count": 1, "coverUrl": legacyRecordCover(record),
"name": legacyRecordName(record), "count": 1,
"value": legacyRecordValue(record), "name": legacyRecordName(record),
"resourceUrl": record.ResourceURL,
"value": legacyRecordValue(record),
}}, }},
}) })
} }

View File

@ -0,0 +1,118 @@
package router
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"chatapp3-golang/internal/config"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/repo"
wheelservice "chatapp3-golang/internal/service/wheel"
"github.com/gin-gonic/gin"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestWheelLegacyRewardsExposeDynamicResourceURL(t *testing.T) {
gin.SetMode(gin.TestMode)
db := newRouterWheelDB(t)
now := time.Now()
resourceID := int64(2045029471274201089)
resourceURL := "https://cdn.example.com/reward.svga"
coverURL := "https://cdn.example.com/reward.png"
if err := db.Create(&model.WheelConfig{
ID: 1,
SysOrigin: "LIKEI",
Enabled: true,
Timezone: "Asia/Shanghai",
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("create wheel config: %v", err)
}
if err := db.Create(&model.WheelPoolConfig{
ID: 10,
ConfigID: 1,
SysOrigin: "LIKEI",
Category: "CLASSIC",
Enabled: true,
PriceOneGold: 500,
Sort: 1,
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("create wheel pool: %v", err)
}
if err := db.Create(&model.WheelRewardConfig{
ID: 100,
ConfigID: 1,
SysOrigin: "LIKEI",
Category: "CLASSIC",
RewardType: "RESOURCE",
ResourceID: &resourceID,
ResourceType: "GIFT",
ResourceName: "Dynamic Gift",
ResourceURL: resourceURL,
CoverURL: coverURL,
DurationDays: 7,
DisplayGoldAmount: 1000,
Probability: 1000000,
Sort: 1,
Enabled: true,
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("create wheel reward: %v", err)
}
service := wheelservice.NewService(config.Config{}, db, nil)
engine := NewRouter(config.Config{}, &repo.Repository{}, appPopupAuthStub{}, Services{Wheel: service})
req := httptest.NewRequest(http.MethodGet, "/client/api/v1/probability/lucky-box/rewards", nil)
req.Header.Set("Authorization", "Bearer token")
rec := httptest.NewRecorder()
engine.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d body = %s", rec.Code, rec.Body.String())
}
var payload map[string]any
if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
t.Fatalf("decode response: %v", err)
}
if payload["code"] != float64(0) {
t.Fatalf("code = %#v body = %s", payload["code"], rec.Body.String())
}
data, ok := payload["data"].([]any)
if !ok || len(data) == 0 {
t.Fatalf("data = %#v, want rewards", payload["data"])
}
item := data[0].(map[string]any)
if item["resourceUrl"] != resourceURL || item["animationUrl"] != resourceURL {
t.Fatalf("dynamic urls = resource:%#v animation:%#v", item["resourceUrl"], item["animationUrl"])
}
merchandise := item["merchandise"].(map[string]any)
if merchandise["resourceUrl"] != resourceURL || merchandise["animationUrl"] != resourceURL {
t.Fatalf("merchandise dynamic urls = resource:%#v animation:%#v", merchandise["resourceUrl"], merchandise["animationUrl"])
}
if merchandise["coverUrl"] != coverURL {
t.Fatalf("coverUrl = %#v, want %q", merchandise["coverUrl"], coverURL)
}
}
func newRouterWheelDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
if err := db.AutoMigrate(&model.WheelConfig{}, &model.WheelPoolConfig{}, &model.WheelRewardConfig{}); err != nil {
t.Fatalf("migrate wheel tables: %v", err)
}
return db
}

View File

@ -272,7 +272,7 @@ func newWheelDrawTestService(t *testing.T) (*Service, *fakeWheelGateway, *gorm.D
if err != nil { if err != nil {
t.Fatalf("open sqlite: %v", err) t.Fatalf("open sqlite: %v", err)
} }
if err := db.AutoMigrate(&model.WheelConfig{}, &model.WheelPoolConfig{}, &model.WheelRewardConfig{}, &model.WheelDrawRecord{}, &model.ActivityIdempotency{}); err != nil { if err := db.AutoMigrate(&model.WheelConfig{}, &model.WheelPoolConfig{}, &model.WheelRewardConfig{}, &model.WheelDrawRecord{}, &model.ActivityIdempotency{}, &model.UserBaseInfo{}); err != nil {
t.Fatalf("migrate sqlite: %v", err) t.Fatalf("migrate sqlite: %v", err)
} }
gateway := &fakeWheelGateway{ gateway := &fakeWheelGateway{

View File

@ -2,12 +2,17 @@ package wheel
import ( import (
"context" "context"
"fmt"
"net/http"
"strings" "strings"
"time"
"chatapp3-golang/internal/model" "chatapp3-golang/internal/model"
"gorm.io/gorm"
) )
// PageRecords returns admin draw records. // PageRecords returns draw records without admin-only enrichment.
func (s *Service) PageRecords( func (s *Service) PageRecords(
ctx context.Context, ctx context.Context,
sysOrigin string, sysOrigin string,
@ -60,6 +65,80 @@ func (s *Service) PageRecords(
}, nil }, nil
} }
// PageAdminRecords returns admin draw records with user info and range summary.
func (s *Service) PageAdminRecords(
ctx context.Context,
sysOrigin string,
category string,
userID int64,
status string,
cursor int,
limit int,
startTime string,
endTime string,
) (*RecordPageResponse, error) {
cursor, limit = normalizeRecordPage(cursor, limit)
filter, err := newWheelAdminRecordFilter(sysOrigin, category, userID, status, startTime, endTime)
if err != nil {
return nil, err
}
if filter.SysOrigin == "" {
return nil, NewAppError(400, "bad_request", "sysOrigin is required")
}
query := s.adminRecordQuery(ctx, filter)
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, err
}
var rows []model.WheelDrawRecord
if err := s.adminRecordQuery(ctx, filter).
Order("create_time desc, id desc").
Offset((cursor - 1) * limit).
Limit(limit).
Find(&rows).Error; err != nil {
return nil, err
}
summary, err := s.adminRecordSummary(ctx, filter)
if err != nil {
return nil, err
}
userInfos, err := s.adminRecordUserInfos(ctx, rows)
if err != nil {
return nil, err
}
userDrawTimes, err := s.adminRecordUserDrawTimes(ctx, filter, rows)
if err != nil {
return nil, err
}
records := make([]DrawRecordPayload, 0, len(rows))
for _, row := range rows {
payload := drawRecordPayload(row)
if user, ok := userInfos[row.UserID]; ok {
payload.UserShortID = user.Account
payload.UserAvatar = user.UserAvatar
payload.UserNickname = user.UserNickname
}
payload.UserTotalDrawTimes = userDrawTimes[row.UserID]
records = append(records, payload)
}
return &RecordPageResponse{
Records: records,
Total: total,
Current: cursor,
Size: limit,
TotalPaidGold: summary.TotalPaidGold,
TotalRewardGold: summary.TotalRewardGold,
TotalDrawTimes: summary.TotalDrawTimes,
ParticipantCount: summary.ParticipantCount,
StartTime: formatDateTime(filter.StartTime),
EndTime: formatDateTime(filter.EndTime),
}, nil
}
// PageUserHistory returns app draw history for the authenticated user. // PageUserHistory returns app draw history for the authenticated user.
func (s *Service) PageUserHistory(ctx context.Context, user AuthUser, cursor int, limit int) (*RecordPageResponse, error) { func (s *Service) PageUserHistory(ctx context.Context, user AuthUser, cursor int, limit int) (*RecordPageResponse, error) {
return s.PageRecords(ctx, user.SysOrigin, "", user.UserID, drawStatusSuccess, cursor, limit) return s.PageRecords(ctx, user.SysOrigin, "", user.UserID, drawStatusSuccess, cursor, limit)
@ -160,3 +239,205 @@ func displayValueForRecord(row model.WheelDrawRecord) int64 {
} }
return row.DisplayGoldAmount return row.DisplayGoldAmount
} }
type wheelAdminRecordFilter struct {
SysOrigin string
Category string
UserID int64
Status string
StartTime time.Time
EndTime time.Time
}
type wheelAdminRecordSummary struct {
TotalPaidGold int64
TotalRewardGold int64
TotalDrawTimes int64
ParticipantCount int64
}
func newWheelAdminRecordFilter(sysOrigin string, category string, userID int64, status string, startRaw string, endRaw string) (wheelAdminRecordFilter, error) {
start, end, err := parseWheelAdminRecordTimeRange(startRaw, endRaw)
if err != nil {
return wheelAdminRecordFilter{}, err
}
return wheelAdminRecordFilter{
SysOrigin: normalizeSysOrigin(sysOrigin),
Category: strings.ToUpper(strings.TrimSpace(category)),
UserID: userID,
Status: strings.ToUpper(strings.TrimSpace(status)),
StartTime: start,
EndTime: end,
}, nil
}
func parseWheelAdminRecordTimeRange(startRaw string, endRaw string) (time.Time, time.Time, error) {
startRaw = strings.TrimSpace(startRaw)
endRaw = strings.TrimSpace(endRaw)
now := time.Now()
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
if startRaw == "" && endRaw == "" {
return todayStart, todayStart.Add(24*time.Hour - time.Nanosecond), nil
}
var start time.Time
var end time.Time
var err error
if startRaw != "" {
start, err = parseWheelAdminRecordTime(startRaw, false)
if err != nil {
return time.Time{}, time.Time{}, err
}
}
if endRaw != "" {
end, err = parseWheelAdminRecordTime(endRaw, true)
if err != nil {
return time.Time{}, time.Time{}, err
}
}
if startRaw == "" {
start = time.Date(end.Year(), end.Month(), end.Day(), 0, 0, 0, 0, end.Location())
}
if endRaw == "" {
end = time.Date(start.Year(), start.Month(), start.Day(), 23, 59, 59, int(time.Second-time.Nanosecond), start.Location())
}
if end.Before(start) {
return time.Time{}, time.Time{}, NewAppError(http.StatusBadRequest, "bad_time_range", "endTime must be greater than startTime")
}
return start, end, nil
}
func parseWheelAdminRecordTime(raw string, endOfDay bool) (time.Time, error) {
for _, layout := range []string{
"2006-01-02 15:04:05",
time.RFC3339,
"2006-01-02T15:04:05.000Z07:00",
"2006-01-02",
} {
var parsed time.Time
var err error
if strings.Contains(layout, "Z07:00") {
parsed, err = time.Parse(layout, raw)
} else {
parsed, err = time.ParseInLocation(layout, raw, time.Local)
}
if err == nil {
if layout == "2006-01-02" && endOfDay {
parsed = parsed.Add(24*time.Hour - time.Nanosecond)
}
return parsed, nil
}
}
return time.Time{}, NewAppError(http.StatusBadRequest, "bad_time", fmt.Sprintf("invalid time: %s", raw))
}
func (s *Service) adminRecordQuery(ctx context.Context, filter wheelAdminRecordFilter) *gorm.DB {
query := s.db.WithContext(ctx).Model(&model.WheelDrawRecord{}).
Where("sys_origin = ? AND create_time >= ? AND create_time <= ?", filter.SysOrigin, filter.StartTime, filter.EndTime)
if strings.TrimSpace(filter.Category) != "" {
query = query.Where("category = ?", normalizeCategory(filter.Category))
}
if filter.UserID > 0 {
query = query.Where("user_id = ?", filter.UserID)
}
if filter.Status != "" {
query = query.Where("status = ?", filter.Status)
}
return query
}
func (s *Service) adminRecordSummary(ctx context.Context, filter wheelAdminRecordFilter) (wheelAdminRecordSummary, error) {
var summary wheelAdminRecordSummary
if err := s.adminRecordQuery(ctx, filter).
Select("COALESCE(SUM(CASE WHEN status = ? THEN CASE WHEN reward_type = ? THEN gold_amount ELSE display_gold_amount END ELSE 0 END), 0)", drawStatusSuccess, rewardTypeGold).
Scan(&summary.TotalRewardGold).Error; err != nil {
return summary, err
}
if err := s.adminRecordQuery(ctx, filter).
Select("COUNT(DISTINCT user_id)").
Scan(&summary.ParticipantCount).Error; err != nil {
return summary, err
}
drawRows := s.adminRecordQuery(ctx, filter).
Select("draw_no, MAX(paid_gold) AS paid_gold, MAX(draw_times) AS draw_times").
Group("draw_no")
if err := s.db.WithContext(ctx).
Table("(?) AS draw_rows", drawRows).
Select("COALESCE(SUM(paid_gold), 0), COALESCE(SUM(draw_times), 0)").
Row().
Scan(&summary.TotalPaidGold, &summary.TotalDrawTimes); err != nil {
return summary, err
}
return summary, nil
}
func (s *Service) adminRecordUserInfos(ctx context.Context, rows []model.WheelDrawRecord) (map[int64]model.UserBaseInfo, error) {
ids := make([]int64, 0, len(rows))
seen := make(map[int64]struct{}, len(rows))
for _, row := range rows {
if row.UserID <= 0 {
continue
}
if _, ok := seen[row.UserID]; ok {
continue
}
seen[row.UserID] = struct{}{}
ids = append(ids, row.UserID)
}
if len(ids) == 0 {
return map[int64]model.UserBaseInfo{}, nil
}
var users []model.UserBaseInfo
if err := s.db.WithContext(ctx).
Where("id IN ?", ids).
Find(&users).Error; err != nil {
return nil, err
}
result := make(map[int64]model.UserBaseInfo, len(users))
for _, user := range users {
result[user.ID] = user
}
return result, nil
}
func (s *Service) adminRecordUserDrawTimes(ctx context.Context, filter wheelAdminRecordFilter, pageRows []model.WheelDrawRecord) (map[int64]int64, error) {
ids := make([]int64, 0, len(pageRows))
seen := make(map[int64]struct{}, len(pageRows))
for _, row := range pageRows {
if row.UserID <= 0 {
continue
}
if _, ok := seen[row.UserID]; ok {
continue
}
seen[row.UserID] = struct{}{}
ids = append(ids, row.UserID)
}
if len(ids) == 0 {
return map[int64]int64{}, nil
}
drawRows := s.adminRecordQuery(ctx, filter).
Where("user_id IN ?", ids).
Select("user_id, draw_no, MAX(draw_times) AS draw_times").
Group("user_id, draw_no")
type userDrawTotal struct {
UserID int64
TotalDrawTimes int64
}
var rows []userDrawTotal
if err := s.db.WithContext(ctx).
Table("(?) AS user_draw_rows", drawRows).
Select("user_id, COALESCE(SUM(draw_times), 0) AS total_draw_times").
Group("user_id").
Scan(&rows).Error; err != nil {
return nil, err
}
result := make(map[int64]int64, len(rows))
for _, row := range rows {
result[row.UserID] = row.TotalDrawTimes
}
return result, nil
}

View File

@ -0,0 +1,185 @@
package wheel
import (
"context"
"testing"
"time"
"chatapp3-golang/internal/model"
)
func TestPageAdminRecordsIncludesUserInfoAndRangeSummary(t *testing.T) {
service, _, db := newWheelDrawTestService(t)
now := time.Now()
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
recordTime := todayStart.Add(time.Hour)
oldRecordTime := todayStart.Add(-time.Hour)
giftID := int64(9001)
if err := db.Create([]model.UserBaseInfo{
{
ID: 1001,
Account: "short1001",
UserAvatar: "https://example.com/a.png",
UserNickname: "Alice",
OriginSys: "LIKEI",
CreateTime: recordTime,
},
{
ID: 1002,
Account: "short1002",
UserAvatar: "https://example.com/b.png",
UserNickname: "Bob",
OriginSys: "LIKEI",
CreateTime: recordTime,
},
}).Error; err != nil {
t.Fatalf("seed users: %v", err)
}
rows := []model.WheelDrawRecord{
{
ID: 1,
DrawNo: "WHEEL-1001-1",
EventID: "event-1",
SysOrigin: "LIKEI",
Category: categoryClassic,
UserID: 1001,
DrawTimes: 10,
PaidGold: 1000,
RewardType: rewardTypeResource,
ResourceID: &giftID,
ResourceType: resourceTypeGift,
ResourceName: "Gift A",
DisplayGoldAmount: 50,
Probability: 500000,
Status: drawStatusSuccess,
CreateTime: recordTime,
UpdateTime: recordTime,
},
{
ID: 2,
DrawNo: "WHEEL-1001-1",
EventID: "event-2",
SysOrigin: "LIKEI",
Category: categoryClassic,
UserID: 1001,
DrawTimes: 10,
PaidGold: 1000,
RewardType: rewardTypeGold,
GoldAmount: 70,
DisplayGoldAmount: 999,
Probability: 500000,
Status: drawStatusSuccess,
CreateTime: recordTime.Add(time.Minute),
UpdateTime: recordTime.Add(time.Minute),
},
{
ID: 3,
DrawNo: "WHEEL-1002-1",
EventID: "event-3",
SysOrigin: "LIKEI",
Category: categoryClassic,
UserID: 1002,
DrawTimes: 1,
PaidGold: 100,
RewardType: rewardTypeResource,
ResourceID: &giftID,
ResourceType: resourceTypeGift,
ResourceName: "Gift Failed",
DisplayGoldAmount: 999,
Probability: 500000,
Status: drawStatusFailed,
CreateTime: recordTime.Add(2 * time.Minute),
UpdateTime: recordTime.Add(2 * time.Minute),
},
{
ID: 4,
DrawNo: "WHEEL-1002-2",
EventID: "event-4",
SysOrigin: "LIKEI",
Category: categoryClassic,
UserID: 1002,
DrawTimes: 1,
PaidGold: 200,
RewardType: rewardTypeResource,
ResourceID: &giftID,
ResourceType: resourceTypeGift,
ResourceName: "Gift B",
DisplayGoldAmount: 30,
Probability: 500000,
Status: drawStatusSuccess,
CreateTime: recordTime.Add(3 * time.Minute),
UpdateTime: recordTime.Add(3 * time.Minute),
},
{
ID: 5,
DrawNo: "WHEEL-OLD",
EventID: "event-old",
SysOrigin: "LIKEI",
Category: categoryClassic,
UserID: 1001,
DrawTimes: 50,
PaidGold: 5000,
RewardType: rewardTypeResource,
ResourceID: &giftID,
ResourceType: resourceTypeGift,
ResourceName: "Old Gift",
DisplayGoldAmount: 500,
Probability: 500000,
Status: drawStatusSuccess,
CreateTime: oldRecordTime,
UpdateTime: oldRecordTime,
},
}
if err := db.Create(&rows).Error; err != nil {
t.Fatalf("seed records: %v", err)
}
resp, err := service.PageAdminRecords(context.Background(), "LIKEI", "", 0, "", 1, 20, "", "")
if err != nil {
t.Fatalf("PageAdminRecords() error = %v", err)
}
if resp.Total != 4 {
t.Fatalf("total = %d, want 4", resp.Total)
}
if resp.TotalPaidGold != 1300 {
t.Fatalf("total paid = %d, want 1300", resp.TotalPaidGold)
}
if resp.TotalRewardGold != 150 {
t.Fatalf("total reward = %d, want 150", resp.TotalRewardGold)
}
if resp.TotalDrawTimes != 12 {
t.Fatalf("total draw times = %d, want 12", resp.TotalDrawTimes)
}
if resp.ParticipantCount != 2 {
t.Fatalf("participant count = %d, want 2", resp.ParticipantCount)
}
var user1001 *DrawRecordPayload
var user1002 *DrawRecordPayload
for index := range resp.Records {
record := &resp.Records[index]
switch record.UserID {
case 1001:
user1001 = record
case 1002:
user1002 = record
}
}
if user1001 == nil {
t.Fatal("missing user 1001 record")
}
if user1001.UserShortID != "short1001" || user1001.UserAvatar == "" || user1001.UserNickname != "Alice" {
t.Fatalf("user 1001 info = %+v", user1001)
}
if user1001.UserTotalDrawTimes != 10 {
t.Fatalf("user 1001 total draw times = %d, want 10", user1001.UserTotalDrawTimes)
}
if user1002 == nil {
t.Fatal("missing user 1002 record")
}
if user1002.UserTotalDrawTimes != 2 {
t.Fatalf("user 1002 total draw times = %d, want 2", user1002.UserTotalDrawTimes)
}
}

View File

@ -274,39 +274,49 @@ type DrawRewardPayload struct {
// DrawRecordPayload is one draw record row. // DrawRecordPayload is one draw record row.
type DrawRecordPayload struct { type DrawRecordPayload struct {
ID int64 `json:"id"` ID int64 `json:"id"`
DrawNo string `json:"drawNo"` DrawNo string `json:"drawNo"`
EventID string `json:"eventId,omitempty"` EventID string `json:"eventId,omitempty"`
UserID int64 `json:"userId"` UserID int64 `json:"userId"`
SysOrigin string `json:"sysOrigin"` UserShortID string `json:"userShortId,omitempty"`
Category string `json:"category"` UserAvatar string `json:"userAvatar,omitempty"`
DrawTimes int `json:"drawTimes"` UserNickname string `json:"userNickname,omitempty"`
PaidGold int64 `json:"paidGold"` UserTotalDrawTimes int64 `json:"userTotalDrawTimes,omitempty"`
RewardConfigID int64 `json:"rewardConfigId,omitempty"` SysOrigin string `json:"sysOrigin"`
RewardType string `json:"rewardType"` Category string `json:"category"`
ResourceID ResourceID `json:"resourceId,omitempty"` DrawTimes int `json:"drawTimes"`
ResourceType string `json:"resourceType,omitempty"` PaidGold int64 `json:"paidGold"`
ResourceName string `json:"resourceName,omitempty"` RewardConfigID int64 `json:"rewardConfigId,omitempty"`
ResourceURL string `json:"resourceUrl,omitempty"` RewardType string `json:"rewardType"`
CoverURL string `json:"coverUrl,omitempty"` ResourceID ResourceID `json:"resourceId,omitempty"`
DurationDays int `json:"durationDays,omitempty"` ResourceType string `json:"resourceType,omitempty"`
DisplayGoldAmount int64 `json:"displayGoldAmount,omitempty"` ResourceName string `json:"resourceName,omitempty"`
GoldAmount int64 `json:"goldAmount,omitempty"` ResourceURL string `json:"resourceUrl,omitempty"`
Probability int `json:"probability"` CoverURL string `json:"coverUrl,omitempty"`
Status string `json:"status"` DurationDays int `json:"durationDays,omitempty"`
RetryCount int `json:"retryCount,omitempty"` DisplayGoldAmount int64 `json:"displayGoldAmount,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"` GoldAmount int64 `json:"goldAmount,omitempty"`
GrantTime string `json:"grantTime,omitempty"` Probability int `json:"probability"`
CreateTime string `json:"createTime,omitempty"` Status string `json:"status"`
UpdateTime string `json:"updateTime,omitempty"` RetryCount int `json:"retryCount,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
GrantTime string `json:"grantTime,omitempty"`
CreateTime string `json:"createTime,omitempty"`
UpdateTime string `json:"updateTime,omitempty"`
} }
// RecordPageResponse is an admin record page. // RecordPageResponse is an admin record page.
type RecordPageResponse struct { type RecordPageResponse struct {
Records []DrawRecordPayload `json:"records"` Records []DrawRecordPayload `json:"records"`
Total int64 `json:"total"` Total int64 `json:"total"`
Current int `json:"current"` Current int `json:"current"`
Size int `json:"size"` Size int `json:"size"`
TotalPaidGold int64 `json:"totalPaidGold,omitempty"`
TotalRewardGold int64 `json:"totalRewardGold,omitempty"`
TotalDrawTimes int64 `json:"totalDrawTimes,omitempty"`
ParticipantCount int64 `json:"participantCount,omitempty"`
StartTime string `json:"startTime,omitempty"`
EndTime string `json:"endTime,omitempty"`
} }
func normalizeSysOrigin(sysOrigin string) string { func normalizeSysOrigin(sysOrigin string) string {