using Microsoft.EntityFrameworkCore; using MilliSim.Common.Dtos; using MilliSim.Common.Entities; using MilliSim.Common.Models; using MilliSim.Data; using MilliSim.Services.Infrastructure; namespace MilliSim.Services; public class UserService : IUserService { private readonly MilliSimDbContext _db; private readonly ICurrentUserService _currentUser; private readonly IOperationLogService _operationLog; private readonly INotificationService _notification; private readonly IFileStorageService _fileStorage; private readonly IUploadTrackingService _uploadTracking; public UserService( MilliSimDbContext db, ICurrentUserService currentUser, IOperationLogService operationLog, INotificationService notification, IFileStorageService fileStorage, IUploadTrackingService uploadTracking) { _db = db; _currentUser = currentUser; _operationLog = operationLog; _notification = notification; _fileStorage = fileStorage; _uploadTracking = uploadTracking; } public async Task>> GetUsersAsync(UserQueryRequest request) { var query = _db.Users.AsQueryable(); // 销售数据隔离:销售只能看自己创建的客户 if (_currentUser.Role == UserRole.Sales) { query = query.Where(u => u.CreatedBy == _currentUser.UserId); } if (request.Role.HasValue) { query = query.Where(u => u.Role == request.Role.Value); } else if (request.StaffOnly) { // 员工管理页面:未指定具体角色时,排除客户角色 query = query.Where(u => u.Role != UserRole.Customer); } if (request.Status.HasValue) { query = query.Where(u => u.Status == request.Status.Value); } if (request.CreatedBy.HasValue) { query = query.Where(u => u.CreatedBy == request.CreatedBy.Value); } if (!string.IsNullOrWhiteSpace(request.Keyword)) { var keyword = request.Keyword.Trim(); query = query.Where(u => u.Username.Contains(keyword) || u.Nickname.Contains(keyword) || u.Phone.Contains(keyword) || u.Email.Contains(keyword)); } var total = await query.LongCountAsync(); var users = await query .OrderByDescending(u => u.CreatedAt) .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); // 预加载创建人名称 var creatorIds = users.Where(u => u.CreatedBy.HasValue).Select(u => u.CreatedBy!.Value).Distinct().ToList(); var creators = await _db.Users .Where(u => creatorIds.Contains(u.Id)) .ToDictionaryAsync(u => u.Id, u => u.Nickname); // 预加载客户授权信息(一个客户最多一条授权记录,历史数据可能多条,取最新一条) var userIds = users.Select(u => u.Id).ToList(); var authList = await _db.Authorizations .Where(a => userIds.Contains(a.UserId)) .ToListAsync(); var authDict = authList .GroupBy(a => a.UserId) .ToDictionary(g => g.Key, g => g.OrderByDescending(a => a.CreatedAt).First()); var items = users.Select(u => { var dto = AuthService.MapToDto(u, _fileStorage); if (u.CreatedBy.HasValue && creators.TryGetValue(u.CreatedBy.Value, out var name)) { dto.CreatedByName = name; } if (authDict.TryGetValue(u.Id, out var auth)) { dto.AuthorizationId = auth.Id; dto.AuthorizationStatus = auth.Status; dto.AuthorizationEndTime = auth.EndTime; } return dto; }).ToList(); var result = PagedResult.Create(items, total, request.PageIndex, request.PageSize); return ApiResponse>.Success(result); } public async Task> GetUserByIdAsync(long id) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return ApiResponse.Fail("用户不存在", 404); } // 销售数据隔离 if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId) { return ApiResponse.Fail("无权查看该用户", 403); } var dto = AuthService.MapToDto(user, _fileStorage); if (user.CreatedBy.HasValue) { var creator = await _db.Users.FirstOrDefaultAsync(u => u.Id == user.CreatedBy.Value); if (creator != null) { dto.CreatedByName = creator.Nickname; } } return ApiResponse.Success(dto); } public async Task> CreateUserAsync(CreateUserRequest request) { if (string.IsNullOrWhiteSpace(request.Username)) { return ApiResponse.Fail("用户名不能为空", 400); } var exists = await _db.Users.AnyAsync(u => u.Username == request.Username); if (exists) { return ApiResponse.Fail("用户名已存在", 400); } if (string.IsNullOrWhiteSpace(request.Password)) { return ApiResponse.Fail("密码不能为空", 400); } var strengthError = AuthService.ValidatePasswordStrength(request.Password); if (strengthError != null) { return ApiResponse.Fail(strengthError, 400); } var user = new User { Username = request.Username, Password = BCryptHelper.HashPassword(request.Password), Nickname = string.IsNullOrWhiteSpace(request.Nickname) ? request.Username : request.Nickname, Phone = request.Phone ?? string.Empty, Email = request.Email ?? string.Empty, Avatar = _fileStorage.ExtractKey(request.Avatar ?? string.Empty), Role = request.Role, Status = UserStatus.Active, CreatedBy = _currentUser.UserId }; _db.Users.Add(user); await _db.SaveChangesAsync(); // 消费上传跟踪(头像) if (!string.IsNullOrEmpty(request.Avatar)) { await _uploadTracking.ConsumeAsync(_fileStorage.ExtractKey(request.Avatar)); } await _operationLog.LogAsync("用户管理", "创建用户", $"创建用户 {user.Username}({user.Role})"); // 给新用户发送欢迎通知 try { await _notification.CreateNotificationAsync( NotificationType.Account, "欢迎加入平台", $"{user.Nickname},欢迎加入!您可以在项目中心浏览和体验已发布的项目,如有疑问请联系销售或客服。", userId: user.Id, eventId: $"user-welcome-{user.Id}"); } catch { // 通知发送失败不影响用户创建结果 } return ApiResponse.Success(AuthService.MapToDto(user, _fileStorage)); } public async Task> UpdateUserAsync(long id, UpdateUserRequest request) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return ApiResponse.Fail("用户不存在", 404); } // 销售数据隔离 if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId) { return ApiResponse.Fail("无权修改该用户", 403); } // 记录旧头像 key(用于替换时删除旧文件) var oldAvatarKey = _fileStorage.ExtractKey(user.Avatar); var newAvatarKey = oldAvatarKey; if (request.Nickname != null) { user.Nickname = request.Nickname; } if (request.Avatar != null) { user.Avatar = _fileStorage.ExtractKey(request.Avatar); newAvatarKey = _fileStorage.ExtractKey(request.Avatar); } if (request.Phone != null) { user.Phone = request.Phone; } if (request.Email != null) { user.Email = request.Email; } // 更新状态(所有 Staff 角色均可修改) user.Status = request.Status; user.UpdatedAt = DateTime.Now; await _db.SaveChangesAsync(); // 头像替换:删除旧文件 if (!string.IsNullOrEmpty(oldAvatarKey) && oldAvatarKey != newAvatarKey) { await _fileStorage.DeleteAsync(oldAvatarKey); } // 消费新上传的头像 if (request.Avatar != null) { await _uploadTracking.ConsumeAsync(newAvatarKey); } await _operationLog.LogAsync("用户管理", "更新用户", $"更新用户 {user.Username}"); return ApiResponse.Success(AuthService.MapToDto(user, _fileStorage)); } public async Task DeleteUserAsync(long id) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return ApiResponse.Fail("用户不存在", 404); } if (user.Id == _currentUser.UserId) { return ApiResponse.Fail("不能删除当前登录用户", 400); } // 软删除 user.IsDeleted = true; user.UpdatedAt = DateTime.Now; await _db.SaveChangesAsync(); await _operationLog.LogAsync("用户管理", "删除用户", $"删除用户 {user.Username}"); return ApiResponse.Success("删除成功"); } public async Task ResetPasswordAsync(long id) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return ApiResponse.Fail("用户不存在", 404); } const string defaultPassword = "hm123456"; user.Password = BCryptHelper.HashPassword(defaultPassword); user.UpdatedAt = DateTime.Now; await _db.SaveChangesAsync(); await _operationLog.LogAsync("用户管理", "重置密码", $"重置用户 {user.Username} 的密码"); return ApiResponse.Success("密码已重置为 hm123456"); } public async Task ToggleStatusAsync(long id) { var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id); if (user == null) { return ApiResponse.Fail("用户不存在", 404); } if (user.Id == _currentUser.UserId) { return ApiResponse.Fail("不能禁用当前登录用户", 400); } user.Status = user.Status == UserStatus.Active ? UserStatus.Disabled : UserStatus.Active; user.UpdatedAt = DateTime.Now; await _db.SaveChangesAsync(); var action = user.Status == UserStatus.Active ? "启用" : "禁用"; await _operationLog.LogAsync("用户管理", "切换状态", $"{action}用户 {user.Username}"); return ApiResponse.Success($"{action}成功"); } }