feat: integrate cultivate admin internal APIs
This commit is contained in:
@@ -0,0 +1,696 @@
|
||||
# Cultivate Server 管理后台内部接口开发对接文档
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文用于指导 `cultivate-server` 开发管理后台内部接口,使 IM Manager 可以通过服务间 HTTP API 管理养成业务,不再直接连接 Cultivate 数据库。
|
||||
|
||||
改造完成后必须满足:
|
||||
|
||||
1. IM Manager 不配置 `CULTIVATE_MYSQL_DSN`,也不持有 Cultivate 数据库账号。
|
||||
2. Cultivate 数据只能由 `cultivate-server` 读写。
|
||||
3. Manager 只负责管理员登录态、RBAC、页面协议转换和调用内部接口。
|
||||
4. Cultivate 服务不可用时,只影响养成管理菜单,不得影响 Manager 启动及其他菜单。
|
||||
5. 所有查询和写入必须按商户隔离,禁止信任前端传入的 `tenant_id`、`operator_id`。
|
||||
|
||||
## 2. 当前问题
|
||||
|
||||
当前 IM Manager 在配置中注册了独立数据库组:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
cultivate:
|
||||
link: "$CULTIVATE_MYSQL_DSN"
|
||||
```
|
||||
|
||||
养成管理逻辑通过 `g.DB("cultivate")` 及 group 为 `cultivate` 的 DAO 直接访问以下表:
|
||||
|
||||
- `cv_flower_catalog`
|
||||
- `cv_flower_stage_rule`
|
||||
- `cv_user_flower`
|
||||
- `cv_garden_active_day`
|
||||
- `cv_feed_post`
|
||||
- `cv_feed_comment`
|
||||
- `cv_feed_media`
|
||||
- `cv_feed_report`
|
||||
- `cv_content_audit_log`
|
||||
- `cv_admin_operation_log`
|
||||
|
||||
该实现造成:
|
||||
|
||||
- Manager 启动依赖 Cultivate 数据库配置。
|
||||
- Cultivate 数据库账号扩散到 Manager。
|
||||
- Cultivate 表结构变化会直接影响 Manager。
|
||||
- 内容审核、通知、文件校验等事务边界跨服务泄漏。
|
||||
|
||||
本次应将上述数据库逻辑迁移到 `cultivate-server`,不能以给 Manager 增加数据库 Secret 的方式规避。
|
||||
|
||||
## 3. 服务职责
|
||||
|
||||
### 3.1 Cultivate Server
|
||||
|
||||
负责:
|
||||
|
||||
- 查询养成概览。
|
||||
- 花种及成长阶段配置的增删改查和状态变更。
|
||||
- 动态、评论审核列表、详情和人工审核。
|
||||
- 举报列表、详情和处理。
|
||||
- 管理操作日志查询和写入。
|
||||
- 调用 IM 内部接口查询用户摘要、校验文件、发送系统通知及执行用户治理。
|
||||
- 数据事务、幂等、租户隔离和审计。
|
||||
|
||||
### 3.2 IM Manager
|
||||
|
||||
负责:
|
||||
|
||||
- 校验后台管理员登录态和菜单权限。
|
||||
- 从登录态解析商户、操作人和角色。
|
||||
- 对 Cultivate 内部请求签名。
|
||||
- 将 Cultivate 返回结果转换为现有后台页面响应。
|
||||
- 将下游不可用错误转换成清晰的中文业务错误。
|
||||
|
||||
Manager 不再保存 Cultivate DAO、实体模型或数据库连接。
|
||||
|
||||
## 4. 接口基础约定
|
||||
|
||||
### 4.1 路径
|
||||
|
||||
内部管理接口统一使用:
|
||||
|
||||
```text
|
||||
/internal/admin/cultivate/tenants/{tenant_code}/...
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
GET /internal/admin/cultivate/tenants/default/dashboard
|
||||
```
|
||||
|
||||
这些接口只能在集群内访问,不挂到公网 Ingress。
|
||||
|
||||
### 4.2 数据格式
|
||||
|
||||
- 请求和响应均使用 UTF-8 JSON。
|
||||
- JSON 字段统一使用 `snake_case`。
|
||||
- ID 统一使用十进制字符串返回,避免 JavaScript 超出安全整数范围。
|
||||
- 时间统一使用 UTC RFC3339,例如 `2026-09-23T08:30:00Z`。
|
||||
- 分页参数为 `page`、`per_page`,默认分别为 `1`、`20`。
|
||||
- `per_page` 最大为 `100`。
|
||||
|
||||
统一成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {},
|
||||
"server_time": "2026-09-23T08:30:00Z",
|
||||
"request_id": "req_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
统一失败响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 40001,
|
||||
"reason": "INVALID_ARGUMENT",
|
||||
"message": "内容类型不正确",
|
||||
"details": null,
|
||||
"server_time": "2026-09-23T08:30:00Z",
|
||||
"request_id": "req_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
分页数据统一放在 `data` 中:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [],
|
||||
"total": 0,
|
||||
"page": 1,
|
||||
"per_page": 20
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 内部鉴权
|
||||
|
||||
### 5.1 请求头
|
||||
|
||||
| 请求头 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `X-Service-Name` | 是 | 调用方服务名,Manager 固定为 `im-manager` |
|
||||
| `X-Timestamp` | 是 | UTC Unix 秒 |
|
||||
| `X-Nonce` | 是 | 每次请求随机值,服务端防重放 |
|
||||
| `X-Signature` | 是 | HMAC-SHA256 十六进制小写字符串 |
|
||||
| `X-Tenant-ID` | 是 | Manager 登录态解析出的商户 ID |
|
||||
| `X-Operator-ID` | 是 | Manager 登录态解析出的管理员 ID |
|
||||
| `X-Role-Key` | 否 | 管理员角色标识,用于审计,不作为唯一授权依据 |
|
||||
| `X-Request-ID` | 是 | 全链路请求 ID |
|
||||
|
||||
### 5.2 签名串
|
||||
|
||||
双方复用现有内部服务密钥,不新增数据库相关 Secret。签名原文为:
|
||||
|
||||
```text
|
||||
METHOD\n
|
||||
REQUEST_URI\n
|
||||
SHA256_HEX(BODY)\n
|
||||
TIMESTAMP\n
|
||||
NONCE\n
|
||||
TENANT_ID\n
|
||||
OPERATOR_ID\n
|
||||
ROLE_KEY\n
|
||||
REQUEST_ID
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `REQUEST_URI` 包含路径和排序后的 Query String。
|
||||
- GET 请求的 Body 为空字节数组。
|
||||
- 使用共享密钥计算 `HMAC-SHA256`。
|
||||
- 时间窗口建议为正负 300 秒。
|
||||
- `X-Service-Name + X-Nonce` 在时间窗口内只能使用一次。
|
||||
- 必须使用常量时间比较签名。
|
||||
- 商户、操作人及角色头已经纳入签名,业务层不得接受 Body 中同名字段覆盖。
|
||||
|
||||
### 5.3 商户校验
|
||||
|
||||
1. `{tenant_code}` 必须与签名后的 `X-Tenant-ID` 对应。
|
||||
2. 所有 SQL 必须带 `tenant_id = X-Tenant-ID`。
|
||||
3. 查不到商户或编码与 ID 不匹配时返回 `TENANT_NOT_FOUND` 或 `TENANT_MISMATCH`。
|
||||
4. 禁止使用默认商户兜底处理错误的商户参数。
|
||||
|
||||
## 6. 接口清单
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| GET | `/dashboard` | 养成管理概览 |
|
||||
| GET | `/flowers` | 花种列表 |
|
||||
| GET | `/flowers/{id}` | 花种详情 |
|
||||
| POST | `/flowers` | 新建花种 |
|
||||
| PUT | `/flowers/{id}` | 编辑花种 |
|
||||
| POST | `/flowers/{id}/status` | 花种状态变更 |
|
||||
| GET | `/content-reviews` | 内容审核列表 |
|
||||
| GET | `/content-reviews/{object_type}/{id}` | 内容审核详情 |
|
||||
| POST | `/content-reviews/{object_type}/{id}/actions` | 单条审核操作 |
|
||||
| POST | `/content-reviews/batch-actions` | 批量审核操作 |
|
||||
| GET | `/reports` | 举报列表 |
|
||||
| GET | `/reports/{id}` | 举报详情 |
|
||||
| POST | `/reports/{id}/actions` | 处理举报 |
|
||||
| GET | `/operation-logs` | 操作日志列表 |
|
||||
|
||||
以下章节中的路径均省略前缀:
|
||||
|
||||
```text
|
||||
/internal/admin/cultivate/tenants/{tenant_code}
|
||||
```
|
||||
|
||||
## 7. 数据概览
|
||||
|
||||
### 7.1 `GET /dashboard`
|
||||
|
||||
响应 `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plant_count": 0,
|
||||
"growing_count": 0,
|
||||
"bloom_count": 0,
|
||||
"harvested_count": 0,
|
||||
"removed_count": 0,
|
||||
"active_7d": 0,
|
||||
"active_30d": 0,
|
||||
"post_count": 0,
|
||||
"public_post_count": 0,
|
||||
"friends_post_count": 0,
|
||||
"private_post_count": 0,
|
||||
"comment_count": 0,
|
||||
"like_count": 0,
|
||||
"taken_down_count": 0,
|
||||
"pending_audit_count": 0,
|
||||
"approved_audit_count": 0,
|
||||
"rejected_audit_count": 0,
|
||||
"pending_report_count": 0,
|
||||
"processing_report_count": 0,
|
||||
"processed_report_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
统计口径必须沿用当前业务状态,且全部按商户过滤。没有数据时返回 `0`,不能返回 `null`。
|
||||
|
||||
## 8. 花种管理
|
||||
|
||||
### 8.1 `GET /flowers`
|
||||
|
||||
Query 参数:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `flower_code` | string | 编码模糊查询 |
|
||||
| `flower_name` | string | 名称模糊查询 |
|
||||
| `status` | string | 状态精确查询 |
|
||||
| `config_version` | string | 配置版本 |
|
||||
| `created_from` | RFC3339 | 创建开始时间 |
|
||||
| `created_to` | RFC3339 | 创建结束时间 |
|
||||
| `page` | int | 页码 |
|
||||
| `per_page` | int | 每页数量 |
|
||||
|
||||
列表项:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "10001",
|
||||
"flower_code": "rose",
|
||||
"flower_name": "玫瑰",
|
||||
"flower_desc": "",
|
||||
"icon_file_id": "file_xxx",
|
||||
"status": "online",
|
||||
"config_version": "cat_20260923_163000",
|
||||
"daily_water_limit": 3,
|
||||
"daily_fertilize_limit": 1,
|
||||
"sort_no": 10,
|
||||
"stage_count": 4,
|
||||
"created_at": "2026-09-23T08:30:00Z",
|
||||
"updated_at": "2026-09-23T08:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
排序规则:`sort_no ASC, id DESC`。
|
||||
|
||||
### 8.2 `GET /flowers/{id}`
|
||||
|
||||
在列表字段基础上增加:
|
||||
|
||||
```json
|
||||
{
|
||||
"share_config": {},
|
||||
"stage_rules": [
|
||||
{
|
||||
"id": "10002",
|
||||
"stage": "seed",
|
||||
"next_stage": "sprout",
|
||||
"required_points": 10,
|
||||
"water_points": 2,
|
||||
"fertilize_points": 3,
|
||||
"allowed_actions": ["water", "fertilize"],
|
||||
"image_file_id": "file_xxx",
|
||||
"animation_file_id": "file_xxx",
|
||||
"progress_text_template": "成长中",
|
||||
"completion_text": "已进入发芽阶段",
|
||||
"sort_no": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 `POST /flowers` 与 `PUT /flowers/{id}`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"flower_code": "rose",
|
||||
"flower_name": "玫瑰",
|
||||
"flower_desc": "",
|
||||
"icon_file_id": "file_xxx",
|
||||
"status": "offline",
|
||||
"daily_water_limit": 3,
|
||||
"daily_fertilize_limit": 1,
|
||||
"share_config": {},
|
||||
"sort_no": 10,
|
||||
"stage_rules": []
|
||||
}
|
||||
```
|
||||
|
||||
业务规则:
|
||||
|
||||
- `flower_code`、`flower_name` 必填。
|
||||
- `flower_code` 创建后不可修改。
|
||||
- 状态枚举:`online`、`offline`、`activity_only`、`maintenance`。
|
||||
- 阶段枚举:`seed`、`sprout`、`bud`、`bloom`。
|
||||
- 阶段链必须从 `seed` 连续配置并以 `bloom` 结束,不能重复或成环。
|
||||
- 非 `bloom` 阶段 `required_points > 0`,且至少允许一个动作。
|
||||
- 动作枚举:`water`、`fertilize`。
|
||||
- 文件 ID 必须通过 IM 内部文件校验,风险或待审核素材不能保存。
|
||||
- 花种和阶段规则必须在一个数据库事务内保存。
|
||||
- 每次新增或编辑生成新的 `config_version` 并写管理操作日志。
|
||||
- 新建返回 `data.id`,ID 使用字符串。
|
||||
|
||||
### 8.4 `POST /flowers/{id}/status`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "online",
|
||||
"reason": "审核通过后上架"
|
||||
}
|
||||
```
|
||||
|
||||
更新上下架时间、配置版本并写操作日志。
|
||||
|
||||
## 9. 内容审核
|
||||
|
||||
### 9.1 `GET /content-reviews`
|
||||
|
||||
Query 参数:
|
||||
|
||||
- `object_type`:必填,`feed_post` 或 `feed_comment`。
|
||||
- `author_id`:作者 ID。
|
||||
- `status`:业务状态。
|
||||
- `keyword`:正文关键字。
|
||||
- `created_from`、`created_to`。
|
||||
- `page`、`per_page`。
|
||||
|
||||
列表项:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "20001",
|
||||
"object_type": "feed_post",
|
||||
"post_id": "",
|
||||
"author_id": "30001",
|
||||
"content_text": "正文",
|
||||
"flower_code": "rose",
|
||||
"visibility": "public",
|
||||
"status": "reviewing",
|
||||
"audit_status": "pending",
|
||||
"reject_reason": "",
|
||||
"like_count": 0,
|
||||
"comment_count": 0,
|
||||
"created_at": "2026-09-23T08:30:00Z",
|
||||
"updated_at": "2026-09-23T08:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
按 `id DESC` 排序。
|
||||
|
||||
### 9.2 `GET /content-reviews/{object_type}/{id}`
|
||||
|
||||
详情在列表项基础上增加:
|
||||
|
||||
- `author_nickname`
|
||||
- `author_avatar_file_id`
|
||||
- `media[]`:`id`、`file_id`、`thumbnail_file_id`、`media_type`、`audit_status`、`sort_no`
|
||||
- `audits[]`:`id`、`audit_source`、`status_before`、`status_after`、`result_code`、`risk_labels`、`operator_id`、`occurred_at`
|
||||
- `related_reports[]`:关联举报摘要
|
||||
|
||||
用户摘要通过 IM 内部 API 查询。用户服务临时不可用时,允许昵称和头像为空,但内容主体仍应返回。
|
||||
|
||||
### 9.3 `POST /content-reviews/{object_type}/{id}/actions`
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "approve",
|
||||
"reason": ""
|
||||
}
|
||||
```
|
||||
|
||||
动作枚举:
|
||||
|
||||
- `approve`:审核通过。
|
||||
- `reject`:审核拒绝,必须填写原因。
|
||||
- `take_down`:下架,必须填写原因。
|
||||
- `restore`:恢复内容。
|
||||
|
||||
要求:
|
||||
|
||||
- 使用行锁或等价并发控制。
|
||||
- 校验合法的前置状态。
|
||||
- 更新内容状态和审核字段。
|
||||
- 评论首次通过时只能增加一次动态评论数。
|
||||
- 下架动态时应按现有业务规则同步处理其可见性或关联评论。
|
||||
- 每次操作写 `cv_content_audit_log` 和 `cv_admin_operation_log`。
|
||||
- 事务提交后向作者发送系统通知。
|
||||
- 重复请求不能重复计数或重复产生治理副作用。
|
||||
|
||||
### 9.4 `POST /content-reviews/batch-actions`
|
||||
|
||||
```json
|
||||
{
|
||||
"object_type": "feed_post",
|
||||
"ids": ["20001", "20002"],
|
||||
"action": "approve",
|
||||
"reason": ""
|
||||
}
|
||||
```
|
||||
|
||||
- 单次 1 至 100 条。
|
||||
- 每条独立处理,单条失败不回滚其他成功项。
|
||||
- 返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"success_count": 1,
|
||||
"failed_count": 1,
|
||||
"failures": {
|
||||
"20002": "当前状态不允许审核通过"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 举报管理
|
||||
|
||||
### 10.1 `GET /reports`
|
||||
|
||||
Query 参数:
|
||||
|
||||
- `object_type`
|
||||
- `status`
|
||||
- `reporter_id`
|
||||
- `created_from`、`created_to`
|
||||
- `page`、`per_page`
|
||||
|
||||
列表项:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "40001",
|
||||
"reporter_id": "30001",
|
||||
"reported_user_id": "30002",
|
||||
"object_type": "feed_post",
|
||||
"object_id": "20001",
|
||||
"reason_code": "spam",
|
||||
"description": "疑似广告",
|
||||
"evidence_file_ids": ["file_xxx"],
|
||||
"source_page": "feed_detail",
|
||||
"status": "submitted",
|
||||
"handler_id": "",
|
||||
"handle_result": "",
|
||||
"handled_at": null,
|
||||
"created_at": "2026-09-23T08:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 `GET /reports/{id}`
|
||||
|
||||
详情增加:
|
||||
|
||||
- `reporter_nickname`
|
||||
- `reported_user_nickname`
|
||||
- `target_content`
|
||||
- `history[]`:同一对象的历史举报
|
||||
|
||||
不得向被举报方的通知或普通业务接口泄露举报人身份和证据。
|
||||
|
||||
### 10.3 `POST /reports/{id}/actions`
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "resolve",
|
||||
"handle_result": "举报成立,内容已下架",
|
||||
"content_action": "take_down",
|
||||
"user_action": "none",
|
||||
"reason": "违规广告"
|
||||
}
|
||||
```
|
||||
|
||||
枚举:
|
||||
|
||||
- `action`:`processing`、`resolve`、`reject`。
|
||||
- `content_action`:`none`、`take_down`。
|
||||
- `user_action`:`none`、`freeze`、`ban`。
|
||||
|
||||
校验规则:
|
||||
|
||||
- `processing` 不要求处理结果。
|
||||
- `resolve`、`reject` 必须填写 `handle_result`。
|
||||
- 只有 `resolve` 可以联动内容和用户处置。
|
||||
- 发生联动处置时必须填写 `reason`。
|
||||
- 报告状态更新、内容处置和操作日志应处于同一事务。
|
||||
- 用户冻结或封禁通过 IM 内部治理 API 执行;失败时不得将举报错误标记为已完成。
|
||||
- 完成后分别通知举报方和被举报方,被举报方通知不得携带举报人信息。
|
||||
|
||||
## 11. 操作日志
|
||||
|
||||
### 11.1 `GET /operation-logs`
|
||||
|
||||
Query 参数:
|
||||
|
||||
- `operator_id`
|
||||
- `operation_type`
|
||||
- `target_type`
|
||||
- `target_id`
|
||||
- `request_id`
|
||||
- `created_from`、`created_to`
|
||||
- `page`、`per_page`
|
||||
|
||||
列表项:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "50001",
|
||||
"operator_id": "14",
|
||||
"operation_type": "content.approve",
|
||||
"target_type": "feed_post",
|
||||
"target_id": "20001",
|
||||
"request_id": "req_xxx",
|
||||
"before_snapshot": {},
|
||||
"after_snapshot": {},
|
||||
"reason": "",
|
||||
"created_at": "2026-09-23T08:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
日志只读,不提供删除接口。快照不得保存 Token、手机号、数据库连接、完整举报证据或私有文件 URL。
|
||||
|
||||
## 12. 状态和枚举
|
||||
|
||||
| 类型 | 枚举 |
|
||||
| --- | --- |
|
||||
| 内容类型 | `feed_post`、`feed_comment` |
|
||||
| 花种状态 | `online`、`offline`、`activity_only`、`maintenance` |
|
||||
| 成长阶段 | `seed`、`sprout`、`bud`、`bloom` |
|
||||
| 成长动作 | `water`、`fertilize` |
|
||||
| 可见范围 | `public`、`friends`、`private` |
|
||||
| 内容状态 | `reviewing`、`published`、`rejected`、`taken_down` |
|
||||
| 审核动作 | `approve`、`reject`、`take_down`、`restore` |
|
||||
| 举报状态 | `submitted`、`processing`、`resolved`、`rejected` |
|
||||
| 举报动作 | `processing`、`resolve`、`reject` |
|
||||
| 用户处置 | `none`、`freeze`、`ban` |
|
||||
|
||||
如果现有数据库已经使用其他稳定枚举,开发前应与 Manager 对齐,不允许接口层静默转换出第三套状态。
|
||||
|
||||
## 13. 错误码建议
|
||||
|
||||
| HTTP | reason | 场景 |
|
||||
| --- | --- | --- |
|
||||
| 400 | `INVALID_ARGUMENT` | 参数或枚举错误 |
|
||||
| 401 | `INTERNAL_AUTH_REQUIRED` | 缺少内部签名 |
|
||||
| 403 | `INTERNAL_AUTH_FORBIDDEN` | 签名错误、服务未授权 |
|
||||
| 404 | `TENANT_NOT_FOUND` | 商户不存在 |
|
||||
| 404 | `RESOURCE_NOT_FOUND` | 花种、内容或举报不存在 |
|
||||
| 409 | `STATE_CONFLICT` | 当前状态不允许执行动作 |
|
||||
| 409 | `DUPLICATE_FLOWER_CODE` | 商户内花种编码重复 |
|
||||
| 422 | `ASSET_REVIEW_PENDING` | 素材仍在审核中 |
|
||||
| 422 | `ASSET_REVIEW_REJECTED` | 素材审核未通过 |
|
||||
| 429 | `RATE_LIMITED` | 调用频率过高 |
|
||||
| 503 | `DEPENDENCY_UNAVAILABLE` | IM 用户、文件、通知或治理服务不可用 |
|
||||
| 500 | `INTERNAL_ERROR` | 未知内部错误 |
|
||||
|
||||
响应不得返回 SQL、数据库地址、堆栈或 Secret。
|
||||
|
||||
## 14. 数据一致性与安全要求
|
||||
|
||||
1. 每条 SQL 都必须显式过滤 `tenant_id`。
|
||||
2. 更新语句同时按主键和 `tenant_id` 定位。
|
||||
3. 写接口记录签名后的 `X-Operator-ID` 和 `X-Request-ID`。
|
||||
4. 审核、举报处置和计数变更必须考虑重试幂等。
|
||||
5. 批量操作最多 100 条,避免长事务。
|
||||
6. 私有媒体只返回 `file_id`,访问地址由受控文件接口临时签发。
|
||||
7. 关键操作日志永久保留;普通查询对已注销用户做匿名化。
|
||||
8. 内部接口不可复用 C 端 Token 鉴权,必须单独使用服务间 HMAC。
|
||||
9. 内部 API 路由应在普通 C 端认证中间件之前单独注册,不能误走 Redis 用户会话认证。
|
||||
|
||||
## 15. Cultivate Server 建议目录
|
||||
|
||||
按现有分层新增,禁止在 controller 中写数据库逻辑:
|
||||
|
||||
```text
|
||||
api/internal/admin/
|
||||
cultivate.go
|
||||
internal/controller/admin/
|
||||
cultivate.go
|
||||
internal/service/
|
||||
admin_cultivate.go
|
||||
internal/logic/admin/
|
||||
dashboard.go
|
||||
flower.go
|
||||
content_review.go
|
||||
report.go
|
||||
operation_log.go
|
||||
internal/middleware/
|
||||
internal_admin_auth.go
|
||||
internal/model/
|
||||
admin_cultivate.go
|
||||
```
|
||||
|
||||
路由建议独立注册:
|
||||
|
||||
```text
|
||||
/internal/admin/cultivate/*
|
||||
```
|
||||
|
||||
不要将管理接口混入 `/v1/cultivate/*` C 端路由。
|
||||
|
||||
## 16. Manager 后续改造
|
||||
|
||||
Cultivate Server 接口完成后,Manager 侧将执行:
|
||||
|
||||
1. 新增 Cultivate 内部 API Client。
|
||||
2. 将现有页面 API 转发到本文定义的内部接口。
|
||||
3. 删除 Manager 内的 Cultivate 直接数据库逻辑。
|
||||
4. 删除 Cultivate DAO、DO、Entity 等仅为直连数据库生成的代码。
|
||||
5. 删除配置模板中的 `database.cultivate` 和 `CULTIVATE_MYSQL_DSN`。
|
||||
6. 保持现有后台页面路径和前端交互不变。
|
||||
|
||||
服务地址建议支持配置,但 Kubernetes 环境可提供集群内默认值:
|
||||
|
||||
```text
|
||||
http://cultivate-server.cultivate-system.svc.cluster.local:8080
|
||||
```
|
||||
|
||||
该地址不是数据库配置。密钥复用双方已有内部 HMAC Secret,不增加 Cultivate 数据库 Secret。
|
||||
|
||||
## 17. 测试要求
|
||||
|
||||
### 17.1 单元测试
|
||||
|
||||
- HMAC 正确、错误、过期和 Nonce 重放。
|
||||
- 商户编码与 ID 不匹配。
|
||||
- 所有列表过滤和分页。
|
||||
- 花种阶段链缺失、重复、成环和合法场景。
|
||||
- 内容每种状态动作的合法与非法转换。
|
||||
- 重复审核不重复增加评论数。
|
||||
- 举报各种状态和联动处置。
|
||||
- 跨商户 ID 查询和更新均返回不存在。
|
||||
|
||||
### 17.2 集成测试
|
||||
|
||||
- 使用真实 Cultivate 测试库验证全部接口。
|
||||
- 模拟 IM 用户、文件、通知和治理内部接口。
|
||||
- 验证事务失败不会留下半完成数据。
|
||||
- 验证操作日志中的商户、管理员和请求 ID 正确。
|
||||
- 验证接口响应不包含数据库信息和堆栈。
|
||||
|
||||
### 17.3 联调验收
|
||||
|
||||
1. Manager 不设置 `CULTIVATE_MYSQL_DSN` 可以正常启动。
|
||||
2. Manager 登录、IM 管理和工作台管理均正常。
|
||||
3. 养成概览、花种、内容审核、举报和操作日志页面可正常查询。
|
||||
4. 花种新增、编辑、上下架生效。
|
||||
5. 内容审核和举报处置生效,通知与审计记录正确。
|
||||
6. 停止 Cultivate Server 后,Manager 仍保持健康,仅养成页面提示“养成服务暂不可用”。
|
||||
7. Manager Pod 环境变量和 Secret 中不存在 Cultivate 数据库 DSN。
|
||||
|
||||
## 18. 交付清单
|
||||
|
||||
Cultivate Server 开发完成后需提供:
|
||||
|
||||
- 接口代码及单元测试。
|
||||
- OpenAPI 或等价接口定义。
|
||||
- 内部 HMAC 鉴权说明。
|
||||
- 测试环境服务地址。
|
||||
- 联调使用的服务名和 Secret 键名,不提供明文 Secret。
|
||||
- 数据迁移说明,如无迁移需明确说明。
|
||||
- 一份 curl 签名测试脚本或 Go 测试客户端。
|
||||
|
||||
上述内容齐备后,再开始 Manager 侧切换和删除直连代码。
|
||||
Reference in New Issue
Block a user