package handler import ( "net/http" "os" "path/filepath" "strconv" "api-server/internal/auth" "api-server/internal/db" "github.com/gin-gonic/gin" ) // AdminHandler 管理后台处理器 type AdminHandler struct { db *db.DB } // NewAdminHandler 创建 func NewAdminHandler(database *db.DB) *AdminHandler { return &AdminHandler{db: database} } // RegisterRoutes 注册路由 func (h *AdminHandler) RegisterRoutes(r *gin.RouterGroup) { admin := r.Group("/admin") admin.Use(auth.RequireAuth(h.db), auth.RequireAdmin()) { admin.GET("/users", h.ListUsers) admin.POST("/users", h.CreateUser) admin.PUT("/users/:id", h.UpdateUser) admin.DELETE("/users/:id", h.DeleteUser) admin.PUT("/users/:id/role", h.UpdateUserRole) admin.PUT("/users/:id/disable", h.ToggleUserDisable) admin.PUT("/users/:id/password", h.ResetUserPassword) admin.GET("/users/roles", h.UserRoles) admin.POST("/users/:id/points", h.AdjustUserPoints) admin.GET("/users/:id/points/transactions", h.UserPointTransactions) admin.GET("/points/config", h.GetPointsConfig) admin.PUT("/points/config", h.SetPointsConfig) admin.GET("/keys", h.ListAllKeys) admin.PATCH("/keys/:key", h.ToggleKey) admin.DELETE("/keys/:key", h.RevokeKey) admin.DELETE("/keys/:key/force", h.DeleteKey) admin.PATCH("/keys/:key/quota", h.SetKeyQuota) admin.GET("/config", h.GetHomeConfig) admin.PUT("/config", h.SetHomeConfig) } } // ListUsers 列出所有用户 func (h *AdminHandler) ListUsers(c *gin.Context) { users, err := h.db.ListUsers() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "data": users}) } // ── 积分管理 ── // GetPointsConfig 获取积分定价配置 func (h *AdminHandler) GetPointsConfig(c *gin.Context) { price, _ := h.db.GetSystemSetting("direct_call_price") c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{ "direct_call_price": price, }}) } // SetPointsConfig 设置积分定价配置 func (h *AdminHandler) SetPointsConfig(c *gin.Context) { var req struct { DirectCallPrice string `json:"direct_call_price"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if err := h.db.SetSystemSetting("direct_call_price", req.DirectCallPrice); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "已保存"}) } // AdjustUserPoints 调整用户积分(amount 为正数增加,负数扣除) func (h *AdminHandler) AdjustUserPoints(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } var req struct { Amount int64 `json:"amount"` Reason string `json:"reason"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if req.Reason == "" { req.Reason = "管理员调整" } if req.Amount >= 0 { if err := h.db.AddPoints(id, req.Amount, req.Reason); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } } else { if _, err := h.db.DeductPoints(id, -req.Amount, req.Reason); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": err.Error()}) return } } user, _ := h.db.GetUserByID(id) c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{"points": user.Points}, "msg": "积分已更新"}) } // UserPointTransactions 获取用户积分交易记录 func (h *AdminHandler) UserPointTransactions(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } txs, err := h.db.GetPointTransactionsByUser(id, 100, 0) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } if txs == nil { txs = []db.PointTransaction{} } c.JSON(http.StatusOK, gin.H{"code": 0, "data": txs}) } // CreateUser 管理员创建用户 func (h *AdminHandler) CreateUser(c *gin.Context) { var req struct { Email string `json:"email" binding:"required"` Password string `json:"password" binding:"required,min=6"` Name string `json:"name"` Role string `json:"role"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误: " + err.Error()}) return } if req.Role == "" { req.Role = "user" } if req.Role != "user" && req.Role != "admin" { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "无效的角色,仅支持 user/admin"}) return } hash, err := auth.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "密码加密失败"}) return } user, err := h.db.AdminCreateUser(req.Email, hash, req.Name, req.Role) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "data": user, "msg": "用户创建成功"}) } // UpdateUser 更新用户信息(名称、邮箱) func (h *AdminHandler) UpdateUser(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } var req struct { Name string `json:"name"` Email string `json:"email"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if req.Name != "" { if err := h.db.UpdateUser(id, req.Name); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } } if req.Email != "" { if err := h.db.UpdateUserEmail(id, req.Email); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } } user, err := h.db.GetUserByID(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "data": user, "msg": "已更新"}) } // DeleteUser 管理员删除用户 func (h *AdminHandler) DeleteUser(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } currentUser := auth.GetUser(c) if currentUser != nil && currentUser.ID == id { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "不能删除自己"}) return } if err := h.db.DeleteUserByID(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "用户已删除"}) } // ToggleUserDisable 禁用/启用用户 func (h *AdminHandler) ToggleUserDisable(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } currentUser := auth.GetUser(c) if currentUser != nil && currentUser.ID == id { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "不能禁用自己"}) return } var req struct { Disabled bool `json:"disabled"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if err := h.db.SetUserDisabled(id, req.Disabled); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } label := "已启用" if req.Disabled { label = "已禁用" } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": label}) } // ResetUserPassword 管理员重置用户密码 func (h *AdminHandler) ResetUserPassword(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } var req struct { Password string `json:"password" binding:"required,min=6"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "密码至少6位"}) return } user, err := h.db.GetUserByID(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"code": 1, "msg": "用户不存在"}) return } hash, err := auth.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": "密码加密失败"}) return } if err := h.db.UpdatePassword(user.Email, hash); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "密码已重置"}) } // UserRoles 获取角色分布统计 func (h *AdminHandler) UserRoles(c *gin.Context) { roleCounts, err := h.db.CountUsersByRole() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } // 构建角色详情列表 type roleInfo struct { Role string `json:"role"` UserCount int `json:"user_count"` Permission string `json:"permission"` } roles := []roleInfo{ {Role: "admin", Permission: "全部权限,包括用户管理、系统设置、产品管理等"}, {Role: "user", Permission: "基本权限,API 调用、工作台、个人设置"}, } for i := range roles { roles[i].UserCount = roleCounts[roles[i].Role] } c.JSON(http.StatusOK, gin.H{"code": 0, "data": roles}) } // UpdateUserRole 更新用户角色 func (h *AdminHandler) UpdateUserRole(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } var req struct { Role string `json:"role"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if req.Role != "user" && req.Role != "admin" { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "无效的角色,仅支持 user/admin"}) return } if err := h.db.UpdateUserRole(id, req.Role); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "角色已更新"}) } // ListAllKeys 列出所有 API Key func (h *AdminHandler) ListAllKeys(c *gin.Context) { keys, err := h.db.ListAllAPIKeys() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } type keyInfo struct { ID int64 `json:"id"` UserID int64 `json:"user_id"` Key string `json:"key"` Name string `json:"name"` Quota int64 `json:"quota"` Used int64 `json:"used"` Active bool `json:"active"` QuotaLocked bool `json:"quota_locked"` } list := make([]keyInfo, 0, len(keys)) for _, k := range keys { list = append(list, keyInfo{ ID: k.ID, UserID: k.UserID, Key: k.Key, Name: k.Name, Quota: k.Quota, Used: k.Used, Active: k.Active, QuotaLocked: k.QuotaLocked, }) } c.JSON(http.StatusOK, gin.H{"code": 0, "data": list}) } // ToggleKey 启用/禁用 API Key func (h *AdminHandler) ToggleKey(c *gin.Context) { key := c.Param("key") var req struct { Active bool `json:"active"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if err := h.db.SetAPIKeyActive(key, req.Active); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "已更新"}) } // RevokeKey 吊销 API Key func (h *AdminHandler) RevokeKey(c *gin.Context) { key := c.Param("key") if err := h.db.RevokeAPIKey(key); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "已吊销"}) } // DeleteKey 物理删除 API Key(管理员) func (h *AdminHandler) DeleteKey(c *gin.Context) { key := c.Param("key") if err := h.db.DeleteAPIKey(key); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "已删除"}) } // SetKeyQuota 修改配额 func (h *AdminHandler) SetKeyQuota(c *gin.Context) { key := c.Param("key") var req struct { Quota int64 `json:"quota"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } if err := h.db.SetAPIKeyQuota(key, req.Quota); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "配额已更新"}) } // GetHomeConfig 获取首页配置 func (h *AdminHandler) GetHomeConfig(c *gin.Context) { cfg, err := h.db.GetHomeConfig() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } for _, fk := range []string{"content_file", "dashboard_content_file", "dashboard_quickstart_file"} { if fpath, ok := cfg[fk]; ok && fpath != "" { abs := fpath if !filepath.IsAbs(fpath) { abs = filepath.Join(".", fpath) } data, err := os.ReadFile(abs) if err == nil { cfg[fk+"_content"] = string(data) } } } c.JSON(http.StatusOK, gin.H{"code": 0, "data": cfg}) } // SetHomeConfig 更新首页配置 func (h *AdminHandler) SetHomeConfig(c *gin.Context) { var req map[string]string if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "参数错误"}) return } for k, v := range req { if err := h.db.SetHomeConfig(k, v); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 1, "msg": err.Error()}) return } } c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "配置已更新"}) }