转盘抽奖修改
This commit is contained in:
parent
78bddf406d
commit
c4d4f03ddf
@ -101,7 +101,7 @@ func registerWheelRoutes(engine *gin.Engine, cfg config.Config, javaClient authG
|
||||
})
|
||||
residentGroup.GET("/record/page", func(c *gin.Context) {
|
||||
cursor, limit := parseCursorLimit(c)
|
||||
resp, err := wheelService.PageRecords(
|
||||
resp, err := wheelService.PageAdminRecords(
|
||||
c.Request.Context(),
|
||||
c.Query("sysOrigin"),
|
||||
c.Query("category"),
|
||||
@ -109,6 +109,8 @@ func registerWheelRoutes(engine *gin.Engine, cfg config.Config, javaClient authG
|
||||
strings.TrimSpace(c.Query("status")),
|
||||
cursor,
|
||||
limit,
|
||||
c.Query("startTime"),
|
||||
c.Query("endTime"),
|
||||
)
|
||||
if err != nil {
|
||||
writeError(c, err)
|
||||
@ -217,11 +219,15 @@ func legacyRewardsHandler(wheelService *wheel.Service, category string) gin.Hand
|
||||
items := make([]gin.H, 0, len(pool.Rewards))
|
||||
for _, reward := range pool.Rewards {
|
||||
items = append(items, gin.H{
|
||||
"id": reward.ID,
|
||||
"value": legacyRewardValue(reward),
|
||||
"id": reward.ID,
|
||||
"animationUrl": reward.ResourceURL,
|
||||
"resourceUrl": reward.ResourceURL,
|
||||
"value": legacyRewardValue(reward),
|
||||
"merchandise": gin.H{
|
||||
"name": legacyRewardName(reward),
|
||||
"coverUrl": legacyRewardCover(reward),
|
||||
"animationUrl": reward.ResourceURL,
|
||||
"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))
|
||||
for _, reward := range resp.Rewards {
|
||||
rewards = append(rewards, gin.H{
|
||||
"coverUrl": legacyDrawRewardCover(reward),
|
||||
"count": reward.Count,
|
||||
"name": legacyDrawRewardName(reward),
|
||||
"value": reward.Value,
|
||||
"animationUrl": reward.ResourceURL,
|
||||
"coverUrl": legacyDrawRewardCover(reward),
|
||||
"count": reward.Count,
|
||||
"name": legacyDrawRewardName(reward),
|
||||
"resourceUrl": reward.ResourceURL,
|
||||
"value": reward.Value,
|
||||
})
|
||||
}
|
||||
legacyOK(c, gin.H{
|
||||
@ -276,10 +284,12 @@ func legacyHistoryHandler(wheelService *wheel.Service, category string) gin.Hand
|
||||
"drawTimes": record.DrawTimes,
|
||||
"rewardValue": legacyRecordValue(record),
|
||||
"rewards": []gin.H{{
|
||||
"coverUrl": legacyRecordCover(record),
|
||||
"count": 1,
|
||||
"name": legacyRecordName(record),
|
||||
"value": legacyRecordValue(record),
|
||||
"animationUrl": record.ResourceURL,
|
||||
"coverUrl": legacyRecordCover(record),
|
||||
"count": 1,
|
||||
"name": legacyRecordName(record),
|
||||
"resourceUrl": record.ResourceURL,
|
||||
"value": legacyRecordValue(record),
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
118
internal/router/wheel_routes_test.go
Normal file
118
internal/router/wheel_routes_test.go
Normal 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
|
||||
}
|
||||
@ -272,7 +272,7 @@ func newWheelDrawTestService(t *testing.T) (*Service, *fakeWheelGateway, *gorm.D
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
gateway := &fakeWheelGateway{
|
||||
|
||||
@ -2,12 +2,17 @@ package wheel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chatapp3-golang/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PageRecords returns admin draw records.
|
||||
// PageRecords returns draw records without admin-only enrichment.
|
||||
func (s *Service) PageRecords(
|
||||
ctx context.Context,
|
||||
sysOrigin string,
|
||||
@ -60,6 +65,80 @@ func (s *Service) PageRecords(
|
||||
}, 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.
|
||||
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)
|
||||
@ -160,3 +239,205 @@ func displayValueForRecord(row model.WheelDrawRecord) int64 {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
185
internal/service/wheel/records_test.go
Normal file
185
internal/service/wheel/records_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -274,39 +274,49 @@ type DrawRewardPayload struct {
|
||||
|
||||
// DrawRecordPayload is one draw record row.
|
||||
type DrawRecordPayload struct {
|
||||
ID int64 `json:"id"`
|
||||
DrawNo string `json:"drawNo"`
|
||||
EventID string `json:"eventId,omitempty"`
|
||||
UserID int64 `json:"userId"`
|
||||
SysOrigin string `json:"sysOrigin"`
|
||||
Category string `json:"category"`
|
||||
DrawTimes int `json:"drawTimes"`
|
||||
PaidGold int64 `json:"paidGold"`
|
||||
RewardConfigID int64 `json:"rewardConfigId,omitempty"`
|
||||
RewardType string `json:"rewardType"`
|
||||
ResourceID ResourceID `json:"resourceId,omitempty"`
|
||||
ResourceType string `json:"resourceType,omitempty"`
|
||||
ResourceName string `json:"resourceName,omitempty"`
|
||||
ResourceURL string `json:"resourceUrl,omitempty"`
|
||||
CoverURL string `json:"coverUrl,omitempty"`
|
||||
DurationDays int `json:"durationDays,omitempty"`
|
||||
DisplayGoldAmount int64 `json:"displayGoldAmount,omitempty"`
|
||||
GoldAmount int64 `json:"goldAmount,omitempty"`
|
||||
Probability int `json:"probability"`
|
||||
Status string `json:"status"`
|
||||
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"`
|
||||
ID int64 `json:"id"`
|
||||
DrawNo string `json:"drawNo"`
|
||||
EventID string `json:"eventId,omitempty"`
|
||||
UserID int64 `json:"userId"`
|
||||
UserShortID string `json:"userShortId,omitempty"`
|
||||
UserAvatar string `json:"userAvatar,omitempty"`
|
||||
UserNickname string `json:"userNickname,omitempty"`
|
||||
UserTotalDrawTimes int64 `json:"userTotalDrawTimes,omitempty"`
|
||||
SysOrigin string `json:"sysOrigin"`
|
||||
Category string `json:"category"`
|
||||
DrawTimes int `json:"drawTimes"`
|
||||
PaidGold int64 `json:"paidGold"`
|
||||
RewardConfigID int64 `json:"rewardConfigId,omitempty"`
|
||||
RewardType string `json:"rewardType"`
|
||||
ResourceID ResourceID `json:"resourceId,omitempty"`
|
||||
ResourceType string `json:"resourceType,omitempty"`
|
||||
ResourceName string `json:"resourceName,omitempty"`
|
||||
ResourceURL string `json:"resourceUrl,omitempty"`
|
||||
CoverURL string `json:"coverUrl,omitempty"`
|
||||
DurationDays int `json:"durationDays,omitempty"`
|
||||
DisplayGoldAmount int64 `json:"displayGoldAmount,omitempty"`
|
||||
GoldAmount int64 `json:"goldAmount,omitempty"`
|
||||
Probability int `json:"probability"`
|
||||
Status string `json:"status"`
|
||||
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.
|
||||
type RecordPageResponse struct {
|
||||
Records []DrawRecordPayload `json:"records"`
|
||||
Total int64 `json:"total"`
|
||||
Current int `json:"current"`
|
||||
Size int `json:"size"`
|
||||
Records []DrawRecordPayload `json:"records"`
|
||||
Total int64 `json:"total"`
|
||||
Current int `json:"current"`
|
||||
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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user