feat: add daily task claim record filters

This commit is contained in:
hy001 2026-05-15 15:57:10 +08:00
parent 31433b0a1b
commit fc214f9991
5 changed files with 284 additions and 3 deletions

View File

@ -121,6 +121,8 @@ func registerTaskCenterRoutes(engine *gin.Engine, cfg config.Config, javaClient
taskcenter.ParseInt64(c.Query("userId")),
c.Query("taskCode"),
c.Query("status"),
c.Query("startTime"),
c.Query("endTime"),
int(taskcenter.ParseInt64(c.Query("cursor"))),
int(taskcenter.ParseInt64(c.Query("limit"))),
)

View File

@ -5,6 +5,7 @@ import (
"errors"
"net/http"
"sort"
"strconv"
"strings"
"time"
@ -195,25 +196,36 @@ func (s *Service) SaveConfig(ctx context.Context, req SaveConfigRequest) (*Confi
}
// PageClaimRecords 分页查询领取记录。
func (s *Service) PageClaimRecords(ctx context.Context, sysOrigin string, taskCategory string, userID int64, taskCode string, status string, cursor int, limit int) (*PageResponse[ClaimRecordView], error) {
func (s *Service) PageClaimRecords(ctx context.Context, sysOrigin string, taskCategory string, userID int64, taskCode string, status string, startTime string, endTime string, cursor int, limit int) (*ClaimRecordPageResponse, error) {
sysOrigin = s.normalizeSysOrigin(sysOrigin)
category := normalizeCategory(taskCategory)
if err := validateCategory(category); err != nil {
return nil, err
}
cursor, limit = NormalizePage(cursor, limit)
startAt, endAt, err := parseClaimRecordTimeRange(startTime, endTime)
if err != nil {
return nil, err
}
code := strings.ToUpper(strings.TrimSpace(taskCode))
query := s.db.WithContext(ctx).Model(&model.TaskCenterClaimRecord{}).
Where("sys_origin = ? AND task_category = ?", sysOrigin, category)
if userID > 0 {
query = query.Where("user_id = ?", userID)
}
if code := strings.ToUpper(strings.TrimSpace(taskCode)); code != "" {
if code != "" {
query = query.Where("task_code = ?", code)
}
if normalizedStatus := strings.ToUpper(strings.TrimSpace(status)); normalizedStatus != "" {
query = query.Where("status = ?", normalizedStatus)
}
if !startAt.IsZero() {
query = query.Where("create_time >= ?", startAt)
}
if !endAt.IsZero() {
query = query.Where("create_time <= ?", endAt)
}
var total int64
if err := query.Count(&total).Error; err != nil {
@ -230,7 +242,52 @@ func (s *Service) PageClaimRecords(ctx context.Context, sysOrigin string, taskCa
for _, row := range rows {
records = append(records, claimRecordView(row))
}
return &PageResponse[ClaimRecordView]{Records: records, Total: total, Current: cursor, Size: limit}, nil
summary, err := s.claimRecordRewardSummary(ctx, sysOrigin, category, userID, code, startAt, endAt)
if err != nil {
return nil, err
}
return &ClaimRecordPageResponse{Records: records, Total: total, Current: cursor, Size: limit, Summary: summary}, nil
}
func (s *Service) claimRecordRewardSummary(ctx context.Context, sysOrigin string, taskCategory string, userID int64, taskCode string, startAt time.Time, endAt time.Time) (ClaimRecordSummary, error) {
total, err := s.claimRecordRewardGoldSum(ctx, sysOrigin, taskCategory, userID, "", startAt, endAt)
if err != nil {
return ClaimRecordSummary{}, err
}
selected := int64(0)
if taskCode != "" {
selected, err = s.claimRecordRewardGoldSum(ctx, sysOrigin, taskCategory, userID, taskCode, startAt, endAt)
if err != nil {
return ClaimRecordSummary{}, err
}
}
return ClaimRecordSummary{
TotalRewardGold: total,
SelectedTaskRewardGold: selected,
TaskCode: taskCode,
}, nil
}
func (s *Service) claimRecordRewardGoldSum(ctx context.Context, sysOrigin string, taskCategory string, userID int64, taskCode string, startAt time.Time, endAt time.Time) (int64, error) {
query := s.db.WithContext(ctx).Model(&model.TaskCenterClaimRecord{}).
Where("sys_origin = ? AND task_category = ? AND status = ?", sysOrigin, taskCategory, ClaimStatusSuccess)
if userID > 0 {
query = query.Where("user_id = ?", userID)
}
if strings.TrimSpace(taskCode) != "" {
query = query.Where("task_code = ?", strings.ToUpper(strings.TrimSpace(taskCode)))
}
if !startAt.IsZero() {
query = query.Where("create_time >= ?", startAt)
}
if !endAt.IsZero() {
query = query.Where("create_time <= ?", endAt)
}
var total int64
if err := query.Select("COALESCE(SUM(reward_gold), 0)").Scan(&total).Error; err != nil {
return 0, err
}
return total, nil
}
func (s *Service) normalizeTaskInputs(inputs []SaveTaskConfigInput, category string) ([]model.TaskCenterTaskConfig, error) {
@ -579,6 +636,67 @@ func normalizeConditionJumpPage(jumpType string, jumpPage string) string {
return strings.TrimSpace(jumpPage)
}
func parseClaimRecordTimeRange(startRaw string, endRaw string) (time.Time, time.Time, error) {
var start time.Time
var end time.Time
var err error
if strings.TrimSpace(startRaw) != "" {
start, err = parseClaimRecordTime(startRaw, false)
if err != nil {
return time.Time{}, time.Time{}, NewAppError(http.StatusBadRequest, "bad_start_time", "startTime is invalid")
}
}
if strings.TrimSpace(endRaw) != "" {
end, err = parseClaimRecordTime(endRaw, true)
if err != nil {
return time.Time{}, time.Time{}, NewAppError(http.StatusBadRequest, "bad_end_time", "endTime is invalid")
}
}
if !start.IsZero() && !end.IsZero() && 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 parseClaimRecordTime(raw string, endOfDay bool) (time.Time, error) {
value := strings.TrimSpace(raw)
if value == "" {
return time.Time{}, nil
}
if millis, err := strconv.ParseInt(value, 10, 64); err == nil {
if millis > 1_000_000_000_000 {
return time.UnixMilli(millis), nil
}
return time.Unix(millis, 0), nil
}
layouts := []string{
time.RFC3339Nano,
time.RFC3339,
"2006-01-02 15:04:05",
"2006-01-02 15:04",
"2006-01-02",
}
var lastErr error
for _, layout := range layouts {
var parsed time.Time
var err error
if layout == time.RFC3339Nano || layout == time.RFC3339 {
parsed, err = time.Parse(layout, value)
} else {
parsed, err = time.ParseInLocation(layout, value, time.Local)
}
if err != nil {
lastErr = err
continue
}
if layout == "2006-01-02" && endOfDay {
parsed = parsed.Add(24*time.Hour - time.Nanosecond)
}
return parsed, nil
}
return time.Time{}, lastErr
}
func claimRecordView(row model.TaskCenterClaimRecord) ClaimRecordView {
return ClaimRecordView{
ID: row.ID,

View File

@ -410,6 +410,107 @@ func TestConditionJumpConfigAppliesToTasks(t *testing.T) {
}
}
func TestPageClaimRecordsFiltersTimeAndSummarizesGold(t *testing.T) {
service, db := newTestService(t, nil)
ctx := context.Background()
base := time.Date(2026, 5, 15, 10, 0, 0, 0, time.Local)
claimedAt := base
rows := []model.TaskCenterClaimRecord{
{
ID: 1,
SysOrigin: "LIKEI",
UserID: 1001,
TaskID: 11,
TaskCode: "DAILY_MIC",
TaskName: "Mic",
TaskCategory: TaskCategoryDaily,
CycleKey: "20260515",
RewardGold: 100,
WalletEventID: "wallet-1",
Status: ClaimStatusSuccess,
ClaimedAt: &claimedAt,
CreateTime: base,
UpdateTime: base,
},
{
ID: 2,
SysOrigin: "LIKEI",
UserID: 1002,
TaskID: 12,
TaskCode: "DAILY_GIFT",
TaskName: "Gift",
TaskCategory: TaskCategoryDaily,
CycleKey: "20260515",
RewardGold: 200,
WalletEventID: "wallet-2",
Status: ClaimStatusSuccess,
ClaimedAt: &claimedAt,
CreateTime: base.Add(time.Hour),
UpdateTime: base.Add(time.Hour),
},
{
ID: 3,
SysOrigin: "LIKEI",
UserID: 1003,
TaskID: 11,
TaskCode: "DAILY_MIC",
TaskName: "Mic",
TaskCategory: TaskCategoryDaily,
CycleKey: "20260515",
RewardGold: 999,
WalletEventID: "wallet-3",
Status: ClaimStatusFailed,
FailureReason: "wallet failed",
CreateTime: base.Add(2 * time.Hour),
UpdateTime: base.Add(2 * time.Hour),
},
{
ID: 4,
SysOrigin: "LIKEI",
UserID: 1004,
TaskID: 13,
TaskCode: "DAILY_OLD",
TaskName: "Old",
TaskCategory: TaskCategoryDaily,
CycleKey: "20260514",
RewardGold: 700,
WalletEventID: "wallet-4",
Status: ClaimStatusSuccess,
ClaimedAt: &claimedAt,
CreateTime: base.AddDate(0, 0, -1),
UpdateTime: base.AddDate(0, 0, -1),
},
}
if err := db.Create(&rows).Error; err != nil {
t.Fatalf("create claim records: %v", err)
}
resp, err := service.PageClaimRecords(
ctx,
"LIKEI",
TaskCategoryDaily,
0,
"daily_mic",
"",
base.Format("2006-01-02 15:04:05"),
base.Add(3*time.Hour).Format("2006-01-02 15:04:05"),
1,
20,
)
if err != nil {
t.Fatalf("PageClaimRecords() error = %v", err)
}
if resp.Total != 2 || len(resp.Records) != 2 {
t.Fatalf("records total=%d len=%d, want 2", resp.Total, len(resp.Records))
}
if resp.Summary.TotalRewardGold != 300 {
t.Fatalf("total reward gold = %d, want 300", resp.Summary.TotalRewardGold)
}
if resp.Summary.SelectedTaskRewardGold != 100 {
t.Fatalf("selected task reward gold = %d, want 100", resp.Summary.SelectedTaskRewardGold)
}
}
func TestSaveConfigDerivesConditionJumpsWhenPayloadOmitted(t *testing.T) {
service, _ := newTestService(t, nil)
ctx := context.Background()

View File

@ -216,6 +216,20 @@ type PageResponse[T any] struct {
Size int `json:"size"`
}
type ClaimRecordSummary struct {
TotalRewardGold int64 `json:"totalRewardGold"`
SelectedTaskRewardGold int64 `json:"selectedTaskRewardGold"`
TaskCode string `json:"taskCode,omitempty"`
}
type ClaimRecordPageResponse struct {
Records []ClaimRecordView `json:"records"`
Total int64 `json:"total"`
Current int `json:"current"`
Size int `json:"size"`
Summary ClaimRecordSummary `json:"summary"`
}
type ClaimRecordView struct {
ID int64 `json:"id,string"`
SysOrigin string `json:"sysOrigin"`

View File

@ -0,0 +1,46 @@
SET @current_schema := DATABASE();
SET @add_task_center_claim_admin_time_sql := IF(
NOT EXISTS (
SELECT 1
FROM information_schema.statistics
WHERE table_schema = @current_schema
AND table_name = 'task_center_claim_record'
AND index_name = 'idx_task_center_claim_admin_time'
),
'ALTER TABLE `task_center_claim_record` ADD INDEX `idx_task_center_claim_admin_time` (`sys_origin`, `task_category`, `create_time`, `id`)',
'SELECT 1'
);
PREPARE add_task_center_claim_admin_time_stmt FROM @add_task_center_claim_admin_time_sql;
EXECUTE add_task_center_claim_admin_time_stmt;
DEALLOCATE PREPARE add_task_center_claim_admin_time_stmt;
SET @add_task_center_claim_admin_task_time_sql := IF(
NOT EXISTS (
SELECT 1
FROM information_schema.statistics
WHERE table_schema = @current_schema
AND table_name = 'task_center_claim_record'
AND index_name = 'idx_task_center_claim_admin_task_time'
),
'ALTER TABLE `task_center_claim_record` ADD INDEX `idx_task_center_claim_admin_task_time` (`sys_origin`, `task_category`, `task_code`, `create_time`, `id`)',
'SELECT 1'
);
PREPARE add_task_center_claim_admin_task_time_stmt FROM @add_task_center_claim_admin_task_time_sql;
EXECUTE add_task_center_claim_admin_task_time_stmt;
DEALLOCATE PREPARE add_task_center_claim_admin_task_time_stmt;
SET @add_task_center_claim_admin_status_time_sql := IF(
NOT EXISTS (
SELECT 1
FROM information_schema.statistics
WHERE table_schema = @current_schema
AND table_name = 'task_center_claim_record'
AND index_name = 'idx_task_center_claim_admin_status_time'
),
'ALTER TABLE `task_center_claim_record` ADD INDEX `idx_task_center_claim_admin_status_time` (`sys_origin`, `task_category`, `status`, `create_time`)',
'SELECT 1'
);
PREPARE add_task_center_claim_admin_status_time_stmt FROM @add_task_center_claim_admin_status_time_sql;
EXECUTE add_task_center_claim_admin_status_time_stmt;
DEALLOCATE PREPARE add_task_center_claim_admin_status_time_stmt;