添加项目文件。
This commit is contained in:
23
TechHelper.Server/Services/ApiResponse.cs
Normal file
23
TechHelper.Server/Services/ApiResponse.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class ApiResponse
|
||||
{
|
||||
public ApiResponse(string message, bool status = false)
|
||||
{
|
||||
this.Message = message;
|
||||
this.Status = status;
|
||||
}
|
||||
|
||||
public ApiResponse(bool status, object result)
|
||||
{
|
||||
this.Status = status;
|
||||
this.Result = result;
|
||||
}
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool Status { get; set; }
|
||||
|
||||
public object Result { get; set; }
|
||||
}
|
||||
}
|
125
TechHelper.Server/Services/AuthenticationService.cs
Normal file
125
TechHelper.Server/Services/AuthenticationService.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using TechHelper.Context;
|
||||
using Entities.Configuration;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Entities.Contracts;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class AuthenticationService : IAuthenticationService
|
||||
{
|
||||
private readonly JwtConfiguration _jwtSettings;
|
||||
private readonly JwtSecurityTokenHandler _jwtHandler;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly IClassService _classService;
|
||||
public AuthenticationService(IOptions<JwtConfiguration> jwtSettings, UserManager<User> userManager, IClassService classService)
|
||||
{
|
||||
_jwtSettings = jwtSettings.Value;
|
||||
_jwtHandler = new JwtSecurityTokenHandler();
|
||||
_userManager = userManager;
|
||||
_classService = classService;
|
||||
}
|
||||
|
||||
public async Task<string> GetToken(User user)
|
||||
{
|
||||
var signingCredentials = GetSigningCredentials();
|
||||
|
||||
var claims = await GetClaims(user);
|
||||
|
||||
var tokenOptions = GenerateTokenOptions(signingCredentials, claims);
|
||||
|
||||
return _jwtHandler.WriteToken(tokenOptions);
|
||||
}
|
||||
|
||||
private SigningCredentials GetSigningCredentials()
|
||||
{
|
||||
var key = Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey);
|
||||
var secret = new SymmetricSecurityKey(key);
|
||||
|
||||
return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Claim>> GetClaims(User user)
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Email)
|
||||
};
|
||||
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
foreach (var role in roles)
|
||||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, role));
|
||||
}
|
||||
|
||||
var classInfo = await _classService.GetUserClass(user.Id);
|
||||
if (classInfo.Status)
|
||||
{
|
||||
(byte, byte) info = ((byte, byte))classInfo.Result;
|
||||
claims.Add(new Claim("Grade", info.Item1.ToString()));
|
||||
claims.Add(new Claim("Class", info.Item2.ToString()));
|
||||
}
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, IEnumerable<Claim> claims)
|
||||
{
|
||||
var tokenOptions = new JwtSecurityToken(
|
||||
issuer: _jwtSettings.ValidIssuer,
|
||||
audience: _jwtSettings.ValidAudience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(Convert.ToDouble(
|
||||
_jwtSettings.ExpiryInMinutes)),
|
||||
signingCredentials: signingCredentials);
|
||||
|
||||
return tokenOptions;
|
||||
}
|
||||
|
||||
public string GenerateRefreshToken()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
|
||||
{
|
||||
var tokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateAudience = true,
|
||||
ValidateIssuer = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey)),
|
||||
ValidateLifetime = false,
|
||||
ValidIssuer = _jwtSettings.ValidIssuer,
|
||||
ValidAudience = _jwtSettings.ValidAudience,
|
||||
};
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
SecurityToken securityToken;
|
||||
|
||||
var principal = tokenHandler.ValidateToken(token,
|
||||
tokenValidationParameters, out securityToken);
|
||||
|
||||
var jwtSecurityToken = securityToken as JwtSecurityToken;
|
||||
if (jwtSecurityToken == null ||
|
||||
!jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
throw new SecurityTokenException("Invalid token");
|
||||
}
|
||||
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
218
TechHelper.Server/Services/ClassService.cs
Normal file
218
TechHelper.Server/Services/ClassService.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using AutoMapper;
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using SharedDATA.Api;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class ClassService : IClassService
|
||||
{
|
||||
private readonly IUnitOfWork _work;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly UserManager<User> _userManager;
|
||||
|
||||
public ClassService(IUnitOfWork work, IMapper mapper, UserManager<User> userManager)
|
||||
{
|
||||
_work = work;
|
||||
_mapper = mapper;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> AddAsync(ClassDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var @class = _mapper.Map<Class>(model);
|
||||
await _work.GetRepository<Class>().InsertAsync(@class);
|
||||
|
||||
if (await _work.SaveChangesAsync() > 0)
|
||||
{
|
||||
return new ApiResponse(true, _mapper.Map<ClassDto>(@class));
|
||||
}
|
||||
return new ApiResponse("添加班级失败。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ApiResponse($"添加班级时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 实现 IBaseService<ClassDto, int>.DeleteAsync
|
||||
public async Task<ApiResponse> DeleteAsync(Guid id) // ID 类型现在是 int
|
||||
{
|
||||
try
|
||||
{
|
||||
var existingClass = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(
|
||||
predicate: c => c.Id == id);
|
||||
|
||||
if (existingClass == null)
|
||||
{
|
||||
return new ApiResponse("班级未找到。");
|
||||
}
|
||||
|
||||
_work.GetRepository<Class>().Delete(existingClass);
|
||||
|
||||
if (await _work.SaveChangesAsync() > 0)
|
||||
{
|
||||
return new ApiResponse(true, "班级删除成功。");
|
||||
}
|
||||
return new ApiResponse("删除班级失败。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ApiResponse($"删除班级时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 实现 IBaseService<ClassDto, int>.GetAllAsync
|
||||
public async Task<ApiResponse> GetAllAsync(QueryParameter query)
|
||||
{
|
||||
try
|
||||
{
|
||||
var repository = _work.GetRepository<Class>();
|
||||
|
||||
// 构建查询条件 (可根据 QueryParameter.Search 进行筛选)
|
||||
Func<IQueryable<Class>, IOrderedQueryable<Class>> orderBy = null; // 默认不排序
|
||||
if (query.Search != null && !string.IsNullOrWhiteSpace(query.Search))
|
||||
{
|
||||
// 在 Name 字段中进行模糊搜索
|
||||
var classes = await repository.GetPagedListAsync(
|
||||
orderBy: orderBy,
|
||||
pageSize: query.PageSize,
|
||||
pageIndex: query.PageIndex
|
||||
);
|
||||
var classDtosFiltered = _mapper.Map<IEnumerable<ClassDto>>(classes);
|
||||
return new ApiResponse(true, classDtosFiltered);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有搜索条件,获取所有(带分页)
|
||||
var classes = await repository.GetPagedListAsync(
|
||||
orderBy: orderBy,
|
||||
pageSize: query.PageSize,
|
||||
pageIndex: query.PageIndex
|
||||
);
|
||||
var classDtos = _mapper.Map<IEnumerable<ClassDto>>(classes);
|
||||
return new ApiResponse(true, classDtos);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ApiResponse($"获取所有班级时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 实现 IBaseService<ClassDto, int>.GetAsync
|
||||
public async Task<ApiResponse> GetAsync(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var @class = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(
|
||||
predicate: c => c.Id == id);
|
||||
|
||||
if (@class == null)
|
||||
{
|
||||
return new ApiResponse("班级未找到。");
|
||||
}
|
||||
|
||||
var classDto = _mapper.Map<ClassDto>(@class);
|
||||
return new ApiResponse(true, classDto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ApiResponse($"获取班级时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> GetUserClass(Guid user)
|
||||
{
|
||||
var existingUserClass = await _work.GetRepository<ClassStudent>().GetFirstOrDefaultAsync(predicate: c=>c.StudentId == user);
|
||||
if(existingUserClass == null) return new ApiResponse("该学生没有班级。");
|
||||
|
||||
var classId = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(predicate: c => c.Id == existingUserClass.ClassId);
|
||||
return new ApiResponse(true, (classId.Grade, classId.Number));
|
||||
}
|
||||
|
||||
// 实现 IBaseService<ClassDto, int>.UpdateAsync
|
||||
public async Task<ApiResponse> UpdateAsync(ClassDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 首先通过 ID 查找现有实体
|
||||
var existingClass = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(
|
||||
predicate: c => c.Number == model.Class);
|
||||
|
||||
if (existingClass == null)
|
||||
{
|
||||
return new ApiResponse("班级未找到。");
|
||||
}
|
||||
|
||||
_mapper.Map(model, existingClass);
|
||||
_work.GetRepository<Class>().Update(existingClass);
|
||||
|
||||
if (await _work.SaveChangesAsync() > 0)
|
||||
{
|
||||
return new ApiResponse(true, _mapper.Map<ClassDto>(existingClass));
|
||||
}
|
||||
return new ApiResponse("更新班级失败。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ApiResponse($"更新班级时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> UserRegister(UserRegistrationToClassDto user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var usrinfo = await _userManager.FindByEmailAsync(user.User);
|
||||
var existingClass = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(
|
||||
predicate: (c => c.Number == user.ClassId && c.Grade == user.GradeId));
|
||||
|
||||
var finduser = await _userManager.FindByEmailAsync(user.User);
|
||||
|
||||
if (existingClass == null || finduser == null || usrinfo == null)
|
||||
{
|
||||
return new ApiResponse("班级未找到。");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (user.Roles == UserRoles.Student)
|
||||
{
|
||||
var addresult = await _work.GetRepository<ClassStudent>().InsertAsync(new ClassStudent
|
||||
{
|
||||
StudentId = finduser.Id,
|
||||
ClassId = existingClass.Id
|
||||
});
|
||||
|
||||
await _userManager.AddToRoleAsync(usrinfo, UserRoles.Student.ToString());
|
||||
}
|
||||
else if (user.Roles == UserRoles.Teacher)
|
||||
{
|
||||
var classTeacher = new ClassTeacher
|
||||
{
|
||||
ClassId = existingClass.Id,
|
||||
TeacherId = existingClass.Id,
|
||||
SubjectTaught = user.SubjectArea.ToString()
|
||||
};
|
||||
await _work.GetRepository<ClassTeacher>().InsertAsync(classTeacher);
|
||||
|
||||
await _userManager.AddToRoleAsync(usrinfo, UserRoles.Teacher.ToString());
|
||||
}
|
||||
|
||||
if (await _work.SaveChangesAsync() > 0)
|
||||
{
|
||||
return new ApiResponse(true, _mapper.Map<ClassDto>(existingClass));
|
||||
}
|
||||
return new ApiResponse("班级注册失败。");
|
||||
|
||||
}
|
||||
catch (Exception ex) { return new ApiResponse($"注册进班级时发生错误: {ex.Message}"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
TechHelper.Server/Services/IAuthenticationService.cs
Normal file
14
TechHelper.Server/Services/IAuthenticationService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using TechHelper.Context;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Security.Claims;
|
||||
using Entities.Contracts;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public interface IAuthenticationService
|
||||
{
|
||||
public Task<string> GetToken(User user);
|
||||
public string GenerateRefreshToken();
|
||||
public ClaimsPrincipal GetPrincipalFromExpiredToken(string token);
|
||||
}
|
||||
}
|
11
TechHelper.Server/Services/IBaseService.cs
Normal file
11
TechHelper.Server/Services/IBaseService.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public interface IBaseService<T, TId>
|
||||
{
|
||||
Task<ApiResponse> GetAllAsync(QueryParameter query);
|
||||
Task<ApiResponse> GetAsync(TId id);
|
||||
Task<ApiResponse> AddAsync(T model);
|
||||
Task<ApiResponse> UpdateAsync(T model);
|
||||
Task<ApiResponse> DeleteAsync(TId id);
|
||||
}
|
||||
}
|
12
TechHelper.Server/Services/IClassService.cs
Normal file
12
TechHelper.Server/Services/IClassService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using System.Net;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public interface IClassService : IBaseService<ClassDto, Guid>
|
||||
{
|
||||
public Task<ApiResponse> UserRegister(UserRegistrationToClassDto user);
|
||||
public Task<ApiResponse> GetUserClass(Guid user);
|
||||
}
|
||||
}
|
14
TechHelper.Server/Services/IUserRegistrationService.cs
Normal file
14
TechHelper.Server/Services/IUserRegistrationService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Entities.DTO;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public interface IUserRegistrationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册新用户,并根据角色关联到班级。
|
||||
/// </summary>
|
||||
/// <param name="registrationDto">用户注册数据。</param>
|
||||
/// <returns>操作结果。</returns>
|
||||
Task<ApiResponse> RegisterNewUserAsync(UserForRegistrationDto registrationDto);
|
||||
}
|
||||
}
|
9
TechHelper.Server/Services/QueryParameter.cs
Normal file
9
TechHelper.Server/Services/QueryParameter.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class QueryParameter
|
||||
{
|
||||
public int PageIndex { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public string Search { get; set; }
|
||||
}
|
||||
}
|
126
TechHelper.Server/Services/UserRegistrationService.cs
Normal file
126
TechHelper.Server/Services/UserRegistrationService.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using AutoMapper;
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using SharedDATA.Api;
|
||||
using System.Text;
|
||||
using TechHelper.Features;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class UserRegistrationService : IUserRegistrationService
|
||||
{
|
||||
private readonly IUnitOfWork _work;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public UserRegistrationService(
|
||||
IUnitOfWork work,
|
||||
IMapper mapper,
|
||||
UserManager<User> userManager,
|
||||
IEmailSender emailSender)
|
||||
{
|
||||
_work = work;
|
||||
_mapper = mapper;
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> RegisterNewUserAsync(UserForRegistrationDto registrationDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existingUserByEmail = await _userManager.FindByEmailAsync(registrationDto.Email);
|
||||
if (existingUserByEmail != null)
|
||||
{
|
||||
return new ApiResponse("此电子邮件地址已被注册。");
|
||||
}
|
||||
|
||||
var user = _mapper.Map<User>(registrationDto);
|
||||
|
||||
user.UserName = registrationDto.Email;
|
||||
user.DisplayName = registrationDto.Name;
|
||||
user.EmailConfirmed = false;
|
||||
|
||||
var result = await _userManager.CreateAsync(user, registrationDto.Password);
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = result.Errors.Select(e => e.Description).ToList();
|
||||
return new ApiResponse(false, errors);
|
||||
}
|
||||
|
||||
var existingClass = await _work.GetRepository<Class>().GetFirstOrDefaultAsync(
|
||||
predicate: c => c.Number == registrationDto.Class && c.Grade == registrationDto.Grade);
|
||||
|
||||
if (existingClass == null)
|
||||
{
|
||||
existingClass = new Class
|
||||
{
|
||||
Number = (byte)registrationDto.Class,
|
||||
Grade = (byte)registrationDto.Grade
|
||||
};
|
||||
await _work.GetRepository<Class>().InsertAsync(existingClass);
|
||||
}
|
||||
|
||||
//if (registrationDto.Roles == UserRoles.Student)
|
||||
//{
|
||||
// var classStudent = new ClassStudent
|
||||
// {
|
||||
// ClassId = existingClass.Id,
|
||||
// StudentId = user.Id
|
||||
// };
|
||||
// await _work.GetRepository<ClassStudent>().InsertAsync(classStudent);
|
||||
|
||||
// await _userManager.AddToRoleAsync(user, UserRoles.Student.ToString());
|
||||
//}
|
||||
//else if (registrationDto.Roles == UserRoles.Teacher)
|
||||
//{
|
||||
// var classTeacher = new ClassTeacher
|
||||
// {
|
||||
// ClassId = existingClass.Id,
|
||||
// TeacherId = user.Id
|
||||
// };
|
||||
// await _work.GetRepository<ClassTeacher>().InsertAsync(classTeacher);
|
||||
|
||||
// await _userManager.AddToRoleAsync(user, UserRoles.Teacher.ToString());
|
||||
//}
|
||||
|
||||
//var emailConfirmationToken = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
//var encodedToken = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(emailConfirmationToken));
|
||||
//var callbackUrl = QueryHelpers.AddQueryString(registrationDto.ClientURI, new Dictionary<string, string>
|
||||
//{
|
||||
// {"token", encodedToken},
|
||||
// {"email", user.Email}
|
||||
// });
|
||||
|
||||
//try
|
||||
//{
|
||||
// await _emailSender.SendEmailAsync(user.Email, "请确认您的邮箱", $"请点击此链接确认您的邮箱: {callbackUrl}");
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Console.Error.WriteLine($"发送邮箱确认邮件失败: {ex.Message}");
|
||||
//}
|
||||
|
||||
|
||||
if (await _work.SaveChangesAsync() > 0)
|
||||
{
|
||||
return new ApiResponse(true, "用户注册成功,班级关联和邮箱确认邮件已发送。");
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ApiResponse("用户注册成功但班级关联失败。");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"注册过程中发生错误: {ex.Message}");
|
||||
return new ApiResponse($"注册过程中发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user