diff --git a/src/GZCTF/ClientApp/src/Api.ts b/src/GZCTF/ClientApp/src/Api.ts index 219551d2d..1f7b9c23e 100644 --- a/src/GZCTF/ClientApp/src/Api.ts +++ b/src/GZCTF/ClientApp/src/Api.ts @@ -304,6 +304,35 @@ export interface UserInfoModel { emailConfirmed?: boolean | null } +/** 批量用户创建(Admin) */ +export interface UserCreateModel { + /** + * 用户名 + * @minLength 3 + * @maxLength 15 + */ + userName: string + /** + * 密码 + * @minLength 6 + */ + password: string + /** + * 邮箱 + * @format email + * @minLength 1 + */ + email: string + /** 真实姓名 */ + realName?: string | null + /** 学号 */ + stdNumber?: string | null + /** 联系电话 */ + phone?: string | null + /** 用户加入的队伍 */ + teamName?: string | null +} + /** 列表响应 */ export interface ArrayResponseOfTeamInfoModel { /** 数据 */ @@ -1964,6 +1993,23 @@ export class Api extends HttpClient mutate([`/api/admin/users`, query], data, options), + /** + * @description 使用此接口批量添加用户,需要Admin权限 + * + * @tags Admin + * @name AdminAddUsers + * @summary 批量添加用户 + * @request POST:/api/admin/users + */ + adminAddUsers: (data: UserCreateModel[], params: RequestParams = {}) => + this.request({ + path: `/api/admin/users`, + method: 'POST', + body: data, + type: ContentType.Json, + ...params, + }), + /** * @description 使用此接口搜索用户,需要Admin权限 * diff --git a/src/GZCTF/Controllers/AdminController.cs b/src/GZCTF/Controllers/AdminController.cs index 0d60cf571..c549b8feb 100644 --- a/src/GZCTF/Controllers/AdminController.cs +++ b/src/GZCTF/Controllers/AdminController.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Net.Mime; +using System.Net.Mime; using CTFServer.Extensions; using CTFServer.Middlewares; using CTFServer.Models.Internal; @@ -123,6 +122,58 @@ from user in userManager.Users.OrderBy(e => e.Id).Skip(skip).Take(count) select UserInfoModel.FromUserInfo(user) ).ToArrayAsync(token)).ToResponse(await userManager.Users.CountAsync(token))); + /// + /// 批量添加用户 + /// + /// + /// 使用此接口批量添加用户,需要Admin权限 + /// + /// 成功添加 + /// 未授权用户 + /// 禁止访问 + [HttpPost("Users")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task AddUsers([FromBody] List model, CancellationToken token = default) + { + var trans = await teamRepository.BeginTransactionAsync(token); + + try + { + var users = new List<(UserInfo, string?)>(model.Count); + foreach(var user in model) + { + var userInfo = user.ToUserInfo(); + await userManager.CreateAsync(userInfo, user.Password); + users.Add((userInfo, user.TeamName)); + } + + var teams = new List(); + foreach(var (user, teamName) in users) + { + if (teamName is null) + continue; + + var team = teams.Find(team => team.Name == teamName); + if (team is null) + { + team = await teamRepository.CreateTeam(new(teamName), user, token); + teams.Add(team!); + } + else + { + team.Members.Add(user); + } + } + + return Ok(); + } + catch + { + await trans.RollbackAsync(token); + throw; + } + } + /// /// 搜索用户 /// @@ -281,7 +332,7 @@ public async Task DeleteUser(string userid, CancellationToken tok [ProducesResponseType(typeof(RequestResponse), StatusCodes.Status404NotFound)] public async Task DeleteTeam(int id, CancellationToken token = default) { - var team = await teamRepository.GetTeamById(id); + var team = await teamRepository.GetTeamById(id, token); if (team is null) return NotFound(new RequestResponse("队伍未找到", 404)); diff --git a/src/GZCTF/Models/Request/Admin/UserCreateModel.cs b/src/GZCTF/Models/Request/Admin/UserCreateModel.cs new file mode 100644 index 000000000..630e81ff9 --- /dev/null +++ b/src/GZCTF/Models/Request/Admin/UserCreateModel.cs @@ -0,0 +1,62 @@ +using System.ComponentModel.DataAnnotations; + +namespace CTFServer.Models.Request.Admin; + +/// +/// 批量用户创建(Admin) +/// +public class UserCreateModel +{ + /// + /// 用户名 + /// + [Required(ErrorMessage = "用户名是必需的")] + [MinLength(3, ErrorMessage = "用户名过短")] + [MaxLength(15, ErrorMessage = "用户名过长")] + public string UserName { get; set; } = string.Empty; + + /// + /// 密码 + /// + [Required(ErrorMessage = "密码是必需的")] + [MinLength(6)] + public string Password { get; set; } = string.Empty; + + /// + /// 邮箱 + /// + [Required(ErrorMessage = "邮箱是必需的")] + [EmailAddress(ErrorMessage = "邮箱地址无效")] + public string Email { get; set; } = string.Empty; + + /// + /// 真实姓名 + /// + public string? RealName { get; set; } + + /// + /// 学号 + /// + public string? StdNumber { get; set; } + + /// + /// 联系电话 + /// + public string? Phone { get; set; } + + /// + /// 用户加入的队伍 + /// + public string? TeamName { get; set; } + + internal UserInfo ToUserInfo() + => new() + { + Email = Email, + UserName = UserName, + RealName = RealName ?? "", + StdNumber = StdNumber ?? "", + PhoneNumber = Phone, + EmailConfirmed = true + }; +} \ No newline at end of file diff --git a/src/GZCTF/Models/Request/Info/TeamUpdateModel.cs b/src/GZCTF/Models/Request/Info/TeamUpdateModel.cs index 06916160c..d413bea30 100644 --- a/src/GZCTF/Models/Request/Info/TeamUpdateModel.cs +++ b/src/GZCTF/Models/Request/Info/TeamUpdateModel.cs @@ -7,6 +7,11 @@ namespace CTFServer.Models.Request.Info; /// public class TeamUpdateModel { + public TeamUpdateModel(string teamName) + { + Name = teamName; + } + /// /// 队伍名称 ///