333 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ApiResponse<PagedResult<UserDto>>> 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<UserDto>.Create(items, total, request.PageIndex, request.PageSize);
return ApiResponse<PagedResult<UserDto>>.Success(result);
}
public async Task<ApiResponse<UserDto>> GetUserByIdAsync(long id)
{
var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id);
if (user == null)
{
return ApiResponse<UserDto>.Fail("用户不存在", 404);
}
// 销售数据隔离
if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId)
{
return ApiResponse<UserDto>.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<UserDto>.Success(dto);
}
public async Task<ApiResponse<UserDto>> CreateUserAsync(CreateUserRequest request)
{
if (string.IsNullOrWhiteSpace(request.Username))
{
return ApiResponse<UserDto>.Fail("用户名不能为空", 400);
}
var exists = await _db.Users.AnyAsync(u => u.Username == request.Username);
if (exists)
{
return ApiResponse<UserDto>.Fail("用户名已存在", 400);
}
if (string.IsNullOrWhiteSpace(request.Password))
{
return ApiResponse<UserDto>.Fail("密码不能为空", 400);
}
var strengthError = AuthService.ValidatePasswordStrength(request.Password);
if (strengthError != null)
{
return ApiResponse<UserDto>.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<UserDto>.Success(AuthService.MapToDto(user, _fileStorage));
}
public async Task<ApiResponse<UserDto>> UpdateUserAsync(long id, UpdateUserRequest request)
{
var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == id);
if (user == null)
{
return ApiResponse<UserDto>.Fail("用户不存在", 404);
}
// 销售数据隔离
if (_currentUser.Role == UserRole.Sales && user.CreatedBy != _currentUser.UserId)
{
return ApiResponse<UserDto>.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<UserDto>.Success(AuthService.MapToDto(user, _fileStorage));
}
public async Task<ApiResponse> 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<ApiResponse> 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<ApiResponse> 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}成功");
}
}