From 8ace95f78f716a3fa83539e03f6b11fdbf7bb101 Mon Sep 17 00:00:00 2001 From: hy001 Date: Sat, 16 May 2026 02:26:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=BD=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/model/badge_models.go | 1 + internal/service/userbadge/current.go | 142 +++++++++++++++++--------- 2 files changed, 97 insertions(+), 46 deletions(-) diff --git a/internal/model/badge_models.go b/internal/model/badge_models.go index 69a695f..49e26ea 100644 --- a/internal/model/badge_models.go +++ b/internal/model/badge_models.go @@ -21,6 +21,7 @@ func (UserBadgeBackpack) TableName() string { return "user_badge_backpack" } type SysBadgeConfig struct { ID int64 `gorm:"column:id;primaryKey"` Type string `gorm:"column:type;size:64"` + DisplayType string `gorm:"column:display_type;size:16"` BadgeLevel int `gorm:"column:badge_level"` Milestone int64 `gorm:"column:milestone"` BadgeName string `gorm:"column:badge_name;size:128"` diff --git a/internal/service/userbadge/current.go b/internal/service/userbadge/current.go index 7a10435..65aa75f 100644 --- a/internal/service/userbadge/current.go +++ b/internal/service/userbadge/current.go @@ -16,6 +16,7 @@ import ( type badgePictureRow struct { BadgeID int64 `gorm:"column:badge_id"` SourceType sql.NullString `gorm:"column:source_type"` + DisplayType sql.NullString `gorm:"column:display_type"` BadgeLevel sql.NullInt64 `gorm:"column:badge_level"` Milestone sql.NullInt64 `gorm:"column:milestone"` BadgeName sql.NullString `gorm:"column:badge_name"` @@ -56,14 +57,16 @@ func (s *Service) Other(ctx context.Context, viewer AuthUser, targetUserID int64 func (s *Service) currentForUser(ctx context.Context, sysOrigin string, userID int64) (*CurrentBadgeListResponse, error) { now := s.now() - longBadges, vipBadgeID, err := s.loadLongBadges(ctx, sysOrigin, userID, now) + vipLongBadges, vipShortBadges, vipBadgeIDs, err := s.loadVIPStateBadges(ctx, sysOrigin, userID, now) if err != nil { return nil, err } - shortBadges, err := s.loadShortBadges(ctx, sysOrigin, userID, vipBadgeID, now) + backpackLongBadges, backpackShortBadges, err := s.loadBackpackBadges(ctx, sysOrigin, userID, vipBadgeIDs, now) if err != nil { return nil, err } + longBadges := append(vipLongBadges, backpackLongBadges...) + shortBadges := append(vipShortBadges, backpackShortBadges...) badges := make([]CurrentBadgeItem, 0, len(longBadges)+len(shortBadges)) badges = append(badges, longBadges...) @@ -78,57 +81,90 @@ func (s *Service) currentForUser(ctx context.Context, sysOrigin string, userID i }, nil } -func (s *Service) loadLongBadges(ctx context.Context, sysOrigin string, userID int64, now time.Time) ([]CurrentBadgeItem, int64, error) { +func (s *Service) loadVIPStateBadges(ctx context.Context, sysOrigin string, userID int64, now time.Time) ([]CurrentBadgeItem, []CurrentBadgeItem, map[int64]struct{}, error) { var state model.UserVipState err := s.db.WithContext(ctx). Where("sys_origin = ? AND user_id = ?", sysOrigin, userID). First(&state).Error if errors.Is(err, gorm.ErrRecordNotFound) { - return []CurrentBadgeItem{}, 0, nil + return []CurrentBadgeItem{}, []CurrentBadgeItem{}, nil, nil } if err != nil { - return nil, 0, err + return nil, nil, nil, err } - if !isActiveVIPState(state, now) || state.BadgeResourceID <= 0 { - return []CurrentBadgeItem{}, 0, nil + if !isActiveVIPState(state, now) { + return []CurrentBadgeItem{}, []CurrentBadgeItem{}, nil, nil } - row, err := s.loadBadgePicture(ctx, sysOrigin, state.BadgeResourceID) - if err != nil { - return nil, 0, err + type vipStateBadgeResource struct { + id int64 + defaultType string + name string + url string } - item := CurrentBadgeItem{ - ID: formatID(state.BadgeResourceID), - BadgeID: formatID(state.BadgeResourceID), - Type: badgeDisplayTypeLong, - SourceType: badgeSourceTypeVIP, - BadgeLevel: nullInt(row.BadgeLevel), - BadgeKey: nullString(row.BadgeKey), - Level: state.Level, - LevelCode: state.LevelCode, - DisplayName: state.DisplayName, - Use: true, - ExpireTime: formatMillis(state.ExpireAt), - ExpireAt: formatTime(state.ExpireAt), - UpdateTime: formatMillis(firstNonZeroTime(state.UpdateTime, state.StartAt, state.CreateTime)), - UpdatedAt: formatTime(firstNonZeroTime(state.UpdateTime, state.StartAt, state.CreateTime)), + resources := []vipStateBadgeResource{ + {id: state.BadgeResourceID, defaultType: badgeDisplayTypeLong, name: state.BadgeName, url: state.BadgeURL}, + {id: state.ShortBadgeResourceID, defaultType: badgeDisplayTypeShort, name: state.ShortBadgeName, url: state.ShortBadgeURL}, } - item.BadgeName = firstNonEmpty(nullString(row.BadgeName), state.BadgeName, state.DisplayName) - item.Name = item.BadgeName - item.SelectURL = firstNonEmpty(nullString(row.SelectURL), state.BadgeURL) - item.NotSelectURL = nullString(row.NotSelectURL) - item.AnimationURL = nullString(row.AnimationURL) - item.URL = firstNonEmpty(item.SelectURL, state.BadgeURL) - if row.Milestone.Valid && row.Milestone.Int64 > 0 { - item.Milestone = formatID(row.Milestone.Int64) + + longBadges := make([]CurrentBadgeItem, 0, 1) + shortBadges := make([]CurrentBadgeItem, 0, 1) + excludeIDs := make(map[int64]struct{}, len(resources)) + updateAt := firstNonZeroTime(state.UpdateTime, state.StartAt, state.CreateTime) + for _, resource := range resources { + if resource.id <= 0 { + continue + } + if _, exists := excludeIDs[resource.id]; exists { + continue + } + excludeIDs[resource.id] = struct{}{} + row, err := s.loadBadgePicture(ctx, sysOrigin, resource.id) + if err != nil { + return nil, nil, nil, err + } + displayType := normalizeBadgeDisplayType(nullString(row.DisplayType), nullString(row.SourceType), resource.defaultType) + if displayType == "" { + continue + } + item := CurrentBadgeItem{ + ID: formatID(resource.id), + BadgeID: formatID(resource.id), + Type: displayType, + SourceType: nullString(row.SourceType), + BadgeLevel: nullInt(row.BadgeLevel), + BadgeKey: nullString(row.BadgeKey), + Level: state.Level, + LevelCode: state.LevelCode, + DisplayName: state.DisplayName, + Use: true, + ExpireTime: formatMillis(state.ExpireAt), + ExpireAt: formatTime(state.ExpireAt), + UpdateTime: formatMillis(updateAt), + UpdatedAt: formatTime(updateAt), + } + item.BadgeName = firstNonEmpty(nullString(row.BadgeName), resource.name, state.DisplayName) + item.Name = item.BadgeName + item.SelectURL = firstNonEmpty(nullString(row.SelectURL), resource.url) + item.NotSelectURL = nullString(row.NotSelectURL) + item.AnimationURL = nullString(row.AnimationURL) + item.URL = firstNonEmpty(item.SelectURL, resource.url) + if row.Milestone.Valid && row.Milestone.Int64 > 0 { + item.Milestone = formatID(row.Milestone.Int64) + } + if item.Name == "" && item.URL == "" { + continue + } + if displayType == badgeDisplayTypeLong { + longBadges = append(longBadges, item) + } else { + shortBadges = append(shortBadges, item) + } } - if item.Name == "" && item.URL == "" { - return []CurrentBadgeItem{}, state.BadgeResourceID, nil - } - return []CurrentBadgeItem{item}, state.BadgeResourceID, nil + return longBadges, shortBadges, excludeIDs, nil } -func (s *Service) loadShortBadges(ctx context.Context, sysOrigin string, userID int64, excludeBadgeID int64, now time.Time) ([]CurrentBadgeItem, error) { +func (s *Service) loadBackpackBadges(ctx context.Context, sysOrigin string, userID int64, excludeBadgeIDs map[int64]struct{}, now time.Time) ([]CurrentBadgeItem, []CurrentBadgeItem, error) { rows := make([]badgePictureRow, 0) query := s.db.WithContext(ctx). Table("user_badge_backpack AS backpack"). @@ -137,6 +173,7 @@ func (s *Service) loadShortBadges(ctx context.Context, sysOrigin string, userID backpack.expire_time AS expire_time, backpack.update_time AS update_time, config.type AS source_type, + config.display_type AS display_type, config.badge_level AS badge_level, config.milestone AS milestone, config.badge_name AS badge_name, @@ -150,22 +187,30 @@ func (s *Service) loadShortBadges(ctx context.Context, sysOrigin string, userID Where("backpack.user_id = ?", userID). Where("backpack.is_use_props = ?", true). Where("config.is_del = ?", false). - Where("config.type IN ?", []string{badgeSourceTypeActivity, badgeSourceTypeAchievement}). Where("(backpack.expire_type = ? OR backpack.expire_time > ?)", expireTypePermanent, now). Order("backpack.update_time DESC, backpack.id DESC") - if excludeBadgeID > 0 { - query = query.Where("backpack.badge_id <> ?", excludeBadgeID) + if len(excludeBadgeIDs) > 0 { + ids := make([]int64, 0, len(excludeBadgeIDs)) + for id := range excludeBadgeIDs { + ids = append(ids, id) + } + query = query.Where("backpack.badge_id NOT IN ?", ids) } if err := query.Scan(&rows).Error; err != nil { - return nil, err + return nil, nil, err } - items := make([]CurrentBadgeItem, 0, len(rows)) + longBadges := make([]CurrentBadgeItem, 0) + shortBadges := make([]CurrentBadgeItem, 0, len(rows)) for _, row := range rows { + displayType := normalizeBadgeDisplayType(nullString(row.DisplayType), nullString(row.SourceType), "") + if displayType == "" { + continue + } item := CurrentBadgeItem{ ID: formatID(row.BadgeID), BadgeID: formatID(row.BadgeID), - Type: badgeDisplayTypeShort, + Type: displayType, SourceType: nullString(row.SourceType), Name: nullString(row.BadgeName), BadgeName: nullString(row.BadgeName), @@ -184,9 +229,13 @@ func (s *Service) loadShortBadges(ctx context.Context, sysOrigin string, userID if row.Milestone.Valid && row.Milestone.Int64 > 0 { item.Milestone = formatID(row.Milestone.Int64) } - items = append(items, item) + if displayType == badgeDisplayTypeLong { + longBadges = append(longBadges, item) + } else { + shortBadges = append(shortBadges, item) + } } - return items, nil + return longBadges, shortBadges, nil } func (s *Service) loadBadgePicture(ctx context.Context, sysOrigin string, badgeID int64) (badgePictureRow, error) { @@ -196,6 +245,7 @@ func (s *Service) loadBadgePicture(ctx context.Context, sysOrigin string, badgeI Select(` config.id AS badge_id, config.type AS source_type, + config.display_type AS display_type, config.badge_level AS badge_level, config.milestone AS milestone, config.badge_name AS badge_name,