# ADR 0001：公告状态机与多租户平台公告方案

> 最后更新日期：2025-12-20

## Context

公告模块需要支持草稿、发布、撤销等完整生命周期，且平台与租户公告必须在同一数据模型中统一管理。同时要支持并发更新与审计追踪，避免已发布公告被“悄然修改”引发合规风险。

## Decision

1. 使用 `Status` 枚举替代 `IsActive` 布尔值作为主状态字段（Draft / Published / Revoked）。
2. `Published` 状态不可变：已发布公告不允许编辑，需先撤销再重新发布。
3. 使用 `TenantId = 0` 表示平台公告，统一在 `tenant_announcements` 表中存储。
4. 使用 `RowVersion` 字段进行乐观并发控制。

```csharp
public enum AnnouncementStatus
{
    Draft = 0,
    Published = 1,
    Revoked = 2
}
```

```mermaid
stateDiagram-v2
  [*] --> Draft
  Draft --> Published: publish
  Published --> Revoked: revoke
  Revoked --> Published: republish
```

## Consequences

- **优点**：
  - 状态语义清晰，支持审计与合规追踪。
  - 平台与租户公告统一查询与筛选逻辑（`TenantId IN (current, 0)`）。
  - `RowVersion` 能防止并发覆盖更新。
- **代价**：
  - 需要迁移与兼容历史 `IsActive` 字段。
  - 已发布公告不可编辑，操作流程增加一步（撤销后重发）。

## Alternatives Considered

1. **继续使用 `IsActive`**
   - 问题：无法表达撤销、草稿等状态，审计语义不足。
2. **平台公告单独表**
   - 问题：跨表查询复杂，重复实现过滤与排序。
3. **使用悲观锁或数据库触发器**
   - 问题：增加数据库负担，难以跨服务扩展。

> 该 ADR 对应迁移：`20251220160000_AddTenantAnnouncementStatusAndPublisher`。
