Files
jiang13-forum/routers/web/admin_users.go
freefire 4ffcaecba2 feat: Admin 设用户等级并展示徽章于用户页
补齐 SetUserLevel 表单;用户主页/资料页展示徽章并触发 EvaluateAuto;勾选 §H Exp 与防刷。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 04:02:24 +08:00

227 lines
5.4 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 (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"git.iioio.com/freefire/jiang13-forum/models"
"git.iioio.com/freefire/jiang13-forum/modules/webctx"
"git.iioio.com/freefire/jiang13-forum/services"
"github.com/gin-gonic/gin"
)
type adminUserRow struct {
ID uint
Username string
Nickname string
Email string
Role string
RoleLabel string
Points int
Level int
Verified bool
Banned bool
IsAdmin bool
Created string
}
type adminUsersData struct {
AdminChrome
Users []adminUserRow
Keyword string
Filter string
Page int
HasPrev bool
HasMore bool
PrevPage int
NextPage int
QuerySuffix string
LevelOptions []int
}
// AdminUsersGet 用户列表
func (d Deps) AdminUsersGet(c *gin.Context) {
ctx := d.ctx(c)
d.renderAdminUsers(ctx, "")
}
func (d Deps) renderAdminUsers(ctx *webctx.Context, errMsg string) {
chrome := d.adminChrome(ctx, "用户", "users")
chrome.Error = errMsg
page, _ := strconv.Atoi(ctx.C.DefaultQuery("page", "1"))
if page < 1 {
page = 1
}
keyword := strings.TrimSpace(ctx.C.Query("q"))
filter := strings.TrimSpace(ctx.C.DefaultQuery("filter", "all"))
if filter == "" {
filter = "all"
}
size := d.Settings.PageSizeDefault()
if size < 1 {
size = 20
}
data := adminUsersData{
AdminChrome: chrome,
Keyword: keyword,
Filter: filter,
Page: page,
PrevPage: page - 1,
NextPage: page + 1,
HasPrev: page > 1,
LevelOptions: adminLevelOptions(),
}
q := url.Values{}
if keyword != "" {
q.Set("q", keyword)
}
if filter != "" && filter != "all" {
q.Set("filter", filter)
}
data.QuerySuffix = q.Encode()
if d.User != nil {
list, total, err := d.User.ListUsers(services.UserListQuery{
Page: page, Size: size, Keyword: keyword, Filter: filter,
})
if err != nil {
chrome.Error = err.Error()
data.AdminChrome = chrome
ctx.HTML(http.StatusOK, "admin/users", data)
return
}
data.HasMore = int64(page*size) < total
data.Users = make([]adminUserRow, 0, len(list))
for _, u := range list {
nick := strings.TrimSpace(u.Nickname)
if nick == "" {
nick = u.Username
}
data.Users = append(data.Users, adminUserRow{
ID: u.ID, Username: u.Username, Nickname: nick, Email: u.Email,
Role: string(u.Role), RoleLabel: adminUserRoleLabel(u.Role),
Points: u.Points, Level: models.LevelFromExp(u.Exp),
Verified: u.Verified, Banned: u.Banned,
IsAdmin: u.Role == models.RoleAdmin,
Created: u.CreatedAt.Format("2006-01-02"),
})
}
}
ctx.HTML(http.StatusOK, "admin/users", data)
}
func adminUserRoleLabel(role models.Role) string {
switch role {
case models.RoleAdmin:
return "管理员"
default:
return "用户"
}
}
func adminLevelOptions() []int {
n := models.MaxLevel()
out := make([]int, n)
for i := 0; i < n; i++ {
out[i] = i + 1
}
return out
}
// AdminUserBanPost 禁言 / 解禁
func (d Deps) AdminUserBanPost(c *gin.Context) {
ctx := d.ctx(c)
if !ctx.CheckCSRF() {
d.renderAdminUsers(ctx, "无效请求,请重试")
return
}
if d.User == nil {
d.renderAdminUsers(ctx, "用户服务未就绪")
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
banned := c.PostForm("banned") == "1" || c.PostForm("banned") == "on"
if err := d.User.BanUser(uint(id64), banned); err != nil {
d.renderAdminUsers(ctx, err.Error())
return
}
if banned {
ctx.SetFlash("已禁言该用户")
} else {
ctx.SetFlash("已解除禁言")
}
ctx.Redirect("/admin/users")
}
// AdminUserVerifyPost 认证开关
func (d Deps) AdminUserVerifyPost(c *gin.Context) {
ctx := d.ctx(c)
if !ctx.CheckCSRF() {
d.renderAdminUsers(ctx, "无效请求,请重试")
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
verified := c.PostForm("verified") == "1" || c.PostForm("verified") == "on"
if err := services.SetVerified(uint(id64), verified); err != nil {
d.renderAdminUsers(ctx, err.Error())
return
}
if verified {
ctx.SetFlash("已设为认证用户")
} else {
ctx.SetFlash("已取消认证")
}
ctx.Redirect("/admin/users")
}
// AdminUserPointsPost 调整积分
func (d Deps) AdminUserPointsPost(c *gin.Context) {
ctx := d.ctx(c)
if !ctx.CheckCSRF() {
d.renderAdminUsers(ctx, "无效请求,请重试")
return
}
if d.Points == nil {
d.renderAdminUsers(ctx, "积分服务未就绪")
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
delta, err := strconv.Atoi(strings.TrimSpace(c.PostForm("delta")))
if err != nil || delta == 0 {
d.renderAdminUsers(ctx, "请填写非零整数增减额")
return
}
note := strings.TrimSpace(c.PostForm("note"))
bal, err := d.Points.AdminAdjust(uint(id64), delta, note)
if err != nil {
d.renderAdminUsers(ctx, err.Error())
return
}
ctx.SetFlash(fmt.Sprintf("积分已调整,当前余额 %d", bal))
ctx.Redirect("/admin/users")
}
// AdminUserLevelPost 设定等级(同步 Exp 到门槛)
func (d Deps) AdminUserLevelPost(c *gin.Context) {
ctx := d.ctx(c)
if !ctx.CheckCSRF() {
d.renderAdminUsers(ctx, "无效请求,请重试")
return
}
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
level, err := strconv.Atoi(strings.TrimSpace(c.PostForm("level")))
if err != nil {
d.renderAdminUsers(ctx, "请选择有效等级")
return
}
if err := services.SetUserLevel(uint(id64), level); err != nil {
d.renderAdminUsers(ctx, err.Error())
return
}
ctx.SetFlash(fmt.Sprintf("已设为 Lv%d经验门槛 %d", level, models.ExpForLevel(level)))
ctx.Redirect("/admin/users")
}