添加项目文件。

This commit is contained in:
2026-05-09 17:06:30 +08:00
parent c60f5fe117
commit 720ef957d4
378 changed files with 14843 additions and 0 deletions
@@ -0,0 +1,38 @@
using ContactService.Domain;
namespace ContactService.WebApi.Application.Dtos
{
public class FriendRequestResponse
{
public Guid Id { get; private set; }
/// <summary>
/// 申请人
/// </summary>
public Guid OwnerId { get; private set; }
/// <summary>
/// 被申请人
/// </summary>
public Guid TargetId { get; private set; }
/// <summary>
/// 申请附言
/// </summary>
public string Description { get; private set; }
/// <summary>
/// 申请状态(0:待通过,1:拒绝,2:同意,3:拉黑)
/// </summary>
public FriendRequestStatus State { get; private set; }
/// <summary>
/// 备注
/// </summary>
public string? RemarkName { get; private set; }
public DateTimeOffset CreationTime { get; private set; }
public DateTimeOffset? Deletion { get; private set; }
public DateTimeOffset? ModificationTime { get; private set; }
}
}
@@ -0,0 +1,20 @@
using ContactService.Domain;
namespace ContactService.WebApi.Application.Dtos
{
public class FriendResonse
{
public Guid Id { get; private set; }
public Guid TargetId { get; private set; }
public string? Avatar { get; private set; }
public string NickName { get; private set; }
/// <summary>
/// 好友备注名
/// </summary>
public string? RemarkName { get; private set; }
public DateTime CreateTime { get; private set; }
public DateTime? UpdateTime { get; private set; }
public FriendStatus Status { get; private set; }
}
}
@@ -0,0 +1,16 @@
namespace ContactService.WebApi.Application.Dtos
{
public class UserInfoDto
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string NickName { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
public string Region { get; set; }
public string Description { get; set; }
public string? Avatar { get; set; }
public DateTimeOffset CreationTime { get; set; }
public DateTimeOffset? Deletion { get; set; }
}
}
@@ -0,0 +1,32 @@
using ContactService.Domain.Events;
using IM.Commons.IntegrationEvents;
using MassTransit;
using MediatR;
namespace ContactService.WebApi.Application.EventHandler
{
public class FriendAddedHandler : INotificationHandler<FriendAddedDomainEvent>
{
private readonly IPublishEndpoint endpoint;
public FriendAddedHandler(IPublishEndpoint endpoint)
{
this.endpoint = endpoint;
}
public async Task Handle(FriendAddedDomainEvent notification, CancellationToken cancellationToken)
{
await endpoint.Publish(new FriendAddedEvent
{
OwnerAvatar = notification.Friend.Owner.Avatar,
OwnerId = notification.Friend.Owner.Id,
OwnerNickName = notification.Friend.Owner.NickName,
TargetAvatar = notification.Friend.Target.Avatar,
TargetId = notification.Friend.Target.Id,
TargetNickName = notification.Friend.Target.NickName,
RemarkName = notification.Friend.RemarkName,
Status = notification.Friend.Status.ToString(),
}, cancellationToken);
}
}
}
@@ -0,0 +1,58 @@
using ContactService.Domain;
using ContactService.Domain.Events;
using ContactService.Domain.ValueObjects;
using ContactService.Infrastructure;
using ContactService.WebApi.Application.IntegrationServices;
using IM.Commons.IntegrationEvents;
using MassTransit;
using MediatR;
namespace ContactService.WebApi.Application.EventHandler
{
public class FriendRequestStatusUpdateHandler : INotificationHandler<FriendRequestStateUpdateDomainEvent>
{
private readonly FriendDomainService friendService;
private readonly ContactDbContext contactDb;
private readonly IPublishEndpoint endpoint;
private readonly IIdentityIntegrationService idService;
public FriendRequestStatusUpdateHandler(FriendDomainService friendService, ContactDbContext contactDb, IPublishEndpoint endpoint, IIdentityIntegrationService idService)
{
this.friendService = friendService;
this.contactDb = contactDb;
this.endpoint = endpoint;
this.idService = idService;
}
public async Task Handle(FriendRequestStateUpdateDomainEvent notification, CancellationToken cancellationToken)
{
var @event = notification.Request;
if (@event.State == FriendRequestStatus.Passed)
{
var ownerInfo = await idService.FindUserByIdAsync(@event.OwnerId);
var targetInfo = await idService.FindUserByIdAsync(@event.TargetId);
if (!ownerInfo.Succeeded || !targetInfo.Succeeded)
{
return;
}
var ownerProfile = new UserProfile(ownerInfo.Data.Id, ownerInfo.Data.NickName, ownerInfo.Data.Avatar);
var targetProfile = new UserProfile(targetInfo.Data.Id, targetInfo.Data.NickName, targetInfo.Data.Avatar);
await friendService.CreateAsync(ownerProfile, targetProfile, @event.RemarkName);
await friendService.CreateAsync(targetProfile, ownerProfile, notification.AcceptRemarkName);
await contactDb.SaveChangesAsync(cancellationToken);
}
await endpoint.Publish(new FriendRequestStateUpdateEvent
{
CorrelationId = @event.TargetId,
Description = @event.Description,
OwnerId = @event.OwnerId,
RemarkName = @event.RemarkName,
State = @event.State.ToString()
});
}
}
}
@@ -0,0 +1,35 @@
using ContactService.Domain;
using ContactService.Infrastructure;
using IM.Commons.IntegrationEvents;
using MassTransit;
namespace ContactService.WebApi.Application.EventHandler
{
public class UserProfileUpdateHandler : IConsumer<UserProfileUpdateEvent>
{
private readonly ContactDbContext contactDb;
private readonly IFriendReposity reposity;
public UserProfileUpdateHandler(ContactDbContext contactDb, IFriendReposity reposity)
{
this.contactDb = contactDb;
this.reposity = reposity;
}
public async Task Consume(ConsumeContext<UserProfileUpdateEvent> context)
{
var @event = context.Message;
var friends = await reposity.FindByTargetAsync(@event.UserId);
foreach (var friend in friends)
{
friend.UpdateUserInfo(
new Domain.ValueObjects.UserProfile(
@event.UserId, @event.Avatar, @event.NickName));
}
await contactDb.SaveChangesAsync();
}
}
}
@@ -0,0 +1,23 @@
using AutoMapper;
using ContactService.WebApi.Application.Dtos;
using Google.Protobuf.WellKnownTypes;
namespace ContactService.WebApi.Application.Friend
{
public class FriendMapperConfig : Profile
{
public FriendMapperConfig()
{
CreateMap<Domain.Entities.Friend, FriendResonse>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Avatar, opt => opt.MapFrom(src => src.Target.Avatar))
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
.ForMember(dest => dest.UpdateTime, opt => opt.MapFrom(src => src.ModificationTime.Value.DateTime))
.ForMember(dest => dest.CreateTime, opt => opt.MapFrom(src => src.CreationTime.DateTime))
.ForMember(dest => dest.NickName, opt => opt.MapFrom(src => src.Target.NickName))
.ForMember(dest => dest.RemarkName, opt => opt.MapFrom(src => src.RemarkName))
.ForMember(dest => dest.TargetId, opt => opt.MapFrom(src => src.Target.Id))
;
}
}
}
@@ -0,0 +1,73 @@
using AutoMapper;
using ContactService.Domain;
using ContactService.WebApi.Application.Dtos;
using ContactService.WebApi.Application.IntegrationServices;
using IM.Commons;
namespace ContactService.WebApi.Application.Friend
{
public class FriendService
{
private readonly IFriendReposity reposity;
private readonly FriendDomainService service;
private readonly IIdentityIntegrationService idService;
private readonly IMapper mapper;
public FriendService(IFriendReposity reposity, FriendDomainService service
, IIdentityIntegrationService idService, IMapper mapper
)
{
this.reposity = reposity;
this.service = service;
this.idService = idService;
this.mapper = mapper;
}
public async Task<Result<List<FriendResonse>>> GetFriendsByOwnerIdAsync(Guid ownerId)
{
IEnumerable<Domain.Entities.Friend> friend = await reposity.FindByOwnerAsync(ownerId);
return Result<List<FriendResonse>>.Success(mapper.Map<List<FriendResonse>>(friend.ToList()));
}
public async Task<Result<object>> DeleteFriendAsync(Guid userId, Guid friendId)
{
var friend = await reposity.FindByIdAsync(friendId);
if (friend is null)
{
return Result<object>.Fail(ResultCode.FRIEND_RELATION_NOT_FOUND);
}
if (friend.Owner.Id != userId)
{
return Result<object>.Fail(ResultCode.FRIEND_RELATION_NOT_FOUND);
}
friend.SoftDelete();
return Result<object>.Success();
}
public async Task<Result<object>> BlockFriendAsync(Guid userId, Guid friendId)
{
var friend = await reposity.FindByIdAsync(friendId);
if (friend is null)
{
return Result<object>.Fail(ResultCode.FRIEND_RELATION_NOT_FOUND);
}
if (friend.Owner.Id != userId)
{
return Result<object>.Fail(ResultCode.PERMISSION_DENIED);
}
friend.Block();
return Result<object>.Success();
}
public async Task<Result<bool>> CheckFriendAsync(Guid ownerId, Guid targetId)
{
var exist = await reposity.CheckOwnerIdAndTargetIdAsync(ownerId, targetId);
return Result.Success(exist);
}
}
}
@@ -0,0 +1,18 @@
namespace ContactService.WebApi.Application.FriendRequest
{
public record CreateFriendRequestCommand
{
public Guid ownerId { get; private set; }
public Guid targetId { get; private set; }
public string? description { get; private set; }
public string? remarkName { get; private set; }
public CreateFriendRequestCommand(Guid ownerId, Guid targetId, string? description, string? remarkName)
{
this.ownerId = ownerId;
this.targetId = targetId;
this.description = description;
this.remarkName = remarkName;
}
}
}
@@ -0,0 +1,13 @@
using AutoMapper;
using ContactService.WebApi.Application.Dtos;
namespace ContactService.WebApi.Application.FriendRequest
{
public class FriendRequestConfig : Profile
{
public FriendRequestConfig()
{
CreateMap<Domain.Entities.FriendRequest, FriendRequestResponse>();
}
}
}
@@ -0,0 +1,25 @@
namespace ContactService.WebApi.Application.FriendRequest
{
public class FriendRequestHandleCommand
{
public Guid UserId { get; private set; }
public Guid RequestId { get; private set; }
public FriendRequestAction Action { get; private set; }
public string? RemarkName { get; set; }
public FriendRequestHandleCommand(Guid userId, Guid requestId, FriendRequestAction action, string? remarkName)
{
UserId = userId;
RequestId = requestId;
Action = action;
RemarkName = remarkName;
}
}
public enum FriendRequestAction
{
Accpet = 0,
Reject = 1,
Block = 2
}
}
@@ -0,0 +1,81 @@
using AutoMapper;
using ContactService.Domain;
using ContactService.WebApi.Application.Dtos;
using IM.Commons;
namespace ContactService.WebApi.Application.FriendRequest
{
public class FriendRequestService
{
private readonly IFriendRequestReposity reposity;
private readonly FriendRequestDomainService service;
private readonly IMapper mapper;
public FriendRequestService(IFriendRequestReposity reposity, FriendRequestDomainService service, IMapper mapper)
{
this.reposity = reposity;
this.service = service;
this.mapper = mapper;
}
public async Task<Result<FriendRequestResponse>> CreateAsync(CreateFriendRequestCommand command)
{
var request = await service.CreateAsync(command.ownerId, command.targetId, command.description, command.remarkName);
if (!request.Succeeded)
{
return Result<FriendRequestResponse>.Fail(request);
}
return Result<FriendRequestResponse>.Success(mapper.Map<FriendRequestResponse>(request.Data));
}
public async Task<Result<FriendRequestResponse>> UpdateStatusAsync(FriendRequestHandleCommand command)
{
var request = await reposity.FindByIdAsync(command.RequestId);
if (request == null)
{
return Result<FriendRequestResponse>.Fail(ResultCode.FRIEND_REQUEST_NOT_FOUND);
}
if (request.TargetId != command.UserId)
{
return Result<FriendRequestResponse>.Fail(ResultCode.PERMISSION_DENIED);
}
switch (command.Action)
{
case FriendRequestAction.Accpet:
request.Accept(command.RemarkName);
break;
case FriendRequestAction.Block:
request.Block();
break;
case FriendRequestAction.Reject:
request.Reject();
break;
default:
return Result<FriendRequestResponse>.Fail(ResultCode.PARAMETER_ERROR);
}
return Result<FriendRequestResponse>.Success(mapper.Map<FriendRequestResponse>(request));
}
public async Task<Result<List<FriendRequestResponse>>> GetByOwnerIdAsync(Guid ownerId)
{
var requests = await reposity.FindByOwnerIdAsync(ownerId);
return Result<List<FriendRequestResponse>>.Success(mapper.Map<List<FriendRequestResponse>>(requests));
}
public async Task<Result<List<FriendRequestResponse>>> GetByTargetIdAsync(Guid targetId)
{
var requests = await reposity.FindByTargetIdAsync(targetId);
return Result<List<FriendRequestResponse>>.Success(mapper.Map<List<FriendRequestResponse>>(requests));
}
public async Task<Result<List<FriendRequestResponse>>> GetByTargetIdOrOwnerIdAsync(Guid id)
{
var requests = await reposity.FindByTargetIdAsync(id);
var requests2 = await reposity.FindByOwnerIdAsync(id);
return Result<List<FriendRequestResponse>>.Success(mapper.Map<List<FriendRequestResponse>>(requests.Concat(requests2)));
}
}
}
@@ -0,0 +1,10 @@
using ContactService.WebApi.Application.Dtos;
using IM.Commons;
namespace ContactService.WebApi.Application.IntegrationServices
{
public interface IIdentityIntegrationService
{
Task<Result<UserInfoDto>> FindUserByIdAsync(Guid id);
}
}
@@ -0,0 +1,47 @@
using ContactService.WebApi.Application.Dtos;
using Grpc.Core;
using IM.Commons;
using IM.Protocols.Grpc.User;
namespace ContactService.WebApi.Application.IntegrationServices
{
public class IdentityIntegrationService : IIdentityIntegrationService
{
private readonly UserInternal.UserInternalClient client;
public IdentityIntegrationService(UserInternal.UserInternalClient client)
{
this.client = client;
}
public async Task<Result<UserInfoDto>> FindUserByIdAsync(Guid id)
{
var req = new GetUserInfoRequest()
{
UserId = id.ToString()
};
try
{
var res = await client.GetUserInfoAsyncAsync(req);
return Result.Success(new UserInfoDto
{
Avatar = res.Avatar,
CreationTime = res.CreationTime.ToDateTimeOffset(),
Deletion = res.Deletion.ToDateTimeOffset(),
Description = res.Description,
Email = res.Email,
Id = Guid.Parse(res.Id),
NickName = res.NickName,
Phone = res.Phone,
Region = res.Region,
UserName = res.UserName
});
}catch(RpcException e)
{
return Result.Fail<UserInfoDto>(ResultCode.USER_NOT_FOUND);
}
}
}
}