From 40fb3c337f548f77c59b8b05374de8d238d22a00 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:01:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(users):=20=E6=B7=BB=E5=8A=A0=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=B3=A8=E5=86=8C=E4=B8=8E=E6=89=B9=E9=87=8F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E6=88=B7=E5=8A=9F=E8=83=BD;=20revert:?= =?UTF-8?q?=E6=81=A2=E5=A4=8Ddev=E5=88=86=E6=94=AF=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增支持通过 CSV 文件批量注册用户的功能,以及批量修改用户账号类型和角色的功能。 - 添加接口用于批量注册用户,并使用Content-Type区分单个与批量注册请求 - 添加 `/api/users/category` 和 `/api/users/role` 接口用于批量修改用户属性 - 引入 207 Multi-Status 响应机制处理部分失败的批量操作 - 更新错误处理逻辑,细化多种业务错误类型 - 完善 Swagger 文档,补充新接口的定义和说明 - README 中增加新功能使用说明及快速上手示例 --- README.md | 3 - backend/pkg/controller/users.go | 312 ++++++++++++++++++++++++++++- backend/pkg/repository/user.go | 2 - docs/docs.go | 340 +++++++++++++++++++++++++++++++- docs/swagger.json | 340 +++++++++++++++++++++++++++++++- docs/swagger.yaml | 227 ++++++++++++++++++++- go.mod | 2 +- go.sum | 4 +- 8 files changed, 1198 insertions(+), 32 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 5f9852b..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# asynx - -> 核心Restful API diff --git a/backend/pkg/controller/users.go b/backend/pkg/controller/users.go index ec9a5a8..fc6a8dd 100644 --- a/backend/pkg/controller/users.go +++ b/backend/pkg/controller/users.go @@ -1,7 +1,10 @@ package controller import ( + "encoding/csv" + "io" "net/http" + "strings" "asynclab.club/asynx/backend/pkg/security" "asynclab.club/asynx/backend/pkg/service" @@ -22,6 +25,8 @@ func NewControllerUser(g *gin.RouterGroup, serviceManager *service.ServiceManage g.PUT("/:uid/password", security.GuardMiddleware(security.RoleRestricted), gggin.ToGinHandler(ctl.HandleChangePassword)) g.PUT("/:uid/category", security.GuardMiddleware(security.RoleAdmin), gggin.ToGinHandler(ctl.HandleModifyCategory)) g.PUT("/:uid/role", security.GuardMiddleware(security.RoleAdmin), gggin.ToGinHandler(ctl.HandleModifyRole)) + g.PATCH("/category", security.GuardMiddleware(security.RoleAdmin), gggin.ToGinHandler(ctl.HandleBatchModifyCategory)) + g.PATCH("/role", security.GuardMiddleware(security.RoleAdmin), gggin.ToGinHandler(ctl.HandleBatchModifyRole)) // Deprecated g.GET("/:uid/category", security.GuardMiddleware(security.RoleRestricted), gggin.ToGinHandler(ctl.HandleGetCategory)) @@ -128,7 +133,6 @@ func (ctl *ControllerUser) HandleChangePassword(c *gin.Context) (*gggin.Response err = ctl.serviceManager.ChangePassword(uid, req.Password) if err != nil { return nil, service.MapErrorToHttp(err) - } return gggin.Ok, nil @@ -230,24 +234,52 @@ type RequestRegister struct { } // @Summary 注册新用户 -// @Description 创建新用户账号。需要 ADMIN 角色权限。 +// @Description 创建新用户账号。支持两种方式:1) JSON格式单个注册 2) CSV文件批量注册。需要 ADMIN 角色权限。 +// @Description +// @Description **单个注册 (application/json)** +// @Description 发送JSON格式的单个用户数据 +// @Description +// @Description **批量注册 (multipart/form-data 或 text/csv)** +// @Description 上传CSV文件,CSV格式为:username,surName,givenName,mail,category,role +// @Description 示例:user001,张,三,zhangsan@example.com,member,default // @Tags users -// @Accept json +// @Accept json,multipart/form-data,text/csv // @Produce json -// @Param body body RequestRegister true "注册用户请求" -// @Success 200 {object} object{data=string} "成功注册用户,返回 'ok'" +// @Param body body RequestRegister false "单个注册请求 (Content-Type: application/json)" +// @Param file formData file false "批量注册CSV文件 (Content-Type: multipart/form-data)" +// @Success 200 {object} object{data=string} "单个注册成功,返回 'ok'" +// @Success 200 {object} object{data=BatchRegisterResult} "批量注册全部成功" +// @Success 207 {object} object{data=BatchRegisterResult} "批量注册部分失败" // @Failure 400 {object} object{data=string} "请求参数错误" // @Failure 401 {object} object{data=string} "未授权访问" // @Failure 403 {object} object{data=string} "权限不足" // @Failure 409 {object} object{data=string} "用户已存在" +// @Failure 415 {object} object{data=string} "不支持的媒体类型" // @Failure 500 {object} object{data=string} "服务器内部错误" // @Router /users [post] // @Security BearerAuth -func (ctl *ControllerUser) HandleRegister(c *gin.Context) (*gggin.Response[string], *gggin.HttpError) { +func (ctl *ControllerUser) HandleRegister(c *gin.Context) (*gggin.Response[any], *gggin.HttpError) { _, ok := gggin.Get[*security.GuardResult](c, "guard") if !ok { return nil, ErrHttpGuardFail } + + contentType := c.ContentType() + + // 根据 Content-Type 区分单个注册还是批量注册 + if contentType == "application/json" { + // 单个用户注册 + return ctl.handleSingleRegister(c) + } else if strings.HasPrefix(contentType, "multipart/form-data") || contentType == "text/csv" { + // CSV 批量注册 + return ctl.handleBatchRegisterFromCSV(c) + } + + return nil, gggin.NewHttpError(http.StatusUnsupportedMediaType, "不支持的 Content-Type,请使用 application/json (单个注册) 或 multipart/form-data (批量注册)") +} + +// handleSingleRegister 处理单个用户注册(JSON格式) +func (ctl *ControllerUser) handleSingleRegister(c *gin.Context) (*gggin.Response[any], *gggin.HttpError) { req, err := gggin.ShouldBindJSON[RequestRegister](c) if err != nil { return nil, gggin.NewHttpError(http.StatusBadRequest, err.Error()) @@ -258,7 +290,126 @@ func (ctl *ControllerUser) HandleRegister(c *gin.Context) (*gggin.Response[strin return nil, service.MapErrorToHttp(err) } - return gggin.Ok, nil + return gggin.NewResponse[any]("ok"), nil +} + +// handleBatchRegisterFromCSV 处理CSV文件批量注册 +func (ctl *ControllerUser) handleBatchRegisterFromCSV(c *gin.Context) (*gggin.Response[any], *gggin.HttpError) { + file, err := c.FormFile("file") + if err != nil { + return nil, gggin.NewHttpError(http.StatusBadRequest, "未找到上传的文件,请使用 'file' 字段上传CSV文件") + } + + src, err := file.Open() + if err != nil { + return nil, gggin.NewHttpError(http.StatusInternalServerError, "无法读取上传的文件") + } + defer src.Close() + + reader := csv.NewReader(src) + + header, err := reader.Read() + if err != nil { + return nil, gggin.NewHttpError(http.StatusBadRequest, "CSV文件格式错误:无法读取表头") + } + + // 验证表头格式 + expectedHeaders := []string{"username", "surName", "givenName", "mail", "category", "role"} + hasHeader := false + if len(header) == len(expectedHeaders) { + // 检查前3个关键字段来判断是否有表头,避免误判 + h0 := strings.ToLower(header[0]) + h3 := strings.ToLower(header[3]) + h4 := strings.ToLower(header[4]) + if h0 == "username" && h3 == "mail" && h4 == "category" { + hasHeader = true + } + } + + var result BatchRegisterResult + rowNum := 1 + + if !hasHeader { + if err := ctl.processCSVRow(header, &result, rowNum); err != nil { + return nil, err + } + rowNum++ + } + + for { + record, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + result.Failed++ + result.Total++ + result.Failures = append(result.Failures, BatchFailureInfo{ + Row: rowNum, + Username: "", + Error: "CSV行格式错误: " + err.Error(), + }) + rowNum++ + continue + } + + if err := ctl.processCSVRow(record, &result, rowNum); err != nil { + return nil, err + } + rowNum++ + } + + if result.Failed > 0 && result.Success > 0 { + // 部分失败:使用207 Multi-Status + return gggin.NewResponseWithStatusCode[any](http.StatusMultiStatus, result), nil + } + + return gggin.NewResponse[any](result), nil +} + +func (ctl *ControllerUser) processCSVRow(record []string, result *BatchRegisterResult, rowNum int) *gggin.HttpError { + result.Total++ + + if len(record) != 6 { + result.Failed++ + result.Failures = append(result.Failures, BatchFailureInfo{ + Row: rowNum, + Username: "", + Error: "CSV格式错误:应包含6个字段 (username,surName,givenName,mail,category,role)", + }) + return nil + } + + username := strings.TrimSpace(record[0]) + surName := strings.TrimSpace(record[1]) + givenName := strings.TrimSpace(record[2]) + mail := strings.TrimSpace(record[3]) + category := strings.TrimSpace(record[4]) + role := strings.TrimSpace(record[5]) + + if username == "" || mail == "" || category == "" || role == "" { + result.Failed++ + result.Failures = append(result.Failures, BatchFailureInfo{ + Row: rowNum, + Username: username, + Error: "必填字段不能为空 (username, mail, category, role)", + }) + return nil + } + + err := ctl.serviceManager.Register(username, surName, givenName, mail, category, role) + if err != nil { + result.Failed++ + result.Failures = append(result.Failures, BatchFailureInfo{ + Row: rowNum, + Username: username, + Error: err.Error(), + }) + } else { + result.Success++ + } + + return nil } // @Summary 删除用户 @@ -372,3 +523,150 @@ func (ctl *ControllerUser) HandleGetCategory(c *gin.Context) (*gggin.Response[se return gggin.NewResponse(category), nil } + +// ======== 批量操作相关类型定义 ======== + +// BatchRegisterResult 批量注册结果 +type BatchRegisterResult struct { + Success int `json:"success"` + Failed int `json:"failed"` + Total int `json:"total"` + Failures []BatchFailureInfo `json:"failures"` +} + +// BatchFailureInfo 批量操作失败信息 +type BatchFailureInfo struct { + Row int `json:"row"` + Username string `json:"username"` + Error string `json:"error"` +} + +// RequestBatchModifyCategory 批量修改类别请求体 +type RequestBatchModifyCategory struct { + UserIds []string `json:"userIds" binding:"required"` + Category string `json:"category" binding:"required"` +} + +// RequestBatchModifyRole 批量修改角色请求体 +type RequestBatchModifyRole struct { + UserIds []string `json:"userIds" binding:"required"` + Role string `json:"role" binding:"required"` +} + +// BatchModifyResult 批量修改结果 +type BatchModifyResult struct { + Success int `json:"success"` + Failed int `json:"failed"` + Total int `json:"total"` + Failures []BatchModifyFailure `json:"failures"` +} + +// BatchModifyFailure 批量修改失败信息 +type BatchModifyFailure struct { + UserId string `json:"userId"` + Error string `json:"error"` +} + +// ======== 批量操作功能实现 ======== + +// @Summary 批量修改账号类型 +// @Description 批量修改指定用户的账号类型。需要 ADMIN 角色权限。 +// @Tags users +// @Accept json +// @Produce json +// @Param body body RequestBatchModifyCategory true "批量修改账号类型请求" +// @Success 200 {object} object{data=BatchModifyResult} "全部成功:批量修改结果" +// @Success 207 {object} object{data=BatchModifyResult} "部分失败:批量修改结果" +// @Failure 400 {object} object{data=string} "请求参数错误" +// @Failure 401 {object} object{data=string} "未授权访问" +// @Failure 403 {object} object{data=string} "权限不足" +// @Failure 500 {object} object{data=string} "服务器内部错误" +// @Router /users/category [patch] +// @Security BearerAuth +func (ctl *ControllerUser) HandleBatchModifyCategory(c *gin.Context) (*gggin.Response[BatchModifyResult], *gggin.HttpError) { + _, ok := gggin.Get[*security.GuardResult](c, "guard") + if !ok { + return nil, ErrHttpGuardFail + } + + req, err := gggin.ShouldBindJSON[RequestBatchModifyCategory](c) + if err != nil { + return nil, gggin.NewHttpError(http.StatusBadRequest, err.Error()) + } + + var result BatchModifyResult + result.Total = len(req.UserIds) + + for _, uid := range req.UserIds { + err := ctl.serviceManager.ModifyCategory(uid, req.Category) + if err != nil { + result.Failed++ + result.Failures = append(result.Failures, BatchModifyFailure{ + UserId: uid, + Error: err.Error(), + }) + } else { + result.Success++ + } + } + + // 根据结果决定返回方式:全部成功返回200,部分失败返回207 + if result.Failed > 0 && result.Success > 0 { + // 部分失败:使用207 Multi-Status + return gggin.NewResponseWithStatusCode(http.StatusMultiStatus, result), nil + } + + // 全部成功或全部失败:使用200 + return gggin.NewResponse(result), nil +} + +// @Summary 批量修改角色权限 +// @Description 批量修改指定用户的角色权限。需要 ADMIN 角色权限。 +// @Tags users +// @Accept json +// @Produce json +// @Param body body RequestBatchModifyRole true "批量修改角色权限请求" +// @Success 200 {object} object{data=BatchModifyResult} "全部成功:批量修改结果" +// @Success 207 {object} object{data=BatchModifyResult} "部分失败:批量修改结果" +// @Failure 400 {object} object{data=string} "请求参数错误" +// @Failure 401 {object} object{data=string} "未授权访问" +// @Failure 403 {object} object{data=string} "权限不足" +// @Failure 500 {object} object{data=string} "服务器内部错误" +// @Router /users/role [patch] +// @Security BearerAuth +func (ctl *ControllerUser) HandleBatchModifyRole(c *gin.Context) (*gggin.Response[BatchModifyResult], *gggin.HttpError) { + _, ok := gggin.Get[*security.GuardResult](c, "guard") + if !ok { + return nil, ErrHttpGuardFail + } + + req, err := gggin.ShouldBindJSON[RequestBatchModifyRole](c) + if err != nil { + return nil, gggin.NewHttpError(http.StatusBadRequest, err.Error()) + } + + var result BatchModifyResult + result.Total = len(req.UserIds) + + for _, uid := range req.UserIds { + err := ctl.serviceManager.GrantRoleByUidAndRoleName(uid, req.Role) + if err != nil { + result.Failed++ + result.Failures = append(result.Failures, BatchModifyFailure{ + UserId: uid, + Error: err.Error(), + }) + } else { + result.Success++ + } + } + + // 根据结果决定返回方式:全部成功返回200,部分失败返回207 + if result.Failed > 0 && result.Success > 0 { + // 部分失败:使用207 Multi-Status + return gggin.NewResponseWithStatusCode(http.StatusMultiStatus, result), nil + } + + // 全部成功或全部失败:使用200 + return gggin.NewResponse(result), nil +} diff --git a/backend/pkg/repository/user.go b/backend/pkg/repository/user.go index c66776a..a152819 100644 --- a/backend/pkg/repository/user.go +++ b/backend/pkg/repository/user.go @@ -7,8 +7,6 @@ import ( "asynclab.club/asynx/backend/pkg/config" "asynclab.club/asynx/backend/pkg/entity" "asynclab.club/asynx/backend/pkg/transfer" - - ) type RepositoryUser struct { diff --git a/docs/docs.go b/docs/docs.go index afbcac4..145bf37 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -192,9 +192,11 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "创建新用户账号。需要 ADMIN 角色权限。", + "description": "创建新用户账号。支持两种方式:1) JSON格式单个注册 2) CSV文件批量注册。需要 ADMIN 角色权限。\n\n**单个注册 (application/json)**\n发送JSON格式的单个用户数据\n\n**批量注册 (multipart/form-data 或 text/csv)**\n上传CSV文件,CSV格式为:username,surName,givenName,mail,category,role\n示例:user001,张,三,zhangsan@example.com,member,default", "consumes": [ - "application/json" + "application/json", + "multipart/form-data", + "text/csv" ], "produces": [ "application/json" @@ -205,23 +207,39 @@ const docTemplate = `{ "summary": "注册新用户", "parameters": [ { - "description": "注册用户请求", + "description": "单个注册请求 (Content-Type: application/json)", "name": "body", "in": "body", - "required": true, "schema": { "$ref": "#/definitions/controller.RequestRegister" } + }, + { + "type": "file", + "description": "批量注册CSV文件 (Content-Type: multipart/form-data)", + "name": "file", + "in": "formData" } ], "responses": { "200": { - "description": "成功注册用户,返回 'ok'", + "description": "批量注册全部成功", "schema": { "type": "object", "properties": { "data": { - "type": "string" + "$ref": "#/definitions/controller.BatchRegisterResult" + } + } + } + }, + "207": { + "description": "批量注册部分失败", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchRegisterResult" } } } @@ -270,6 +288,215 @@ const docTemplate = `{ } } }, + "415": { + "description": "不支持的媒体类型", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + } + } + } + }, + "/users/category": { + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "批量修改指定用户的账号类型。需要 ADMIN 角色权限。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "批量修改账号类型", + "parameters": [ + { + "description": "批量修改账号类型请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.RequestBatchModifyCategory" + } + } + ], + "responses": { + "200": { + "description": "全部成功:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "207": { + "description": "部分失败:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "401": { + "description": "未授权访问", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "403": { + "description": "权限不足", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + } + } + } + }, + "/users/role": { + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "批量修改指定用户的角色权限。需要 ADMIN 角色权限。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "批量修改角色权限", + "parameters": [ + { + "description": "批量修改角色权限请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.RequestBatchModifyRole" + } + } + ], + "responses": { + "200": { + "description": "全部成功:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "207": { + "description": "部分失败:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "401": { + "description": "未授权访问", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "403": { + "description": "权限不足", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, "500": { "description": "服务器内部错误", "schema": { @@ -977,6 +1204,71 @@ const docTemplate = `{ } }, "definitions": { + "controller.BatchFailureInfo": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "row": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "controller.BatchModifyFailure": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "controller.BatchModifyResult": { + "type": "object", + "properties": { + "failed": { + "type": "integer" + }, + "failures": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.BatchModifyFailure" + } + }, + "success": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "controller.BatchRegisterResult": { + "type": "object", + "properties": { + "failed": { + "type": "integer" + }, + "failures": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.BatchFailureInfo" + } + }, + "success": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "controller.CreateTokenRequest": { "type": "object", "required": [ @@ -992,6 +1284,42 @@ const docTemplate = `{ } } }, + "controller.RequestBatchModifyCategory": { + "type": "object", + "required": [ + "category", + "userIds" + ], + "properties": { + "category": { + "type": "string" + }, + "userIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "controller.RequestBatchModifyRole": { + "type": "object", + "required": [ + "role", + "userIds" + ], + "properties": { + "role": { + "type": "string" + }, + "userIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "controller.RequestChangePassword": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 65be063..42727d5 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -185,9 +185,11 @@ "BearerAuth": [] } ], - "description": "创建新用户账号。需要 ADMIN 角色权限。", + "description": "创建新用户账号。支持两种方式:1) JSON格式单个注册 2) CSV文件批量注册。需要 ADMIN 角色权限。\n\n**单个注册 (application/json)**\n发送JSON格式的单个用户数据\n\n**批量注册 (multipart/form-data 或 text/csv)**\n上传CSV文件,CSV格式为:username,surName,givenName,mail,category,role\n示例:user001,张,三,zhangsan@example.com,member,default", "consumes": [ - "application/json" + "application/json", + "multipart/form-data", + "text/csv" ], "produces": [ "application/json" @@ -198,23 +200,39 @@ "summary": "注册新用户", "parameters": [ { - "description": "注册用户请求", + "description": "单个注册请求 (Content-Type: application/json)", "name": "body", "in": "body", - "required": true, "schema": { "$ref": "#/definitions/controller.RequestRegister" } + }, + { + "type": "file", + "description": "批量注册CSV文件 (Content-Type: multipart/form-data)", + "name": "file", + "in": "formData" } ], "responses": { "200": { - "description": "成功注册用户,返回 'ok'", + "description": "批量注册全部成功", "schema": { "type": "object", "properties": { "data": { - "type": "string" + "$ref": "#/definitions/controller.BatchRegisterResult" + } + } + } + }, + "207": { + "description": "批量注册部分失败", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchRegisterResult" } } } @@ -263,6 +281,215 @@ } } }, + "415": { + "description": "不支持的媒体类型", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + } + } + } + }, + "/users/category": { + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "批量修改指定用户的账号类型。需要 ADMIN 角色权限。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "批量修改账号类型", + "parameters": [ + { + "description": "批量修改账号类型请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.RequestBatchModifyCategory" + } + } + ], + "responses": { + "200": { + "description": "全部成功:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "207": { + "description": "部分失败:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "401": { + "description": "未授权访问", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "403": { + "description": "权限不足", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + } + } + } + }, + "/users/role": { + "patch": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "批量修改指定用户的角色权限。需要 ADMIN 角色权限。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "users" + ], + "summary": "批量修改角色权限", + "parameters": [ + { + "description": "批量修改角色权限请求", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.RequestBatchModifyRole" + } + } + ], + "responses": { + "200": { + "description": "全部成功:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "207": { + "description": "部分失败:批量修改结果", + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/controller.BatchModifyResult" + } + } + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "401": { + "description": "未授权访问", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, + "403": { + "description": "权限不足", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + }, "500": { "description": "服务器内部错误", "schema": { @@ -970,6 +1197,71 @@ } }, "definitions": { + "controller.BatchFailureInfo": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "row": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "controller.BatchModifyFailure": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "controller.BatchModifyResult": { + "type": "object", + "properties": { + "failed": { + "type": "integer" + }, + "failures": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.BatchModifyFailure" + } + }, + "success": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "controller.BatchRegisterResult": { + "type": "object", + "properties": { + "failed": { + "type": "integer" + }, + "failures": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.BatchFailureInfo" + } + }, + "success": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "controller.CreateTokenRequest": { "type": "object", "required": [ @@ -985,6 +1277,42 @@ } } }, + "controller.RequestBatchModifyCategory": { + "type": "object", + "required": [ + "category", + "userIds" + ], + "properties": { + "category": { + "type": "string" + }, + "userIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "controller.RequestBatchModifyRole": { + "type": "object", + "required": [ + "role", + "userIds" + ], + "properties": { + "role": { + "type": "string" + }, + "userIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "controller.RequestChangePassword": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f3e9823..d4fbb36 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,47 @@ basePath: /api definitions: + controller.BatchFailureInfo: + properties: + error: + type: string + row: + type: integer + username: + type: string + type: object + controller.BatchModifyFailure: + properties: + error: + type: string + userId: + type: string + type: object + controller.BatchModifyResult: + properties: + failed: + type: integer + failures: + items: + $ref: '#/definitions/controller.BatchModifyFailure' + type: array + success: + type: integer + total: + type: integer + type: object + controller.BatchRegisterResult: + properties: + failed: + type: integer + failures: + items: + $ref: '#/definitions/controller.BatchFailureInfo' + type: array + success: + type: integer + total: + type: integer + type: object controller.CreateTokenRequest: properties: password: @@ -10,6 +52,30 @@ definitions: - password - username type: object + controller.RequestBatchModifyCategory: + properties: + category: + type: string + userIds: + items: + type: string + type: array + required: + - category + - userIds + type: object + controller.RequestBatchModifyRole: + properties: + role: + type: string + userIds: + items: + type: string + type: array + required: + - role + - userIds + type: object controller.RequestChangePassword: properties: password: @@ -210,23 +276,43 @@ paths: post: consumes: - application/json - description: 创建新用户账号。需要 ADMIN 角色权限。 + - multipart/form-data + - text/csv + description: |- + 创建新用户账号。支持两种方式:1) JSON格式单个注册 2) CSV文件批量注册。需要 ADMIN 角色权限。 + + **单个注册 (application/json)** + 发送JSON格式的单个用户数据 + + **批量注册 (multipart/form-data 或 text/csv)** + 上传CSV文件,CSV格式为:username,surName,givenName,mail,category,role + 示例:user001,张,三,zhangsan@example.com,member,default parameters: - - description: 注册用户请求 + - description: '单个注册请求 (Content-Type: application/json)' in: body name: body - required: true schema: $ref: '#/definitions/controller.RequestRegister' + - description: '批量注册CSV文件 (Content-Type: multipart/form-data)' + in: formData + name: file + type: file produces: - application/json responses: "200": - description: 成功注册用户,返回 'ok' + description: 批量注册全部成功 schema: properties: data: - type: string + $ref: '#/definitions/controller.BatchRegisterResult' + type: object + "207": + description: 批量注册部分失败 + schema: + properties: + data: + $ref: '#/definitions/controller.BatchRegisterResult' type: object "400": description: 请求参数错误 @@ -256,6 +342,13 @@ paths: data: type: string type: object + "415": + description: 不支持的媒体类型 + schema: + properties: + data: + type: string + type: object "500": description: 服务器内部错误 schema: @@ -712,6 +805,130 @@ paths: summary: 更改账号角色 tags: - users + /users/category: + patch: + consumes: + - application/json + description: 批量修改指定用户的账号类型。需要 ADMIN 角色权限。 + parameters: + - description: 批量修改账号类型请求 + in: body + name: body + required: true + schema: + $ref: '#/definitions/controller.RequestBatchModifyCategory' + produces: + - application/json + responses: + "200": + description: 全部成功:批量修改结果 + schema: + properties: + data: + $ref: '#/definitions/controller.BatchModifyResult' + type: object + "207": + description: 部分失败:批量修改结果 + schema: + properties: + data: + $ref: '#/definitions/controller.BatchModifyResult' + type: object + "400": + description: 请求参数错误 + schema: + properties: + data: + type: string + type: object + "401": + description: 未授权访问 + schema: + properties: + data: + type: string + type: object + "403": + description: 权限不足 + schema: + properties: + data: + type: string + type: object + "500": + description: 服务器内部错误 + schema: + properties: + data: + type: string + type: object + security: + - BearerAuth: [] + summary: 批量修改账号类型 + tags: + - users + /users/role: + patch: + consumes: + - application/json + description: 批量修改指定用户的角色权限。需要 ADMIN 角色权限。 + parameters: + - description: 批量修改角色权限请求 + in: body + name: body + required: true + schema: + $ref: '#/definitions/controller.RequestBatchModifyRole' + produces: + - application/json + responses: + "200": + description: 全部成功:批量修改结果 + schema: + properties: + data: + $ref: '#/definitions/controller.BatchModifyResult' + type: object + "207": + description: 部分失败:批量修改结果 + schema: + properties: + data: + $ref: '#/definitions/controller.BatchModifyResult' + type: object + "400": + description: 请求参数错误 + schema: + properties: + data: + type: string + type: object + "401": + description: 未授权访问 + schema: + properties: + data: + type: string + type: object + "403": + description: 权限不足 + schema: + properties: + data: + type: string + type: object + "500": + description: 服务器内部错误 + schema: + properties: + data: + type: string + type: object + security: + - BearerAuth: [] + summary: 批量修改角色权限 + tags: + - users securityDefinitions: BearerAuth: description: 输入 Bearer Token,格式为 "Bearer " diff --git a/go.mod b/go.mod index a0981fa..2177977 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.25.0 require ( aidanwoods.dev/go-paseto v1.5.4 github.com/caarlos0/env/v11 v11.3.1 - github.com/dsx137/gg-gin v0.0.0-20250901065200-84d4cd816961 + github.com/dsx137/gg-gin v0.0.0-20251001191328-6833e9eb9e9a github.com/dsx137/gg-kit v0.0.0-20250901054119-4a75e612b3b8 github.com/dsx137/gg-logging v0.0.0-20250720193954-3aa8e6cfa181 github.com/gin-gonic/gin v1.10.1 diff --git a/go.sum b/go.sum index 8598506..db3a025 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dsx137/gg-gin v0.0.0-20250901065200-84d4cd816961 h1:sjNXtpPx/+p/QNFLrNlAhuip4HJGUM/KL0zBJYMB8fI= -github.com/dsx137/gg-gin v0.0.0-20250901065200-84d4cd816961/go.mod h1:4+/7pDteh1CCT43+AmGsasvXkdTv0mRerUvE7w6MRP4= +github.com/dsx137/gg-gin v0.0.0-20251001191328-6833e9eb9e9a h1:i0Qq9FnKQ3XTsY4t3cNP+NtCscjK83tFCgJkGG2mqJA= +github.com/dsx137/gg-gin v0.0.0-20251001191328-6833e9eb9e9a/go.mod h1:4+/7pDteh1CCT43+AmGsasvXkdTv0mRerUvE7w6MRP4= github.com/dsx137/gg-kit v0.0.0-20250901054119-4a75e612b3b8 h1:0Jq9lZ9Aut6IyVgQY+9VcI5EBlZy4u45bjJwWuUAQJE= github.com/dsx137/gg-kit v0.0.0-20250901054119-4a75e612b3b8/go.mod h1:IP0n2PQ3R6/pmyydUJpHkD23FWnF7Y+e/SZUqFqH7+I= github.com/dsx137/gg-logging v0.0.0-20250720193954-3aa8e6cfa181 h1:ESpeIERZHD14uxnf8mS4BhxQ/37D+nCTj/9IssbNVSc=