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

Reviewed-on: #21
This commit is contained in:
西街长安 2025-12-01 20:47:51 +08:00
commit 1d633c3909
12 changed files with 293 additions and 38 deletions

View File

@ -0,0 +1,20 @@
using IM_API.Models;
namespace IM_API.Aggregate
{
public class FriendRequestAggregate
{
public Friend Friend { get; private set; }
public FriendRequest FriendRequest { get; private set; }
public FriendRequestAggregate() { }
public FriendRequestAggregate(Friend friend,FriendRequest friendRequest)
{
Friend = friend;
FriendRequest = friendRequest;
}
public void Accept(string? remarkName = null)
{
}
}
}

View File

@ -26,6 +26,22 @@ namespace IM_API.Configs
;
//好友信息模型转换
CreateMap<Friend, FriendInfoDto>();
//好友请求通过后新增好友关系
CreateMap<FriendRequest, Friend>()
.ForMember(dest => dest.UserId , opt => opt.MapFrom(src => src.RequestUser))
.ForMember(dest => dest.FriendId , opt => opt.MapFrom(src => src.ResponseUser))
.ForMember(dest => dest.StatusEnum , opt =>opt.MapFrom(src => FriendStatus.Added))
.ForMember(dest => dest.RemarkName , opt => opt.MapFrom(src => src.ResponseUserNavigation.NickName))
.ForMember(dest => dest.Created , opt => opt.MapFrom(src => DateTime.Now))
;
//发起好友请求转换请求对象
CreateMap<FriendRequestDto, FriendRequest>()
.ForMember(dest => dest.RequestUser , opt => opt.MapFrom(src => src.FromUserId))
.ForMember(dest => dest.ResponseUser , opt => opt.MapFrom(src => src.ToUserId))
.ForMember(dest => dest.Created , opt => opt.MapFrom(src => DateTime.Now))
.ForMember(dest => dest.StateEnum , opt => opt.MapFrom(src => FriendRequestState.Pending))
.ForMember(dest => dest.Description , opt => opt.MapFrom(src => src.Description))
;
}
}
}

View File

@ -10,6 +10,7 @@ namespace IM_API.Configs
services.AddAutoMapper(typeof(MapperConfig));
services.AddTransient<IAuthService, AuthService>();
services.AddTransient<IUserService, UserService>();
services.AddTransient<IFriendSerivce, FriendService>();
services.AddSingleton<IJWTService, JWTService>();
services.AddSingleton<IRefreshTokenService,RedisRefreshTokenService>();
return services;

View File

@ -0,0 +1,114 @@
using IM_API.Dtos;
using IM_API.Interface.Services;
using IM_API.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace IM_API.Controllers
{
[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class FriendController : ControllerBase
{
private readonly IFriendSerivce _friendService;
private readonly ILogger<FriendController> _logger;
public FriendController(IFriendSerivce friendService, ILogger<FriendController> logger)
{
_friendService = friendService;
_logger = logger;
}
/// <summary>
/// 发起好友请求
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Request(FriendRequestDto dto)
{
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
int userId = int.Parse(userIdStr);
dto.FromUserId = userId;
await _friendService.SendFriendRequestAsync(dto);
var res = new BaseResponse<object?>();
return Ok(res);
}
/// <summary>
/// 获取好友请求列表
/// </summary>
/// <param name="isReceived"></param>
/// <param name="page"></param>
/// <param name="limit"></param>
/// <param name="desc"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Requests(bool isReceived,int page,int limit,bool desc)
{
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
int userId = int.Parse(userIdStr);
var list = await _friendService.GetFriendRequestListAsync(userId,isReceived,page,limit,desc);
var res = new BaseResponse<List<FriendRequest>>(list);
return Ok(res);
}
/// <summary>
/// 处理好友请求
/// </summary>
/// <param name="id"></param>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> HandleRequest([FromRoute]int id, [FromBody]FriendRequestHandleDto dto)
{
await _friendService.HandleFriendRequestAsync(new HandleFriendRequestDto()
{
RequestId = id,
RemarkName = dto.remarkName,
Action = dto.action
});
var res = new BaseResponse<object?>();
return Ok(res);
}
/// <summary>
/// 获取好友列表
/// </summary>
/// <param name="page"></param>
/// <param name="limit"></param>
/// <param name="desc"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> List(int page,int limit,bool desc)
{
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
int userId = int.Parse(userIdStr);
var list = await _friendService.GetFriendListAsync(userId,page,limit,desc);
var res = new BaseResponse<List<FriendInfoDto>>(list);
return Ok(res);
}
/// <summary>
/// 删除好友
/// </summary>
/// <param name="friendId"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Delete([FromRoute] int friendId)
{
//TODO: 这里存在安全问题当用户传入的id与用户无关时也可以删除成功待修复。
await _friendService.DeleteFriendAsync(friendId);
return Ok(new BaseResponse<object?>());
}
/// <summary>
/// 拉黑好友
/// </summary>
/// <param name="friendId"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Block([FromRoute] int friendId)
{
//TODO: 这里存在安全问题当用户传入的id与用户无关时也可以拉黑成功待修复。
await _friendService.BlockeFriendAsync(friendId);
return Ok(new BaseResponse<object?>());
}
}
}

View File

@ -67,7 +67,7 @@ namespace IM_API.Controllers
/// <param name="username"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Find(string username)
public async Task<IActionResult> FindByUsername(string username)
{
var userinfo = await _userService.GetUserInfoByUsernameAsync(username);
var res = new BaseResponse<UserInfoDto>(userinfo);

View File

@ -3,4 +3,5 @@
namespace IM_API.Dtos
{
public record FriendInfoDto(int Id, int UserId,int FriendId,FriendStatus StatusEnum,DateTime Created,string RemarkName,string? Avatar);
public record FriendRequestHandleDto(HandleFriendRequestAction action,string? remarkName);
}

View File

@ -4,6 +4,7 @@
{
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public string? RemarkName { get; set; }
public string? Description { get; set; }
}
}

View File

@ -10,6 +10,10 @@
/// 处理操作
/// </summary>
public HandleFriendRequestAction Action { get; set; }
/// <summary>
/// 好友备注名
/// </summary>
public string? RemarkName { get; set; }
}
public enum HandleFriendRequestAction
{

View File

@ -27,7 +27,7 @@ namespace IM_API.Interface.Services
/// <param name="page"></param>
/// <param name="limit"></param>
/// <returns></returns>
Task<FriendRequest> GetFriendRequestListAsync(int userId,bool isReceived,int page,int limit, bool desc);
Task<List<FriendRequest>> GetFriendRequestListAsync(int userId,bool isReceived,int page,int limit, bool desc);
/// <summary>
/// 处理好友请求
/// </summary>

View File

@ -1,7 +1,9 @@
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
@ -17,30 +19,51 @@ namespace IM_API.Services
_logger = logger;
_mapper = mapper;
}
public Task<bool> BlockeFriendAsync(int friendId)
#region
public async Task<bool> BlockeFriendAsync(int friendId)
{
throw new NotImplementedException();
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
public Task<bool> BlockFriendByUserIdAsync(int userId, int toUserId)
#endregion
#region id拉黑好友
public async Task<bool> BlockFriendByUserIdAsync(int userId, int toUserId)
{
throw new NotImplementedException();
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
public Task<bool> DeleteFriendAsync(int friendId)
#endregion
#region
public async Task<bool> DeleteFriendAsync(int friendId)
{
throw new NotImplementedException();
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
public Task<bool> DeleteFriendByUserIdAsync(int userId, int toUserId)
#endregion
#region id删除好友关系
public async Task<bool> DeleteFriendByUserIdAsync(int userId, int toUserId)
{
throw new NotImplementedException();
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<List<FriendInfoDto>> GetFriendListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.Friends.Where(x => x.UserId == userId);
var query = _context.Friends.Where(x => x.UserId == userId && x.StatusEnum == FriendStatus.Added);
if (desc)
{
query = query.OrderByDescending(x => x.UserId);
@ -48,20 +71,104 @@ namespace IM_API.Services
var friendList = await query.Skip((page - 1 * limit)).Take(limit).ToListAsync();
return _mapper.Map<List<FriendInfoDto>>(friendList);
}
public Task<FriendRequest> GetFriendRequestListAsync(int userId, bool isReceived, int page, int limit, bool desc)
#endregion
#region
public async Task<List<FriendRequest>> GetFriendRequestListAsync(int userId, bool isReceived, int page, int limit, bool desc)
{
throw new NotImplementedException();
var query = _context.FriendRequests.AsQueryable();
//是否为请求方
if (isReceived)
{
query = _context.FriendRequests.Where(x => x.ResponseUser == userId);
}
else
{
query = _context.FriendRequests.Where(x => x.RequestUser == userId);
}
if (desc)
{
query = query.OrderByDescending(x => x.Id);
}
var friendRequestList = await query.Skip((page - 1 * limit)).Take(limit).ToListAsync();
return friendRequestList;
}
public Task<bool> HandleFriendRequestAsync(HandleFriendRequestDto requestDto)
#endregion
#region
public async Task<bool> HandleFriendRequestAsync(HandleFriendRequestDto requestDto)
{
throw new NotImplementedException();
}
//查询好友请求记录
var friendRequest = await _context.FriendRequests
.Include(e => e.ResponseUserNavigation)
.FirstOrDefaultAsync(x => x.Id == requestDto.RequestId);
if (friendRequest is null) throw new BaseException(CodeDefine.FRIEND_REQUEST_NOT_FOUND);
//查询好友关系
var friend = await _context.Friends.FirstOrDefaultAsync(
x => x.UserId == friendRequest.RequestUser && x.FriendId == friendRequest.ResponseUser
);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
public Task<bool> SendFriendRequestAsync(FriendRequestDto friendRequest)
{
throw new NotImplementedException();
if (friend.StatusEnum != FriendStatus.Pending) throw new BaseException(CodeDefine.FRIEND_REQUEST_EXISTS);
//处理好友请求操作
switch (requestDto.Action)
{
//拒绝后标记
case HandleFriendRequestAction.Reject:
friend.StatusEnum = FriendStatus.Declined;
friendRequest.StateEnum = FriendRequestState.Declined;
break;
//同意后标记
case HandleFriendRequestAction.Accept:
friend.StatusEnum = FriendStatus.Added;
friendRequest.StateEnum = FriendRequestState.Passed;
//根据当前好友请求为被申请方添加一条好友记录(注意:好友记录为双向)
var ResponseFriend = _mapper.Map<Friend>(friendRequest);
if (!string.IsNullOrEmpty(requestDto.RemarkName)) ResponseFriend.RemarkName = requestDto.RemarkName;
_context.Friends.Add(ResponseFriend);
break;
//无效操作
default:
throw new BaseException(CodeDefine.INVALID_ACTION);
}
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<bool> SendFriendRequestAsync(FriendRequestDto dto)
{
//查询用户是否存在
bool isExist = await _context.Users.AnyAsync(x => x.Id == dto.ToUserId);
if (!isExist) throw new BaseException(CodeDefine.USER_NOT_FOUND);
bool isExistUser2 = await _context.Users.AnyAsync(x => x.Id == dto.FromUserId);
if(!isExistUser2) throw new BaseException(CodeDefine.USER_NOT_FOUND);
// 检查是否已有好友关系或待处理请求
bool alreadyExists = await _context.FriendRequests.AnyAsync(x =>
x.RequestUser == dto.FromUserId && x.ResponseUser == dto.ToUserId && x.StateEnum == FriendRequestState.Pending
);
if (alreadyExists)
throw new BaseException(CodeDefine.FRIEND_REQUEST_EXISTS);
//检查是否被对方拉黑
bool isBlocked = await _context.Friends.AnyAsync(x =>
x.UserId == dto.FromUserId && x.FriendId == dto.ToUserId && x.StatusEnum == FriendStatus.Blocked
);
if (isBlocked)
throw new BaseException(CodeDefine.FRIEND_REQUEST_REJECTED);
//生成实体
var friendRequst = _mapper.Map<FriendRequest>(dto);
var friend = _mapper.Map<Friend>(friendRequst);
_context.FriendRequests.Add(friendRequst);
_context.Friends.Add(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
}
}

View File

@ -53,6 +53,10 @@
public static CodeDefine FRIEND_REQUEST_REJECTED = new CodeDefine(2103, "好友请求被拒绝");
/// <summary>被对方拉黑</summary>
public static CodeDefine CANNOT_ADD_FRIEND = new CodeDefine(2104, "无法申请加好友");
/// <summary>好友请求不存在</summary>
public static CodeDefine FRIEND_REQUEST_NOT_FOUND = new CodeDefine(2105, "好友请求不存在");
/// <summary>处理好友请求操作无效</summary>
public static CodeDefine INVALID_ACTION = new CodeDefine(2106, "处理好友请求操作无效");
// 3.5 群聊相关错误2200 ~ 2299
/// <summary>查询不到群</summary>

View File

@ -1,13 +0,0 @@
namespace IM_API
{
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}