跳到主要内容

沸点(短内容)

沸点(ShortMsg,表 cms_short_msg)是类微博的轻量短内容:正文上限 4000 字、可配图、可挂话题/投票、支持公开或仅自己可见、支持引用转发(Repost/Quote),并同步到 MeiliSearch 检索。它与文章同属 UGC 内容,但定位更轻——没有独立的分类专栏与频道体系,而是围绕圈子(Club)话题(Topic)、**投票(Poll)**组织。

概念与关系

Domain/ShortMsgs/ShortMsg.cs 中的外键/导航属性揭示了沸点的关联方式:

  • Club(圈子)ClubId 可选,沸点可归属某个圈子(圈子是社区聚合根,见 社区)。
  • Poll(投票)PollId 可选,沸点可直接携带一个投票。
  • ShortMsgTopic(话题):经 ShortMsgTopic 多对多关联(ShortMsgTopics 导航)。
  • Quote / Repost(引用转发)QuotedSubjectId + QuotedSubjectKindPostKind,区分文章/沸点)指向被引用原帖;转贴瞬间把原帖快照固化到当前实体(QuotedAuthorUserId / QuotedContentSnippet(前 200 字)/ QuotedFirstAttachmentUrl / QuotedCreateTime)——原帖后续编辑不影响已转内容。
  • UserLike / Comment / BookmarkItem:同样以 SubjectId 复用互动层。
  • CommonAttachment:附件经 EntityId 挂载(来自 Identity 通用附件能力)。

与文章的关系:两种内容形态,一套互动/检索机制

文章与沸点共享同一条「被互动/被检索」的通道:评论、点赞、收藏、关注、MeiliSearch 索引都用 SubjectId + 形态枚举区分目标,而不是为每种内容各建一套表。这样设计让新增内容形态时,互动层(engagement)和检索层几乎不用改动。二者的差异在于生产侧语义:文章有频道/分类/草稿箱/SEO,沸点有圈子/话题/投票/引用转发与更轻的审核默认态(ShortMsg 默认 AuditStatus.Pending,文章默认 Approved)。

设计动机(why)

引用转发固化快照

ShortMsg.SetQuoteSnapshot(...) 在转贴时刻把原帖作者、正文前 200 字、首图、时间写入引用帖,且明确注释「快照写入后不会随原帖更新而刷新」。原因是转发是一种社交传播动作,应当冻结被引用内容的那一刻状态,避免原帖被改后引发「断章取义」或「引用悬空」。同时 ValidateCanBeReshared() 规定只有审核通过、且非私密的内容才可被转——私密帖(VisibleOnlyMySelf)禁止被引用。

隐私与评论权限的一致性约束

沸点有 PrivacyType(公开/仅自己)与 CommentType(公开/关闭等)两个维度,UpdatePrivacyWithCommentType 强制「私密内容不能允许公开评论」——与文章 UpdatePrivacyWithCommentable 的约束同源。这是实体层的业务不变量,从根上杜绝「私密帖却对全员开放评论」的非法组合。

草稿态与发布态分离

沸点同样支持暂存:SetAsDraft() 进入 AuditStatus.DraftPublishFromDraft(targetStatus) 从草稿发布(目标审核态由调用方按 ShortMsg.AuditStatus.Default 设置决定)。与文章草稿箱不同的是,沸点草稿是本身状态位而非独立表——沸点更轻,不需要像长文那样频繁自动保存大段正文。

业务能力

  • 发布轻量短内容(≤4000 字),配图/话题/投票;
  • 公开或仅自己可见,并可切换可见性(SetPrivacyType);
  • 引用转发(带原帖快照),可取消转发、查看某帖的转发列表;
  • 从正文抽取话题(ExtractTopic);
  • 编辑器草稿暂存与发布;
  • 同步 MeiliSearch 检索(单一 shortmsgs 索引)。

开发示例

// 注入 IShortMsgService 发布沸点
var sm = await _shortMsgService.CreateAsync(new CreateUpdateShortMsgReq
{
Content = "今天天气真好 ☀️",
PrivacyType = PrivacyType.Public
});

// 引用转发(跨形态:可引用文章或另一则沸点)
var repost = await _shortMsgService.RepostAsync(PostKind.ShortMsg, sm.Id);

// 切换可见性
await _shortMsgService.SetPrivacyTypeAsync(new SetShortMsgPrivacyTypeReq
{
Id = sm.Id, PrivacyType = PrivacyType.Private
});

关联能力

相关文档