# 公告管理 API 文档

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

本文档覆盖公告管理相关 API，包括平台公告、租户公告管理端接口，以及应用端（已认证用户）接口。

## 统一约定

- 认证方式：`Authorization: Bearer <JWT>`
- 时间字段均为 UTC（ISO 8601）。
- 雪花 ID 以字符串形式序列化返回。
- 统一响应结构：`ApiResponse<T>`。

```json
{
  "success": true,
  "code": 200,
  "message": "操作成功",
  "data": {},
  "errors": null,
  "traceId": "01JH...",
  "timestamp": "2025-12-20T12:00:00Z"
}
```

分页结构：
```json
{
  "items": [],
  "page": 1,
  "pageSize": 20,
  "totalCount": 0,
  "totalPages": 0
}
```

## 关键枚举与字段

- `AnnouncementStatus`：`Draft(0)`、`Published(1)`、`Revoked(2)`
- `TenantAnnouncementType`：`System(0)`、`Billing(1)`、`Operation(2)`、`SYSTEM_PLATFORM_UPDATE(3)`、`SYSTEM_SECURITY_NOTICE(4)`、`SYSTEM_COMPLIANCE(5)`、`TENANT_INTERNAL(6)`、`TENANT_FINANCE(7)`、`TENANT_OPERATION(8)`
- `PublisherScope`：`Platform(0)`、`Tenant(1)`（只读字段）
- `RowVersion`：并发控制字段（Base64 字符串）。

## 目标受众（TargetType / TargetParameters）

系统使用 `TargetType`（不区分大小写）+ `TargetParameters(JSON)` 过滤可见公告：

- `ALL_TENANTS`：平台全量（可带约束）
- `TENANT_ALL`：单租户全量
- `SPECIFIC_TENANTS`
- `USERS` / `SPECIFIC_USERS` / `USER_IDS`
- `ROLES` / `ROLE`
- `PERMISSIONS` / `PERMISSION`
- `MERCHANTS` / `MERCHANT_IDS`

`TargetParameters` 示例：
```json
{
  "tenantIds": [100000000000000001],
  "userIds": [200000000000000001],
  "merchantIds": [300000000000000001],
  "roles": ["OpsManager"],
  "permissions": ["tenant-announcement:read"],
  "departments": ["NorthRegion"]
}
```

注意：`TargetParameters` 为字符串 JSON；解析失败会导致公告对该用户不可见（失败即隐藏）。

## 数据流（示意）

```mermaid
flowchart LR
  Client[客户端] --> API[API Controller]
  API --> Mediator[MediatR]
  Mediator --> Handler[Query/Command Handler]
  Handler --> Repo[Repository]
  Repo --> DB[(PostgreSQL)]
```

---

# 平台公告 API

> 路由前缀：`/api/platform/announcements`（无版本前缀）

### 1) 创建平台公告
- **方法**：POST
- **路径**：`/api/platform/announcements`
- **权限**：`platform-announcement:create`
- **请求体**：`CreateTenantAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：400 / 403

请求示例：
```json
{
  "title": "平台升级通知",
  "content": "系统将于今晚 23:00 维护。",
  "announcementType": 0,
  "priority": 10,
  "effectiveFrom": "2025-12-20T00:00:00Z",
  "effectiveTo": null,
  "targetType": "all_tenants",
  "targetParameters": null
}
```

响应示例：
```json
{
  "success": true,
  "code": 200,
  "data": {
    "id": "900123456789012345",
    "tenantId": "0",
    "title": "平台升级通知",
    "status": "Draft"
  }
}
```

### 2) 查询平台公告列表
- **方法**：GET
- **路径**：`/api/platform/announcements`
- **权限**：`platform-announcement:create`
- **查询参数**：
  - `page` / `pageSize`
  - `status`（Draft/Published/Revoked）
  - `announcementType`
  - `isActive`
  - `effectiveFrom` / `effectiveTo`
  - `onlyEffective`
- **响应**：`ApiResponse<PagedResult<TenantAnnouncementDto>>`
- **错误码**：403

示例：`GET /api/platform/announcements?page=1&pageSize=20&status=Published`

### 3) 获取平台公告详情
- **方法**：GET
- **路径**：`/api/platform/announcements/{announcementId}`
- **权限**：`platform-announcement:create`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404

### 4) 更新平台公告（仅草稿）
- **方法**：PUT
- **路径**：`/api/platform/announcements/{announcementId}`
- **权限**：`platform-announcement:create`
- **请求体**：`UpdateTenantAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

请求示例：
```json
{
  "title": "平台升级通知（更新）",
  "content": "维护时间调整为 23:30。",
  "targetType": "all_tenants",
  "targetParameters": null,
  "rowVersion": "AAAAAAAAB9E="
}
```

### 5) 发布平台公告
- **方法**：POST
- **路径**：`/api/platform/announcements/{announcementId}/publish`
- **权限**：`platform-announcement:publish`
- **请求体**：`PublishAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

请求示例：
```json
{ "rowVersion": "AAAAAAAAB9E=" }
```

### 6) 撤销平台公告
- **方法**：POST
- **路径**：`/api/platform/announcements/{announcementId}/revoke`
- **权限**：`platform-announcement:revoke`
- **请求体**：`RevokeAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

请求示例：
```json
{ "rowVersion": "AAAAAAAAB9E=" }
```

---

# 租户公告管理 API（管理端）

> 路由前缀：`/api/admin/v{version}/tenants/{tenantId}/announcements`

### 1) 查询租户公告列表
- **方法**：GET
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements`
- **权限**：`tenant-announcement:read`
- **查询参数**：
  - `page` / `pageSize`
  - `status` / `announcementType`
  - `isActive` / `effectiveFrom` / `effectiveTo` / `onlyEffective`
- **响应**：`ApiResponse<PagedResult<TenantAnnouncementDto>>`
- **错误码**：403

### 2) 获取租户公告详情
- **方法**：GET
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}`
- **权限**：`tenant-announcement:read`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404

### 3) 创建租户公告
- **方法**：POST
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements`
- **权限**：`tenant-announcement:create`
- **请求体**：`CreateTenantAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：400 / 403

请求示例：
```json
{
  "title": "租户公告",
  "content": "新品上线提醒",
  "announcementType": 0,
  "priority": 5,
  "effectiveFrom": "2025-12-20T00:00:00Z",
  "targetType": "roles",
  "targetParameters": "{\"roles\":[\"OpsManager\"]}"
}
```

### 4) 更新租户公告（仅草稿）
- **方法**：PUT
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}`
- **权限**：`tenant-announcement:update`
- **请求体**：`UpdateTenantAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

### 5) 发布租户公告
- **方法**：POST
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}/publish`
- **权限**：`tenant-announcement:publish`
- **请求体**：`PublishAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

### 6) 撤销租户公告
- **方法**：POST
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}/revoke`
- **权限**：`tenant-announcement:revoke`
- **请求体**：`RevokeAnnouncementCommand`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404 / 409

### 7) 删除租户公告
- **方法**：DELETE
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}`
- **权限**：`tenant-announcement:delete`
- **响应**：`ApiResponse<bool>`
- **错误码**：403

### 8) 标记公告已读（兼容旧路径）
- **方法**：POST
- **路径**：`/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}/read`
- **权限**：`tenant-announcement:read`
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：403 / 404

---

# 应用端公告 API（已认证用户）

> 路由前缀：`/api/app/announcements`（目前挂载在 AdminApi）

### 1) 获取可见公告列表
- **方法**：GET
- **路径**：`/api/app/announcements`
- **权限**：登录即可
- **查询参数**：`page` / `pageSize`（其他筛选参数会被覆盖为已发布与有效期内）
- **响应**：`ApiResponse<PagedResult<TenantAnnouncementDto>>`
- **错误码**：401

### 2) 获取未读公告列表
- **方法**：GET
- **路径**：`/api/app/announcements/unread`
- **权限**：登录即可
- **查询参数**：`page` / `pageSize`
- **响应**：`ApiResponse<PagedResult<TenantAnnouncementDto>>`
- **错误码**：401

### 3) 标记公告已读
- **方法**：POST
- **路径**：`/api/app/announcements/{announcementId}/mark-read`
- **权限**：登录即可
- **请求体**：无
- **响应**：`ApiResponse<TenantAnnouncementDto>`
- **错误码**：401 / 404

---

## 常见错误码

- **400**：参数验证失败
- **401**：未认证
- **403**：无权限
- **404**：公告不存在或不可见
- **409**：状态冲突（例如已发布不可编辑）

> 提示：实际错误码与消息由 `BusinessException` 和中间件统一返回。
