This commit is contained in:
hy001 2026-05-16 02:26:20 +08:00
parent 0d8e6f599b
commit 8ace95f78f
2 changed files with 97 additions and 46 deletions

View File

@ -21,6 +21,7 @@ func (UserBadgeBackpack) TableName() string { return "user_badge_backpack" }
type SysBadgeConfig struct { type SysBadgeConfig struct {
ID int64 `gorm:"column:id;primaryKey"` ID int64 `gorm:"column:id;primaryKey"`
Type string `gorm:"column:type;size:64"` Type string `gorm:"column:type;size:64"`
DisplayType string `gorm:"column:display_type;size:16"`
BadgeLevel int `gorm:"column:badge_level"` BadgeLevel int `gorm:"column:badge_level"`
Milestone int64 `gorm:"column:milestone"` Milestone int64 `gorm:"column:milestone"`
BadgeName string `gorm:"column:badge_name;size:128"` BadgeName string `gorm:"column:badge_name;size:128"`

View File

@ -16,6 +16,7 @@ import (
type badgePictureRow struct { type badgePictureRow struct {
BadgeID int64 `gorm:"column:badge_id"` BadgeID int64 `gorm:"column:badge_id"`
SourceType sql.NullString `gorm:"column:source_type"` SourceType sql.NullString `gorm:"column:source_type"`
DisplayType sql.NullString `gorm:"column:display_type"`
BadgeLevel sql.NullInt64 `gorm:"column:badge_level"` BadgeLevel sql.NullInt64 `gorm:"column:badge_level"`
Milestone sql.NullInt64 `gorm:"column:milestone"` Milestone sql.NullInt64 `gorm:"column:milestone"`
BadgeName sql.NullString `gorm:"column:badge_name"` 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) { func (s *Service) currentForUser(ctx context.Context, sysOrigin string, userID int64) (*CurrentBadgeListResponse, error) {
now := s.now() 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
longBadges := append(vipLongBadges, backpackLongBadges...)
shortBadges := append(vipShortBadges, backpackShortBadges...)
badges := make([]CurrentBadgeItem, 0, len(longBadges)+len(shortBadges)) badges := make([]CurrentBadgeItem, 0, len(longBadges)+len(shortBadges))
badges = append(badges, longBadges...) badges = append(badges, longBadges...)
@ -78,30 +81,57 @@ func (s *Service) currentForUser(ctx context.Context, sysOrigin string, userID i
}, nil }, 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 var state model.UserVipState
err := s.db.WithContext(ctx). err := s.db.WithContext(ctx).
Where("sys_origin = ? AND user_id = ?", sysOrigin, userID). Where("sys_origin = ? AND user_id = ?", sysOrigin, userID).
First(&state).Error First(&state).Error
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return []CurrentBadgeItem{}, 0, nil return []CurrentBadgeItem{}, []CurrentBadgeItem{}, nil, nil
} }
if err != nil { if err != nil {
return nil, 0, err return nil, nil, nil, err
} }
if !isActiveVIPState(state, now) || state.BadgeResourceID <= 0 { if !isActiveVIPState(state, now) {
return []CurrentBadgeItem{}, 0, nil return []CurrentBadgeItem{}, []CurrentBadgeItem{}, nil, nil
} }
row, err := s.loadBadgePicture(ctx, sysOrigin, state.BadgeResourceID) type vipStateBadgeResource struct {
id int64
defaultType string
name string
url string
}
resources := []vipStateBadgeResource{
{id: state.BadgeResourceID, defaultType: badgeDisplayTypeLong, name: state.BadgeName, url: state.BadgeURL},
{id: state.ShortBadgeResourceID, defaultType: badgeDisplayTypeShort, name: state.ShortBadgeName, url: state.ShortBadgeURL},
}
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 { if err != nil {
return nil, 0, err return nil, nil, nil, err
}
displayType := normalizeBadgeDisplayType(nullString(row.DisplayType), nullString(row.SourceType), resource.defaultType)
if displayType == "" {
continue
} }
item := CurrentBadgeItem{ item := CurrentBadgeItem{
ID: formatID(state.BadgeResourceID), ID: formatID(resource.id),
BadgeID: formatID(state.BadgeResourceID), BadgeID: formatID(resource.id),
Type: badgeDisplayTypeLong, Type: displayType,
SourceType: badgeSourceTypeVIP, SourceType: nullString(row.SourceType),
BadgeLevel: nullInt(row.BadgeLevel), BadgeLevel: nullInt(row.BadgeLevel),
BadgeKey: nullString(row.BadgeKey), BadgeKey: nullString(row.BadgeKey),
Level: state.Level, Level: state.Level,
@ -110,25 +140,31 @@ func (s *Service) loadLongBadges(ctx context.Context, sysOrigin string, userID i
Use: true, Use: true,
ExpireTime: formatMillis(state.ExpireAt), ExpireTime: formatMillis(state.ExpireAt),
ExpireAt: formatTime(state.ExpireAt), ExpireAt: formatTime(state.ExpireAt),
UpdateTime: formatMillis(firstNonZeroTime(state.UpdateTime, state.StartAt, state.CreateTime)), UpdateTime: formatMillis(updateAt),
UpdatedAt: formatTime(firstNonZeroTime(state.UpdateTime, state.StartAt, state.CreateTime)), UpdatedAt: formatTime(updateAt),
} }
item.BadgeName = firstNonEmpty(nullString(row.BadgeName), state.BadgeName, state.DisplayName) item.BadgeName = firstNonEmpty(nullString(row.BadgeName), resource.name, state.DisplayName)
item.Name = item.BadgeName item.Name = item.BadgeName
item.SelectURL = firstNonEmpty(nullString(row.SelectURL), state.BadgeURL) item.SelectURL = firstNonEmpty(nullString(row.SelectURL), resource.url)
item.NotSelectURL = nullString(row.NotSelectURL) item.NotSelectURL = nullString(row.NotSelectURL)
item.AnimationURL = nullString(row.AnimationURL) item.AnimationURL = nullString(row.AnimationURL)
item.URL = firstNonEmpty(item.SelectURL, state.BadgeURL) item.URL = firstNonEmpty(item.SelectURL, resource.url)
if row.Milestone.Valid && row.Milestone.Int64 > 0 { if row.Milestone.Valid && row.Milestone.Int64 > 0 {
item.Milestone = formatID(row.Milestone.Int64) item.Milestone = formatID(row.Milestone.Int64)
} }
if item.Name == "" && item.URL == "" { if item.Name == "" && item.URL == "" {
return []CurrentBadgeItem{}, state.BadgeResourceID, nil continue
} }
return []CurrentBadgeItem{item}, state.BadgeResourceID, nil if displayType == badgeDisplayTypeLong {
longBadges = append(longBadges, item)
} else {
shortBadges = append(shortBadges, item)
}
}
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) rows := make([]badgePictureRow, 0)
query := s.db.WithContext(ctx). query := s.db.WithContext(ctx).
Table("user_badge_backpack AS backpack"). 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.expire_time AS expire_time,
backpack.update_time AS update_time, backpack.update_time AS update_time,
config.type AS source_type, config.type AS source_type,
config.display_type AS display_type,
config.badge_level AS badge_level, config.badge_level AS badge_level,
config.milestone AS milestone, config.milestone AS milestone,
config.badge_name AS badge_name, 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.user_id = ?", userID).
Where("backpack.is_use_props = ?", true). Where("backpack.is_use_props = ?", true).
Where("config.is_del = ?", false). Where("config.is_del = ?", false).
Where("config.type IN ?", []string{badgeSourceTypeActivity, badgeSourceTypeAchievement}).
Where("(backpack.expire_type = ? OR backpack.expire_time > ?)", expireTypePermanent, now). Where("(backpack.expire_type = ? OR backpack.expire_time > ?)", expireTypePermanent, now).
Order("backpack.update_time DESC, backpack.id DESC") Order("backpack.update_time DESC, backpack.id DESC")
if excludeBadgeID > 0 { if len(excludeBadgeIDs) > 0 {
query = query.Where("backpack.badge_id <> ?", excludeBadgeID) 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 { 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 { for _, row := range rows {
displayType := normalizeBadgeDisplayType(nullString(row.DisplayType), nullString(row.SourceType), "")
if displayType == "" {
continue
}
item := CurrentBadgeItem{ item := CurrentBadgeItem{
ID: formatID(row.BadgeID), ID: formatID(row.BadgeID),
BadgeID: formatID(row.BadgeID), BadgeID: formatID(row.BadgeID),
Type: badgeDisplayTypeShort, Type: displayType,
SourceType: nullString(row.SourceType), SourceType: nullString(row.SourceType),
Name: nullString(row.BadgeName), Name: nullString(row.BadgeName),
BadgeName: 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 { if row.Milestone.Valid && row.Milestone.Int64 > 0 {
item.Milestone = formatID(row.Milestone.Int64) 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) { 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(` Select(`
config.id AS badge_id, config.id AS badge_id,
config.type AS source_type, config.type AS source_type,
config.display_type AS display_type,
config.badge_level AS badge_level, config.badge_level AS badge_level,
config.milestone AS milestone, config.milestone AS milestone,
config.badge_name AS badge_name, config.badge_name AS badge_name,