460 lines
17 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|