feat(admin): add comprehensive admin management system

- Add admin handlers and services for users, posts, comments, groups, and dashboard
- Implement role permission management with ability to view and update permissions
- Add database seeding for roles and Casbin permission policies
- Upgrade Casbin from v2 to v3 with GORM adapter for persistent policy storage
- Add admin-specific repository methods with filtering and pagination
- Register new admin API routes under /api/v1/admin/*
This commit is contained in:
2026-03-14 18:01:55 +08:00
parent ae5b997924
commit d3065b30cc
28 changed files with 4018 additions and 107 deletions

View File

@@ -0,0 +1,151 @@
package handler
import (
"strconv"
"carrot_bbs/internal/dto"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
"github.com/gin-gonic/gin"
)
// AdminCommentHandler 管理端评论处理器
type AdminCommentHandler struct {
adminCommentService service.AdminCommentService
}
// NewAdminCommentHandler 创建管理端评论处理器
func NewAdminCommentHandler(adminCommentService service.AdminCommentService) *AdminCommentHandler {
return &AdminCommentHandler{
adminCommentService: adminCommentService,
}
}
// GetCommentList 获取评论列表
// GET /api/v1/admin/comments
func (h *AdminCommentHandler) GetCommentList(c *gin.Context) {
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
// 获取筛选参数
keyword := c.Query("keyword")
postID := c.Query("post_id")
authorID := c.Query("author_id")
status := c.Query("status")
startDate := c.Query("start_date")
endDate := c.Query("end_date")
// 验证状态参数
if status != "" && status != string(model.CommentStatusDraft) &&
status != string(model.CommentStatusPending) &&
status != string(model.CommentStatusPublished) &&
status != string(model.CommentStatusRejected) &&
status != string(model.CommentStatusDeleted) {
response.BadRequest(c, "invalid status value")
return
}
comments, total, err := h.adminCommentService.GetCommentList(c.Request.Context(), page, pageSize, keyword, postID, authorID, status, startDate, endDate)
if err != nil {
response.InternalServerError(c, "failed to get comment list")
return
}
response.Paginated(c, comments, total, page, pageSize)
}
// GetCommentDetail 获取评论详情
// GET /api/v1/admin/comments/:id
func (h *AdminCommentHandler) GetCommentDetail(c *gin.Context) {
commentID := c.Param("id")
if commentID == "" {
response.BadRequest(c, "comment id is required")
return
}
commentDetail, err := h.adminCommentService.GetCommentDetail(c.Request.Context(), commentID)
if err != nil {
response.NotFound(c, "comment not found")
return
}
response.Success(c, commentDetail)
}
// DeleteComment 删除评论
// DELETE /api/v1/admin/comments/:id
func (h *AdminCommentHandler) DeleteComment(c *gin.Context) {
commentID := c.Param("id")
if commentID == "" {
response.BadRequest(c, "comment id is required")
return
}
err := h.adminCommentService.DeleteComment(c.Request.Context(), commentID)
if err != nil {
response.HandleError(c, err, "failed to delete comment")
return
}
response.Success(c, dto.SuccessResponse{
Success: true,
Message: "comment deleted successfully",
})
}
// BatchDeleteComments 批量删除评论
// POST /api/v1/admin/comments/batch-delete
func (h *AdminCommentHandler) BatchDeleteComments(c *gin.Context) {
var req dto.AdminBatchDeleteCommentsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
result, err := h.adminCommentService.BatchDeleteComments(c.Request.Context(), req.IDs)
if err != nil {
response.InternalServerError(c, "failed to batch delete comments")
return
}
response.Success(c, result)
}
// GetPostComments 获取帖子的评论列表
// GET /api/v1/admin/posts/:postId/comments
func (h *AdminCommentHandler) GetPostComments(c *gin.Context) {
postID := c.Param("postId")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
comments, total, err := h.adminCommentService.GetCommentsByPostID(c.Request.Context(), postID, page, pageSize)
if err != nil {
response.InternalServerError(c, "failed to get post comments")
return
}
response.Paginated(c, comments, total, page, pageSize)
}

View File

@@ -0,0 +1,100 @@
package handler
import (
"strconv"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
"github.com/gin-gonic/gin"
)
// AdminDashboardHandler 管理端仪表盘处理器
type AdminDashboardHandler struct {
dashboardService service.AdminDashboardService
}
// NewAdminDashboardHandler 创建管理端仪表盘处理器
func NewAdminDashboardHandler(dashboardService service.AdminDashboardService) *AdminDashboardHandler {
return &AdminDashboardHandler{
dashboardService: dashboardService,
}
}
// GetStats 获取统计数据
// GET /api/v1/admin/dashboard/stats
func (h *AdminDashboardHandler) GetStats(c *gin.Context) {
stats, err := h.dashboardService.GetStats(c.Request.Context())
if err != nil {
response.InternalServerError(c, "failed to get dashboard stats")
return
}
response.Success(c, stats)
}
// GetUserActivityTrend 获取用户活跃度趋势
// GET /api/v1/admin/dashboard/user-activity
func (h *AdminDashboardHandler) GetUserActivityTrend(c *gin.Context) {
// 解析天数参数默认7天
days, _ := strconv.Atoi(c.DefaultQuery("days", "7"))
if days <= 0 {
days = 7
}
if days > 30 {
days = 30
}
trend, err := h.dashboardService.GetUserActivityTrend(c.Request.Context(), days)
if err != nil {
response.InternalServerError(c, "failed to get user activity trend")
return
}
response.Success(c, trend)
}
// GetContentStats 获取内容统计
// GET /api/v1/admin/dashboard/content-stats
func (h *AdminDashboardHandler) GetContentStats(c *gin.Context) {
stats, err := h.dashboardService.GetContentStats(c.Request.Context())
if err != nil {
response.InternalServerError(c, "failed to get content stats")
return
}
response.Success(c, stats)
}
// GetPendingContent 获取待审核内容
// GET /api/v1/admin/dashboard/pending-content
func (h *AdminDashboardHandler) GetPendingContent(c *gin.Context) {
// 解析limit参数默认10条
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
if limit <= 0 {
limit = 10
}
if limit > 50 {
limit = 50
}
content, err := h.dashboardService.GetPendingContent(c.Request.Context(), limit)
if err != nil {
response.InternalServerError(c, "failed to get pending content")
return
}
response.Success(c, content)
}
// GetOnlineUsers 获取在线用户数
// GET /api/v1/admin/dashboard/online-users
func (h *AdminDashboardHandler) GetOnlineUsers(c *gin.Context) {
onlineUsers, err := h.dashboardService.GetOnlineUsers(c.Request.Context())
if err != nil {
response.InternalServerError(c, "failed to get online users count")
return
}
response.Success(c, onlineUsers)
}

View File

@@ -0,0 +1,202 @@
package handler
import (
"strconv"
"carrot_bbs/internal/dto"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
"github.com/gin-gonic/gin"
)
// AdminGroupHandler 管理端群组处理器
type AdminGroupHandler struct {
adminGroupService service.AdminGroupService
}
// NewAdminGroupHandler 创建管理端群组处理器
func NewAdminGroupHandler(adminGroupService service.AdminGroupService) *AdminGroupHandler {
return &AdminGroupHandler{
adminGroupService: adminGroupService,
}
}
// GetGroupList 获取群组列表
// GET /api/v1/admin/groups
func (h *AdminGroupHandler) GetGroupList(c *gin.Context) {
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
// 获取筛选参数
keyword := c.Query("keyword")
status := c.Query("status")
ownerID := c.Query("owner_id")
startDate := c.Query("start_date")
endDate := c.Query("end_date")
groups, total, err := h.adminGroupService.GetGroupList(c.Request.Context(), page, pageSize, keyword, status, ownerID, startDate, endDate)
if err != nil {
response.InternalServerError(c, "failed to get group list")
return
}
response.Paginated(c, groups, total, page, pageSize)
}
// GetGroupDetail 获取群组详情
// GET /api/v1/admin/groups/:id
func (h *AdminGroupHandler) GetGroupDetail(c *gin.Context) {
groupID := c.Param("id")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
groupDetail, err := h.adminGroupService.GetGroupDetail(c.Request.Context(), groupID)
if err != nil {
response.NotFound(c, "group not found")
return
}
response.Success(c, groupDetail)
}
// GetGroupMembers 获取群组成员列表
// GET /api/v1/admin/groups/:id/members
func (h *AdminGroupHandler) GetGroupMembers(c *gin.Context) {
groupID := c.Param("id")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
members, err := h.adminGroupService.GetGroupMembers(c.Request.Context(), groupID, page, pageSize)
if err != nil {
response.HandleError(c, err, "failed to get group members")
return
}
response.Success(c, members)
}
// DeleteGroup 解散群组
// DELETE /api/v1/admin/groups/:id
func (h *AdminGroupHandler) DeleteGroup(c *gin.Context) {
groupID := c.Param("id")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
err := h.adminGroupService.DeleteGroup(c.Request.Context(), groupID)
if err != nil {
response.HandleError(c, err, "failed to delete group")
return
}
response.Success(c, dto.SuccessResponse{
Success: true,
Message: "group deleted successfully",
})
}
// UpdateGroupStatus 更新群组状态
// PUT /api/v1/admin/groups/:id/status
func (h *AdminGroupHandler) UpdateGroupStatus(c *gin.Context) {
groupID := c.Param("id")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
var req dto.AdminUpdateGroupStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
// 验证状态值
if req.Status != "active" && req.Status != "dissolved" {
response.BadRequest(c, "invalid status value, must be 'active' or 'dissolved'")
return
}
groupDetail, err := h.adminGroupService.UpdateGroupStatus(c.Request.Context(), groupID, req.Status)
if err != nil {
response.HandleError(c, err, "failed to update group status")
return
}
response.Success(c, groupDetail)
}
// RemoveMember 移除群组成员
// DELETE /api/v1/admin/groups/:id/members/:userId
func (h *AdminGroupHandler) RemoveMember(c *gin.Context) {
groupID := c.Param("id")
userID := c.Param("userId")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
if userID == "" {
response.BadRequest(c, "user id is required")
return
}
err := h.adminGroupService.RemoveMember(c.Request.Context(), groupID, userID)
if err != nil {
response.HandleError(c, err, "failed to remove member")
return
}
response.Success(c, dto.SuccessResponse{
Success: true,
Message: "member removed successfully",
})
}
// TransferOwner 转让群主
// PUT /api/v1/admin/groups/:id/transfer
func (h *AdminGroupHandler) TransferOwner(c *gin.Context) {
groupID := c.Param("id")
if groupID == "" {
response.BadRequest(c, "group id is required")
return
}
var req dto.AdminTransferGroupOwnerRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
groupDetail, err := h.adminGroupService.TransferOwner(c.Request.Context(), groupID, req.NewOwnerID)
if err != nil {
response.HandleError(c, err, "failed to transfer owner")
return
}
response.Success(c, groupDetail)
}

View File

@@ -0,0 +1,228 @@
package handler
import (
"strconv"
"carrot_bbs/internal/dto"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
"github.com/gin-gonic/gin"
)
// AdminPostHandler 管理端帖子处理器
type AdminPostHandler struct {
adminPostService service.AdminPostService
}
// NewAdminPostHandler 创建管理端帖子处理器
func NewAdminPostHandler(adminPostService service.AdminPostService) *AdminPostHandler {
return &AdminPostHandler{
adminPostService: adminPostService,
}
}
// GetPostList 获取帖子列表
// GET /api/v1/admin/posts
func (h *AdminPostHandler) GetPostList(c *gin.Context) {
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
// 获取筛选参数
keyword := c.Query("keyword")
status := c.Query("status")
authorID := c.Query("author_id")
startDate := c.Query("start_date")
endDate := c.Query("end_date")
// 验证状态参数
if status != "" && status != string(model.PostStatusDraft) &&
status != string(model.PostStatusPending) &&
status != string(model.PostStatusPublished) &&
status != string(model.PostStatusRejected) {
response.BadRequest(c, "invalid status value")
return
}
posts, total, err := h.adminPostService.GetPostList(c.Request.Context(), page, pageSize, keyword, status, authorID, startDate, endDate)
if err != nil {
response.InternalServerError(c, "failed to get post list")
return
}
response.Paginated(c, posts, total, page, pageSize)
}
// GetPostDetail 获取帖子详情
// GET /api/v1/admin/posts/:id
func (h *AdminPostHandler) GetPostDetail(c *gin.Context) {
postID := c.Param("id")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
postDetail, err := h.adminPostService.GetPostDetail(c.Request.Context(), postID)
if err != nil {
response.NotFound(c, "post not found")
return
}
response.Success(c, postDetail)
}
// DeletePost 删除帖子
// DELETE /api/v1/admin/posts/:id
func (h *AdminPostHandler) DeletePost(c *gin.Context) {
postID := c.Param("id")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
err := h.adminPostService.DeletePost(c.Request.Context(), postID)
if err != nil {
response.HandleError(c, err, "failed to delete post")
return
}
response.Success(c, dto.SuccessResponse{
Success: true,
Message: "post deleted successfully",
})
}
// ModeratePost 审核帖子
// PUT /api/v1/admin/posts/:id/moderate
func (h *AdminPostHandler) ModeratePost(c *gin.Context) {
postID := c.Param("id")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
var req dto.AdminModeratePostRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
// 验证状态值
if req.Status != string(model.PostStatusPublished) && req.Status != string(model.PostStatusRejected) {
response.BadRequest(c, "invalid status value, must be 'published' or 'rejected'")
return
}
// 获取当前用户ID作为审核人
reviewedBy := c.GetString("user_id")
postDetail, err := h.adminPostService.ModeratePost(c.Request.Context(), postID, model.PostStatus(req.Status), req.Reason, reviewedBy)
if err != nil {
response.HandleError(c, err, "failed to moderate post")
return
}
response.Success(c, postDetail)
}
// BatchDeletePosts 批量删除帖子
// POST /api/v1/admin/posts/batch-delete
func (h *AdminPostHandler) BatchDeletePosts(c *gin.Context) {
var req dto.AdminBatchDeletePostsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
result, err := h.adminPostService.BatchDeletePosts(c.Request.Context(), req.IDs)
if err != nil {
response.InternalServerError(c, "failed to batch delete posts")
return
}
response.Success(c, result)
}
// BatchModeratePosts 批量审核帖子
// POST /api/v1/admin/posts/batch-moderate
func (h *AdminPostHandler) BatchModeratePosts(c *gin.Context) {
var req dto.AdminBatchModeratePostsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
// 验证状态值
if req.Status != string(model.PostStatusPublished) && req.Status != string(model.PostStatusRejected) {
response.BadRequest(c, "invalid status value, must be 'published' or 'rejected'")
return
}
// 获取当前用户ID作为审核人
reviewedBy := c.GetString("user_id")
result, err := h.adminPostService.BatchModeratePosts(c.Request.Context(), req.IDs, model.PostStatus(req.Status), reviewedBy)
if err != nil {
response.InternalServerError(c, "failed to batch moderate posts")
return
}
response.Success(c, result)
}
// SetPostPin 置顶/取消置顶帖子
// PUT /api/v1/admin/posts/:id/pin
func (h *AdminPostHandler) SetPostPin(c *gin.Context) {
postID := c.Param("id")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
var req dto.AdminSetPostPinRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
postDetail, err := h.adminPostService.SetPostPin(c.Request.Context(), postID, req.IsPinned)
if err != nil {
response.HandleError(c, err, "failed to set post pin status")
return
}
response.Success(c, postDetail)
}
// SetPostFeature 加精/取消加精帖子
// PUT /api/v1/admin/posts/:id/feature
func (h *AdminPostHandler) SetPostFeature(c *gin.Context) {
postID := c.Param("id")
if postID == "" {
response.BadRequest(c, "post id is required")
return
}
var req dto.AdminSetPostFeatureRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
postDetail, err := h.adminPostService.SetPostFeature(c.Request.Context(), postID, req.IsFeatured)
if err != nil {
response.HandleError(c, err, "failed to set post feature status")
return
}
response.Success(c, postDetail)
}

View File

@@ -0,0 +1,105 @@
package handler
import (
"strconv"
"carrot_bbs/internal/dto"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
"github.com/gin-gonic/gin"
)
// AdminUserHandler 管理端用户处理器
type AdminUserHandler struct {
adminUserService service.AdminUserService
}
// NewAdminUserHandler 创建管理端用户处理器
func NewAdminUserHandler(adminUserService service.AdminUserService) *AdminUserHandler {
return &AdminUserHandler{
adminUserService: adminUserService,
}
}
// GetUserList 获取用户列表
// GET /api/v1/admin/users
func (h *AdminUserHandler) GetUserList(c *gin.Context) {
// 解析分页参数
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
// 获取筛选参数
keyword := c.Query("keyword")
status := c.Query("status")
// 验证状态参数
if status != "" && status != string(model.UserStatusActive) && status != string(model.UserStatusBanned) {
response.BadRequest(c, "invalid status value")
return
}
users, total, err := h.adminUserService.GetUserList(c.Request.Context(), page, pageSize, keyword, status)
if err != nil {
response.InternalServerError(c, "failed to get user list")
return
}
response.Paginated(c, users, total, page, pageSize)
}
// GetUserDetail 获取用户详情
// GET /api/v1/admin/users/:id
func (h *AdminUserHandler) GetUserDetail(c *gin.Context) {
userID := c.Param("id")
if userID == "" {
response.BadRequest(c, "user id is required")
return
}
userDetail, err := h.adminUserService.GetUserDetail(c.Request.Context(), userID)
if err != nil {
response.NotFound(c, "user not found")
return
}
response.Success(c, userDetail)
}
// UpdateUserStatus 更新用户状态
// PUT /api/v1/admin/users/:id/status
func (h *AdminUserHandler) UpdateUserStatus(c *gin.Context) {
userID := c.Param("id")
if userID == "" {
response.BadRequest(c, "user id is required")
return
}
var req dto.AdminUpdateUserStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "invalid request body: "+err.Error())
return
}
// 验证状态值
if req.Status != string(model.UserStatusActive) && req.Status != string(model.UserStatusBanned) {
response.BadRequest(c, "invalid status value, must be 'active' or 'banned'")
return
}
userDetail, err := h.adminUserService.UpdateUserStatus(c.Request.Context(), userID, model.UserStatus(req.Status))
if err != nil {
response.HandleError(c, err, "failed to update user status")
return
}
response.Success(c, userDetail)
}

View File

@@ -202,3 +202,128 @@ func (h *RoleHandler) handleError(c *gin.Context, err error) {
})
}
}
// GetRoleDetail 获取角色详情
// GET /api/v1/admin/roles/:name
func (h *RoleHandler) GetRoleDetail(c *gin.Context) {
name := c.Param("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": "BAD_REQUEST",
"message": "缺少角色名称",
})
return
}
detail, err := h.roleService.GetRoleDetail(c.Request.Context(), name)
if err != nil {
h.handleError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"data": detail,
})
}
// GetRolePermissions 获取角色权限
// GET /api/v1/admin/roles/:name/permissions
func (h *RoleHandler) GetRolePermissions(c *gin.Context) {
name := c.Param("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": "BAD_REQUEST",
"message": "缺少角色名称",
})
return
}
permissions, err := h.roleService.GetRolePermissions(c.Request.Context(), name)
if err != nil {
h.handleError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"data": permissions,
})
}
// GetAllPermissions 获取所有权限定义
// GET /api/v1/admin/permissions
func (h *RoleHandler) GetAllPermissions(c *gin.Context) {
permissions, err := h.roleService.GetAllPermissions(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": "INTERNAL_ERROR",
"message": "获取权限列表失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"data": permissions,
})
}
// UpdateRolePermissionsRequest 更新角色权限请求
type UpdateRolePermissionsRequest struct {
Permissions []PermissionInput `json:"permissions" binding:"required"`
}
// PermissionInput 权限输入
type PermissionInput struct {
Resource string `json:"resource" binding:"required"`
Action string `json:"action" binding:"required"`
}
// UpdateRolePermissions 更新角色权限
// PUT /api/v1/admin/roles/:name/permissions
func (h *RoleHandler) UpdateRolePermissions(c *gin.Context) {
name := c.Param("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": "BAD_REQUEST",
"message": "缺少角色名称",
})
return
}
var req UpdateRolePermissionsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": "BAD_REQUEST",
"message": "请求参数错误",
})
return
}
// 转换权限格式
var permissions []service.Permission
for _, p := range req.Permissions {
permissions = append(permissions, service.Permission{
Resource: p.Resource,
Action: p.Action,
})
}
err := h.roleService.UpdateRolePermissions(c.Request.Context(), name, permissions)
if err != nil {
h.handleError(c, err)
return
}
// 返回更新后的角色信息
detail, err := h.roleService.GetRoleDetail(c.Request.Context(), name)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"message": "权限更新成功",
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "权限更新成功",
"data": detail,
})
}