using Microsoft.Extensions.Logging; using OrpaonVision.Core.Results; using OrpaonVision.Model.Security; using System.Security.Cryptography; using System.Text; namespace OrpaonVision.ConfigApp.Infrastructure.Services; /// /// 用户管理服务实现。 /// public sealed class UserService : IUserService { private readonly ILogger _logger; private readonly List _users; private readonly Dictionary _loginFailCounts; /// /// 构造函数。 /// public UserService(ILogger logger) { _logger = logger; _users = new List(); _loginFailCounts = new Dictionary(); // 初始化示例数据 InitializeSampleData(); } /// public Result CreateUser(UserModel user) { try { if (user == null) { return Result.Fail("USER_NULL", "用户不能为空。"); } if (string.IsNullOrWhiteSpace(user.Username)) { return Result.Fail("USERNAME_REQUIRED", "用户名不能为空。"); } // 检查用户名是否已存在 if (_users.Any(u => u.Username.Equals(user.Username, StringComparison.OrdinalIgnoreCase))) { return Result.Fail("USERNAME_EXISTS", "用户名已存在。"); } _logger.LogInformation("正在创建用户: {Username}", user.Username); // 生成密码哈希 var (hash, salt) = GeneratePasswordHash(user.PasswordHash); user.Id = Guid.NewGuid(); user.PasswordHash = hash; user.PasswordSalt = salt; user.Status = UserStatus.Enabled; user.IsFirstLogin = true; user.LoginFailedCount = 0; user.CreatedAtUtc = DateTime.UtcNow; user.UpdatedAtUtc = DateTime.UtcNow; _users.Add(user); _logger.LogInformation("用户创建成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(user, message: "用户创建成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "创建用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CREATE_USER_FAILED", "创建用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result UpdateUser(UserModel user) { try { if (user == null) { return Result.Fail("USER_NULL", "用户不能为空。"); } var existingUser = _users.FirstOrDefault(u => u.Id == user.Id); if (existingUser == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {user.Id} 的用户。"); } _logger.LogInformation("正在更新用户: {UserId} - {Username}", user.Id, user.Username); // 更新属性(不包含密码相关字段) existingUser.DisplayName = user.DisplayName; existingUser.Email = user.Email; existingUser.PhoneNumber = user.PhoneNumber; existingUser.Remark = user.Remark; existingUser.UpdatedAtUtc = DateTime.UtcNow; existingUser.UpdatedBy = user.UpdatedBy; _logger.LogInformation("用户更新成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(existingUser, message: "用户更新成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_USER_FAILED", "更新用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result DeleteUser(Guid userId) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } _logger.LogInformation("正在删除用户: {UserId} - {Username}", user.Id, user.Username); // 软删除 user.Status = UserStatus.Deleted; user.UpdatedAtUtc = DateTime.UtcNow; user.UpdatedBy = "System"; _logger.LogInformation("用户删除成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(message: "用户删除成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "删除用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "DELETE_USER_FAILED", "删除用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result GetUserById(Guid userId) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } // 清除敏感信息 var safeUser = CloneUserWithoutSensitiveData(user); return Result.Success(safeUser, message: "获取用户成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_USER_FAILED", "获取用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result GetUserByUsername(string username) { try { if (string.IsNullOrWhiteSpace(username)) { return Result.Fail("USERNAME_REQUIRED", "用户名不能为空。"); } var user = _users.FirstOrDefault(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase)); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到用户名为 {username} 的用户。"); } // 清除敏感信息 var safeUser = CloneUserWithoutSensitiveData(user); return Result.Success(safeUser, message: "获取用户成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_USER_FAILED", "获取用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result VerifyPassword(string username, string password) { try { if (string.IsNullOrWhiteSpace(username)) { return Result.Fail("USERNAME_REQUIRED", "用户名不能为空。"); } if (string.IsNullOrWhiteSpace(password)) { return Result.Fail("PASSWORD_REQUIRED", "密码不能为空。"); } var user = _users.FirstOrDefault(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase)); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到用户名为 {username} 的用户。"); } if (user.Status != UserStatus.Enabled) { return Result.Fail("USER_INACTIVE", "用户已被禁用。"); } // 检查登录失败次数 if (_loginFailCounts.TryGetValue(username, out var failCount) && failCount >= 5) { return Result.Fail("ACCOUNT_LOCKED", "账户已被锁定,请联系管理员。"); } // 验证密码(简化版本,实际应该使用哈希比较) var isPasswordValid = VerifyPasswordHash(password, user.PasswordHash); if (isPasswordValid) { // 登录成功,清除失败计数 _loginFailCounts.Remove(username); _logger.LogInformation("用户 {Username} 登录成功", username); return Result.Success(true, "登录成功。"); } else { // 登录失败,增加失败计数 _loginFailCounts[username] = _loginFailCounts.GetValueOrDefault(username, 0) + 1; _logger.LogWarning("用户 {Username} 登录失败,当前失败次数: {FailCount}", username, _loginFailCounts[username]); var remainingAttempts = 5 - _loginFailCounts[username]; var message = remainingAttempts > 0 ? $"密码错误,还有 {remainingAttempts} 次尝试机会。" : "账户已被锁定,请联系管理员。"; return Result.Fail("PASSWORD_INCORRECT", message); } } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "验证密码失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "VERIFY_PASSWORD_FAILED", "验证密码失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result> GetUserPagedList(int pageIndex = 1, int pageSize = 20, UserStatus? status = null, string? keyword = null) { try { var query = _users.Where(u => u.Status != UserStatus.Deleted).AsQueryable(); // 状态过滤 if (status.HasValue) { query = query.Where(u => u.Status == status.Value); } // 关键词搜索 if (!string.IsNullOrWhiteSpace(keyword)) { query = query.Where(u => u.Username.Contains(keyword, StringComparison.OrdinalIgnoreCase) || u.DisplayName.Contains(keyword, StringComparison.OrdinalIgnoreCase) || (u.Email != null && u.Email.Contains(keyword, StringComparison.OrdinalIgnoreCase))); } // 排序 query = query.OrderByDescending(u => u.CreatedAtUtc); var totalCount = query.Count(); var items = query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); // 清除敏感信息 var safeItems = items.Select(CloneUserWithoutSensitiveData).ToList(); var pagedResult = PagedResult.Success(safeItems, totalCount, pageIndex, pageSize); return Result>.Success(pagedResult, message: "获取用户列表成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取用户列表失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_USER_LIST_FAILED", "获取用户列表失败。", traceId); return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result EnableUser(Guid userId, string enabledBy) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } _logger.LogInformation("正在启用用户: {UserId} - {Username}", user.Id, user.Username); user.Status = UserStatus.Enabled; user.UpdatedAtUtc = DateTime.UtcNow; user.UpdatedBy = enabledBy; _logger.LogInformation("用户启用成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(message: "用户启用成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "启用用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "ENABLE_USER_FAILED", "启用用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result DisableUser(Guid userId, string disabledBy) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } _logger.LogInformation("正在禁用用户: {UserId} - {Username}", user.Id, user.Username); user.Status = UserStatus.Disabled; user.UpdatedAtUtc = DateTime.UtcNow; user.UpdatedBy = disabledBy; _logger.LogInformation("用户禁用成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(message: "用户禁用成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "禁用用户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "DISABLE_USER_FAILED", "禁用用户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result ResetPassword(Guid userId, string newPassword, string resetBy) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } if (string.IsNullOrWhiteSpace(newPassword)) { return Result.Fail("PASSWORD_REQUIRED", "新密码不能为空。"); } _logger.LogInformation("正在重置用户密码: {UserId} - {Username}", user.Id, user.Username); // 生成新密码哈希 var (hash, salt) = GeneratePasswordHash(newPassword); user.PasswordHash = hash; user.PasswordSalt = salt; user.IsFirstLogin = true; user.LoginFailedCount = 0; user.LockedUntilUtc = null; user.UpdatedAtUtc = DateTime.UtcNow; user.UpdatedBy = resetBy; _logger.LogInformation("用户密码重置成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(message: "用户密码重置成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "重置用户密码失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "RESET_PASSWORD_FAILED", "重置用户密码失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result UnlockUser(Guid userId, string unlockedBy) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } _logger.LogInformation("正在解锁用户账户: {UserId} - {Username}", user.Id, user.Username); user.Status = UserStatus.Enabled; user.LoginFailedCount = 0; user.LockedUntilUtc = null; user.UpdatedAtUtc = DateTime.UtcNow; user.UpdatedBy = unlockedBy; // 清除登录失败计数 _loginFailCounts.Remove(user.Username); _logger.LogInformation("用户账户解锁成功: {UserId} - {Username}", user.Id, user.Username); return Result.Success(message: "用户账户解锁成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "解锁用户账户失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UNLOCK_USER_FAILED", "解锁用户账户失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result UpdateLastLogin(Guid userId, string ipAddress, string userAgent) { try { var user = _users.FirstOrDefault(u => u.Id == userId); if (user == null) { return Result.Fail("USER_NOT_FOUND", $"未找到ID为 {userId} 的用户。"); } user.LastLoginAtUtc = DateTime.UtcNow; user.LastLoginIp = ipAddress; user.LastLoginIp = userAgent; // 这里应该是UserAgent,但为了简化使用同一个字段 _logger.LogInformation("更新用户最后登录信息成功: {UserId}", userId); return Result.Success(message: "更新用户最后登录信息成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新用户最后登录信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_LAST_LOGIN_FAILED", "更新用户最后登录信息失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public Result GetUserStatistics() { try { var now = DateTime.UtcNow; var today = now.Date; var thisWeekStart = today.AddDays(-(int)today.DayOfWeek); var thisMonthStart = new DateTime(now.Year, now.Month, 1); var stats = new UserStatistics { TotalUsers = _users.Count(u => u.Status != UserStatus.Deleted), EnabledUsers = _users.Count(u => u.Status == UserStatus.Enabled), DisabledUsers = _users.Count(u => u.Status == UserStatus.Disabled), LockedUsers = _users.Count(u => u.Status == UserStatus.Locked), TodayLoginUsers = _users.Count(u => u.LastLoginAtUtc.HasValue && u.LastLoginAtUtc.Value.Date == today), ThisWeekLoginUsers = _users.Count(u => u.LastLoginAtUtc.HasValue && u.LastLoginAtUtc.Value >= thisWeekStart), ThisMonthLoginUsers = _users.Count(u => u.LastLoginAtUtc.HasValue && u.LastLoginAtUtc.Value >= thisMonthStart) }; return Result.Success(stats, message: "获取用户统计信息成功。"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取用户统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_USER_STATISTICS_FAILED", "获取用户统计信息失败。", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// /// 生成密码哈希。 /// private static (string Hash, string Salt) GeneratePasswordHash(string password) { using var hmac = new HMACSHA256(); var salt = Convert.ToBase64String(hmac.Key); var hash = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(password))); return (hash, salt); } /// /// 验证密码哈希。 /// private static bool VerifyPasswordHash(string password, string storedHash) { try { // 简化版本:直接比较明文(实际项目中应该使用安全的哈希算法) // 这里为了演示,使用简单的哈希方式 var computedHash = ComputeSimpleHash(password); return storedHash.Equals(computedHash, StringComparison.OrdinalIgnoreCase); } catch { return false; } } /// /// 计算简单哈希(仅用于演示,生产环境应使用 BCrypt 等安全算法)。 /// private static string ComputeSimpleHash(string input) { using var sha256 = SHA256.Create(); var bytes = Encoding.UTF8.GetBytes(input + "OrpaonVision2024"); // 加盐 var hash = sha256.ComputeHash(bytes); return Convert.ToBase64String(hash); } /// /// 克隆用户信息,排除敏感数据。 /// private static UserModel CloneUserWithoutSensitiveData(UserModel user) { return new UserModel { Id = user.Id, Username = user.Username, DisplayName = user.DisplayName, Email = user.Email, PhoneNumber = user.PhoneNumber, Status = user.Status, LastLoginAtUtc = user.LastLoginAtUtc, LastLoginIp = user.LastLoginIp, LoginFailedCount = user.LoginFailedCount, LockedUntilUtc = user.LockedUntilUtc, IsFirstLogin = user.IsFirstLogin, CreatedAtUtc = user.CreatedAtUtc, UpdatedAtUtc = user.UpdatedAtUtc, CreatedBy = user.CreatedBy, UpdatedBy = user.UpdatedBy, Remark = user.Remark, // 不包含密码相关字段 PasswordHash = string.Empty, PasswordSalt = string.Empty }; } /// /// 初始化示例数据。 /// private void InitializeSampleData() { var sampleUsers = new List { new UserModel { Id = Guid.NewGuid(), Username = "admin", DisplayName = "系统管理员", Email = "admin@orpaon.com", Status = UserStatus.Enabled, PasswordHash = ComputeSimpleHash("admin"), PasswordSalt = "OrpaonVision2024", CreatedAtUtc = DateTime.UtcNow.AddDays(-30), UpdatedAtUtc = DateTime.UtcNow.AddDays(-1), CreatedBy = "System", UpdatedBy = "System" }, new UserModel { Id = Guid.NewGuid(), Username = "operator1", DisplayName = "操作员1", Email = "operator1@orpaon.com", Status = UserStatus.Enabled, PasswordHash = ComputeSimpleHash("operator1"), PasswordSalt = "OrpaonVision2024", CreatedAtUtc = DateTime.UtcNow.AddDays(-20), UpdatedAtUtc = DateTime.UtcNow.AddDays(-2), CreatedBy = "admin", UpdatedBy = "admin" }, new UserModel { Id = Guid.NewGuid(), Username = "engineer1", DisplayName = "工艺工程师1", Email = "engineer1@orpaon.com", Status = UserStatus.Enabled, PasswordHash = ComputeSimpleHash("engineer1"), PasswordSalt = "OrpaonVision2024", CreatedAtUtc = DateTime.UtcNow.AddDays(-15), UpdatedAtUtc = DateTime.UtcNow.AddDays(-3), CreatedBy = "admin", UpdatedBy = "admin" } }; _users.AddRange(sampleUsers); foreach (var user in sampleUsers) { var (hash, salt) = GeneratePasswordHash("123456"); // 默认密码 user.PasswordHash = hash; user.PasswordSalt = salt; } _users.AddRange(sampleUsers); _logger.LogInformation("已初始化 {Count} 个示例用户", sampleUsers.Count); } }