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 {
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"`

View File

@ -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,