using Knot.Shared.Kernel; namespace Knot.Contracts.Conversations.Domain; public sealed record ChatCreatedDomainEvent(Chat Chat) : IDomainEvent; public sealed record ChatMemberAddedDomainEvent(Guid ChatId, Guid UserId) : IDomainEvent; /// /// Тип чата: личный или групповой. /// public enum ChatType { Personal, Group, Favorites } /// /// Роль участника в чате. /// public static class ChatRole { public const string Owner = "owner"; public const string Admin = "admin"; public const string Member = "member"; } /// /// Сущность чата (Агрегат). /// public sealed class Chat : AggregateRoot { public ChatType Type { get; private set; } public string? Name { get; private set; } public string? Description { get; private set; } public string? Avatar { get; private set; } public DateTime CreatedAt { get; private set; } public long LastMessageSequenceId { get; private set; } private readonly List _members = new(); public IReadOnlyCollection Members => _members.AsReadOnly(); private Chat(Guid id, ChatType type, string? name, string? avatar, string? description = null) : base(id) { Type = type; Name = name; Avatar = avatar; Description = description; CreatedAt = DateTime.UtcNow; } public static Chat CreatePersonal() { var chat = new Chat(Guid.NewGuid(), ChatType.Personal, null, null); chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat)); return chat; } public static Chat CreateGroup(string name, string? avatar = null) { var chat = new Chat(Guid.NewGuid(), ChatType.Group, name, avatar); chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat)); return chat; } public static Chat Create(string? name, ChatType type, string? avatar = null, string? description = null) { var chat = new Chat(Guid.NewGuid(), type, name, avatar, description); chat.RaiseDomainEvent(new ChatCreatedDomainEvent(chat)); return chat; } public void AddMember(Guid userId, string role = "member") { if (_members.Any(m => m.UserId == userId)) { return; } _members.Add(new ChatMember(Id, userId, role)); RaiseDomainEvent(new ChatMemberAddedDomainEvent(Id, userId)); } public void RemoveMember(Guid userId) { var member = _members.FirstOrDefault(m => m.UserId == userId); if (member != null) { _members.Remove(member); } } public void UpdateName(string name) => Name = name; public void UpdateDescription(string? description) => Description = description; public void UpdateAvatar(string? avatarUrl) => Avatar = avatarUrl; public long IncrementSequenceId() { return ++LastMessageSequenceId; } } /// /// Участник чата. /// public sealed class ChatMember : Entity { public Guid ChatId { get; private set; } public Guid UserId { get; private set; } public string Role { get; private set; } public DateTime JoinedAt { get; private set; } public bool IsPinned { get; private set; } public bool IsMuted { get; private set; } public Guid? LastReadMessageId { get; private set; } public long LastReadSequenceId { get; private set; } public Guid? LastDeliveredMessageId { get; private set; } private ChatMember() : base(Guid.Empty) { Role = "member"; } internal ChatMember(Guid chatId, Guid userId, string role) : base(Guid.NewGuid()) { ChatId = chatId; UserId = userId; Role = role; JoinedAt = DateTime.UtcNow; } public void TogglePin() => IsPinned = !IsPinned; public void UpdateReadCursor(Guid messageId, long sequenceId) { if (sequenceId > LastReadSequenceId) { LastReadMessageId = messageId; LastReadSequenceId = sequenceId; } } public void UpdateDeliveredCursor(Guid messageId) { LastDeliveredMessageId = messageId; } }