518 lines
18 KiB
Markdown
518 lines
18 KiB
Markdown
# 朋友圈设计
|
||
|
||
> 对照:微信「发现 → 朋友圈」
|
||
> 形态:Spark IM 模块化单体内的独立业务,不进会话、不走 `messages`
|
||
> 状态:v1 HTTP 已落地(发帖/Feed/相册/赞评/通知/设置)。未做 TCP `MOMENT_PUSH`。
|
||
> 硬约束:遵守 `docs/architecture-v3.md` C1–C7。不上 Kafka、K8s、微服务、ConversationSeq。
|
||
|
||
朋友圈是好友之间的时间线,不是群动态,也不是聊天消息。帖子用独立表、独立 REST、独立对象前缀。Snowflake `post_id` 是主键、排序键和翻页游标,与聊天的 C1 同构,互不混用。
|
||
|
||
---
|
||
|
||
# 0. 范围
|
||
|
||
## 0.1 做(对齐微信核心)
|
||
|
||
| 微信 | Spark |
|
||
| --- | --- |
|
||
| 只对好友,不对群 | 双向好友可见;拉黑任一侧不可见 |
|
||
| 文字 / 最多 9 图 / **或** 1 条视频 | 同样互斥;图允许 jpeg / png / gif |
|
||
| 可见范围:公开好友、私密、部分可见、不给谁看 | 同四个;v1 用 uid 列表,**不做好友标签** |
|
||
| 不看他 / 不让他看 | 独立有向边,和拉黑分开 |
|
||
| 允许朋友查看范围:最近 3 天 / 1 个月 / 半年 / 全部 | 作者侧设置;Feed 与相册都裁 |
|
||
| 点赞、评论、回复 | 有;评论按「共同好友」过滤 |
|
||
| 封面、发现页红点、赞评消息列表 | 封面 + 未读红点 + 互动通知 |
|
||
| 随时删除自己的动态 | 作者软删;删帖后删对象 |
|
||
|
||
## 0.2 不做
|
||
|
||
- 好友标签选人、转载公众号/链接、@好友、视频号、直播
|
||
- 「仅聊天」好友类型(Spark 尚无此关系)
|
||
- 把动态写进 `messages` / 会话 inbox / `SYNC_INBOX`
|
||
- 写扩散:给每个好友插一行 MySQL inbox(违反 C7 精神,也无 Kafka 可扛)
|
||
- 新动态推送给全部好友(微信也不把发帖当聊天 `MESSAGE`)
|
||
- 收藏朋友圈(`favorites` 只收藏聊天;媒体不从 `chat/` 拷过来)
|
||
|
||
## 0.3 和 V3 的关系
|
||
|
||
| 约束 | 朋友圈怎么守 |
|
||
| --- | --- |
|
||
| C1 | 聊天仍是 `messages` + `MsgId`。朋友圈是 `moment_posts.id`,**没有 ConversationSeq** |
|
||
| C2 | 发帖/赞评在 HTTP → Application。gnet EventLoop 不碰朋友圈 |
|
||
| C3 | 无新握手。可选 TCP `MOMENT_PUSH` 仍是薄 Frame + JSON 信封 |
|
||
| C4 | 仍走 `sparkapi` HTTPS。不新开公网端口 |
|
||
| C5 | 发帖不是 `SEND_ACK`。落库成功即 HTTP 200。赞评若要实时,COMMIT 后再 `deliver:queue` Push |
|
||
| C6 | 发帖/赞评限流用 `reqctx.RealIP` |
|
||
| C7 | 朋友圈红点**不**进会话 ZSET。未读用 `moment_settings.last_read_post_id` |
|
||
|
||
HTTP 与(若加)TCP 共用 `internal/module/moment/logic`。Gin handler 只绑参、写信封。`logic` 不 import `model`。
|
||
|
||
---
|
||
|
||
# 1. 总览
|
||
|
||
```text
|
||
客户端
|
||
│ HTTPS /v1/moments
|
||
│ POST /v1/files (moment_image / moment_video / moment_cover)
|
||
▼
|
||
Gin → moment/logic
|
||
│
|
||
├─ MySQL 帖 / 可见名单 / 赞 / 评 / 设置 / 通知
|
||
├─ RustFS perm/moment/{image|video|cover}/...
|
||
└─ 可选 RPUSH deliver:queue (仅赞、评、回复)
|
||
│
|
||
Deliver Worker
|
||
│
|
||
在线设备 MOMENT_PUSH
|
||
不写会话列表、不写 mailbox_events
|
||
```
|
||
|
||
实时靠连接只服务「有人赞/评了我」;时间线正文靠 MySQL;媒体只存 object_key。
|
||
|
||
---
|
||
|
||
# 2. 模块与目录
|
||
|
||
新模块 `internal/module/moment`,三层拆分与现有业务一致。
|
||
|
||
```text
|
||
internal/module/moment/
|
||
├── entity/ 帖、可见性枚举、content JSON、设置
|
||
├── model/ 一表一文件(GORM + COMMENT)
|
||
└── logic/ 一功能一文件:publish、feed、album、like、comment、notice、settings
|
||
internal/transport/http/handler/moment/
|
||
migrations/00xx_moments.sql
|
||
```
|
||
|
||
REST 分组:`/v1/moments`。OpenAPI tag:`moments`。
|
||
上传 purpose 扩在 `upload/entity`,不在 moment 里自己签 URL。
|
||
|
||
依赖(只通过 interface,bootstrap 注入):
|
||
|
||
| 依赖 | 用途 |
|
||
| --- | --- |
|
||
| `friend` | `AreFriends`、`ListFriends`、`BlockedEither` |
|
||
| `upload` | 校验 object_key 前缀;不代签 |
|
||
| `user` | 展示昵称、头像 |
|
||
| `id.Generator` | `post_id` / `comment_id` / `notice_id` |
|
||
| `objectstore` | 删帖 / 换封面时 `Delete` |
|
||
| `deliver` | 赞评 Push(可后做) |
|
||
|
||
---
|
||
|
||
# 3. 数据模型
|
||
|
||
主键一律 Snowflake,不用 AUTO_INCREMENT 业务 id。表和列必须有 COMMENT。时间展示用 `created_at`,**排序和游标用 `id`**。
|
||
|
||
## 3.1 `moment_posts`
|
||
|
||
| 列 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `id` | BIGINT PK | Snowflake。Feed / 相册游标 |
|
||
| `user_id` | BIGINT | 作者 `users.id` |
|
||
| `kind` | SMALLINT | 1 纯文 2 图 3 视频 |
|
||
| `text` | VARCHAR(1000) | 可空;与媒体至少有一项 |
|
||
| `content` | BLOB | 图/视频 JSON 字节,不存媒体二进制 |
|
||
| `poi_lat` / `poi_lng` | DOUBLE | 可选 |
|
||
| `poi_name` / `poi_address` | VARCHAR | 可选地点文案 |
|
||
| `visibility` | SMALLINT | 0 好友公开 1 私密 2 白名单 3 黑名单 |
|
||
| `status` | SMALLINT | 0 正常 1 已删 |
|
||
| `like_count` | INT | 事务内维护 |
|
||
| `comment_count` | INT | 事务内维护(只计未删评论) |
|
||
| `created_at` | DATETIME(3) | 展示与 `visible_days` 裁剪 |
|
||
|
||
索引:
|
||
|
||
- `PRIMARY KEY (id)`
|
||
- `KEY idx_user_id (user_id, id)` — 相册
|
||
|
||
不建「全表时间线」二级索引。Feed 用 `id < cursor AND user_id IN (好友列表)`。
|
||
|
||
## 3.2 `moment_audience`
|
||
|
||
仅 `visibility` 为 2 或 3 时有行。
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `post_id` | 帖 id |
|
||
| `peer_id` | 好友 uid |
|
||
| PK | `(post_id, peer_id)` |
|
||
|
||
白名单:列出**能看**的人。黑名单:列出**不能看**的人(其余好友能看)。单帖最多 **200** 人,且必须是发帖时的双向好友。
|
||
|
||
## 3.3 `moment_likes`
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `post_id` | 帖 id |
|
||
| `user_id` | 点赞人 |
|
||
| `created_at` | DATETIME(3) |
|
||
| PK | `(post_id, user_id)` |
|
||
|
||
重复 PUT 幂等。取消赞删行并减 `like_count`。
|
||
|
||
## 3.4 `moment_comments`
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `id` | Snowflake PK |
|
||
| `post_id` | 帖 id |
|
||
| `user_id` | 评论人 |
|
||
| `reply_to_id` | 父评论 id,0 表示直接评帖 |
|
||
| `reply_to_uid` | 被回复的用户,方便通知 |
|
||
| `text` | VARCHAR(500),必填 |
|
||
| `status` | 0 正常 1 已删 |
|
||
| `created_at` | DATETIME(3) |
|
||
|
||
索引:`KEY idx_post (post_id, id)`。
|
||
|
||
## 3.5 `moment_notices`
|
||
|
||
互动通知,**不是**聊天 mailbox。
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `id` | Snowflake PK,列表游标 |
|
||
| `to_user_id` | 接收人 |
|
||
| `from_user_id` | 动作发起人 |
|
||
| `kind` | 1 赞 2 评论 3 回复 |
|
||
| `post_id` | 帖 |
|
||
| `comment_id` | 评论,赞则为 0 |
|
||
| `read_at` | 可空 |
|
||
| `created_at` | DATETIME(3) |
|
||
|
||
索引:`KEY idx_to_user (to_user_id, id)`。
|
||
自己赞/评自己的帖不写通知。
|
||
|
||
## 3.6 `moment_settings`
|
||
|
||
每用户一行。无行则用默认:无封面、可见范围为全部、`last_read_post_id=0`。
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `user_id` | PK |
|
||
| `cover_key` | `perm/moment/cover/...`,空表示未设 |
|
||
| `visible_days` | 0 全部;3 / 30 / 180 |
|
||
| `last_read_post_id` | 发现页红点水位 |
|
||
| `updated_at` | DATETIME(3) |
|
||
|
||
## 3.7 `moment_hides`
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| `user_id` | 设置方 |
|
||
| `peer_id` | 对方 |
|
||
| `hide_him` | 不看他(我的 Feed 去掉他) |
|
||
| `hide_me` | 不让他看(他拉我的 Feed/相册时当看不见) |
|
||
| PK | `(user_id, peer_id)` |
|
||
|
||
拉黑仍走 `friend_relations.blocked` / `BlockedEither`,不在本表重复。
|
||
|
||
## 3.8 枚举(entity + OpenAPI `$ref` + i18n)
|
||
|
||
| 名 | 值 |
|
||
| --- | --- |
|
||
| `MomentKind` | 1 text、2 image、3 video |
|
||
| `MomentVisibility` | 0 friends、1 private、2 include、3 exclude |
|
||
| `MomentStatus` | 0 normal、1 deleted |
|
||
| `MomentNoticeKind` | 1 like、2 comment、3 reply |
|
||
| `MomentVisibleDays` | 0 / 3 / 30 / 180 |
|
||
|
||
---
|
||
|
||
# 4. 可见性
|
||
|
||
## 4.1 读时过滤(v1 选定)
|
||
|
||
好友量按几百计。发帖只写作者一行(加可选 `moment_audience`),**不**给每个好友写 inbox。
|
||
|
||
原因:
|
||
|
||
- 解绑、改「不看他」、改可见天数、改单帖可见范围,读时立刻生效
|
||
- 写扩散要在解绑时扫 inbox,且 V3 禁止 Kafka
|
||
- `IN (friend_ids)` + `id` 游标对当前规模足够
|
||
|
||
观众 V 看作者 A 的帖 P,须同时满足:
|
||
|
||
1. `P.status = normal`
|
||
2. `A == V` → 可见(含私密、含超出 `visible_days` 的旧帖)
|
||
3. 否则:双向好友,且 `BlockedEither(A,V) == false`
|
||
4. V 没有对 A 设 `hide_him`;A 没有对 V 设 `hide_me`
|
||
5. 非本人时:按 A 的 `visible_days` 裁 `P.created_at`(0 表示不裁)
|
||
6. `visibility`:
|
||
- 0 好友公开 → 过
|
||
- 1 私密 → 否
|
||
- 2 白名单 → `moment_audience` 含 V
|
||
- 3 黑名单 → `moment_audience` 不含 V
|
||
|
||
非好友访问他人相册:**404**,不返回「有朋友圈但不能看」。
|
||
|
||
## 4.2 Feed 查询
|
||
|
||
```text
|
||
ids = ListFriends(me) ∪ {me}
|
||
ids -= { peer | hide_him(me, peer) }
|
||
SELECT * FROM moment_posts
|
||
WHERE id < cursor AND user_id IN (ids) AND status = 0
|
||
ORDER BY id DESC
|
||
LIMIT overfetch -- 建议 50
|
||
```
|
||
|
||
内存套用 4.1。返回 `limit`(默认 20,最大 50)。过滤后不足则再往下翻,**单次请求最多 3 轮 overfetch**,避免大量私密帖拖死接口。下一页 `cursor` 为本页最后一条**已返回**的 `post_id`。
|
||
|
||
## 4.3 相册
|
||
|
||
`GET /v1/moments/users/:uid`
|
||
|
||
- `uid == me`:只按 `status` 过滤,不过可见天数,能看到自己的私密帖
|
||
- 否则:必须过 4.1;游标同样是 `post_id`
|
||
|
||
## 4.4 赞、评对谁可见
|
||
|
||
V 必须先能看见该帖。
|
||
|
||
评论对 V 可见,当且仅当:V 是帖作者,或 V 是评论人,或 V 与评论人双向好友。
|
||
|
||
点赞列表同样过滤:帖作者看见全部赞;其他人只看见自己的赞 + 共同好友的赞。
|
||
|
||
这是微信「共同好友才看得到这条互动」的简化版,不做「好友的好友」更远的扩散。
|
||
|
||
---
|
||
|
||
# 5. 发帖与媒体
|
||
|
||
## 5.1 客户端顺序
|
||
|
||
```text
|
||
1. POST /v1/files purpose=moment_image|moment_video|moment_cover
|
||
2. Presigned PUT(Content-Type / Content-Length / If-None-Match: *)
|
||
3. POST /v1/moments
|
||
4. 服务端校验 key 前缀、数量、互斥、audience 都是好友
|
||
5. Snowflake id → INSERT
|
||
6. 信封返回帖视图
|
||
```
|
||
|
||
客户端不自选 object_key。不占用聊天 `client_msg_id`。发帖失败可换新凭证重试;已 PUT 的孤儿对象暂不扫(与当前头像策略相同,删帖路径会删)。
|
||
|
||
## 5.2 内容规则
|
||
|
||
| 规则 | 值 |
|
||
| --- | --- |
|
||
| 文本 | 0–1000 字;可与媒体同时有 |
|
||
| 图 | 1–9;不可同时带视频 |
|
||
| 视频 | 0 或 1;不可同时带图 |
|
||
| 纯文本 | 允许(无媒体) |
|
||
| 空正文且无媒体 | 拒绝 |
|
||
| 地点 | 可选;有 lat 必须有 lng |
|
||
| 白/黑名单 | 仅 visibility 2/3;人数 1–200;必须双向好友且不含自己 |
|
||
| 私密 | 禁止带 audience |
|
||
|
||
`kind` 由服务端根据媒体推断,客户端不必传对。
|
||
|
||
## 5.3 object_key
|
||
|
||
路径不含 uid。文件名 16 位 base62(12 字节随机,`reqctx.NewObjectName`)。
|
||
|
||
| purpose | key | 生命周期 |
|
||
| --- | --- | --- |
|
||
| `moment_image` | `perm/moment/image/{yyyyMMdd}/{name}.{ext}` | 永久;删帖删除 |
|
||
| `moment_video` | `perm/moment/video/{yyyyMMdd}/{name}.{ext}` | 同上 |
|
||
| `moment_cover` | `perm/moment/cover/{yyyyMMdd}/{name}.{ext}` | 换封面删除旧对象 |
|
||
|
||
Lifecycle **不**扫 `perm/`(现有只过期 `chat/`)。朋友圈媒体与头像一样走永久前缀,但**删帖 / 换封面必须 `Delete`**,不把孤儿留给 bucket。
|
||
|
||
规格(v1):
|
||
|
||
| purpose | MIME | 大小 | 其它 |
|
||
| --- | --- | --- | --- |
|
||
| `moment_image` | image/jpeg、image/png、image/gif | ≤ 10MB | 九宫格 |
|
||
| `moment_video` | 与 `chat_video` 相同 | 与聊天视频上限相同 | 时长 ≤ 30s(逻辑校验 `duration_ms`) |
|
||
| `moment_cover` | jpeg / png | ≤ 2MB | 一张 |
|
||
|
||
`content` JSON 形状对齐聊天 `Image` / `Video`(`key`、`thumb_key`、`cover_key`、`w`、`h`、`size`、`duration_ms`),可多图为数组。禁止引用 `chat/` 或 `perm/avatar/` 等其它前缀。
|
||
|
||
发帖不把媒体从 `chat/` 复制到 `perm/moment/`。从聊天「转发到朋友圈」若以后要做,再走 copy,与收藏相同。
|
||
|
||
## 5.4 限流与审计
|
||
|
||
`reqctx.RealIP` + uid:建议 **2 条/分钟**、**20 条/天**。赞评另限,例如 60 次/分钟。超限 `errs.ErrRateLimited`。
|
||
|
||
## 5.5 删除
|
||
|
||
仅作者。`status=deleted`,Feed/相册/单条均当不存在。
|
||
随后异步删 `content` 与封面以外的对象;失败只打日志,不回滚软删。
|
||
赞评行可保留但对外不可见。通知仍可列出,点开单条返回 404。
|
||
|
||
无「2 分钟撤回」窗口,与微信删动态一致。
|
||
|
||
---
|
||
|
||
# 6. HTTP API
|
||
|
||
统一信封 `{code,msg,request_id,data}`。鉴权与其它 `/v1` 资源相同。
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
| --- | --- | --- |
|
||
| `POST` | `/v1/moments` | 发帖 |
|
||
| `GET` | `/v1/moments` | 好友 Feed。query:`cursor`(post_id,0 表示最新)、`limit` |
|
||
| `GET` | `/v1/moments/users/:uid` | 相册 |
|
||
| `GET` | `/v1/moments/:id` | 单条;不可见则 404 |
|
||
| `DELETE` | `/v1/moments/:id` | 作者删帖 |
|
||
| `PUT` | `/v1/moments/:id/like` | 点赞(幂等) |
|
||
| `DELETE` | `/v1/moments/:id/like` | 取消赞 |
|
||
| `POST` | `/v1/moments/:id/comments` | 评论或回复。body:`text`、`reply_to_id` |
|
||
| `DELETE` | `/v1/moments/:id/comments/:cid` | 帖作者或评论人可删 |
|
||
| `GET` | `/v1/moments/notices` | 赞评通知。`cursor`=`notice_id` |
|
||
| `POST` | `/v1/moments/notices/read` | 标已读;可带 `cursor` 表示已读到该 id |
|
||
| `GET` | `/v1/moments/unread` | `{has_new, latest_id}` 发现页红点 |
|
||
| `PUT` | `/v1/moments/read` | body:`post_id`,更新 `last_read_post_id`(只增不减) |
|
||
| `GET` | `/v1/moments/settings` | 封面、可见天数、不看列表 |
|
||
| `PATCH` | `/v1/moments/settings` | 部分更新 |
|
||
| `PUT` | `/v1/moments/settings/hides/:uid` | 设置对某人的 hide_him / hide_me |
|
||
| `DELETE` | `/v1/moments/settings/hides/:uid` | 清除对该人的两条隐藏 |
|
||
|
||
Feed / 相册 / 单条的 `data` 建议同一视图:
|
||
|
||
```json
|
||
{
|
||
"id": 0,
|
||
"user": { "uid": 0, "spark_id": "", "nickname": "", "avatar": "", "remark": "" },
|
||
"kind": 2,
|
||
"text": "",
|
||
"content": {},
|
||
"poi": { "lat": 0, "lng": 0, "name": "", "address": "" },
|
||
"visibility": 0,
|
||
"like_count": 0,
|
||
"comment_count": 0,
|
||
"liked": false,
|
||
"likes": [],
|
||
"comments": [],
|
||
"created_at": 0
|
||
}
|
||
```
|
||
|
||
`likes` / `comments` 已按 4.4 过滤。`remark` 为观众对该作者的好友备注,没有则空串。列表接口可只带赞/评摘要(例如赞 10 人、评 20 条),单条带全量过滤后列表。
|
||
|
||
`POST /v1/moments` 请求例:
|
||
|
||
```json
|
||
{
|
||
"text": "hello",
|
||
"images": [{ "key": "perm/moment/image/20260918/xxxx.jpg", "w": 1080, "h": 1080 }],
|
||
"video": null,
|
||
"visibility": 0,
|
||
"audience_uids": [],
|
||
"poi": { "lat": 31.2, "lng": 121.5, "name": "外滩" }
|
||
}
|
||
```
|
||
|
||
路由注意:静态路径(`/unread`、`/notices`、`/settings`、`/users/:uid`)必须注册在 `/:id` 之前。
|
||
|
||
---
|
||
|
||
# 7. 赞、评、通知、红点
|
||
|
||
## 7.1 赞
|
||
|
||
能看见帖的人可赞。`PUT` 已赞则原样返回。计数与 `moment_likes` 同一事务。
|
||
|
||
通知:`to_user_id = 作者`,`kind=like`,作者自己赞跳过。
|
||
|
||
## 7.2 评论
|
||
|
||
能看见帖的人可评。`reply_to_id != 0` 时:父评必须同帖、未删。
|
||
删评:软删,`comment_count-1`。子评仍在,展示层把父评当「已删除」。
|
||
|
||
通知:
|
||
|
||
- 评帖 → 作者(自己评自己跳过)
|
||
- 回复 → 被回复人;若被回复人不是作者,作者再收一条 `kind=comment`(与微信「有人评论了我的动态」一致)。同一动作不要给同一人写两行。
|
||
|
||
v1 **不**把「曾经赞过/评过该帖的其他人」全部再通知一遍。
|
||
|
||
## 7.3 发现页红点
|
||
|
||
```text
|
||
unread.has_new = Feed 上第一条「别人的」可见 post_id > last_read_post_id
|
||
```
|
||
|
||
打开 Feed 并成功返回后,客户端 `PUT /v1/moments/read` 带本页最大别人 `post_id`。服务端只允许水位前进。
|
||
|
||
这不是会话未读,禁止写入 `im:dialog:*`。
|
||
|
||
## 7.4 实时 Push(可后做)
|
||
|
||
HTTP 赞评 COMMIT 成功后 `DeliverQueue.Push`:
|
||
|
||
- `Job.MsgID` = `notice_id`(给 `deliver:done:{id}` 幂等用)
|
||
- `Cmd` = 新命令 `MOMENT_PUSH`(具体数值接现有 TCP 命令表末尾)
|
||
- `ToUserIDs` = 通知接收人
|
||
- `NotMailbox = true`、`NotPush` 按设备在线情况
|
||
- **不**更新会话 ZSET、**不**写 `mailbox_events`
|
||
|
||
离线用户下次 `GET /v1/moments/notices`。
|
||
**发帖本身不 Push。**
|
||
|
||
gnet EventLoop 只 `Submit`;解析和查库在 TaskPool(若走 TCP 命令)。首期可以没有 TCP 命令,仅 HTTP 拉通知。
|
||
|
||
---
|
||
|
||
# 8. 设置
|
||
|
||
`PATCH /v1/moments/settings`:
|
||
|
||
| 字段 | 规则 |
|
||
| --- | --- |
|
||
| `cover_key` | 必须是本人刚上传的 `perm/moment/cover/` 前缀;空串表示清除封面,并删旧对象 |
|
||
| `visible_days` | `oneof=0 3 30 180`;只影响**别人**看我,不影响我看自己 |
|
||
|
||
`PUT .../hides/:uid`:
|
||
|
||
```json
|
||
{ "hide_him": true, "hide_me": false }
|
||
```
|
||
|
||
对方必须是双向好友。拉黑后 Feed 已不可见,隐藏边可保留,解除拉黑后仍生效。
|
||
|
||
---
|
||
|
||
# 9. 和现有模块的边界
|
||
|
||
| 模块 | 朋友圈 |
|
||
| --- | --- |
|
||
| `message` / `conversation` / `inbox` / `sync` | 不引用、不写入 |
|
||
| `friend` | 好友与拉黑的唯一来源 |
|
||
| `upload` | 新 purpose 与 `BuildObjectKey` 分支 |
|
||
| `favorite` | 不收藏动态 |
|
||
| `user` | 封面不进 `users.avatar` |
|
||
| `deliver` / TCP | 仅互动 Push;发现页红点不走 Push |
|
||
| 对象存储 lifecycle | 仍只过期 `chat/` |
|
||
|
||
解绑:不必删帖。读路径 `AreFriends==false` 即不可见。对方相册 404。
|
||
|
||
---
|
||
|
||
# 10. 落地顺序
|
||
|
||
按垂直切片合入,每步可运行、有测试、有 OpenAPI。
|
||
|
||
| 步 | 内容 |
|
||
| --- | --- |
|
||
| 1 | migration + entity 枚举 + upload purpose / key |
|
||
| 2 | `POST/DELETE /v1/moments`,`GET` 单条(仅自己) |
|
||
| 3 | Feed、相册、4.1 可见性、`moment_audience`、hides、拉黑 |
|
||
| 4 | 赞、评、通知 HTTP、红点 / `last_read_post_id` |
|
||
| 5 | 封面、`visible_days`、删帖删对象、换封面删旧对象 |
|
||
| 6 | (可选)`MOMENT_PUSH` + deliver |
|
||
|
||
每步:`internal/pkg/i18n` 键、`GET /v1/meta/enums`、handler 薄、`make lint`。改了 `migrations/*.sql` 后在本机执行 `go run ./cmd/migrate -config configs/config.yaml`。
|
||
|
||
---
|
||
|
||
# 11. 以后才考虑
|
||
|
||
好友上万导致 `IN (ids)` 变慢时,再对**可见好友**做 Redis ZSET 写扩散(`moment:feed:{uid}` score=`post_id`),在 Application 里同步写,不引入 Kafka。解绑时 `ZREM`。那是压测之后的复杂度,不作为 v1。
|
||
|
||
举报复用 `friend` 举报或另表 `moment_reports`,产品要时再开。
|