Files
jiang13-bbs/backend/handler/admin_points.go
freefire 4ae57d9392 feat: 用户徽章与等级体系,积分经济管理重构
- 新增徽章系统:badge 服务与 handler、管理端配置页、BadgeChip/LevelBadge 展示组件
- 新增等级模型:model/level 及前端 levels 元数据
- 管理端经济模块拆分为 points/badges/levels 独立页面,EconomySettings 迁入设置
- 积分、签到、评论等服务适配等级与徽章逻辑
2026-09-25 16:38:29 +08:00

68 lines
2.1 KiB
Go
Raw Permalink 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 handler
import (
"net/http"
"strconv"
"strings"
"unicode/utf8"
"github.com/gin-gonic/gin"
)
// ===== 积分管理(usersAPI 组:RequireStaff + CSRF + PermUsers,超管/站长) =====
// adminAdjustMaxDelta 单次调整量的绝对值上界(与积分规则值上界同级)
const adminAdjustMaxDelta = 100000
// AdminAdjustUserPoints 管理员手动调整用户积分:
// 加分走 CreditTx(同步累加累计获得→可升级),扣分走 DebitTx(仅扣余额、不降级);
// 理由必填,写入流水 note(reason=admin_adjust),响应返回调整后余额/累计/等级。
func (h *Handlers) AdminAdjustUserPoints(c *gin.Context) {
id, ok := parseAdminUserID(c)
if !ok {
return
}
var body struct {
Delta int `json:"delta"`
Note string `json:"note"`
}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"})
return
}
if body.Delta == 0 || body.Delta > adminAdjustMaxDelta || body.Delta < -adminAdjustMaxDelta {
c.JSON(http.StatusBadRequest, gin.H{"error": "积分数量无效"})
return
}
note := strings.TrimSpace(body.Note)
if note == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "调整理由必填"})
return
}
if utf8.RuneCountInString(note) > 200 {
c.JSON(http.StatusBadRequest, gin.H{"error": "调整理由过长(≤200 字)"})
return
}
bal, total, level, err := h.Points.AdminAdjustPoints(id, body.Delta, note)
if err != nil {
respondAdminUserError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{"balance": bal, "total_points": total, "level": level})
}
// AdminUserPointLedger 某用户的积分流水(分页,id DESC)
func (h *Handlers) AdminUserPointLedger(c *gin.Context) {
id, ok := parseAdminUserID(c)
if !ok {
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
items, total, err := h.Points.AdminUserLedger(id, page, 20)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取积分流水失败"})
return
}
c.JSON(http.StatusOK, gin.H{"items": nonNilSlice(items), "total": total, "page": page, "size": 20})
}