feat(circle): 添加酒友圈社交模块

- 新增酒友圈、圈子详情、发布动态、好友列表、活动创建等页面配置
- 添加酒友圈相关API接口,包括动态、评论、好友、活动等功能
- 实现模拟数据用于前端开发和测试
- 创建评论项、空状态、活动卡片、动态卡片、好友项等组件
- 编写酒友圈模块详细的接口文档,定义数据结构和业务规则
This commit is contained in:
cg
2026-07-20 22:53:04 +08:00
parent 9f5c69e1e5
commit fabbb167cf
18 changed files with 3992 additions and 18 deletions
+675
View File
@@ -0,0 +1,675 @@
# 干杯日记 - 酒友圈模块接口文档
> 版本: v1.0
> 日期: 2026-07-20
> 模块: 酒友圈(社交模块)
> 小程序: 干杯日记(uni-app 微信小程序)
> 基础URL: `https://your-domain.com/api/v1`
---
## 1. 模块概述
酒友圈是干杯日记的核心社交模块,包含三大子功能:
| 子功能 | 说明 |
|--------|------|
| 动态信息流 | 酒友发布饮酒动态,支持点赞、评论 |
| 酒友管理 | 好友请求、酒友列表、搜索、删除 |
| 约酒活动 | 发起酒局、报名、取消、签到 |
**前端当前状态:** 所有接口已在前端 `common/api.js` 中预留调用方法,暂用 Mock 数据。后端实现后前端仅需切换调用方式,无需改动页面逻辑。**请严格按照本文档的字段名和数据结构实现**,确保前端零改动对接。
---
## 2. 通用约定
### 2.1 鉴权
- 请求头:`Authorization: Bearer {token}`
- 所有接口均需登录态
### 2.2 统一响应格式
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
### 2.3 错误码
| code | 含义 |
|------|------|
| 0 | 成功 |
| 400 | 参数错误 |
| 401 | 未登录/token过期 |
| 403 | 无权限(如:非酒友关系、非发起人) |
| 404 | 资源不存在 |
| 409 | 冲突(如:重复报名、已是酒友) |
| 500 | 服务器错误 |
### 2.4 时间格式
- 所有时间字段使用 ISO 8601`2026-07-20T22:30:00.000Z`
- 前端会自行转换为相对时间("2小时前"等)
---
## 3. 动态信息流
### 3.1 获取动态列表
```
GET /circle/feeds
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码,默认1 |
| pageSize | int | 否 | 每页数量,默认10 |
**响应 data**
```json
{
"list": [
{
"id": "feed001",
"userId": "user_002",
"nickname": "老张",
"avatar": "https://xxx/avatar.jpg",
"time": "2026-07-20T20:30:00.000Z",
"text": "今晚和几个老兄弟整了瓶飞天,配重庆火锅,巴适得很!",
"drinks": [
{ "category": "baijiu", "name": "飞天茅台53度", "amount": "100ml" }
],
"feeling": "tipsy",
"images": ["https://xxx/img1.jpg"],
"likes": 12,
"liked": false,
"comments": 3
}
],
"total": 50,
"hasMore": true
}
```
**Feed 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 动态ID |
| userId | string | 发布者用户ID |
| nickname | string | 发布者昵称 |
| avatar | string | 发布者头像URL(可为空字符串) |
| time | string | 发布时间 ISO 8601 |
| text | string | 动态文字内容 |
| drinks | DrinkTag[] | 关联酒水标签(见下方结构) |
| feeling | string/null | 感受IDsober/buzzed/tipsy/drunk |
| images | string[] | 配图URL数组(最多9张) |
| likes | int | 点赞数 |
| liked | boolean | 当前用户是否已点赞 |
| comments | int | 评论数 |
**DrinkTag 结构:**
| 字段 | 类型 | 说明 |
|------|------|------|
| category | string | 酒类IDbaijiu/beer/wine/whisky/huangjiu/sake/fruit/cocktail |
| name | string | 酒名(如"飞天茅台53度" |
| amount | string | 用量展示文本(如"100ml"、"3瓶" |
**业务规则:**
- 仅返回当前用户**酒友**发布的动态(visibility=friends+ 公开动态(visibility=public
- 按发布时间倒序
- 自己的动态也包含在内
---
### 3.2 发布动态
```
POST /circle/feeds
```
**请求参数:**
```json
{
"text": "今晚小酌一杯",
"images": ["https://xxx/img1.jpg"],
"recordId": "record_1689234567890",
"visibility": "friends"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| text | string | 否 | 文字内容(最长500字),text和images至少一项非空 |
| images | string[] | 否 | 图片URL数组(最多9张,先调上传接口获取URL) |
| recordId | string/null | 否 | 关联的饮酒记录ID,传入时自动提取酒水信息生成drinks标签 |
| visibility | string | 否 | 可见范围:friends(默认)/private |
**响应 data**
```json
{
"success": true,
"feedId": "feed_1689234567890"
}
```
**业务规则:**
- 传入 recordId 时,后端从记录中提取 drinks 信息填充到动态的 drinks 字段
- UGC 内容(text)需接入微信内容安全审核(msgSecCheck
---
### 3.3 点赞动态
```
POST /circle/feeds/:id/like
```
**响应 data**
```json
{ "success": true }
```
### 3.4 取消点赞
```
POST /circle/feeds/:id/unlike
```
**响应 data**
```json
{ "success": true }
```
> 重复点赞/取消应幂等处理,不报错。
---
### 3.5 获取动态评论
```
GET /circle/feeds/:id/comments
```
**响应 data**
```json
{
"list": [
{
"id": "c001",
"userId": "user_003",
"nickname": "酒仙李白",
"avatar": "https://xxx/avatar3.jpg",
"content": "茅台配火锅,土豪啊老张!",
"time": "2026-07-20T21:00:00.000Z"
}
]
}
```
**Comment 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 评论ID |
| userId | string | 评论者ID |
| nickname | string | 评论者昵称 |
| avatar | string | 评论者头像 |
| content | string | 评论内容 |
| time | string | 评论时间 ISO 8601 |
> 按时间正序返回(最早在前)。
---
### 3.6 发表评论
```
POST /circle/feeds/:id/comments
```
**请求参数:**
```json
{ "content": "看着就馋了,下次带上我" }
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| content | string | 是 | 评论内容(最长200字) |
**响应 data**
```json
{
"success": true,
"comment": {
"id": "c_1689234567890",
"content": "看着就馋了,下次带上我",
"time": "2026-07-20T22:00:00.000Z"
}
}
```
**业务规则:**
- 评论需接入内容安全审核
- 评论成功后,动态的 comments 计数 +1
---
## 4. 酒友管理
### 4.1 获取酒友列表
```
GET /circle/friends
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| keyword | string | 否 | 按昵称模糊搜索 |
**响应 data**
```json
{
"list": [
{
"id": "user_002",
"nickname": "老张",
"avatar": "https://xxx/avatar.jpg",
"lastDrink": "昨晚喝了飞天茅台",
"lastDrinkTime": "2026-07-20T20:30:00.000Z",
"online": true
}
]
}
```
**Friend 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 酒友用户ID |
| nickname | string | 昵称 |
| avatar | string | 头像URL |
| lastDrink | string | 最近一次饮酒摘要文本(如"昨晚喝了飞天茅台"),无记录则为空 |
| lastDrinkTime | string | 最近饮酒时间 ISO 8601 |
| online | boolean | 是否在线(24小时内活跃视为在线) |
---
### 4.2 获取好友请求列表
```
GET /circle/friend-requests
```
**响应 data**
```json
{
"list": [
{
"id": "req001",
"userId": "user_101",
"nickname": "夜猫子",
"avatar": "https://xxx/avatar101.jpg",
"message": "一起喝过酒,加个好友",
"time": "2026-07-20T21:00:00.000Z"
}
]
}
```
> 仅返回**待处理**的请求(status=pending),按时间倒序。
---
### 4.3 发送好友请求
```
POST /circle/friend-requests
```
**请求参数:**
```json
{
"userId": "user_101",
"message": "一起喝过酒,加个好友"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| userId | string | 是 | 目标用户ID |
| message | string | 否 | 验证消息(最长50字) |
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 已是酒友返回 409
- 已有待处理请求则幂等返回成功
---
### 4.4 接受好友请求
```
POST /circle/friend-requests/:id/accept
```
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 接受后双方建立酒友关系
- 请求状态更新为 accepted
---
### 4.5 删除酒友
```
DELETE /circle/friends/:userId
```
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 双向解除酒友关系
- 删除后对方动态不再出现在自己的信息流中
---
## 5. 约酒活动(酒局)
### 5.1 获取酒局列表
```
GET /circle/events
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| status | string | 否 | 状态筛选:open/ongoing/ended,不传返回全部 |
**响应 data**
```json
{
"list": [
{
"id": "evt001",
"title": "周五夜·老地方火锅局",
"organizer": {
"id": "user_002",
"nickname": "老张",
"avatar": "https://xxx/avatar.jpg"
},
"time": "2026-07-25T19:00:00.000Z",
"location": "渝味晓宇火锅(解放碑店)",
"maxPeople": 8,
"joined": 5,
"participants": [
{ "id": "user_002", "nickname": "老张", "avatar": "https://xxx/avatar.jpg" }
],
"status": "open",
"note": "自带酒水,AA制餐费",
"isJoined": false,
"isOrganizer": false
}
]
}
```
**Event 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 酒局ID |
| title | string | 酒局名称(最长30字) |
| organizer | UserInfo | 发起人信息 |
| time | string | 开始时间 ISO 8601 |
| location | string | 地点文本 |
| maxPeople | int | 人数上限(2-50 |
| joined | int | 已报名人数(含发起人) |
| participants | UserInfo[] | 参与者列表 |
| status | string | 状态:open(报名中)/ongoing(进行中)/ended(已结束) |
| note | string | 备注 |
| isJoined | boolean | 当前用户是否已报名 |
| isOrganizer | boolean | 当前用户是否为发起人 |
**状态流转规则(后端自动维护):**
- `open`:当前时间 < 开始时间
- `ongoing`:开始时间 <= 当前时间 < 开始时间+6小时
- `ended`:当前时间 >= 开始时间+6小时
**可见范围:** 仅酒友可见(同动态信息流规则)
---
### 5.2 获取酒局详情
```
GET /circle/events/:id
```
**响应 data** 同 5.1 中单个 Event 结构
---
### 5.3 发起酒局
```
POST /circle/events
```
**请求参数:**
```json
{
"title": "周五夜·老地方火锅局",
"time": "2026-07-25T19:00:00.000Z",
"location": "渝味晓宇火锅(解放碑店)",
"maxPeople": 8,
"note": "自带酒水,AA制餐费"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| title | string | 是 | 酒局名称(最长30字) |
| time | string | 是 | 开始时间(必须为未来时间) |
| location | string | 是 | 地点(最长50字) |
| maxPeople | int | 是 | 人数上限(2-50 |
| note | string | 否 | 备注(最长200字) |
**响应 data**
```json
{
"success": true,
"eventId": "evt_1689234567890"
}
```
**业务规则:**
- 发起人自动算作已报名(joined 初始为1)
- 发起人 isOrganizer = true
---
### 5.4 报名酒局
```
POST /circle/events/:id/join
```
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 人数已满返回 409
- 酒局非 open 状态返回 403
- 重复报名幂等处理
---
### 5.5 取消报名
```
POST /circle/events/:id/quit
```
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 发起人不可取消报名(返回 403
- 未报名状态幂等处理
---
### 5.6 酒局签到
```
POST /circle/events/:id/checkin
```
**响应 data**
```json
{ "success": true }
```
**业务规则:**
- 仅 ongoing 状态可签到(返回 403
- 仅已报名用户可签到
- 签到后可在参与者信息中标记 checkedIn(可选扩展)
---
## 6. 数据库表设计参考
### 6.1 酒友关系表 (friendships)
```sql
CREATE TABLE friendships (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
friend_id VARCHAR(32) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_user_friend (user_id, friend_id),
INDEX idx_friend (friend_id)
);
```
> 双向关系存两条记录(A->B 和 B->A),便于查询。
### 6.2 好友请求表 (friend_requests)
```sql
CREATE TABLE friend_requests (
id VARCHAR(32) PRIMARY KEY,
from_user_id VARCHAR(32) NOT NULL,
to_user_id VARCHAR(32) NOT NULL,
message VARCHAR(64),
status ENUM('pending', 'accepted', 'rejected') DEFAULT 'pending',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_to_status (to_user_id, status),
INDEX idx_from_to (from_user_id, to_user_id)
);
```
### 6.3 动态表 (feeds)
```sql
CREATE TABLE feeds (
id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
text VARCHAR(512),
drinks JSON,
feeling VARCHAR(16),
images JSON,
record_id VARCHAR(32),
visibility ENUM('friends', 'private') DEFAULT 'friends',
like_count INT DEFAULT 0,
comment_count INT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_time (user_id, created_at),
INDEX idx_created (created_at)
);
```
### 6.4 评论表 (comments)
```sql
CREATE TABLE comments (
id VARCHAR(32) PRIMARY KEY,
feed_id VARCHAR(32) NOT NULL,
user_id VARCHAR(32) NOT NULL,
content VARCHAR(256) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_feed (feed_id, created_at)
);
```
### 6.5 酒局表 (events)
```sql
CREATE TABLE events (
id VARCHAR(32) PRIMARY KEY,
organizer_id VARCHAR(32) NOT NULL,
title VARCHAR(64) NOT NULL,
start_time DATETIME NOT NULL,
location VARCHAR(128) NOT NULL,
max_people INT NOT NULL DEFAULT 6,
joined_count INT NOT NULL DEFAULT 1,
note VARCHAR(256),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_organizer (organizer_id),
INDEX idx_start_time (start_time)
);
```
### 6.6 酒局报名表 (event_participants)
```sql
CREATE TABLE event_participants (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
event_id VARCHAR(32) NOT NULL,
user_id VARCHAR(32) NOT NULL,
checked_in TINYINT(1) DEFAULT 0,
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_event_user (event_id, user_id),
INDEX idx_user (user_id)
);
```
---
## 7. 前端对接说明
### 7.1 前端调用位置
所有接口调用集中在 `common/api.js`,当前为 Mock 实现,方法签名如下:
```js
// 动态
GetCircleFeeds({ page, pageSize }) // GET /circle/feeds
PublishFeed({ text, images, recordId, visibility }) // POST /circle/feeds
LikeFeed({ feedId }) // POST /circle/feeds/:id/like
UnlikeFeed({ feedId }) // POST /circle/feeds/:id/unlike
GetFeedComments({ feedId }) // GET /circle/feeds/:id/comments
AddComment({ feedId, content }) // POST /circle/feeds/:id/comments
// 酒友
GetFriends({ keyword }) // GET /circle/friends
GetFriendRequests() // GET /circle/friend-requests
SendFriendRequest({ userId }) // POST /circle/friend-requests
AcceptFriendRequest({ requestId }) // POST /circle/friend-requests/:id/accept
RemoveFriend({ userId }) // DELETE /circle/friends/:userId
// 酒局
GetEvents({ status }) // GET /circle/events
GetEventDetail({ eventId }) // GET /circle/events/:id
CreateEvent({ title, time, location, maxPeople, note }) // POST /circle/events
JoinEvent({ eventId }) // POST /circle/events/:id/join
QuitEvent({ eventId }) // POST /circle/events/:id/quit
CheckInEvent({ eventId }) // POST /circle/events/:id/checkin
```
### 7.2 响应解包约定
前端 SDK 会自动解包 `data.data`,因此接口实际返回给前端的结构为:
```json
{ "code": 0, "data": { ... } }
```
前端拿到的就是 `data` 内的对象。
### 7.3 注意事项
1. **字段名严格一致**:前端已按本文档字段名渲染,请勿更改命名(如 `likes` 不能改为 `likeCount`
2. **头像可为空**:avatar 字段允许空字符串,前端有默认占位头像
3. **时间格式**:统一 ISO 8601,前端自行格式化
4. **内容安全**text、content、title、note 等 UGC 字段需接入微信 msgSecCheck 审核
5. **幂等性**:点赞/取消、报名/取消等操作需幂等,避免前端重试导致计数错误