feat: 用户徽章与等级体系,积分经济管理重构
- 新增徽章系统:badge 服务与 handler、管理端配置页、BadgeChip/LevelBadge 展示组件 - 新增等级模型:model/level 及前端 levels 元数据 - 管理端经济模块拆分为 points/badges/levels 独立页面,EconomySettings 迁入设置 - 积分、签到、评论等服务适配等级与徽章逻辑
This commit is contained in:
@@ -123,11 +123,22 @@ type User struct {
|
||||
Banned bool `gorm:"default:false" json:"banned"`
|
||||
CanManageMessages bool `gorm:"not null;default:false" json:"can_manage_messages"` // 站点消息管理(仅站长可授;站长/超管固有全站监管)
|
||||
Points int `gorm:"not null;default:0" json:"points"` // 可用积分余额
|
||||
TotalPoints int `gorm:"not null;default:0" json:"total_points"` // 累计获得积分(经验值,定级依据;消费不减)
|
||||
Level int `gorm:"-" json:"level"` // 用户等级(AfterFind 由 TotalPoints 计算,纯展示不入库)
|
||||
TokenVersion int `gorm:"default:0" json:"-"` // token 版本号,改密码/封禁时递增使旧 JWT 失效
|
||||
LastSeenAt *time.Time `gorm:"index" json:"-"` // 最近活跃时间(在线统计,限频更新)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
// 管理员颁发的徽章;仅显式 Preload("User.Badges.Badge") 的查询点加载,未加载时省略
|
||||
Badges []UserBadge `gorm:"foreignKey:UserID" json:"badges,omitempty"`
|
||||
}
|
||||
|
||||
// AfterFind 由累计获得积分计算等级(纯算术,随行加载,所有序列化 User 的接口自动携带 level)
|
||||
func (u *User) AfterFind(tx *gorm.DB) error {
|
||||
u.Level = LevelOf(u.TotalPoints)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshToken 刷新令牌表(支持服务端撤销、一次性轮转与盗用检测)
|
||||
@@ -276,6 +287,32 @@ type PointLedger struct {
|
||||
CreatedAt time.Time `gorm:"index" json:"created_at"`
|
||||
}
|
||||
|
||||
// Badge 管理员自定义徽章定义(名称/图标/颜色)
|
||||
type Badge struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"size:32;not null;uniqueIndex" json:"name"` // ≤16 字(runes 校验见 service 层)
|
||||
Icon string `gorm:"size:32;not null;default:''" json:"icon"` // lucide 图标名(前端白名单渲染,未知回落 Medal)
|
||||
Color string `gorm:"size:16;not null;default:''" json:"color"` // CSS token 色板键(gold/accent/ok/danger/info/purple/teal)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// UserBadge 徽章授予记录(同一徽章每用户至多一枚)
|
||||
type UserBadge struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
BadgeID uint `gorm:"not null;uniqueIndex:idx_badge_user" json:"badge_id"`
|
||||
UserID uint `gorm:"not null;uniqueIndex:idx_badge_user;index" json:"user_id"`
|
||||
Badge Badge `gorm:"foreignKey:BadgeID" json:"badge"` // 嵌套 Preload("User.Badges.Badge") 时填充
|
||||
AwardedBy uint `json:"awarded_by"`
|
||||
AwardedAt time.Time `json:"awarded_at"`
|
||||
}
|
||||
|
||||
// 徽章色板键(对应前端 CSS token --<color> / --<color>-soft)
|
||||
var BadgeColors = map[string]bool{
|
||||
"gold": true, "accent": true, "ok": true, "danger": true,
|
||||
"info": true, "purple": true, "teal": true,
|
||||
}
|
||||
|
||||
// 积分流水原因
|
||||
const (
|
||||
PointReasonCheckin = "checkin"
|
||||
@@ -285,6 +322,11 @@ const (
|
||||
PointReasonBountyEscrow = "bounty_escrow"
|
||||
PointReasonBountyRefund = "bounty_refund"
|
||||
PointReasonBountyAward = "bounty_award"
|
||||
PointReasonPostReward = "post_reward" // 发帖奖励(可配置,0=关闭)
|
||||
PointReasonReplyReward = "reply_reward" // 回复奖励(可配置,0=关闭)
|
||||
PointReasonStreakBonus = "streak_bonus" // 连续签到里程碑加成
|
||||
PointReasonRecommend = "recommend_reward" // 帖子被推荐奖励(可配置,0=关闭,每帖仅一次)
|
||||
PointReasonAdminAdjust = "admin_adjust" // 管理员手动调整(加分计累计可升级;扣分仅扣余额)
|
||||
)
|
||||
|
||||
// PostPollVote 投票记录(多选时同一用户多行)
|
||||
@@ -396,6 +438,7 @@ const (
|
||||
NotificationTypeRejected = "rejected" // 内容审核未通过
|
||||
NotificationTypeMention = "mention" // 群聊中 @ 了你(PostID=0,用 RoomID/MessageID)
|
||||
NotificationTypePendingReview = "pending_review" // 有新内容待审核(发给可审者)
|
||||
NotificationTypeBadge = "badge" // 获得管理员颁发的徽章
|
||||
NotificationTypeDeleted = "deleted" // 帖子被管理员删除
|
||||
)
|
||||
|
||||
@@ -414,8 +457,9 @@ type Notification struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
Actor User `gorm:"foreignKey:ActorID" json:"actor,omitempty"`
|
||||
Post Post `gorm:"foreignKey:PostID" json:"post,omitempty"`
|
||||
Room *ChatRoom `gorm:"foreignKey:RoomID;constraint:-" json:"room,omitempty"`
|
||||
// PostID 允许为 0(mention/badge 等无关联帖子),禁建 FK 约束,否则 post_id=0 插入违反外键
|
||||
Post Post `gorm:"foreignKey:PostID;constraint:-" json:"post,omitempty"`
|
||||
Room *ChatRoom `gorm:"foreignKey:RoomID;constraint:-" json:"room,omitempty"`
|
||||
}
|
||||
|
||||
// Announcement 站点公告(后台文章管理:支持草稿、标签与标签预设色)
|
||||
|
||||
Reference in New Issue
Block a user