Files
jiang13-forum/routers/web/admin_badges.go
freefire 0c5fe8a1b4 feat: Admin 徽章定义 CRUD 与限定颁发收回
SSR /admin/badges 复用 UpsertDef/AwardLimited/Revoke,并新增 DeleteDef;指针指向 media。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 03:42:41 +08:00

320 lines
8.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package web
import (
"errors"
"net/http"
"strconv"
"strings"
"git.iioio.com/freefire/jiang13-forum/models"
"git.iioio.com/freefire/jiang13-forum/modules/webctx"
"github.com/gin-gonic/gin"
)
type adminBadgeRow struct {
ID uint
Code string
Name string
Description string
Icon string
Kind string
KindLabel string
Metric string
Threshold int
SortOrder int
Enabled bool
IsLimited bool
}
type adminBadgeForm struct {
ID uint
Code string
Name string
Description string
Icon string
Kind string
Metric string
Threshold int
SortOrder int
Enabled bool
IsEdit bool
}
type adminBadgesData struct {
AdminChrome
Badges []adminBadgeRow
LimitedBadges []adminBadgeRow
Form adminBadgeForm
AwardUser string
AwardBadgeID uint
}
// AdminBadgesGet 徽章定义列表
func (d Deps) AdminBadgesGet(c *gin.Context) {
ctx := d.ctx(c)
d.renderAdminBadges(ctx, "", adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}, "", 0)
}
func (d Deps) renderAdminBadges(ctx *webctx.Context, errMsg string, form adminBadgeForm, awardUser string, awardBadgeID uint) {
chrome := d.adminChrome(ctx, "徽章", "badges")
chrome.Error = errMsg
if form.Kind == "" {
form.Kind = models.BadgeKindLimited
}
data := adminBadgesData{
AdminChrome: chrome,
Form: form,
AwardUser: awardUser,
AwardBadgeID: awardBadgeID,
}
if d.Badge != nil {
list, err := d.Badge.ListDefs(true)
if err != nil {
chrome.Error = err.Error()
data.AdminChrome = chrome
} else {
data.Badges = make([]adminBadgeRow, 0, len(list))
data.LimitedBadges = make([]adminBadgeRow, 0)
for _, b := range list {
row := adminBadgeRowFrom(b)
data.Badges = append(data.Badges, row)
if row.IsLimited && row.Enabled {
data.LimitedBadges = append(data.LimitedBadges, row)
}
}
}
}
ctx.HTML(http.StatusOK, "admin/badges", data)
}
func adminBadgeRowFrom(b models.BadgeDef) adminBadgeRow {
label := "自动"
if b.Kind == models.BadgeKindLimited {
label = "限定"
}
return adminBadgeRow{
ID: b.ID, Code: b.Code, Name: b.Name, Description: b.Description, Icon: b.Icon,
Kind: b.Kind, KindLabel: label, Metric: b.Metric, Threshold: b.Threshold,
SortOrder: b.SortOrder, Enabled: b.Enabled, IsLimited: b.Kind == models.BadgeKindLimited,
}
}
func adminBadgeFormFrom(c *gin.Context) adminBadgeForm {
th, _ := strconv.Atoi(strings.TrimSpace(c.PostForm("threshold")))
so, _ := strconv.Atoi(strings.TrimSpace(c.PostForm("sort_order")))
kind := strings.TrimSpace(c.PostForm("kind"))
if kind == "" {
kind = models.BadgeKindLimited
}
return adminBadgeForm{
Code: strings.TrimSpace(c.PostForm("code")),
Name: strings.TrimSpace(c.PostForm("name")),
Description: strings.TrimSpace(c.PostForm("description")),
Icon: strings.TrimSpace(c.PostForm("icon")),
Kind: kind,
Metric: strings.TrimSpace(c.PostForm("metric")),
Threshold: th,
SortOrder: so,
Enabled: c.PostForm("enabled") == "1" || c.PostForm("enabled") == "on",
}
}
func (f adminBadgeForm) toDef() models.BadgeDef {
return models.BadgeDef{
Code: f.Code, Name: f.Name, Description: f.Description, Icon: f.Icon,
Kind: f.Kind, Metric: f.Metric, Threshold: f.Threshold,
SortOrder: f.SortOrder, Enabled: f.Enabled,
}
}
// AdminBadgeCreate 新建徽章定义
func (d Deps) AdminBadgeCreate(c *gin.Context) {
ctx := d.ctx(c)
form := adminBadgeFormFrom(c)
if !ctx.CheckCSRF() {
d.renderAdminBadges(ctx, "无效请求,请重试", form, "", 0)
return
}
if d.Badge == nil {
d.renderAdminBadges(ctx, "徽章服务未就绪", form, "", 0)
return
}
def := form.toDef()
if err := d.Badge.UpsertDef(&def); err != nil {
d.renderAdminBadges(ctx, err.Error(), form, "", 0)
return
}
ctx.SetFlash("徽章已保存")
ctx.Redirect("/admin/badges")
}
// AdminBadgeEditGet 编辑表单
func (d Deps) AdminBadgeEditGet(c *gin.Context) {
ctx := d.ctx(c)
if d.Badge == nil {
d.renderAdminBadges(ctx, "徽章服务未就绪", adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}, "", 0)
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
list, err := d.Badge.ListDefs(true)
if err != nil {
d.renderAdminBadges(ctx, err.Error(), adminBadgeForm{}, "", 0)
return
}
for _, b := range list {
if b.ID == uint(id64) {
d.renderAdminBadges(ctx, "", adminBadgeForm{
ID: b.ID, Code: b.Code, Name: b.Name, Description: b.Description, Icon: b.Icon,
Kind: b.Kind, Metric: b.Metric, Threshold: b.Threshold, SortOrder: b.SortOrder,
Enabled: b.Enabled, IsEdit: true,
}, "", 0)
return
}
}
ctx.SetFlash("徽章不存在")
ctx.Redirect("/admin/badges")
}
// AdminBadgeUpdate 更新徽章定义
func (d Deps) AdminBadgeUpdate(c *gin.Context) {
ctx := d.ctx(c)
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
form := adminBadgeFormFrom(c)
form.ID = uint(id64)
form.IsEdit = true
if !ctx.CheckCSRF() {
d.renderAdminBadges(ctx, "无效请求,请重试", form, "", 0)
return
}
if d.Badge == nil {
d.renderAdminBadges(ctx, "徽章服务未就绪", form, "", 0)
return
}
// 编辑时 code 不可改:从已有定义取 code避免误覆盖其它徽章
list, err := d.Badge.ListDefs(true)
if err != nil {
d.renderAdminBadges(ctx, err.Error(), form, "", 0)
return
}
var found *models.BadgeDef
for i := range list {
if list[i].ID == uint(id64) {
found = &list[i]
break
}
}
if found == nil {
d.renderAdminBadges(ctx, "徽章不存在", form, "", 0)
return
}
form.Code = found.Code
def := form.toDef()
if err := d.Badge.UpsertDef(&def); err != nil {
d.renderAdminBadges(ctx, err.Error(), form, "", 0)
return
}
ctx.SetFlash("徽章已更新")
ctx.Redirect("/admin/badges")
}
// AdminBadgeDelete 删除徽章定义
func (d Deps) AdminBadgeDelete(c *gin.Context) {
ctx := d.ctx(c)
if !ctx.CheckCSRF() {
d.renderAdminBadges(ctx, "无效请求,请重试", adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}, "", 0)
return
}
if d.Badge == nil {
d.renderAdminBadges(ctx, "徽章服务未就绪", adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}, "", 0)
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
if err := d.Badge.DeleteDef(uint(id64)); err != nil {
d.renderAdminBadges(ctx, err.Error(), adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}, "", 0)
return
}
ctx.SetFlash("徽章已删除")
ctx.Redirect("/admin/badges")
}
func (d Deps) resolveAwardUser(raw string) (uint, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return 0, errors.New("请填写用户名或用户 ID")
}
if id, err := strconv.ParseUint(raw, 10, 64); err == nil && id > 0 {
u, e := d.User.GetByID(uint(id))
if e != nil || u == nil {
return 0, errors.New("用户不存在")
}
return u.ID, nil
}
u, err := d.User.GetByUsername(raw)
if err != nil || u == nil {
return 0, errors.New("用户不存在")
}
return u.ID, nil
}
// AdminBadgeAwardPost 颁发限定徽章
func (d Deps) AdminBadgeAwardPost(c *gin.Context) {
ctx := d.ctx(c)
userRaw := strings.TrimSpace(c.PostForm("user"))
badgeID, _ := strconv.ParseUint(strings.TrimSpace(c.PostForm("badge_id")), 10, 64)
form := adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}
if !ctx.CheckCSRF() {
d.renderAdminBadges(ctx, "无效请求,请重试", form, userRaw, uint(badgeID))
return
}
if d.Badge == nil || d.User == nil {
d.renderAdminBadges(ctx, "服务未就绪", form, userRaw, uint(badgeID))
return
}
uid, err := d.resolveAwardUser(userRaw)
if err != nil {
d.renderAdminBadges(ctx, err.Error(), form, userRaw, uint(badgeID))
return
}
if badgeID == 0 {
d.renderAdminBadges(ctx, "请选择徽章", form, userRaw, 0)
return
}
if err := d.Badge.AwardLimited(uid, uint(badgeID), ctx.UserID()); err != nil {
d.renderAdminBadges(ctx, err.Error(), form, userRaw, uint(badgeID))
return
}
ctx.SetFlash("已颁发徽章")
ctx.Redirect("/admin/badges")
}
// AdminBadgeRevokePost 收回徽章
func (d Deps) AdminBadgeRevokePost(c *gin.Context) {
ctx := d.ctx(c)
userRaw := strings.TrimSpace(c.PostForm("user"))
badgeID, _ := strconv.ParseUint(strings.TrimSpace(c.PostForm("badge_id")), 10, 64)
form := adminBadgeForm{Kind: models.BadgeKindLimited, Enabled: true}
if !ctx.CheckCSRF() {
d.renderAdminBadges(ctx, "无效请求,请重试", form, userRaw, uint(badgeID))
return
}
if d.Badge == nil || d.User == nil {
d.renderAdminBadges(ctx, "服务未就绪", form, userRaw, uint(badgeID))
return
}
uid, err := d.resolveAwardUser(userRaw)
if err != nil {
d.renderAdminBadges(ctx, err.Error(), form, userRaw, uint(badgeID))
return
}
if badgeID == 0 {
d.renderAdminBadges(ctx, "请选择徽章", form, userRaw, 0)
return
}
if err := d.Badge.Revoke(uid, uint(badgeID)); err != nil {
d.renderAdminBadges(ctx, err.Error(), form, userRaw, uint(badgeID))
return
}
ctx.SetFlash("已收回徽章")
ctx.Redirect("/admin/badges")
}