package user import ( "context" "bindbox-game/internal/repository/mysql/model" ) type ItemCardWithTemplate struct { *model.UserItemCards Name string `json:"name"` CardType int32 `json:"card_type"` ScopeType int32 `json:"scope_type"` EffectType int32 `json:"effect_type"` StackingStrategy int32 `json:"stacking_strategy"` Remark string `json:"remark"` Count int64 `json:"count"` UsedActivityName string `json:"used_activity_name"` UsedIssueNumber string `json:"used_issue_number"` UsedRewardName string `json:"used_reward_name"` } // ListAggregatedUserItemCards 获取聚合后的用户道具卡列表(按卡种分组) func (s *service) ListAggregatedUserItemCards(ctx context.Context, userID int64, status int32, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) { // 1. 计算分组总数 (Using UnderlyingDB for raw SQL flexibility) var countResult []struct { CardID int64 Total int64 } tx := s.readDB.UserItemCards.WithContext(ctx).ReadDB().UnderlyingDB(). Model(&model.UserItemCards{}). Where("user_id = ? AND status = ?", userID, status) // Count Distinct CardID err = tx.Distinct("card_id").Count(&total).Error if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } // 2. 分页查询分组数据 err = tx.Select("card_id, count(*) as total"). Group("card_id"). Order("card_id desc"). Offset((page - 1) * pageSize). Limit(pageSize). Scan(&countResult).Error if err != nil { return nil, 0, err } if len(countResult) == 0 { return []*ItemCardWithTemplate{}, 0, nil } // 3. 获取每组的一个实例(用于获取有效期等信息)和模板信息 cardIDs := make([]int64, 0, len(countResult)) for _, r := range countResult { cardIDs = append(cardIDs, r.CardID) } // 获取模板信息 tpls := map[int64]*model.SystemItemCards{} // Using GEN helpers for simple queries tplList, err := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(cardIDs...)).Find() if err != nil { return nil, 0, err } for _, t := range tplList { tpls[t.ID] = t } items = make([]*ItemCardWithTemplate, 0, len(countResult)) for _, r := range countResult { // Find latest instance using GEN helpers instance, _ := s.readDB.UserItemCards.WithContext(ctx).ReadDB(). Where(s.readDB.UserItemCards.UserID.Eq(userID), s.readDB.UserItemCards.Status.Eq(status), s.readDB.UserItemCards.CardID.Eq(r.CardID)). Order(s.readDB.UserItemCards.ID.Desc()). First() if instance == nil { instance = &model.UserItemCards{UserID: userID, CardID: r.CardID, Status: status} } tpl := tpls[r.CardID] var name, remark string var cardType, scopeType, effectType, stacking int32 if tpl != nil { name = tpl.Name cardType = tpl.CardType scopeType = tpl.ScopeType effectType = tpl.EffectType stacking = tpl.StackingStrategy remark = tpl.Remark } items = append(items, &ItemCardWithTemplate{ UserItemCards: instance, Name: name, CardType: cardType, ScopeType: scopeType, EffectType: effectType, StackingStrategy: stacking, Remark: remark, Count: r.Total, }) } return items, total, nil } // ListUserItemCards 获取用户道具卡列表 // 功能描述: // - 查询指定用户的道具卡列表 // - 支持分页查询,默认每页20条,最大100条 // - 按创建时间倒序排列 // // 参数说明: // - ctx: 上下文 // - userID: 用户ID // - page: 页码,从1开始,小于1时自动设为1 // - pageSize: 每页条数,小于1时设为20,大于100时设为100 // // 返回说明: // - items: 道具卡列表 // - total: 总记录数 // - err: 错误信息 func (s *service) ListUserItemCards(ctx context.Context, userID int64, page, pageSize int) (items []*model.UserItemCards, total int64, err error) { q := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where(s.readDB.UserItemCards.UserID.Eq(userID)) total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } items, err = q.Order(s.readDB.UserItemCards.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } return items, total, nil } func (s *service) ListUserItemCardsWithTemplate(ctx context.Context, userID int64, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) { q := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where(s.readDB.UserItemCards.UserID.Eq(userID)) total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } rows, err := q.Order(s.readDB.UserItemCards.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } cidMap := make(map[int64]struct{}) for _, r := range rows { if r.CardID > 0 { cidMap[r.CardID] = struct{}{} } } tpls := map[int64]*model.SystemItemCards{} if len(cidMap) > 0 { ids := make([]int64, 0, len(cidMap)) for id := range cidMap { ids = append(ids, id) } list, err := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(ids...)).Find() if err != nil { return nil, 0, err } for _, it := range list { tpls[it.ID] = it } } items = make([]*ItemCardWithTemplate, len(rows)) for i, r := range rows { tpl := tpls[r.CardID] var name string var cardType, scopeType, effectType, stacking int32 var remark string if tpl != nil { name = tpl.Name cardType = tpl.CardType scopeType = tpl.ScopeType effectType = tpl.EffectType stacking = tpl.StackingStrategy remark = tpl.Remark } items[i] = &ItemCardWithTemplate{ UserItemCards: r, Name: name, CardType: cardType, ScopeType: scopeType, EffectType: effectType, StackingStrategy: stacking, Remark: remark, Count: 1, // Individual record } } return items, total, nil } func (s *service) ListUserItemCardsWithTemplateUsable(ctx context.Context, userID int64, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) { q := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where( s.readDB.UserItemCards.UserID.Eq(userID), s.readDB.UserItemCards.Status.Eq(1), ) total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } rows, err := q.Order(s.readDB.UserItemCards.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } cidMap := make(map[int64]struct{}) for _, r := range rows { if r.CardID > 0 { cidMap[r.CardID] = struct{}{} } } tpls := map[int64]*model.SystemItemCards{} if len(cidMap) > 0 { ids := make([]int64, 0, len(cidMap)) for id := range cidMap { ids = append(ids, id) } list, err := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(ids...)).Find() if err != nil { return nil, 0, err } for _, it := range list { tpls[it.ID] = it } } items = make([]*ItemCardWithTemplate, len(rows)) for i, r := range rows { tpl := tpls[r.CardID] var name string var cardType, scopeType, effectType, stacking int32 var remark string if tpl != nil { name = tpl.Name cardType = tpl.CardType scopeType = tpl.ScopeType effectType = tpl.EffectType stacking = tpl.StackingStrategy remark = tpl.Remark } items[i] = &ItemCardWithTemplate{ UserItemCards: r, Name: name, CardType: cardType, ScopeType: scopeType, EffectType: effectType, StackingStrategy: stacking, Remark: remark, Count: 1, } } return items, total, nil } // ListUserItemCardsWithTemplateByStatus 按状态获取用户道具卡列表 func (s *service) ListUserItemCardsWithTemplateByStatus(ctx context.Context, userID int64, status int32, page, pageSize int) (items []*ItemCardWithTemplate, total int64, err error) { q := s.readDB.UserItemCards.WithContext(ctx).ReadDB().Where( s.readDB.UserItemCards.UserID.Eq(userID), s.readDB.UserItemCards.Status.Eq(status), ) total, err = q.Count() if err != nil { return nil, 0, err } if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 } rows, err := q.Order(s.readDB.UserItemCards.ID.Desc()).Offset((page - 1) * pageSize).Limit(pageSize).Find() if err != nil { return nil, 0, err } cidMap := make(map[int64]struct{}) for _, r := range rows { if r.CardID > 0 { cidMap[r.CardID] = struct{}{} } } tpls := map[int64]*model.SystemItemCards{} if len(cidMap) > 0 { ids := make([]int64, 0, len(cidMap)) for id := range cidMap { ids = append(ids, id) } list, err := s.readDB.SystemItemCards.WithContext(ctx).ReadDB().Where(s.readDB.SystemItemCards.ID.In(ids...)).Find() if err != nil { return nil, 0, err } for _, it := range list { tpls[it.ID] = it } } // Data collection for batch fetching var usedActivityIDs []int64 var usedIssueIDs []int64 var usedDrawLogIDs []int64 for _, r := range rows { if status == 2 { // ONLY for Used status if r.UsedActivityID > 0 { usedActivityIDs = append(usedActivityIDs, r.UsedActivityID) } if r.UsedIssueID > 0 { usedIssueIDs = append(usedIssueIDs, r.UsedIssueID) } if r.UsedDrawLogID > 0 { usedDrawLogIDs = append(usedDrawLogIDs, r.UsedDrawLogID) } } } // Maps for enrichment activityNameMap := make(map[int64]string) issueNumberMap := make(map[int64]string) rewardNameMap := make(map[int64]string) if len(usedActivityIDs) > 0 { var err error var acts []*model.Activities if acts, err = s.readDB.Activities.WithContext(ctx).ReadDB().Select(s.readDB.Activities.ID, s.readDB.Activities.Name).Where(s.readDB.Activities.ID.In(usedActivityIDs...)).Find(); err == nil { for _, a := range acts { activityNameMap[a.ID] = a.Name } } } if len(usedIssueIDs) > 0 { var err error var issues []*model.ActivityIssues if issues, err = s.readDB.ActivityIssues.WithContext(ctx).ReadDB().Select(s.readDB.ActivityIssues.ID, s.readDB.ActivityIssues.IssueNumber).Where(s.readDB.ActivityIssues.ID.In(usedIssueIDs...)).Find(); err == nil { for _, i := range issues { issueNumberMap[i.ID] = i.IssueNumber } } } if len(usedDrawLogIDs) > 0 { var err error var logs []*model.ActivityDrawLogs // join reward settings to get name if logs, err = s.readDB.ActivityDrawLogs.WithContext(ctx).ReadDB().Select(s.readDB.ActivityDrawLogs.ID, s.readDB.ActivityDrawLogs.RewardID).Where(s.readDB.ActivityDrawLogs.ID.In(usedDrawLogIDs...)).Find(); err == nil { var rewardIDs []int64 logToRewardID := make(map[int64]int64) for _, l := range logs { if l.RewardID > 0 { rewardIDs = append(rewardIDs, l.RewardID) logToRewardID[l.ID] = l.RewardID } } if len(rewardIDs) > 0 { var rewards []*model.ActivityRewardSettings if rewards, err = s.readDB.ActivityRewardSettings.WithContext(ctx).ReadDB().Select(s.readDB.ActivityRewardSettings.ID, s.readDB.ActivityRewardSettings.Name).Where(s.readDB.ActivityRewardSettings.ID.In(rewardIDs...)).Find(); err == nil { rewardNameMapByID := make(map[int64]string) for _, r := range rewards { rewardNameMapByID[r.ID] = r.Name } // Map log ID back to reward name for logID, rewardID := range logToRewardID { if name, ok := rewardNameMapByID[rewardID]; ok { rewardNameMap[logID] = name } } } } } } items = make([]*ItemCardWithTemplate, len(rows)) for i, r := range rows { tpl := tpls[r.CardID] var name string var cardType, scopeType, effectType, stacking int32 var remark string if tpl != nil { name = tpl.Name cardType = tpl.CardType scopeType = tpl.ScopeType effectType = tpl.EffectType stacking = tpl.StackingStrategy remark = tpl.Remark } item := &ItemCardWithTemplate{ UserItemCards: r, Name: name, CardType: cardType, ScopeType: scopeType, EffectType: effectType, StackingStrategy: stacking, Remark: remark, Count: 1, // Individual record } // Fill enrichment data if r.UsedActivityID > 0 { item.UsedActivityName = activityNameMap[r.UsedActivityID] } if r.UsedIssueID > 0 { item.UsedIssueNumber = issueNumberMap[r.UsedIssueID] } if r.UsedDrawLogID > 0 { item.UsedRewardName = rewardNameMap[r.UsedDrawLogID] } items[i] = item } return items, total, nil }