核心功能: - 公告状态机(草稿/已发布/已撤销)支持发布、撤销和重新发布 - 发布者范围区分平台级和租户级公告 - 目标受众定向推送(全部租户/指定角色/指定用户) - 平台管理、租户管理和应用端查询API - 已读/未读管理和未读统计 技术实现: - CQRS+DDD架构,清晰的领域边界和事件驱动 - 查询性能优化:数据库端排序和限制,估算策略减少内存占用 - 并发控制:修复RowVersion配置(IsRowVersion→IsConcurrencyToken) - 完整的FluentValidation验证器和输入保护 测试验证: - 36个测试全部通过(27单元+9集成) - 性能测试达标(1000条数据<5秒) - 代码质量评级A(优秀) 文档: - 完整的ADR、API文档和迁移指南 - 交付报告和技术债务记录
315 lines
8.7 KiB
Markdown
315 lines
8.7 KiB
Markdown
# 公告管理 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` 和中间件统一返回。
|