внедрил систему статусов (BIO)

This commit is contained in:
max
2026-04-17 18:33:46 +03:00
parent ae710c3d43
commit d2e6eb578a
14 changed files with 280 additions and 17 deletions
@@ -9,4 +9,6 @@ public static class ProfilesErrors
public static Error AvatarNotFound => new("Profiles.AvatarNotFound", "Avatar not found");
public static Error InvalidAvatarFormat => new("Profiles.InvalidAvatarFormat", "Invalid avatar format");
public static Error AvatarUploadFailed => new("Profiles.AvatarUploadFailed", "Avatar upload failed");
public static Error BioTooLong => new("Profiles.BioTooLong", "Bio must be 200 characters or less");
public static Error StatusTextTooLong => new("Profiles.StatusTextTooLong", "Status text must be 50 characters or less");
}
@@ -12,5 +12,8 @@ public class UserProfileDto
public DateTime? LastSeen { get; set; }
public DateTime? Birthday { get; set; }
public bool IsPremium { get; set; }
public string? StatusText { get; set; }
public string? StatusEmoji { get; set; }
public DateTime? StatusExpiresAt { get; set; }
public DateTime CreatedAt { get; set; }
}
@@ -2,5 +2,11 @@ using System;
namespace Knot.Modules.Profiles.Application.Profiles.DTOs;
public record UpdateProfileRequest(string? DisplayName, string? Bio, DateTime? Birthday);
public record UpdateProfileRequest(
string? DisplayName,
string? Bio,
DateTime? Birthday,
string? StatusText,
string? StatusEmoji,
DateTime? StatusExpiresAt);
public record UpdateSettingsRequest(bool? HideStoryViews);
@@ -11,7 +11,10 @@ public sealed record UpdateProfileCommand(
Guid UserId,
string? DisplayName,
string? Bio,
DateTime? Birthday) : ICommand<UserProfileDto>;
DateTime? Birthday,
string? StatusText,
string? StatusEmoji,
DateTime? StatusExpiresAt) : ICommand<UserProfileDto>;
internal sealed class UpdateProfileCommandHandler : ICommandHandler<UpdateProfileCommand, UserProfileDto>
{
@@ -28,9 +31,18 @@ internal sealed class UpdateProfileCommandHandler : ICommandHandler<UpdateProfil
if (profile is null)
return Result.Failure<UserProfileDto>(ProfilesErrors.ProfileNotFound);
if ((request.Bio?.Length ?? 0) > 200)
return Result.Failure<UserProfileDto>(ProfilesErrors.BioTooLong);
if ((request.StatusText?.Length ?? 0) > 50)
return Result.Failure<UserProfileDto>(ProfilesErrors.StatusTextTooLong);
profile.DisplayName = request.DisplayName ?? profile.DisplayName;
profile.About = request.Bio;
profile.Birthday = request.Birthday;
profile.StatusText = request.StatusText;
profile.StatusEmoji = request.StatusEmoji;
profile.StatusExpiresAt = request.StatusExpiresAt;
var result = await _repository.UpdateAsync(profile, cancellationToken);
if (result.IsFailure)
@@ -19,6 +19,12 @@ public sealed class ProfileDocument
public string? Bio { get; private set; }
public string? StatusText { get; private set; }
public string? StatusEmoji { get; private set; }
public DateTime? StatusExpiresAt { get; private set; }
public string? AvatarUrl { get; private set; }
public DateTime? Birthday { get; private set; }
@@ -65,6 +71,13 @@ public sealed class ProfileDocument
public void UpdateSettings(bool hideStoryViews)
=> HideStoryViews = hideStoryViews;
public void UpdateStatus(string? statusText, string? statusEmoji, DateTime? statusExpiresAt)
{
StatusText = statusText;
StatusEmoji = statusEmoji;
StatusExpiresAt = statusExpiresAt;
}
public void UpdateStatus(bool isBanned, bool isDeleted)
{
IsBanned = isBanned;
@@ -80,6 +80,7 @@ internal class ProfileRepository : IProfileRepository
dto.Birthday);
document.UpdateAvatar(dto.Avatar);
document.UpdateStatus(dto.StatusText, dto.StatusEmoji, dto.StatusExpiresAt);
await _profiles.ReplaceOneAsync(p => p.Id == dto.UserId, document, cancellationToken: ct);
return Result.Success(document.ToDto());
@@ -1,5 +1,6 @@
using Knot.Contracts.Profiles.Application.DTOs;
using Knot.Modules.Profiles.Domain;
using System;
namespace Knot.Modules.Profiles.Infrastructure.Mappings;
@@ -7,6 +8,8 @@ public static class ProfileMappings
{
public static UserProfileDto ToDto(this ProfileDocument document)
{
var hasActiveStatus = !document.StatusExpiresAt.HasValue || document.StatusExpiresAt.Value > DateTime.UtcNow;
return new UserProfileDto
{
UserId = document.Id,
@@ -18,6 +21,9 @@ public static class ProfileMappings
LastSeen = null,
Birthday = document.Birthday,
IsPremium = false,
StatusText = hasActiveStatus ? document.StatusText : null,
StatusEmoji = hasActiveStatus ? document.StatusEmoji : null,
StatusExpiresAt = hasActiveStatus ? document.StatusExpiresAt : null,
CreatedAt = document.CreatedAt
};
}
@@ -17,6 +17,8 @@ public static class ProfilesEndpoints
public static void MapProfilesEndpoints(this WebApplication app)
{
var group = app.MapGroup("api/profiles").RequireAuthorization();
var userGroup = app.MapGroup("api/user").RequireAuthorization();
var usersGroup = app.MapGroup("api/users").RequireAuthorization();
group.MapGet("search", async ([FromQuery] string q, ISender sender, IUserContext userContext, CancellationToken ct) =>
{
@@ -67,16 +69,39 @@ public static class ProfilesEndpoints
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(result.Error);
});
group.MapPut("profile", async ([FromBody] Knot.Modules.Profiles.Application.Profiles.DTOs.UpdateProfileRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
async Task<IResult> UpdateProfileHandler(
[FromBody] Knot.Modules.Profiles.Application.Profiles.DTOs.UpdateProfileRequest request,
ISender sender,
IUserContext userContext,
CancellationToken ct)
{
var result = await sender.Send(new UpdateProfileCommand(userContext.UserId, request.DisplayName, request.Bio, request.Birthday), ct);
var result = await sender.Send(
new UpdateProfileCommand(
userContext.UserId,
request.DisplayName,
request.Bio,
request.Birthday,
request.StatusText,
request.StatusEmoji,
request.StatusExpiresAt),
ct);
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(result.Error);
});
}
group.MapPut("profile", UpdateProfileHandler);
group.MapPatch("profile", UpdateProfileHandler);
userGroup.MapPatch("profile", UpdateProfileHandler);
group.MapGet("{id:guid}", async (Guid id, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(new GetProfileQuery(id), ct);
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(result.Error);
});
usersGroup.MapGet("{id:guid}", async (Guid id, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(new GetProfileQuery(id), ct);
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(result.Error);
});
}
}