Структура

This commit is contained in:
Халимов Рустам
2026-03-30 01:45:19 +03:00
parent 89b2eeea6c
commit 9438cf1b35
55 changed files with 955 additions and 398 deletions
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using Knot.Shared.Kernel;
namespace Knot.Contracts.Messaging.Domain;
public abstract class Message : AggregateRoot<Guid>
{
public Guid ChatId { get; protected set; }
public Guid SenderId { get; protected set; }
public DateTime CreatedAt { get; protected set; }
public long SequenceId { get; protected set; }
public void SetSequenceId(long sequenceId) => SequenceId = sequenceId;
public Guid? ReplyToId { get; protected set; }
public Guid? ForwardedFromId { get; protected set; }
public MessageState State { get; protected set; }
public abstract string Type { get; }
public abstract string? Content { get; protected set; }
public virtual string? Quote { get; protected set; } = null;
public virtual Guid? StoryId => null;
public virtual string? StoryMediaUrl => null;
public virtual string? StoryMediaType => null;
public virtual IReadOnlyCollection<Media> Media => Array.Empty<Media>();
public bool IsEdited => HasState(MessageState.IsEdited);
public bool IsDeleted => HasState(MessageState.IsDeleted);
public bool IsDeletedForUser(Guid userId) => _deletedFor.Exists(d => d.UserId == userId);
protected List<DeletedMessage> _deletedFor = new();
public IReadOnlyCollection<DeletedMessage> DeletedFor => _deletedFor.AsReadOnly();
protected Message() : base(Guid.Empty) { }
protected Message(Guid id, Guid chatId, Guid senderId, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported) : base(id)
{
ChatId = chatId;
SenderId = senderId;
ReplyToId = replyToId;
ForwardedFromId = forwardedFromId;
CreatedAt = createdAt;
if (isImported) AddState(MessageState.IsImported);
}
public void AddState(MessageState state) => State |= state;
public void RemoveState(MessageState state) => State &= ~state;
public bool HasState(MessageState state) => (State & state) == state;
public virtual void Delete() => AddState(MessageState.IsDeleted);
public virtual void Edit(string newContent) { Content = newContent; AddState(MessageState.IsEdited); }
public void DeleteForUser(Guid userId) { if (!_deletedFor.Exists(x => x.UserId == userId)) _deletedFor.Add(new DeletedMessage(Id, userId)); }
}
public class Media
{
public Guid Id { get; set; }
public string Type { get; set; } = string.Empty;
public string? Url { get; set; }
public string? ThumbnailUrl { get; set; }
public long? Size { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public string? FileId { get; set; }
public string? Filename { get; set; }
public string? Duration { get; set; }
}
public class TextMessage : Message
{
public override string Type => "text";
public override string? Content { get; protected set; }
public TextMessage() : base() { }
public TextMessage(Guid id, Guid chatId, Guid senderId, string content, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported = false)
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported) => Content = content;
public TextMessage(Guid id, Guid chatId, Guid senderId, string content, Guid? replyToId, string? quote, Guid? forwardedFromId, DateTime createdAt, bool isImported = false)
: this(id, chatId, senderId, content, replyToId, forwardedFromId, createdAt, isImported) => Quote = quote;
}
public class MediaMessage : Message
{
public override string Type => MediaType.ToString().ToLower();
public override string? Content { get; protected set; }
public string? Caption { get => Content; private set => Content = value; }
public MediaType MediaType { get; private set; }
private List<Media> _media = new();
public override IReadOnlyCollection<Media> Media => _media.AsReadOnly();
private MediaMessage() : base() { MediaType = MediaType.File; }
public MediaMessage(Guid id, Guid chatId, Guid senderId, MediaType mediaType, string? caption, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported) { MediaType = mediaType; Caption = caption; }
public MediaMessage(Guid id, Guid chatId, Guid senderId, string mediaType, string? caption, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
: this(id, chatId, senderId, Enum.TryParse<MediaType>(mediaType, true, out var mt) ? mt : MediaType.File, caption, replyToId, forwardedFromId, createdAt, isImported) { }
public void AddMedia(string type, string url, string? filename, long? size) => _media.Add(new Media { Type = type, Url = url, FileId = filename, Size = size });
public override void Edit(string newCaption) => base.Edit(newCaption);
public override void Delete() { Caption = null; base.Delete(); }
}
public class StoryMessage : Message
{
public override string Type => "story";
public override string? Content { get; protected set; }
public override Guid? StoryId { get; }
public string? InternalStoryMediaUrl { get; private set; }
public override string? StoryMediaUrl => InternalStoryMediaUrl;
public override string? StoryMediaType { get; }
public StoryMessage() : base() { }
public StoryMessage(Guid id, Guid chatId, Guid senderId, Guid storyId, string? storyMediaUrl, string? storyMediaType, DateTime createdAt)
: base(id, chatId, senderId, null, null, createdAt, false) { StoryId = storyId; InternalStoryMediaUrl = storyMediaUrl; StoryMediaType = storyMediaType; }
public StoryMessage(Guid id, Guid chatId, Guid senderId, Guid storyId, string? storyMediaUrl, string? storyMediaType, string? content, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
: this(id, chatId, senderId, storyId, storyMediaUrl, storyMediaType, createdAt) { Content = content; ReplyToId = replyToId; ForwardedFromId = forwardedFromId; if (isImported) AddState(MessageState.IsImported); }
}
public class PollMessage : Message
{
public override string Type => "poll";
public override string? Content { get; protected set; }
public List<PollOption> Options { get; } = new();
public List<PollVote> Votes { get; } = new();
public bool IsMultipleChoice { get; set; }
public DateTime? ExpiresAt { get; set; }
public bool IsClosed { get; set; }
public PollMessage() : base() { }
public PollMessage(Guid id, Guid chatId, Guid senderId, string? question, List<string>? options, bool isAnonymous, bool isMultiple, DateTime? expiresAt, Guid? replyToId, Guid? forwardedFromId, DateTime createdAt, bool isImported)
: base(id, chatId, senderId, replyToId, forwardedFromId, createdAt, isImported)
{
Content = question ?? "Poll";
if (options != null) foreach (var opt in options) Options.Add(new PollOption { Text = opt });
IsMultipleChoice = isMultiple;
ExpiresAt = expiresAt;
}
}
public class PollOption { public string Text { get; set; } = string.Empty; public int VoteCount { get; set; } }
public class PollVote { public Guid OptionIndex { get; set; } public Guid UserId { get; set; } public DateTime VotedAt { get; set; } }