diff --git a/backend/IM_API/Configs/MapperConfig.cs b/backend/IM_API/Configs/MapperConfig.cs new file mode 100644 index 0000000..c4d57e0 --- /dev/null +++ b/backend/IM_API/Configs/MapperConfig.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using IM_API.Dtos; +using IM_API.Models; + +namespace IM_API.Configs +{ + public class MapperConfig:Profile + { + public MapperConfig() + { + CreateMap(); + } + } +} diff --git a/backend/IM_API/Configs/ServiceCollectionExtensions.cs b/backend/IM_API/Configs/ServiceCollectionExtensions.cs index b7ee2c6..89e400c 100644 --- a/backend/IM_API/Configs/ServiceCollectionExtensions.cs +++ b/backend/IM_API/Configs/ServiceCollectionExtensions.cs @@ -1,9 +1,16 @@ -namespace IM_API.Configs +using IM_API.Interface.Services; +using IM_API.Services; + +namespace IM_API.Configs { public static class ServiceCollectionExtensions { public static IServiceCollection AddAllService(this IServiceCollection services, IConfiguration configuration) { + services.AddAutoMapper(typeof(MapperConfig)); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); return services; } } diff --git a/backend/IM_API/Controllers/AuthController.cs b/backend/IM_API/Controllers/AuthController.cs new file mode 100644 index 0000000..bca7b5c --- /dev/null +++ b/backend/IM_API/Controllers/AuthController.cs @@ -0,0 +1,37 @@ +using IM_API.Dtos; +using IM_API.Interface.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace IM_API.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + public class AuthController : ControllerBase + { + private readonly ILogger _logger; + private readonly IAuthService _authService; + private readonly IJWTService _jwtService; + private readonly IRefreshTokenService _refreshTokenService; + private readonly IConfiguration _configuration; + public AuthController(ILogger logger, IAuthService authService, IJWTService jwtService, IRefreshTokenService refreshTokenService, IConfiguration configuration) + { + _logger = logger; + _authService = authService; + _jwtService = jwtService; + _refreshTokenService = refreshTokenService; + _configuration = configuration; + } + [HttpPost] + public async Task Login(LoginRequestDto dto) + { + var user = await _authService.LoginAsync(dto); + //生成凭证 + (string token,DateTime expiresAt) = _jwtService.CreateAccessTokenForUser(user.Id,user.Username,"user"); + //生成刷新凭证 + string refreshToken = await _refreshTokenService.CreateRefreshTokenAsync(user.Id); + var res = new BaseResponse(new LoginDto(user.Id,token,refreshToken,expiresAt)); + return Ok(res); + } + } +} diff --git a/backend/IM_API/Controllers/WeatherForecastController.cs b/backend/IM_API/Controllers/WeatherForecastController.cs deleted file mode 100644 index 6749fdd..0000000 --- a/backend/IM_API/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace IM_API.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/backend/IM_API/Dtos/BaseResponse.cs b/backend/IM_API/Dtos/BaseResponse.cs index e238946..f75c89b 100644 --- a/backend/IM_API/Dtos/BaseResponse.cs +++ b/backend/IM_API/Dtos/BaseResponse.cs @@ -1,4 +1,6 @@ -namespace IM_API.Dtos +using IM_API.Tools; + +namespace IM_API.Dtos { public class BaseResponse { @@ -20,13 +22,23 @@ this.Data = data; } /// + /// 默认成功响应返回仅数据 + /// + /// + public BaseResponse(T data) + { + this.Code = CodeDefine.SUCCESS.Code; + this.Message = CodeDefine.SUCCESS.Message; + this.Data = data; + } + /// /// 默认成功响应返回,不带数据 /// /// /// public BaseResponse(string msg) { - this.Code = 0; + this.Code = CodeDefine.SUCCESS.Code; this.Message = msg; } /// diff --git a/backend/IM_API/Dtos/LoginDto.cs b/backend/IM_API/Dtos/LoginDto.cs index 03cd5e0..ba83096 100644 --- a/backend/IM_API/Dtos/LoginDto.cs +++ b/backend/IM_API/Dtos/LoginDto.cs @@ -5,5 +5,12 @@ public int Id { get; set; } public string Token { get; set; } public string RefreshToken { get; set; } + public DateTime ExpireAt { get; set; } + public LoginDto(int id,string token,string refreshToken,DateTime expireAt) { + this.Id = id; + this.Token = token; + this.RefreshToken = refreshToken; + this.ExpireAt = expireAt; + } } } diff --git a/backend/IM_API/Dtos/RegisterRequestDto.cs b/backend/IM_API/Dtos/RegisterRequestDto.cs index e4ac4d0..64a1f1f 100644 --- a/backend/IM_API/Dtos/RegisterRequestDto.cs +++ b/backend/IM_API/Dtos/RegisterRequestDto.cs @@ -4,5 +4,6 @@ { public string Username { get; set; } public string Password { get; set; } + public string NickName { get; set; } } } diff --git a/backend/IM_API/Exceptions/BaseException.cs b/backend/IM_API/Exceptions/BaseException.cs new file mode 100644 index 0000000..cafaf70 --- /dev/null +++ b/backend/IM_API/Exceptions/BaseException.cs @@ -0,0 +1,17 @@ +using IM_API.Tools; + +namespace IM_API.Exceptions +{ + public class BaseException:Exception + { + public int Code { get; set; } + + public BaseException(int code,string message) : base(message) { + this.Code = code; + } + public BaseException(CodeDefine codeDefine) : base(codeDefine.Message) + { + this.Code = codeDefine.Code; + } + } +} diff --git a/backend/IM_API/IM_API.csproj b/backend/IM_API/IM_API.csproj index dfcdc88..d2ea7fb 100644 --- a/backend/IM_API/IM_API.csproj +++ b/backend/IM_API/IM_API.csproj @@ -10,6 +10,8 @@ + + all diff --git a/backend/IM_API/IM_API.csproj.user b/backend/IM_API/IM_API.csproj.user index 983ecfc..e5a2ec0 100644 --- a/backend/IM_API/IM_API.csproj.user +++ b/backend/IM_API/IM_API.csproj.user @@ -2,6 +2,8 @@ http + ApiControllerEmptyScaffolder + root/Common/Api ProjectDebugger diff --git a/backend/IM_API/Interface/Services/IAuthService.cs b/backend/IM_API/Interface/Services/IAuthService.cs index 38b493f..1c4ce71 100644 --- a/backend/IM_API/Interface/Services/IAuthService.cs +++ b/backend/IM_API/Interface/Services/IAuthService.cs @@ -10,30 +10,12 @@ namespace IM_API.Interface.Services /// /// /// - Task LoginAsync(LoginRequestDto dto); + Task LoginAsync(LoginRequestDto dto); /// /// 注册 /// /// /// Task RegisterAsync(RegisterRequestDto dto); - /// - /// 生成登录凭证 - /// - /// - /// - string GenerateToken(User user); - /// - /// 验证登录凭证 - /// - /// - /// - int? ValidateToken(string token); - /// - /// 刷新令牌 - /// - /// - /// - LoginDto RefreshToken(string refreshToken); } } diff --git a/backend/IM_API/Models/ImContext.cs b/backend/IM_API/Models/ImContext.cs index 7a8e476..b5afb0a 100644 --- a/backend/IM_API/Models/ImContext.cs +++ b/backend/IM_API/Models/ImContext.cs @@ -62,6 +62,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -101,6 +102,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -145,6 +147,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.DtypeEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -216,6 +219,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StatusEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -258,6 +262,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -301,6 +306,9 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StatusEnum); + entity.Ignore(x => x.AllMembersBannedEnum); + entity.Ignore(x => x.AuhorityEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -345,6 +353,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -393,6 +402,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.RoleEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -436,6 +446,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -477,6 +488,7 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -511,6 +523,8 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + entity.Ignore(x => x.StateEnum); + entity.Ignore(x => x.MsgTypeEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity @@ -675,6 +689,10 @@ public partial class ImContext : DbContext modelBuilder.Entity(entity => { + + // 忽略包装枚举属性 + entity.Ignore(e => e.OnlineStatusEnum); + entity.Ignore(e => e.StatusEnum); entity.HasKey(e => e.Id).HasName("PRIMARY"); entity diff --git a/backend/IM_API/Services/AuthService.cs b/backend/IM_API/Services/AuthService.cs new file mode 100644 index 0000000..0746084 --- /dev/null +++ b/backend/IM_API/Services/AuthService.cs @@ -0,0 +1,57 @@ +using AutoMapper; +using IM_API.Dtos; +using IM_API.Exceptions; +using IM_API.Interface.Services; +using IM_API.Models; +using IM_API.Tools; +using Microsoft.EntityFrameworkCore; + +namespace IM_API.Services +{ + public class AuthService : IAuthService + { + private readonly ImContext _context; + private readonly ILogger _logger; + private readonly IMapper _mapper; + public AuthService(ImContext context, ILogger logger, IMapper mapper) + { + _context = context; + _logger = logger; + _mapper = mapper; + } + + public async Task LoginAsync(LoginRequestDto dto) + { + string username = dto.Username; + string password = dto.Password; + var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username && x.Password == password); + if(user is null) + { + throw new BaseException(CodeDefine.PASSWORD_ERROR); + } + return user; + } + + public async Task RegisterAsync(RegisterRequestDto dto) + { + string username = dto.Username; + string password = dto.Password; + string nickname = dto.NickName; + //用户是否存在 + bool isExist = await _context.Users.AnyAsync(x => x.Username == username); + if (isExist) throw new BaseException(CodeDefine.USER_ALREADY_EXISTS); + User user = new User + { + Username = username, + Password = password, + NickName = nickname, + OnlineStatusEnum = UserOnlineStatus.Offline, + StatusEnum = UserStatus.Normal, + Created = DateTime.Now + }; + _context.Users.Add(user); + await _context.SaveChangesAsync(); + return _mapper.Map(user); + } + } +} diff --git a/backend/IM_API/Tools/CodeDefine.cs b/backend/IM_API/Tools/CodeDefine.cs new file mode 100644 index 0000000..5ed3bff --- /dev/null +++ b/backend/IM_API/Tools/CodeDefine.cs @@ -0,0 +1,102 @@ +namespace IM_API.Tools +{ + public class CodeDefine + { + public int Code { get; set; } + public string Message { get; set; } + + public CodeDefine(int code, string message) + { + Code = code; + Message = message; + } + // 3.1 成功类 + /// 成功响应 + public static CodeDefine SUCCESS = new CodeDefine(0, "成功"); + + // 3.2 系统级错误(1000 ~ 1999) + /// 未知异常 + public static CodeDefine SYSTEM_ERROR = new CodeDefine(1000, "系统错误"); + /// 服务器维护中或宕机 + public static CodeDefine SERVICE_UNAVAILABLE = new CodeDefine(1001, "服务不可用"); + /// 后端超时 + public static CodeDefine REQUEST_TIMEOUT = new CodeDefine(1002, "请求超时"); + /// 缺少或参数不合法 + public static CodeDefine PARAMETER_ERROR = new CodeDefine(1003, "参数错误"); + /// 数据库读写失败 + public static CodeDefine DATABASE_ERROR = new CodeDefine(1004, "数据库错误"); + /// 无权限访问 + public static CodeDefine PERMISSION_DENIED = new CodeDefine(1005, "权限不足"); + /// Token 无效/过期 + public static CodeDefine AUTH_FAILED = new CodeDefine(1006, "认证失败"); + + // 3.3 用户相关错误(2000 ~ 2099) + /// 查询不到用户 + public static CodeDefine USER_NOT_FOUND = new CodeDefine(2000, "用户不存在"); + /// 注册时用户已存在 + public static CodeDefine USER_ALREADY_EXISTS = new CodeDefine(2001, "用户已存在"); + /// 登录密码错误 + public static CodeDefine PASSWORD_ERROR = new CodeDefine(2002, "密码错误"); + /// 被管理员封禁 + public static CodeDefine USER_DISABLED = new CodeDefine(2003, "用户被禁用"); + /// 需重新登录 + public static CodeDefine LOGIN_EXPIRED = new CodeDefine(2004, "登录过期"); + + // 3.4 好友相关错误(2100 ~ 2199) + /// 重复申请 + public static CodeDefine FRIEND_REQUEST_EXISTS = new CodeDefine(2100, "好友申请已存在"); + /// 不是好友 + public static CodeDefine FRIEND_RELATION_NOT_FOUND = new CodeDefine(2101, "好友关系不存在"); + /// 重复添加 + public static CodeDefine ALREADY_FRIENDS = new CodeDefine(2102, "已经是好友"); + /// 被对方拒绝 + public static CodeDefine FRIEND_REQUEST_REJECTED = new CodeDefine(2103, "好友请求被拒绝"); + /// 被对方拉黑 + public static CodeDefine CANNOT_ADD_FRIEND = new CodeDefine(2104, "无法申请加好友"); + + // 3.5 群聊相关错误(2200 ~ 2299) + /// 查询不到群 + public static CodeDefine GROUP_NOT_FOUND = new CodeDefine(2200, "群不存在"); + /// 不能重复加入 + public static CodeDefine ALREADY_IN_GROUP = new CodeDefine(2201, "已在群中"); + /// 超出限制 + public static CodeDefine GROUP_FULL = new CodeDefine(2202, "群成员已满"); + /// 需要邀请/验证 + public static CodeDefine NO_GROUP_PERMISSION = new CodeDefine(2203, "无加群权限"); + /// 邀请链接过期 + public static CodeDefine GROUP_INVITE_EXPIRED = new CodeDefine(2204, "群邀请已过期"); + + // 3.6 消息相关错误(2300 ~ 2399) + /// 发送时异常 + public static CodeDefine MESSAGE_SEND_FAILED = new CodeDefine(2300, "消息发送失败"); + /// 查询不到消息 + public static CodeDefine MESSAGE_NOT_FOUND = new CodeDefine(2301, "消息不存在"); + /// 超过时间限制 + public static CodeDefine MESSAGE_RECALL_FAILED = new CodeDefine(2302, "消息撤回失败"); + /// message_type 不合法 + public static CodeDefine UNSUPPORTED_MESSAGE_TYPE = new CodeDefine(2303, "不支持的消息类型"); + + // 3.7 文件相关错误(2400 ~ 2499) + /// 存储服务错误 + public static CodeDefine FILE_UPLOAD_FAILED = new CodeDefine(2400, "文件上传失败"); + /// 下载时未找到 + public static CodeDefine FILE_NOT_FOUND = new CodeDefine(2401, "文件不存在"); + /// 超过配置限制 + public static CodeDefine FILE_TOO_LARGE = new CodeDefine(2402, "文件大小超限"); + /// 格式不允许 + public static CodeDefine FILE_TYPE_NOT_SUPPORTED = new CodeDefine(2403, "文件类型不支持"); + + // 3.8 管理后台相关错误(3000 ~ 3099) + /// 账号错误 + public static CodeDefine ADMIN_NOT_FOUND = new CodeDefine(3000, "管理员不存在"); + /// 后台登录失败 + public static CodeDefine ADMIN_PASSWORD_ERROR = new CodeDefine(3001, "密码错误"); + /// 角色未找到 + public static CodeDefine ROLE_NOT_FOUND = new CodeDefine(3002, "角色不存在"); + /// 无操作权限 + public static CodeDefine ADMIN_PERMISSION_DENIED = new CodeDefine(3003, "权限不足"); + /// 后台日志写入失败 + public static CodeDefine OPERATION_LOG_FAILED = new CodeDefine(3004, "操作记录失败"); + + } +}