From a4a75188bd17e31b39a02511bbd6d628bab5c909 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 25 Apr 2021 21:20:04 +0800 Subject: ... --- BackEnd/Timeline/Services/User/UserService.cs | 214 ++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 BackEnd/Timeline/Services/User/UserService.cs (limited to 'BackEnd/Timeline/Services/User/UserService.cs') diff --git a/BackEnd/Timeline/Services/User/UserService.cs b/BackEnd/Timeline/Services/User/UserService.cs new file mode 100644 index 00000000..bbbe15b0 --- /dev/null +++ b/BackEnd/Timeline/Services/User/UserService.cs @@ -0,0 +1,214 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Timeline.Entities; +using Timeline.Models.Validation; + +namespace Timeline.Services.User +{ + /// + /// Null means not change. + /// + public class ModifyUserParams + { + public string? Username { get; set; } + public string? Password { get; set; } + public string? Nickname { get; set; } + } + + public interface IUserService : IBasicUserService + { + /// + /// Try to get a user by id. + /// + /// The id of the user. + /// The user info. + /// Thrown when the user with given id does not exist. + Task GetUser(long id); + + /// + /// List all users. + /// + /// The user info of users. + Task> GetUsers(); + + /// + /// Create a user with given info. + /// + /// The username of new user. + /// The password of new user. + /// The the new user. + /// Thrown when or is null. + /// Thrown when or is of bad format. + /// Thrown when a user with given username already exists. + Task CreateUser(string username, string password); + + /// + /// Modify a user. + /// + /// The id of the user. + /// The new information. + /// The new user info. + /// Thrown when some fields in is bad. + /// Thrown when user with given id does not exist. + /// + /// Version will increase if password is changed. + /// + Task ModifyUser(long id, ModifyUserParams? param); + } + + public class UserService : BasicUserService, IUserService + { + private readonly ILogger _logger; + private readonly IClock _clock; + + private readonly DatabaseContext _databaseContext; + + private readonly IPasswordService _passwordService; + + private readonly UsernameValidator _usernameValidator = new UsernameValidator(); + private readonly NicknameValidator _nicknameValidator = new NicknameValidator(); + + public UserService(ILogger logger, DatabaseContext databaseContext, IPasswordService passwordService, IClock clock) : base(databaseContext) + { + _logger = logger; + _databaseContext = databaseContext; + _passwordService = passwordService; + _clock = clock; + } + + private void CheckUsernameFormat(string username, string? paramName) + { + if (!_usernameValidator.Validate(username, out var message)) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.ExceptionUsernameBadFormat, message), paramName); + } + } + + private static void CheckPasswordFormat(string password, string? paramName) + { + if (password.Length == 0) + { + throw new ArgumentException(Resource.ExceptionPasswordEmpty, paramName); + } + } + + private void CheckNicknameFormat(string nickname, string? paramName) + { + if (!_nicknameValidator.Validate(nickname, out var message)) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.ExceptionNicknameBadFormat, message), paramName); + } + } + + private static void ThrowUsernameConflict(object? user) + { + throw new UserAlreadyExistException(user); + } + + public async Task GetUser(long id) + { + var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); + + if (user == null) + throw new UserNotExistException(id); + + return user; + } + + public async Task> GetUsers() + { + return await _databaseContext.Users.ToListAsync(); + } + + public async Task CreateUser(string username, string password) + { + if (username == null) + throw new ArgumentNullException(nameof(username)); + if (password == null) + throw new ArgumentNullException(nameof(password)); + + CheckUsernameFormat(username, nameof(username)); + CheckPasswordFormat(password, nameof(password)); + + var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username); + if (conflict) + ThrowUsernameConflict(null); + + var newEntity = new UserEntity + { + Username = username, + Password = _passwordService.HashPassword(password), + Version = 1 + }; + _databaseContext.Users.Add(newEntity); + await _databaseContext.SaveChangesAsync(); + + return newEntity; + } + + public async Task ModifyUser(long id, ModifyUserParams? param) + { + if (param != null) + { + if (param.Username != null) + CheckUsernameFormat(param.Username, nameof(param)); + + if (param.Password != null) + CheckPasswordFormat(param.Password, nameof(param)); + + if (param.Nickname != null) + CheckNicknameFormat(param.Nickname, nameof(param)); + } + + var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); + if (entity == null) + throw new UserNotExistException(id); + + if (param != null) + { + var now = _clock.GetCurrentTime(); + bool updateLastModified = false; + + var username = param.Username; + if (username != null && username != entity.Username) + { + var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username); + if (conflict) + ThrowUsernameConflict(null); + + entity.Username = username; + entity.UsernameChangeTime = now; + updateLastModified = true; + } + + var password = param.Password; + if (password != null) + { + entity.Password = _passwordService.HashPassword(password); + entity.Version += 1; + } + + var nickname = param.Nickname; + if (nickname != null && nickname != entity.Nickname) + { + entity.Nickname = nickname; + updateLastModified = true; + } + + if (updateLastModified) + { + entity.LastModified = now; + } + + await _databaseContext.SaveChangesAsync(); + } + + return entity; + } + } +} -- cgit v1.2.3