LSFE/LSFE.Domain/Services/AccountService.cs
2026-07-30 10:31:04 +08:00

460 lines
17 KiB
C#

using LSFE.Domain.Contracts;
using LSFE.Infrastructure.Database;
using LSFE.Infrastructure.Dto;
using LSFE.Infrastructure.Entities;
using LSFE.Infrastructure.Entities.Auth;
using LSFE.Infrastructure.Model;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace LSFE.Domain.Services
{
public class AccountService : IAccountService
{
private readonly UserManager<AppsUsers> _userManager;
private readonly IConfiguration _configuration;
private readonly RoleManager<Role> _roleManager;
private readonly LSFEDbContext _context;
public AccountService(UserManager<AppsUsers> userManager, IConfiguration configuration,
RoleManager<Role> roleManager , LSFEDbContext context )
{
_userManager = userManager;
_configuration = configuration;
_roleManager = roleManager;
_context = context;
}
public async Task<Response> AddUserToRoleAsync(string userId, string roleName)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
return new Response
{
Success = false,
Message = "User not found",
MessCode = 0
};
// Ensure role exists
if (!await _roleManager.RoleExistsAsync(roleName))
{
var roleResult = await _roleManager.CreateAsync(new Role());
if (!roleResult.Succeeded)
return new Response
{
Success = false,
Message = roleResult.ToString(),
MessCode = 0
};
}
await _userManager.AddToRoleAsync(user, roleName);
return new Response
{
Success = true,
Message = "Add user role successfully!",
MessCode = 1
};
}
public async Task<Response> AddClaimToUserAsync(string userId, Claim claim)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
return new Response
{
Success = false,
Message = "User not found",
MessCode = 0
};
await _userManager.AddClaimAsync(user, claim);
return new Response
{
Success = true,
Message = "Success",
MessCode = 1
};
}
public async Task<Response> RegisterUserAsync(RegisterUserDto dto)
{
try
{
// 🔍 1. Validate required fields
if (string.IsNullOrWhiteSpace(dto.UserName) ||
string.IsNullOrWhiteSpace(dto.Email) ||
string.IsNullOrWhiteSpace(dto.Password))
{
return new Response
{
Success = false,
Message = "Username, Email, and Password are required.",
MessCode = 0
};
}
// 🧩 2. Check if username or email already exists
var existingUserByName = await _userManager.FindByNameAsync(dto.UserName);
if (existingUserByName != null)
{
return new Response
{
Success = false,
Message = $"Username '{dto.UserName}' is already taken.",
MessCode = 0
};
}
var existingUserByEmail = await _userManager.FindByEmailAsync(dto.Email);
if (existingUserByEmail != null)
{
return new Response
{
Success = false,
Message = $"Email '{dto.Email}' is already registered.",
MessCode = 0
};
}
// 🏢 3. Optional: Validate Department
if (dto.DepartmentId.HasValue)
{
var departmentExists = await _context.Departments
.AnyAsync(d => d.DepartmentId == dto.DepartmentId && d.IsActive);
if (!departmentExists)
{
return new Response
{
Success = false,
Message = "Invalid Department selected.",
MessCode = 0
};
}
}
// 🧍‍♂️ 4. Create new user
var user = new AppsUsers
{
UserName = dto.UserName,
Email = dto.Email,
FullName = dto.FullName,
EmployeeId = dto.EmployeeId,
Company = dto.Company,
DepartmentId = dto.DepartmentId,
Address = dto.Address,
PhoneNumber = dto.PhoneNumber,
CreatedDate = DateTime.UtcNow,
UpdatedDate = DateTime.UtcNow,
CreatedBy = dto.UpdatedBy,
UpdatedBy = dto.UpdatedBy,
EmailConfirmed = true // optional: mark as verified by admin
};
var createResult = await _userManager.CreateAsync(user, dto.Password);
if (!createResult.Succeeded)
{
var errors = string.Join("; ", createResult.Errors.Select(e => e.Description));
return new Response
{
Success = false,
Message = $"Failed to create user: {errors}",
MessCode = 0
};
}
// 🛡️ 5. Assign Role if provided
if (!string.IsNullOrWhiteSpace(dto.RoleId))
{
var role = await _roleManager.FindByIdAsync(dto.RoleId);
if (role == null)
{
return new Response
{
Success = false,
Message = $"Role with ID '{dto.RoleId}' does not exist.",
MessCode = 0
};
}
await _userManager.AddToRoleAsync(user, role.Name);
// optional: add user claim for role
await _userManager.AddClaimAsync(user, new Claim("RoleName", role.Name));
}
return new Response
{
Success = true,
Message = "User registered successfully!",
MessCode = 1
};
}
catch (Exception ex)
{
return new Response
{
Success = false,
Message = $"Unexpected error: {ex.Message}",
MessCode = 0
};
}
}
public async Task<string> CreateToken(AppsUsers user)
{
var authClaims = await BuildClaims(user);
var authSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
var token = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
audience: _configuration["JWT:ValidAudience"],
expires: DateTime.UtcNow.AddMinutes(30),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private async Task<List<Claim>> BuildClaims(AppsUsers user)
{
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim("FullName", user.FullName ?? ""),
new Claim("Company", user.Company ?? ""),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
return claims;
}
public async Task<AppsUsers?> GetUserById(string userId)
{
return await _userManager.Users.FirstOrDefaultAsync(u => u.Id == userId);
}
public async Task<Response> UpdateUserAsync(UpdateUserDto dto)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var user = await _userManager.FindByIdAsync(dto.Id.ToString());
if (user == null)
return new Response { Success = false, Message = "User not found.", MessCode = 0 };
// --- Update basic info ---
user.Email = dto.Email;
user.FullName = dto.FullName;
user.EmployeeId = dto.EmployeeId;
user.Company = dto.Company;
user.DepartmentId = dto.DepartmentId;
user.Address = dto.Address;
user.PhoneNumber = dto.PhoneNumber;
user.UpdatedBy = dto.UpdatedBy;
user.UpdatedDate = DateTime.UtcNow;
// --- Update password (if provided) ---
if (!string.IsNullOrWhiteSpace(dto.Password))
{
var passwordHasher = new PasswordHasher<AppsUsers>();
user.PasswordHash = passwordHasher.HashPassword(user, dto.Password);
}
var updateResult = await _userManager.UpdateAsync(user);
if (!updateResult.Succeeded)
return new Response
{
Success = false,
Message = string.Join(", ", updateResult.Errors.Select(e => e.Description)),
MessCode = 0
};
// --- Update roles ---
if (!string.IsNullOrEmpty(dto.RoleName))
{
var currentRoles = await _userManager.GetRolesAsync(user);
if (currentRoles.Any())
await _userManager.RemoveFromRolesAsync(user, currentRoles);
if (await _roleManager.RoleExistsAsync(dto.RoleName))
await _userManager.AddToRoleAsync(user, dto.RoleName);
else
return new Response
{
Success = false,
Message = $"Role '{dto.RoleName}' not found.",
MessCode = 0
};
}
// --- Update claims ---
if (!string.IsNullOrEmpty(dto.RoleName))
{
var existingClaims = await _userManager.GetClaimsAsync(user);
var existingRoleClaim = existingClaims.FirstOrDefault(c => c.Type == "Role");
if (existingRoleClaim != null)
await _userManager.RemoveClaimAsync(user, existingRoleClaim);
await _userManager.AddClaimAsync(user, new Claim("Role", dto.RoleName));
}
await transaction.CommitAsync();
return new Response
{
Success = true,
Message = "User updated successfully!",
MessCode = 1
};
}
catch (Exception ex)
{
await transaction.RollbackAsync();
return new Response
{
Success = false,
Message = $"Updating user failed: {ex.Message}",
MessCode = 0
};
}
}
public async Task<List<UserWithRoleDto>> GetUsers(string company)
{
try
{
var users = await _userManager.Users
.Where(u=>u.Company== company)
.Include(u => u.Department)
.ToListAsync();
var roles = await _roleManager.Roles.ToListAsync(); // cache all roles once
var result = new List<UserWithRoleDto>();
foreach (var user in users)
{
var roleNames = await _userManager.GetRolesAsync(user);
// Map role names to RoleId + RoleName
var userRoles = roles
.Where(r => roleNames.Contains(r.Name))
.Select(r => new RoleDto
{
RoleId = r.Id,
RoleName = r.Name!
})
.ToList();
result.Add(new UserWithRoleDto
{
Id = user.Id,
EmployeeId = user.EmployeeId,
Company = user.Company,
Address = user.Address,
UpdatedDate = user.UpdatedDate,
UpdatedBy = user.UpdatedBy,
Email = user.Email,
FullName = user.FullName,
RoleName = userRoles[0].RoleName,
CreatedBy = user.CreatedBy,
CreatedDate = user.CreatedDate,
UserName = user.UserName,
DepartmentId = user.DepartmentId,
RoleId = userRoles[0].RoleId,
Department = user.Department,
AccessFailedCount = user.AccessFailedCount,
PhoneNumber = user.PhoneNumber,
LockoutEnd = user.LockoutEnd,
LockoutEnabled = user.LockoutEnabled,
UserRoles = userRoles
});
}
return result;
}
catch (Exception ex)
{
ex.ToString();
throw;
}
}
public async Task<Response> PostPutDepartmentAsync(Departments dto)
{
var existing = await _context.Departments
.FirstOrDefaultAsync(d => d.DepartmentId == dto.DepartmentId);
if (existing == null)
{
var duplicate = await _context.Departments
.AnyAsync(d => d.DepartmentName == dto.DepartmentName
|| d.DepartmentCode == dto.DepartmentCode);
if (duplicate)
{
return new Response
{
Success = false,
Message = "Department name or code already exists.",
MessCode = 0
};
}
_context.Departments.Add(dto);
await _context.SaveChangesAsync();
return new Response
{
Success = true,
Message = "Department created successfully.",
MessCode = 1
};
}
else
{
existing.DepartmentName = dto.DepartmentName;
existing.DepartmentCode = dto.DepartmentCode;
existing.IsActive = dto.IsActive;
_context.Departments.Update(existing);
await _context.SaveChangesAsync();
return new Response
{
Success = true,
Message = "Department updated successfully.",
MessCode = 1
};
}
}
public async Task<List<Departments>> GetDepartment()
{
return await _context.Departments.ToListAsync();
}
public async Task<List<RoleDto>> GetUserRole()
{
return await _roleManager.Roles
.Select(r => new RoleDto
{
RoleId = r.Id,
RoleName = r.Name
})
.ToListAsync();
}
}
}