Merge pull request 'feature-nxdev' (#19) from feature-nxdev into main

Reviewed-on: #19
This commit is contained in:
西街长安 2025-11-16 16:21:49 +08:00
commit 7c0cf6941f
14 changed files with 280 additions and 55 deletions

View File

@ -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<User, UserInfoDto>();
}
}
}

View File

@ -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<IAuthService, AuthService>();
services.AddSingleton<IJWTService, JWTService>();
services.AddSingleton<IRefreshTokenService,RedisRefreshTokenService>();
return services;
}
}

View File

@ -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<AuthController> _logger;
private readonly IAuthService _authService;
private readonly IJWTService _jwtService;
private readonly IRefreshTokenService _refreshTokenService;
private readonly IConfiguration _configuration;
public AuthController(ILogger<AuthController> logger, IAuthService authService, IJWTService jwtService, IRefreshTokenService refreshTokenService, IConfiguration configuration)
{
_logger = logger;
_authService = authService;
_jwtService = jwtService;
_refreshTokenService = refreshTokenService;
_configuration = configuration;
}
[HttpPost]
public async Task<IActionResult> 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<LoginDto>(new LoginDto(user.Id,token,refreshToken,expiresAt));
return Ok(res);
}
}
}

View File

@ -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<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> 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();
}
}
}

View File

@ -1,4 +1,6 @@
namespace IM_API.Dtos
using IM_API.Tools;
namespace IM_API.Dtos
{
public class BaseResponse<T>
{
@ -20,13 +22,23 @@
this.Data = data;
}
/// <summary>
/// 默认成功响应返回仅数据
/// </summary>
/// <param name="data"></param>
public BaseResponse(T data)
{
this.Code = CodeDefine.SUCCESS.Code;
this.Message = CodeDefine.SUCCESS.Message;
this.Data = data;
}
/// <summary>
/// 默认成功响应返回,不带数据
/// </summary>
/// <param name="msg"></param>
/// <param name="data"></param>
public BaseResponse(string msg)
{
this.Code = 0;
this.Code = CodeDefine.SUCCESS.Code;
this.Message = msg;
}
/// <summary>

View File

@ -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;
}
}
}

View File

@ -4,5 +4,6 @@
{
public string Username { get; set; }
public string Password { get; set; }
public string NickName { get; set; }
}
}

View File

@ -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;
}
}
}

View File

@ -10,6 +10,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.21" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.21">
<PrivateAssets>all</PrivateAssets>

View File

@ -2,6 +2,8 @@
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>http</ActiveDebugProfile>
<Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>

View File

@ -10,30 +10,12 @@ namespace IM_API.Interface.Services
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<LoginDto> LoginAsync(LoginRequestDto dto);
Task<User> LoginAsync(LoginRequestDto dto);
/// <summary>
/// 注册
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto);
/// <summary>
/// 生成登录凭证
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
string GenerateToken(User user);
/// <summary>
/// 验证登录凭证
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
int? ValidateToken(string token);
/// <summary>
/// 刷新令牌
/// </summary>
/// <param name="refreshToken"></param>
/// <returns></returns>
LoginDto RefreshToken(string refreshToken);
}
}

View File

@ -62,6 +62,7 @@ public partial class ImContext : DbContext
modelBuilder.Entity<Admin>(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<Conversation>(entity =>
{
entity.HasKey(e => e.Id).HasName("PRIMARY");
entity
@ -145,6 +147,7 @@ public partial class ImContext : DbContext
modelBuilder.Entity<Device>(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<Friend>(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<FriendRequest>(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<Group>(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<GroupInvite>(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<GroupMember>(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<GroupRequest>(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<LoginLog>(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<Message>(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<User>(entity =>
{
// 忽略包装枚举属性
entity.Ignore(e => e.OnlineStatusEnum);
entity.Ignore(e => e.StatusEnum);
entity.HasKey(e => e.Id).HasName("PRIMARY");
entity

View File

@ -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<AuthService> _logger;
private readonly IMapper _mapper;
public AuthService(ImContext context, ILogger<AuthService> logger, IMapper mapper)
{
_context = context;
_logger = logger;
_mapper = mapper;
}
public async Task<User> 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<UserInfoDto> 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<UserInfoDto>(user);
}
}
}

View File

@ -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 成功类
/// <summary>成功响应</summary>
public static CodeDefine SUCCESS = new CodeDefine(0, "成功");
// 3.2 系统级错误1000 ~ 1999
/// <summary>未知异常</summary>
public static CodeDefine SYSTEM_ERROR = new CodeDefine(1000, "系统错误");
/// <summary>服务器维护中或宕机</summary>
public static CodeDefine SERVICE_UNAVAILABLE = new CodeDefine(1001, "服务不可用");
/// <summary>后端超时</summary>
public static CodeDefine REQUEST_TIMEOUT = new CodeDefine(1002, "请求超时");
/// <summary>缺少或参数不合法</summary>
public static CodeDefine PARAMETER_ERROR = new CodeDefine(1003, "参数错误");
/// <summary>数据库读写失败</summary>
public static CodeDefine DATABASE_ERROR = new CodeDefine(1004, "数据库错误");
/// <summary>无权限访问</summary>
public static CodeDefine PERMISSION_DENIED = new CodeDefine(1005, "权限不足");
/// <summary>Token 无效/过期</summary>
public static CodeDefine AUTH_FAILED = new CodeDefine(1006, "认证失败");
// 3.3 用户相关错误2000 ~ 2099
/// <summary>查询不到用户</summary>
public static CodeDefine USER_NOT_FOUND = new CodeDefine(2000, "用户不存在");
/// <summary>注册时用户已存在</summary>
public static CodeDefine USER_ALREADY_EXISTS = new CodeDefine(2001, "用户已存在");
/// <summary>登录密码错误</summary>
public static CodeDefine PASSWORD_ERROR = new CodeDefine(2002, "密码错误");
/// <summary>被管理员封禁</summary>
public static CodeDefine USER_DISABLED = new CodeDefine(2003, "用户被禁用");
/// <summary>需重新登录</summary>
public static CodeDefine LOGIN_EXPIRED = new CodeDefine(2004, "登录过期");
// 3.4 好友相关错误2100 ~ 2199
/// <summary>重复申请</summary>
public static CodeDefine FRIEND_REQUEST_EXISTS = new CodeDefine(2100, "好友申请已存在");
/// <summary>不是好友</summary>
public static CodeDefine FRIEND_RELATION_NOT_FOUND = new CodeDefine(2101, "好友关系不存在");
/// <summary>重复添加</summary>
public static CodeDefine ALREADY_FRIENDS = new CodeDefine(2102, "已经是好友");
/// <summary>被对方拒绝</summary>
public static CodeDefine FRIEND_REQUEST_REJECTED = new CodeDefine(2103, "好友请求被拒绝");
/// <summary>被对方拉黑</summary>
public static CodeDefine CANNOT_ADD_FRIEND = new CodeDefine(2104, "无法申请加好友");
// 3.5 群聊相关错误2200 ~ 2299
/// <summary>查询不到群</summary>
public static CodeDefine GROUP_NOT_FOUND = new CodeDefine(2200, "群不存在");
/// <summary>不能重复加入</summary>
public static CodeDefine ALREADY_IN_GROUP = new CodeDefine(2201, "已在群中");
/// <summary>超出限制</summary>
public static CodeDefine GROUP_FULL = new CodeDefine(2202, "群成员已满");
/// <summary>需要邀请/验证</summary>
public static CodeDefine NO_GROUP_PERMISSION = new CodeDefine(2203, "无加群权限");
/// <summary>邀请链接过期</summary>
public static CodeDefine GROUP_INVITE_EXPIRED = new CodeDefine(2204, "群邀请已过期");
// 3.6 消息相关错误2300 ~ 2399
/// <summary>发送时异常</summary>
public static CodeDefine MESSAGE_SEND_FAILED = new CodeDefine(2300, "消息发送失败");
/// <summary>查询不到消息</summary>
public static CodeDefine MESSAGE_NOT_FOUND = new CodeDefine(2301, "消息不存在");
/// <summary>超过时间限制</summary>
public static CodeDefine MESSAGE_RECALL_FAILED = new CodeDefine(2302, "消息撤回失败");
/// <summary>message_type 不合法</summary>
public static CodeDefine UNSUPPORTED_MESSAGE_TYPE = new CodeDefine(2303, "不支持的消息类型");
// 3.7 文件相关错误2400 ~ 2499
/// <summary>存储服务错误</summary>
public static CodeDefine FILE_UPLOAD_FAILED = new CodeDefine(2400, "文件上传失败");
/// <summary>下载时未找到</summary>
public static CodeDefine FILE_NOT_FOUND = new CodeDefine(2401, "文件不存在");
/// <summary>超过配置限制</summary>
public static CodeDefine FILE_TOO_LARGE = new CodeDefine(2402, "文件大小超限");
/// <summary>格式不允许</summary>
public static CodeDefine FILE_TYPE_NOT_SUPPORTED = new CodeDefine(2403, "文件类型不支持");
// 3.8 管理后台相关错误3000 ~ 3099
/// <summary>账号错误</summary>
public static CodeDefine ADMIN_NOT_FOUND = new CodeDefine(3000, "管理员不存在");
/// <summary>后台登录失败</summary>
public static CodeDefine ADMIN_PASSWORD_ERROR = new CodeDefine(3001, "密码错误");
/// <summary>角色未找到</summary>
public static CodeDefine ROLE_NOT_FOUND = new CodeDefine(3002, "角色不存在");
/// <summary>无操作权限</summary>
public static CodeDefine ADMIN_PERMISSION_DENIED = new CodeDefine(3003, "权限不足");
/// <summary>后台日志写入失败</summary>
public static CodeDefine OPERATION_LOG_FAILED = new CodeDefine(3004, "操作记录失败");
}
}