Skip to content
bitzorcas
中EN

Concept

系统公告

负责租户系统公告从草稿、发布、撤回到归档的完整生命周期,并管理受众筛选与用户已读事实。

Last updated

负责租户系统公告从草稿、发布、撤回到归档的完整生命周期,并管理受众筛选与用户已读事实。

一张图读懂主路径

管理员创建草稿

规范受众与生效窗口

发布不可变内容

按可信用户上下文筛选

记录幂等已读事实

珊瑚色节点表示本模块的核心决策或状态边界。

能力边界

  • 草稿、发布、撤回与归档状态迁移
  • 全员、角色与部门受众筛选
  • 用户唯一已读事实与管理端已读计数

模块明确不包含:Announcements 只决定公告生命周期与可见性。当前用户、角色和部门仍以 Identity 与组织成员关系为准;站外消息投递仍归 Notifications。

代码地图

项目主要职责
BitzOrcas.Platform.Announcements.Contracts公开契约、端口、DTO 与事件
BitzOrcas.Platform.Announcements.Application命令、查询、处理器与应用策略
BitzOrcas.Platform.Announcements.Infrastructure持久化、连接器与框架适配器

源码位置: src/Platform/Announcements

核心用例

  • CreateAnnouncementCommand
  • UpdateAnnouncementCommand
  • PublishAnnouncementCommand
  • WithdrawAnnouncementCommand
  • ArchiveAnnouncementCommand
  • ListMyAnnouncementsQuery
  • MarkAnnouncementReadCommand

阅读用例时,先看请求契约和权限声明,再跟进处理器调用的聚合或端口,最后检查提交后的事件、缓存和审计。

关键模型与契约

关键类型阅读重点
AnnouncementDto管理端生命周期投影
AnnouncementView经过受众筛选的用户投影
IAnnouncementStore生命周期与可见性 Store 端口
IAnnouncementReadStore幂等已读状态端口

用例边界示例

字段说明
代表性契约PublishAnnouncementCommand
返回结果Result<AnnouncementDto>
审查重点只允许发布草稿,按服务端时钟校验生效窗口,并在公告可见前持久化新的并发版本。

示例伪代码

PublishAnnouncementCommand 伪代码
var existing = await store.FindByIdAsync(command.AnnouncementId, cancellationToken);
// ① 只有 Draft 可以发布,所有时间判断都使用服务端时钟。
if (existing is null) return AnnouncementErrors.NotFound;
if (existing.Status != AnnouncementStatus.Draft.Name) return AnnouncementErrors.InvalidTransition;
var now = clock.UtcNow;
var effectiveAt = existing.EffectiveAt ?? now;
// ② 已经过期或晚于生效时间的失效窗口都必须拒绝。
if (existing.ExpiresAt is not null
&& (existing.ExpiresAt <= effectiveAt || existing.ExpiresAt <= now))
return AnnouncementErrors.InvalidEffectiveWindow;
// ③ 迁移用 record with 表达式生成新快照;SaveAsync 成功后才对用户侧查询可见。
var published = existing with
{
Status = AnnouncementStatus.Published.Name,
PublishedAt = now,
EffectiveAt = effectiveAt,
WithdrawnAt = null,
};
return await store.SaveAsync(published, cancellationToken);

集成关系

用户侧查询通过组织成员仓储解析可信部门标识,再与当前用户角色共同筛选;模块不接受请求自行声明部门归属。

跨模块协作遵循以下方向:

  • 调用方依赖本模块公开 Contracts 或窄端口。
  • 状态变化通过版本化集成事件传播。
  • 具体数据库、消息、缓存、文件或厂商 SDK 留在 Infrastructure。
  • 组合根先注册失败关闭默认实现,再接入生产适配器。

安全、租户与隐私

管理端路由受公告权限与默认关闭的 announcements.manage Feature 约束。用户端从可信上下文取得租户、用户、角色和部门;公告不在受众或有效期内时按不可见处理。

模块保存租户数据时必须写入 TenantId,列表和详情使用同一授权规则。日志、事件和审计只保留诊断所需字段。

失败语义、幂等与可观测性

草稿更新携带显式并发版本;已发布正文不可修改,撤回立即停止展示,归档为终态。已读 Store 必须保证每个租户、用户与公告只有一条已读事实。

情况处理原则
校验、未找到、冲突或禁止返回类型化 Result/Error,并由统一映射生成 Problem Details
适配器或关键状态不可用安全或高价值操作失败关闭;只读降级必须显式标记
重复请求或重复事件使用稳定幂等键,数据库唯一约束作为最后防线
外部超时传播取消,记录脱敏诊断,仅对安全操作执行有界重试

测试矩阵

#必须覆盖建议层级
1合法生命周期与过期并发版本单元 + 集成
2角色与部门受众筛选失败关闭集成/契约
3生效、过期与服务端时钟边界集成/契约
4幂等已读与管理端已读计数集成/契约

测试还要固定一条通用红线:租户 A 的标识、缓存键、事件或查询条件不能让租户 B 读取或修改数据。

源码导航与变更检查

在 BitzOrcasVNext 仓库根目录运行:

Terminal window
# 列出公开类型,确认新增契约是否真的属于模块边界。
rg -n "^public (sealed |abstract |static |partial )*(record|class|interface|enum)" src/Platform/Announcements -g '*.cs'
# 检查跨层引用;调用方不应依赖另一个模块的 Infrastructure。
rg -n "ProjectReference|PackageReference" src/Platform/Announcements -g '*.csproj'
# 查找待补实现和省略式代码;预期没有命中。
rg -n "TODO|FIXME|// \.\.\.|省略" src/Platform/Announcements -g '*.cs'

扩展与发布检查

  1. 新能力是否由明确的 Contracts、权限和 Feature Key 表达?
  2. 持久化、连接器和 Provider 是否可以通过窄适配器替换?
  3. 生产组合是否仍命中 Null/Unavailable 默认实现?健康检查会不会误报绿色?
  4. 事务、事件、缓存失效、审计和后台任务是否有失败与重试证据?
  5. 中英文文档、图表、契约测试和运维手册是否随代码一起更新?

返回平台模块目录 · 查看模块依赖图 · 新增模块指南

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%