using AX.FireTrainingSys.DTOs; using AX.FireTrainingSys.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using static BCrypt.Net.BCrypt; namespace AX.FireTrainingSys.Controllers { /// /// 用户控制器。 /// [Produces("application/json")] [Route("api/[controller]")] [ApiVersion("1.0")] [Authorize(Roles = nameof(RoleType.Admin))] [ApiController] public class UsersController : ControllerBase { private readonly DriveDbContext dbContext; public UsersController(DriveDbContext dbContext) { this.dbContext = dbContext; } /// /// 获得所有用户信息。 /// /// 查询条件 /// [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status200OK)] [HttpGet] public async Task>> Get([FromQuery] UserQueryOptions options) { if (options.PageNumber == default) options.PageNumber = 1; if (options.PageSize == default) options.PageSize = 10; if (options.RoleType == default) options.RoleType = RoleType.Student; var query = dbContext.Users .Include(e => e.Posts).ThenInclude(e => e.Post) .AsNoTracking(); query = query.Where(e => e.RoleType == options.RoleType); if (!string.IsNullOrEmpty(options.Name)) query = query.Where(e => e.Name.Contains(options.Name)); if (!string.IsNullOrEmpty(options.RealName)) query = query.Where(e => e.RealName.Contains(options.RealName)); var count = await query.CountAsync(); query = query.OrderByDescending(e => e.CreationTime); if (options.PageNumber > 1) query = query.Skip((options.PageNumber.Value - 1) * options.PageSize.Value); query = query.Take(options.PageSize.Value); var items = await query.Select(e => e.ToDTO()) .ToListAsync(); var page = new Page { PageNumber = (int)options.PageNumber, PageSize = (int)options.PageSize, TotalPages = (int)Math.Ceiling((double)count / (int)options.PageSize), TotalCount = count, Items = items }; return Ok(page); } /// /// 创建用户。 /// /// 用户信息 /// //[ProducesResponseType(ErrorCodes.E610)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status201Created)] [HttpPost] public async Task> Post([FromBody] UserInfo info) { if (string.IsNullOrEmpty(info.Name)) return BadRequest(info); if (info.RoleType == RoleType.Teacher) { if (!Regex.IsMatch(info.Name, RegexHelper.Username)) return BadRequest(info); } else if (info.RoleType == RoleType.Student) { if (!Regex.IsMatch(info.Name, RegexHelper.IdentityCard)) return BadRequest(info); } else return BadRequest(info); await using (var transaction = dbContext.Database.BeginTransaction()) { var model = await dbContext.Users .AsNoTracking() .Where(e => e.Name == info.Name) .FirstOrDefaultAsync(); if (model != default) return this.ErrorCode(ErrorCodes.E610); model = info.ToModel(); //处理多对多关系 if (info.Posts != default && info.Posts.Any()) { var list = new List(info.Posts.Count); foreach (var post in info.Posts) { list.Add(new UserPost { User = model, PostId = post.Id }); } dbContext.Set().AddRange(list); } dbContext.Users.Add(model); await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); var result = model.ToDTO(); return CreatedAtAction(nameof(Post), result); } } /// /// 修改用户。 /// /// 用户帐号(身份证号) /// 用户信息 /// //[ProducesResponseType(ErrorCodes.E610)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status204NoContent)] [HttpPut("{name}")] public async Task Put(string name, [FromBody] UserInfo info) { if (name != info.Name) return BadRequest($"{nameof(name)} and {nameof(info.Name)} are not equal."); if (info.RoleType == RoleType.Admin) return BadRequest(info); await using (var transaction = dbContext.Database.BeginTransaction()) { var model = await dbContext.Users .Include(e => e.Posts) .Where(e => e.Name == info.Name) .FirstOrDefaultAsync(); if (model == default) return NotFound(); info.MapTo(model); //处理多对多关系 if (info.Posts != default && info.Posts.Any()) { var posts = model.Posts; var list = new List(info.Posts.Count); foreach (var post in info.Posts) { list.Add(new UserPost { UserId = model.Id, PostId = post.Id }); } var comparer = new UserPostComparer(); var removeItems = posts.Except(list, comparer); var addItems = list.Except(posts, comparer); dbContext.Set().RemoveRange(removeItems); dbContext.Set().AddRange(addItems); } await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); return NoContent(); } } /// /// 重置密码。 /// /// 用户帐号(身份证号) /// [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status204NoContent)] [HttpPut("{name}/ResetPassword")] public async Task Put(string name) { var model = await dbContext.Users.FirstOrDefaultAsync(e => e.Name == name); if (model == default) return NotFound(); model.ResetPassword(); await dbContext.SaveChangesAsync(); return NoContent(); } /// /// 删除用户。 /// /// 用户帐号(身份证号) /// [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status204NoContent)] [HttpDelete("{name}")] public async Task Delete(string name) { if (name == "admin") return Forbid(); var model = await dbContext.Users .Where(e => e.Name == name) .FirstOrDefaultAsync(); if (model == default) return NotFound(); model.Deleted = true; model.Name = $"{model.Name}-{ObjectId.NewId()}"; await dbContext.SaveChangesAsync(); return NoContent(); } } internal static partial class Extensions { private const string DefaultPassword = "12345678"; public static UserInfo ToDTO(this User model) => new UserInfo { Name = model.Name, RealName = model.RealName, RoleType = model.RoleType, Enabled = model.Enabled, CreationTime = model.CreationTime.ToLocalTime(), Posts = model.Posts?.Select(e => e.Post?.ToDTO()).ToList() }; public static ProfileInfo ToDTO2(this User model) => new ProfileInfo { Id = model.Id, Name = model.Name, RealName = model.RealName, RoleType = model.RoleType, Enabled = model.Enabled, CreationTime = model.CreationTime.ToLocalTime(), Posts = model.Posts.Select(e => e.Post?.ToDTO()).ToList() }; public static User ToModel(this UserInfo dto) => new User { Name = dto.Name, Password = HashPassword(DefaultPassword), RealName = dto.RealName, RoleType = dto.RoleType }; //专用于数据同步 public static User ToModel2(this UserInfo dto) => new User { Name = dto.Name, Password = HashPassword(DefaultPassword), RealName = dto.RealName, RoleType = dto.RoleType, Enabled = dto.Enabled ?? true }; public static void MapTo(this UserInfo dto, User model) { model.RealName = dto.RealName; model.RoleType = dto.RoleType; model.Enabled = dto.Enabled ?? true; } public static void ResetPassword(this User model) { model.Password = HashPassword(DefaultPassword); } } }