feat(identity): 支持用户和用户组批量管理
增加原子批量启用、禁用和删除接口及管理端多选操作,目标缺失时整批回滚。\n\n拆分管理端与 API Key 权限缓存并在弹窗保存后刷新候选;补齐失效规则一键清理样式、固定右侧操作列和 OpenAPI 契约。
This commit is contained in:
@@ -2,12 +2,83 @@ package httpapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
)
|
||||
|
||||
// batchGatewayUsers godoc
|
||||
// @Summary 批量操作用户
|
||||
// @Description 管理端原子批量启用、禁用或软删除最多 500 个用户;任一目标不存在时整批不变更。
|
||||
// @Tags identity
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param input body store.IdentityBatchInput true "用户批量操作"
|
||||
// @Success 200 {object} IdentityBatchResponse
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 404 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/admin/users/batch [post]
|
||||
func (s *Server) batchGatewayUsers(w http.ResponseWriter, r *http.Request) {
|
||||
var input store.IdentityBatchInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid json body")
|
||||
return
|
||||
}
|
||||
result, err := s.store.BatchGatewayUsers(r.Context(), input)
|
||||
if err != nil {
|
||||
writeIdentityBatchError(w, s, err, "users")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
// batchUserGroups godoc
|
||||
// @Summary 批量操作用户组
|
||||
// @Description 管理端原子批量启用、禁用或删除最多 500 个用户组;删除时同步删除其访问规则,关联默认用户组外键按数据库约束置空。
|
||||
// @Tags identity
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param input body store.IdentityBatchInput true "用户组批量操作"
|
||||
// @Success 200 {object} IdentityBatchResponse
|
||||
// @Failure 400 {object} ErrorEnvelope
|
||||
// @Failure 401 {object} ErrorEnvelope
|
||||
// @Failure 403 {object} ErrorEnvelope
|
||||
// @Failure 404 {object} ErrorEnvelope
|
||||
// @Failure 500 {object} ErrorEnvelope
|
||||
// @Router /api/admin/user-groups/batch [post]
|
||||
func (s *Server) batchUserGroups(w http.ResponseWriter, r *http.Request) {
|
||||
var input store.IdentityBatchInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid json body")
|
||||
return
|
||||
}
|
||||
result, err := s.store.BatchUserGroups(r.Context(), input)
|
||||
if err != nil {
|
||||
writeIdentityBatchError(w, s, err, "user groups")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func writeIdentityBatchError(w http.ResponseWriter, s *Server, err error, target string) {
|
||||
switch {
|
||||
case errors.Is(err, store.ErrInvalidIdentityBatch):
|
||||
writeError(w, http.StatusBadRequest, "action must be enable, disable or delete and ids must contain 1 to 500 UUIDs")
|
||||
case errors.Is(err, store.ErrIdentityBatchTargetNotFound):
|
||||
writeError(w, http.StatusNotFound, "one or more "+target+" were not found")
|
||||
default:
|
||||
s.logger.Error("batch identity operation failed", "target", target, "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "batch "+target+" operation failed")
|
||||
}
|
||||
}
|
||||
|
||||
// createTenant godoc
|
||||
// @Summary 创建租户
|
||||
// @Description 管理端创建网关租户,tenantKey 和 name 必填。
|
||||
|
||||
@@ -144,6 +144,13 @@ type UserGroupListResponse struct {
|
||||
Items []store.UserGroup `json:"items"`
|
||||
}
|
||||
|
||||
type IdentityBatchResponse struct {
|
||||
Action string `json:"action" example:"disable"`
|
||||
RequestedCount int `json:"requestedCount" example:"2"`
|
||||
AffectedCount int `json:"affectedCount" example:"2"`
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
type AccessRuleListResponse struct {
|
||||
Items []store.AccessRule `json:"items"`
|
||||
}
|
||||
|
||||
@@ -197,6 +197,7 @@ func NewServerWithStores(
|
||||
mux.Handle("DELETE /api/admin/tenants/{tenantID}", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.deleteTenant)))
|
||||
mux.Handle("GET /api/admin/users", server.requireAdmin(auth.PermissionPower, http.HandlerFunc(server.listUsers)))
|
||||
mux.Handle("POST /api/admin/users", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.createGatewayUser)))
|
||||
mux.Handle("POST /api/admin/users/batch", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.batchGatewayUsers)))
|
||||
mux.Handle("PATCH /api/admin/users/{userID}", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.updateGatewayUser)))
|
||||
mux.Handle("PATCH /api/admin/users/{userID}/wallet", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.setUserWalletBalance)))
|
||||
mux.Handle("POST /api/admin/users/{userID}/wallet/recharge", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.rechargeUserWalletBalance)))
|
||||
@@ -204,6 +205,7 @@ func NewServerWithStores(
|
||||
mux.Handle("GET /api/admin/audit-logs", server.requireAdmin(auth.PermissionPower, http.HandlerFunc(server.listAuditLogs)))
|
||||
mux.Handle("GET /api/admin/user-groups", server.requireAdmin(auth.PermissionPower, http.HandlerFunc(server.listUserGroups)))
|
||||
mux.Handle("POST /api/admin/user-groups", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.createUserGroup)))
|
||||
mux.Handle("POST /api/admin/user-groups/batch", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.batchUserGroups)))
|
||||
mux.Handle("PATCH /api/admin/user-groups/{groupID}", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.updateUserGroup)))
|
||||
mux.Handle("DELETE /api/admin/user-groups/{groupID}", server.requireAdmin(auth.PermissionManager, http.HandlerFunc(server.deleteUserGroup)))
|
||||
mux.Handle("GET /api/admin/access-rules", server.requireAdmin(auth.PermissionPower, http.HandlerFunc(server.listAccessRules)))
|
||||
|
||||
Reference in New Issue
Block a user