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