218 lines
8.0 KiB
C#
218 lines
8.0 KiB
C#
using IM.Commons.IntegrationEvents;
|
||
using IM.DomainCommons;
|
||
using MessageService.Domain.Enums;
|
||
using MessageService.Domain.Events;
|
||
using MessageService.Domain.KeyObjects;
|
||
using MessageService.Domain.Tools;
|
||
using System.Text.Json;
|
||
|
||
namespace MessageService.Domain.Entities
|
||
{
|
||
public class Message : AggregateRootEntity
|
||
{
|
||
/// <summary>
|
||
/// 聊天类型
|
||
/// (0:私聊,1:群聊)
|
||
/// </summary>
|
||
public ChatType ChatType { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 消息类型
|
||
/// (0:文本,1:图片,2:语音,3:视频,4:文件,5:语音聊天,6:视频聊天)
|
||
/// </summary>
|
||
public MessageType MsgType { get; private set; }
|
||
public Guid ClientMsgId { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 消息内容
|
||
/// </summary>
|
||
public MessageContent Content { get; private set; } = null!;
|
||
|
||
/// <summary>
|
||
/// 发送者
|
||
/// </summary>
|
||
public Guid SenderId { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 接收者(私聊为用户ID,群聊为群聊ID)
|
||
/// </summary>
|
||
public Guid TargetId { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 消息状态(0:已发送,1:已撤回)
|
||
/// </summary>
|
||
public MessageState State { get; private set; } = MessageState.Sent;
|
||
|
||
/// <summary>
|
||
/// 消息推送唯一标识符
|
||
/// </summary>
|
||
public string StreamKey { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 消息排序标识
|
||
/// </summary>
|
||
|
||
public long SequenceId { get; private set; }
|
||
|
||
private Message() { }
|
||
|
||
private Message(MessageCreateContext ctx, long sequenceId, MessageType messageType
|
||
, MessageContent content
|
||
)
|
||
{
|
||
ChatType = ctx.ChatType;
|
||
SenderId = ctx.SenderId;
|
||
ClientMsgId = ctx.ClientMsgId;
|
||
TargetId = ctx.TargetId;
|
||
SequenceId = sequenceId;
|
||
MsgType = messageType;
|
||
StreamKey = ChatType == ChatType.GROUP ? StreamKeyBuilder.Group(ctx.TargetId) : StreamKeyBuilder.Private(ctx.SenderId, ctx.TargetId);
|
||
Content = content;
|
||
}
|
||
/// <summary>
|
||
/// 文本消息
|
||
/// </summary>
|
||
/// <param name="ctx"></param>
|
||
/// <param name="text">文本内容</param>
|
||
/// <param name="sequenceId"></param>
|
||
/// <returns></returns>
|
||
public static Message BuildTxt(MessageCreateContext ctx, string text, long sequenceId)
|
||
{
|
||
var content = new MessageContent(text, new TextBody(text));
|
||
return new Message(ctx, sequenceId, MessageType.Text, content);
|
||
}
|
||
/// <summary>
|
||
/// 图片消息
|
||
/// </summary>
|
||
/// <param name="ctx"></param>
|
||
/// <param name="url">相对路径</param>
|
||
/// <param name="width">宽度</param>
|
||
/// <param name="height">高度</param>
|
||
/// <param name="thumb">预览图</param>
|
||
/// <param name="sequenceId"></param>
|
||
/// <returns></returns>
|
||
public static Message BuildImg(MessageCreateContext ctx, string? url,
|
||
int width, int height, string thumb, long sequenceId, Guid? fileId = null)
|
||
{
|
||
var content = new MessageContent("[图片]", new ImageBody(url, width, height, thumb, fileId));
|
||
return new Message(ctx, sequenceId, MessageType.Image, content);
|
||
}
|
||
/// <summary>
|
||
/// 视频消息
|
||
/// </summary>
|
||
/// <param name="ctx"></param>
|
||
/// <param name="url"></param>
|
||
/// <param name="width"></param>
|
||
/// <param name="height"></param>
|
||
/// <param name="thumb"></param>
|
||
/// <param name="sequenceId"></param>
|
||
/// <returns></returns>
|
||
public static Message BuildVideo(MessageCreateContext ctx, string? url,
|
||
int width, int height, string thumb, long sequenceId, Guid? fileId = null)
|
||
{
|
||
var content = new MessageContent("[视频]", new VideoBody(url, width, height, thumb, fileId));
|
||
return new Message(ctx, sequenceId, MessageType.Video, content);
|
||
}
|
||
/// <summary>
|
||
/// 语音消息
|
||
/// </summary>
|
||
/// <param name="ctx"></param>
|
||
/// <param name="url"></param>
|
||
/// <param name="duration">持续时间</param>
|
||
/// <param name="sequenceId"></param>
|
||
/// <returns></returns>
|
||
public static Message BuildVoice(MessageCreateContext ctx, string? url,
|
||
int duration, long sequenceId, Guid? fileId = null)
|
||
{
|
||
var content = new MessageContent("[音频]", new VoiceBody(url, duration, fileId));
|
||
return new Message(ctx, sequenceId, MessageType.Voice, content);
|
||
}
|
||
/// <summary>
|
||
/// 文件
|
||
/// </summary>
|
||
/// <param name="ctx"></param>
|
||
/// <param name="url">相对路径</param>
|
||
/// <param name="name">文件名</param>
|
||
/// <param name="size">文件大小(KB)</param>
|
||
/// <param name="format">文件格式</param>
|
||
/// <param name="sequenceId"></param>
|
||
/// <returns></returns>
|
||
public static Message BuildFile(MessageCreateContext ctx, Guid fileId, string? url,
|
||
string name, long size,
|
||
string format, long sequenceId)
|
||
{
|
||
var content = new MessageContent("[文件]", new FileBody(fileId, url, name, size, format));
|
||
return new Message(ctx, sequenceId, MessageType.File, content);
|
||
}
|
||
/// <summary>
|
||
/// 添加扩展字段
|
||
/// </summary>
|
||
/// <param name="key">字段名</param>
|
||
/// <param name="value">值</param>
|
||
/// <returns></returns>
|
||
public Message WithExt(string key, string value)
|
||
{
|
||
var dic = new Dictionary<string, string>();
|
||
dic.Add(key, value);
|
||
Content.RawExt = JsonSerializer.Serialize(dic);
|
||
return this;
|
||
}
|
||
/// <summary>
|
||
/// 添加消息引用
|
||
/// </summary>
|
||
/// <param name="quote"></param>
|
||
/// <returns></returns>
|
||
public Message WithQuote(QuoteInfo quote)
|
||
{
|
||
this.Content.Quote = quote;
|
||
return this;
|
||
}
|
||
/// <summary>
|
||
/// 完成消息
|
||
/// </summary>
|
||
public void Send()
|
||
{
|
||
AddDomainEvent(new MessageCreatedDomainEvent(this));
|
||
}
|
||
|
||
public void Withdraw()
|
||
{
|
||
if (State != MessageState.Sent)
|
||
{
|
||
throw new DomainException("消息不可撤回");
|
||
}
|
||
State = MessageState.Withdrwan;
|
||
AddDomainEvent(new MessageWithdrawDomainEvent(this));
|
||
}
|
||
public MsgCreatedEvent ToIntegrationEvent()
|
||
{
|
||
return new MsgCreatedEvent
|
||
{
|
||
Id = this.Id,
|
||
ClientId = this.ClientMsgId, // 假设你有存发送端的设备ID
|
||
ChatType = this.ChatType.ToString(), // 比如 "private" 或 "group"
|
||
MsgType = this.MsgType.ToString() ?? MessageType.Text.ToString(), // 动态获取类型名,或从 Content 里的定义拿
|
||
SenderId = this.SenderId,
|
||
TargetId = this.TargetId,
|
||
State = State.ToString(),
|
||
StreamKey = this.StreamKey,
|
||
SequenceId = this.SequenceId,
|
||
// 核心载荷:利用计算属性触发反序列化,打包成 DTO
|
||
Content = new MsgContent(
|
||
this.Content.Fallback,
|
||
this.Content.Body, // 触发实体内部的反序列化逻辑
|
||
this.Content.Ext,
|
||
this.Content.Quote != null ? new QuoteInfoDto(
|
||
this.Content.Quote.MessageId,
|
||
this.Content.Quote.SenderId,
|
||
this.Content.Quote.SenderName,
|
||
this.Content.Quote.MessageType.ToString(),
|
||
this.Content.Quote.Preview
|
||
) : null
|
||
)
|
||
};
|
||
}
|
||
|
||
}
|
||
}
|