Files
hejiu/docs/API接口文档.md
T
cg 9f5c69e1e5 feat(app): 项目重命名为干杯日记并优化UI设计
- 将应用名称从"喝了么"更改为"干杯日记"
- 更新所有相关文件中的品牌标识和描述信息
- 升级HaveADrink依赖包从1.0.3到1.0.4版本
- 在pages.json中新增分享个人资料页面配置
- 重构DrinkCard组件UI,增加装饰背景、酒类图标支持、饮酒感受徽章等功能
- 优化API错误处理逻辑,增加静默模式支持
- 修改白酒类别图标为 urn emoji,黄酒图标为 tea emoji
- 添加分享功能按钮并优化页面返回交互体验
- 引入新的工具函数用于获取酒类图标和路径判断
- 更新主题混入、常量定义等基础配置文件
- 调整全局样式和设计令牌以匹配新品牌形象
2026-07-20 22:12:27 +08:00

701 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 干杯日记 - 后端接口文档
> 版本: v1.0
> 日期: 2026-07-13
> 小程序: 干杯日记(uni-app 微信小程序)
> 基础URL: `https://your-domain.com/api/v1`
---
## 1. 通用说明
### 1.1 请求格式
- Content-Type: `application/json`
- 鉴权方式: 请求头 `Authorization: Bearer {token}`(微信登录获取)
- 所有时间字段使用 ISO 8601 格式
### 1.2 统一响应格式
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
| code | 含义 |
|------|------|
| 0 | 成功 |
| 400 | 参数错误 |
| 401 | 未登录/token过期 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 500 | 服务器错误 |
### 1.3 分页参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码,默认1 |
| pageSize | int | 否 | 每页数量,默认20 |
### 1.4 分页响应
```json
{
"code": 0,
"data": {
"list": [],
"total": 100,
"page": 1,
"pageSize": 20
}
}
```
---
## 2. 用户模块
### 2.1 微信登录
```
POST /auth/wechat-login
```
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| code | string | 是 | 微信登录codewx.login获取) |
| nickName | string | 否 | 用户昵称 |
| avatarUrl | string | 否 | 头像URL |
**响应 data**
```json
{
"token": "jwt_token_xxx",
"user": {
"id": "user_001",
"nickname": "酒友小王",
"avatar": "https://xxx/avatar.jpg",
"joinedAt": "2026-01-15T00:00:00.000Z",
"preferences": {
"favoriteCategories": ["baijiu", "beer"],
"frequency": "often"
}
},
"isNew": false
}
```
### 2.2 获取用户信息
```
GET /user/profile
```
**响应 data**
```json
{
"id": "user_001",
"nickname": "酒友小王",
"avatar": "https://xxx/avatar.jpg",
"joinedAt": "2026-01-15T00:00:00.000Z",
"preferences": {
"favoriteCategories": ["baijiu", "beer"],
"frequency": "often"
}
}
```
### 2.3 更新用户偏好
```
PUT /user/preferences
```
**请求参数:**
```json
{
"favoriteCategories": ["baijiu", "beer"],
"frequency": "often"
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| favoriteCategories | string[] | 偏好酒类ID列表 |
| frequency | string | 饮酒频率:rarely / sometimes / often |
---
## 3. 打卡记录模块(核心)
### 3.1 创建打卡记录
```
POST /records
```
**请求参数:**
```json
{
"date": "2026-07-13",
"mode": "drank",
"drinks": [
{
"category": "baijiu",
"brand": "茅台",
"product": "飞天茅台53度",
"amount": 100,
"unit": "ml",
"degree": 53,
"standardCups": 2.12
}
],
"food": {
"category": "hotpot",
"name": "重庆老火锅"
},
"feeling": "tipsy",
"photos": [],
"visibility": "friends",
"quote": "人生得意须尽欢,莫使金樽空对月"
}
```
**字段说明:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| date | string | 是 | 日期 YYYY-MM-DD |
| mode | string | 是 | 打卡模式:`drank`(喝了) / `abstain`(未喝) |
| drinks | DrinkItem[] | 否 | 酒水列表(mode=abstain时为空数组) |
| food | FoodInfo / null | 否 | 配餐信息 |
| feeling | string / null | 否 | 感受ID |
| photos | string[] | 否 | 照片URL数组(最多9张) |
| visibility | string | 否 | 可见范围:`public` / `friends` / `private`,默认 `friends` |
| quote | string | 否 | 酒言酒语(用户选择或自定义) |
**DrinkItem 结构:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| category | string | 是 | 酒类分类ID |
| brand | string | 是 | 品牌名称 |
| product | string | 否 | 产品名 |
| amount | number | 是 | 饮用量数值 |
| unit | string | 是 | 单位:ml / liang(两) / bottle(瓶) / cup(杯) / can(听) / shot |
| degree | number | 是 | 酒精度数(%) |
| standardCups | number | 否 | 标准杯数(前端计算,后端应校验) |
**FoodInfo 结构:**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| category | string | 是 | 配餐分类ID |
| name | string | 否 | 具体菜名 |
**响应 data**
```json
{
"id": "record_1689234567890",
"date": "2026-07-13",
"mode": "drank",
"drinks": [...],
"food": {...},
"feeling": "tipsy",
"photos": [],
"visibility": "friends",
"quote": "...",
"standardCupsTotal": 2.12,
"createdAt": "2026-07-13T22:30:00.000Z"
}
```
> **标准杯计算公式:**
> `标准杯 = (饮用量ml × 酒精度数% × 0.8) / 10`
> 1标准杯 = 10g纯酒精
> **单位换算为ml**
> | 单位 | 换算 |
> |------|------|
> | ml | ×1 |
> | liang(两) | ×50 |
> | bottle(瓶) | ×500 |
> | cup(杯) | ×150 |
> | can(听) | ×330 |
> | shot | ×30 |
### 3.2 获取记录列表
```
GET /records
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码 |
| pageSize | int | 否 | 每页数量 |
| month | string | 否 | 月份筛选 YYYY-MM |
| mode | string | 否 | 打卡模式筛选 drank/abstain |
**响应 data**
```json
{
"list": [
{
"id": "record_xxx",
"date": "2026-07-13",
"mode": "drank",
"drinks": [...],
"food": {...},
"feeling": "tipsy",
"photos": [],
"visibility": "friends",
"standardCupsTotal": 2.12,
"createdAt": "2026-07-13T22:30:00.000Z"
}
],
"total": 30,
"page": 1,
"pageSize": 20
}
```
### 3.3 获取单条记录详情
```
GET /records/:id
```
**响应 data** 同 3.1 创建记录的返回结构
### 3.4 删除记录
```
DELETE /records/:id
```
### 3.5 获取日历打卡数据
```
GET /records/calendar
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| year | int | 是 | 年份 |
| month | int | 是 | 月份(1-12) |
**响应 data**
```json
{
"year": 2026,
"month": 7,
"days": {
"2026-07-01": { "mode": "drank", "standardCupsTotal": 2.5 },
"2026-07-02": { "mode": "abstain", "standardCupsTotal": 0 },
"2026-07-03": null
}
}
```
> 前端根据此数据渲染日历打卡状态。null表示当天无记录。
---
## 4. 照片上传
### 4.1 上传照片
```
POST /upload/photo
Content-Type: multipart/form-data
```
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| file | File | 是 | 图片文件(jpg/png,最大10MB |
| recordId | string | 否 | 关联记录ID(可选,用于后续关联) |
**响应 data**
```json
{
"url": "https://cdn.xxx.com/photos/2026/07/abc123.jpg",
"width": 1080,
"height": 1920,
"size": 256000
}
```
### 4.2 批量上传
```
POST /upload/photos
Content-Type: multipart/form-data
```
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| files | File[] | 是 | 图片文件数组(最多9张) |
**响应 data**
```json
{
"photos": [
{ "url": "https://...", "width": 1080, "height": 1920, "size": 256000 },
...
]
}
```
---
## 5. 统计模块
### 5.1 获取用户统计概览
```
GET /stats/overview
```
**响应 data**
```json
{
"weekDrinkCount": 3,
"weekCups": 6.5,
"monthDrinkCount": 12,
"monthCups": 28.3,
"streak": 5,
"streakType": "drank",
"totalDays": 45,
"totalRecords": 48,
"totalCups": 134.4,
"favCategory": "baijiu",
"categoryVariety": 5
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| weekDrinkCount | int | 本周饮酒天数 |
| weekCups | float | 本周标准杯总数 |
| monthDrinkCount | int | 本月饮酒天数 |
| monthCups | float | 本月标准杯总数 |
| streak | int | 当前连续打卡天数 |
| streakType | string | 连续类型:drank/abstain |
| totalDays | int | 总饮酒天数(去重) |
| totalRecords | int | 总记录数 |
| totalCups | float | 历史累计标准杯 |
| favCategory | string/null | 最爱酒类ID |
| categoryVariety | int | 尝试过的酒类品种数 |
### 5.2 获取月度统计
```
GET /stats/monthly
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| year | int | 否 | 年份,默认当前年 |
| month | int | 否 | 月份,默认当前月 |
**响应 data**
```json
{
"year": 2026,
"month": 7,
"drinkDays": 18,
"abstainDays": 12,
"totalCups": 45.6,
"avgCupsPerDay": 2.53,
"categoryBreakdown": [
{ "category": "baijiu", "count": 8, "cups": 22.4 },
{ "category": "beer", "count": 6, "cups": 15.2 },
{ "category": "wine", "count": 4, "cups": 8.0 }
],
"feelingBreakdown": [
{ "feeling": "tipsy", "count": 10 },
{ "feeling": "buzzed", "count": 5 },
{ "feeling": "drunk", "count": 2 },
{ "feeling": "blackout", "count": 1 }
],
"foodBreakdown": [
{ "category": "hotpot", "count": 5 },
{ "category": "bbq", "count": 4 }
]
}
```
### 5.3 获取酒类分布统计
```
GET /stats/categories
```
**响应 data**
```json
{
"categories": [
{
"id": "baijiu",
"name": "白酒",
"recordCount": 20,
"totalCups": 56.8,
"percentage": 42.3
},
{
"id": "beer",
"name": "啤酒",
"recordCount": 15,
"totalCups": 38.2,
"percentage": 28.5
}
]
}
```
---
## 6. 成就模块
### 6.1 获取成就列表
```
GET /achievements
```
**响应 data**
```json
{
"achievements": [
{
"id": "first_checkin",
"name": "初来乍到",
"desc": "完成第一次打卡,开启你的饮酒记录之旅",
"icon": "🏅",
"unlocked": true,
"unlockedAt": "2026-01-15T20:00:00.000Z",
"condition": {
"type": "total_records",
"value": 1
},
"progress": 1.0
},
{
"id": "hundred_cups",
"name": "百杯不醉",
"desc": "累计饮用100标准杯",
"icon": "🏆",
"unlocked": false,
"unlockedAt": null,
"condition": {
"type": "total_standard_cups",
"value": 100
},
"progress": 0.75
}
]
}
```
**成就条件类型:**
| type | 说明 | value含义 |
|------|------|-----------|
| total_records | 总记录数 | 次数 |
| total_standard_cups | 累计标准杯 | 杯数 |
| streak_days | 连续打卡天数 | 天数 |
| category_records | 某酒类打卡次数 | 次数(需配合category字段) |
| category_variety | 尝试酒类品种数 | 品种数 |
---
## 7. 社交模块(朋友圈/卡片分享)
### 7.1 获取朋友圈动态
```
GET /feed
```
**查询参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码 |
| pageSize | int | 否 | 每页数量 |
| lastId | string | 否 | 游标分页,上一页最后一条ID |
**响应 data**
```json
{
"list": [
{
"id": "record_xxx",
"user": {
"id": "user_002",
"nickname": "酒友小李",
"avatar": "https://xxx/avatar2.jpg"
},
"date": "2026-07-13",
"mode": "drank",
"drinks": [...],
"food": {...},
"feeling": "tipsy",
"photos": ["https://..."],
"standardCupsTotal": 3.5,
"quote": "酒逢知己千杯少",
"likeCount": 5,
"commentCount": 2,
"liked": false,
"createdAt": "2026-07-13T22:30:00.000Z"
}
],
"total": 100,
"page": 1,
"pageSize": 20
}
```
### 7.2 点赞/取消点赞
```
POST /records/:id/like
POST /records/:id/unlike
```
---
## 8. 数据字典
### 8.1 酒类分类 (category)
| ID | 名称 | 默认度数 |
|----|------|----------|
| baijiu | 白酒 | 52 |
| beer | 啤酒 | 5 |
| wine | 红酒 | 13 |
| whisky | 洋酒 | 40 |
| huangjiu | 黄酒 | 15 |
| sake | 清酒 | 15 |
| fruit | 果酒 | 8 |
| cocktail | 调酒 | 15 |
| other | 其他 | 10 |
### 8.2 饮用量单位 (unit)
| ID | 名称 | 换算(ml) |
|----|------|----------|
| ml | ml | 1 |
| liang | 两 | 50 |
| bottle | 瓶 | 500 |
| cup | 杯 | 150 |
| can | 听 | 330 |
| shot | shot | 30 |
### 8.3 配餐分类 (food category)
| ID | 名称 |
|----|------|
| hotpot | 火锅 |
| bbq | 烧烤 |
| stirfry | 炒菜 |
| seafood | 海鲜 |
| japanese | 日料 |
| western | 西餐 |
| snack | 小吃卤味 |
| junk | 零食 |
| none | 无配餐 |
### 8.4 饮用感受 (feeling)
| ID | 名称 | 描述 |
|----|------|------|
| tipsy | 微醺 | 刚刚好的快乐 |
| buzzed | 到位 | 今夜刚刚好 |
| drunk | 醉了 | 有点上头了 |
| blackout | 断片 | 什么都不记得了 |
### 8.5 可见范围 (visibility)
| ID | 名称 | 说明 |
|----|------|------|
| public | 公开 | 所有人可见 |
| friends | 仅好友 | 好友可见 |
| private | 仅自己 | 仅自己可见 |
### 8.6 打卡模式 (mode)
| ID | 名称 | 说明 |
|----|------|------|
| drank | 今日喝了 | 有饮酒记录 |
| abstain | 今日未喝 | 戒酒打卡 |
---
## 9. 数据库表设计参考
### 9.1 用户表 (users)
```sql
CREATE TABLE users (
id VARCHAR(32) PRIMARY KEY,
openid VARCHAR(64) UNIQUE NOT NULL,
union_id VARCHAR(64),
nickname VARCHAR(64),
avatar VARCHAR(512),
preferences JSON,
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
### 9.2 打卡记录表 (records)
```sql
CREATE TABLE records (
id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
date DATE NOT NULL,
mode ENUM('drank', 'abstain') NOT NULL,
drinks JSON,
food JSON,
feeling VARCHAR(16),
photos JSON,
visibility ENUM('public', 'friends', 'private') DEFAULT 'friends',
quote VARCHAR(256),
standard_cups_total DECIMAL(8,2) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_date (user_id, date),
INDEX idx_user_mode (user_id, mode, date)
);
```
### 9.3 照片表 (photos)
```sql
CREATE TABLE photos (
id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
record_id VARCHAR(32),
url VARCHAR(512) NOT NULL,
width INT,
height INT,
size INT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_record (record_id),
INDEX idx_user (user_id)
);
```
### 9.4 点赞表 (likes)
```sql
CREATE TABLE likes (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
record_id VARCHAR(32) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_user_record (user_id, record_id)
);
```
### 9.5 成就记录表 (user_achievements)
```sql
CREATE TABLE user_achievements (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
achievement_id VARCHAR(32) NOT NULL,
unlocked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX idx_user_achievement (user_id, achievement_id)
);
```
---
## 10. 备注
1. **标准杯计算建议由后端完成**,前端传入的 `standardCups` 仅作参考,后端应以 `amount``unit``degree` 为准重新计算
2. **照片上传**建议走对象存储(OSS/COS),返回CDN链接
3. **日历接口**建议缓存,避免每次查全月记录
4. **成就检测**可在创建记录后异步触发,避免影响主流程
5. **可见范围**控制需在 feed 接口和记录详情接口中做权限过滤
6. 前端目前使用 Mock 数据(`mock-data.js`),切换到真实接口时只需替换 `common/mock-data.js` 中的方法为 HTTP 调用即可