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);
}
}
}