Files
hejiu/docs/私信模块接口文档.md
T
cg 99723497fa rename(app): 将应用名称从"干杯日记"更改为"碰盏日记"
- 替换所有文件中的品牌名称引用,包括App.vue、pages.json、uni.scss等
- 更新全局样式、导航栏标题和组件中的文案显示
- 修改分享功能中的应用名称显示
- 调整聊天页面的输入栏键盘适配逻辑
- 在多个页面组件中添加事件发射声明以支持交互功能
2026-07-21 22:20:08 +08:00

12 KiB
Raw Blame History

碰盏日记 - 私信模块接口文档

版本: v1.0 日期: 2026-07-21 模块: 私信(实时通信) 小程序: 碰盏日记(uni-app 微信小程序) 基础URL: https://dev.wash-painting.cn/api/v1 WebSocket: wss://dev.wash-painting.cn/ws


1. 模块概述

私信是酒友圈的实时通信子模块,允许酒友之间一对一发送消息。

子功能 说明
会话列表 展示所有聊天会话,含最后消息、未读数
聊天记录 分页加载历史消息
实时收发 通过 WebSocket 实时推送/接收消息
已读回执 标记消息已读,通知对方
输入状态 实时显示"对方正在输入..."

前端当前状态: 所有 REST 接口已在 common/api.js 中预留方法(暂用 Mock);WebSocket 管理器已在 common/websocket.js 中实现(含心跳、重连、消息队列)。后端实现后前端仅需切换调用方式。请严格按照本文档的字段名和协议格式实现


2. 通用约定

2.1 鉴权

  • REST 请求头:Authorization: Bearer {token}
  • WebSocket 连接:wss://dev.wash-painting.cn/ws?token={token}
  • 所有接口均需登录态

2.2 REST 统一响应格式

{
  "code": 0,
  "message": "success",
  "data": {}
}

2.3 错误码

code 含义
0 成功
400 参数错误
401 未登录/token过期
403 无权限(如:非酒友关系不可发私信)
404 资源不存在
500 服务器错误

2.4 时间格式

  • REST 接口时间字段使用 ISO 8601:2026-07-21T22:30:00.000Z
  • WebSocket 消息中 timestamp 使用毫秒时间戳(如 1721560000000),便于前端排序

3. REST 接口

3.1 获取会话列表

GET /chat/conversations

查询参数:

响应 data

{
  "list": [
    {
      "id": "conv_001",
      "friendId": "user_002",
      "nickname": "老张",
      "avatar": "https://xxx/avatar.jpg",
      "lastMessage": "今晚一起喝点?",
      "lastTime": "2026-07-21T10:32:00.000Z",
      "unread": 2,
      "online": true
    }
  ]
}

Conversation 字段说明:

字段 类型 说明
id string 会话ID
friendId string 对方用户ID
nickname string 对方昵称
avatar string 对方头像URL(可为空字符串)
lastMessage string 最后一条消息摘要(截取前30字)
lastTime string 最后消息时间 ISO 8601
unread int 未读消息数
online boolean 对方是否在线

业务规则:

  • 按 lastTime 倒序排列
  • 仅返回有消息记录的会话
  • 仅酒友之间可存在会话

3.2 获取历史消息

GET /chat/messages

查询参数:

参数 类型 必填 说明
conversationId string 会话ID
page int 页码,默认1
pageSize int 每页数量,默认20

响应 data

{
  "list": [
    {
      "id": "msg_001",
      "conversationId": "conv_001",
      "senderId": "user_002",
      "receiverId": "user_001",
      "type": "text",
      "content": "今晚一起喝点?",
      "timestamp": 1721556300000,
      "status": "read"
    }
  ],
  "hasMore": true
}

Message 字段说明:

字段 类型 说明
id string 消息ID(服务端生成)
conversationId string 所属会话ID
senderId string 发送者用户ID
receiverId string 接收者用户ID
type string 消息类型:text / image
content string 消息内容(文本内容 或 图片URL
timestamp long 发送时间(毫秒时间戳)
status string 消息状态:sent / delivered / read

业务规则:

  • 按 timestamp 正序返回(最早在前)
  • status 表示该消息对接收方的状态(发送方自己的消息显示对方是否已读)

3.3 标记会话已读

POST /chat/conversations/:id/read

请求参数: 无(路径参数 conversationId

响应 data

{ "success": true }

业务规则:

  • 将该会话中所有对方发来的消息标记为 read
  • 会话的 unread 清零
  • 幂等处理

3.4 获取未读消息总数

GET /chat/unread-count

响应 data

{ "total": 3 }
字段 类型 说明
total int 所有会话未读消息总和

4. WebSocket 协议

4.1 连接

wss://dev.wash-painting.cn/ws?token={jwt_token}
  • 连接建立后服务端验证 token,无效则返回 close code 4001
  • 连接成功后服务端推送:
{ "type": "connected", "data": { "userId": "user_001" } }

4.2 消息帧格式

所有 WebSocket 消息均为 JSON 文本帧:

{
  "type": "消息类型",
  "data": { ... },
  "ts": 1721560000000
}
字段 类型 说明
type string 消息类型标识
data object 业务数据(type=ping/pong 时可省略)
ts long 客户端发送时间戳(毫秒),服务端原样返回用于延迟计算

4.3 客户端 → 服务端

发送聊天消息

{
  "type": "chat",
  "data": {
    "receiverId": "user_002",
    "content": "今晚一起喝点?",
    "msgType": "text",
    "clientMsgId": "msg_1721560000000_a3f2"
  },
  "ts": 1721560000000
}
字段 类型 必填 说明
receiverId string 接收者用户ID
content string 消息内容(文本 或 图片URL
msgType string text / image
clientMsgId string 客户端生成的临时消息ID(用于ACK匹配)

发送输入状态

{
  "type": "typing",
  "data": { "receiverId": "user_002" },
  "ts": 1721560000000
}

发送已读回执

{
  "type": "read",
  "data": {
    "conversationId": "conv_001",
    "lastMsgId": "msg_006"
  },
  "ts": 1721560000000
}
字段 类型 说明
conversationId string 会话ID
lastMsgId string 已读到的最后一条消息ID

心跳

{ "type": "ping" }

4.4 服务端 → 客户端

推送新消息

{
  "type": "chat",
  "data": {
    "msgId": "msg_srv_001",
    "senderId": "user_002",
    "receiverId": "user_001",
    "conversationId": "conv_001",
    "content": "好啊,几点?",
    "msgType": "text",
    "timestamp": 1721560060000
  }
}
字段 类型 说明
msgId string 服务端生成的消息ID
senderId string 发送者ID
receiverId string 接收者ID
conversationId string 会话ID
content string 消息内容
msgType string text / image
timestamp long 服务端接收时间(毫秒时间戳)

消息送达确认(ACK

{
  "type": "ack",
  "data": {
    "clientMsgId": "msg_1721560000000_a3f2",
    "msgId": "msg_srv_001",
    "status": "sent"
  }
}
字段 类型 说明
clientMsgId string 客户端原始消息ID(用于匹配)
msgId string 服务端分配的正式消息ID
status string sent(已入库)

前端收到 ACK 后将本地消息的 id 替换为 msgIdstatus 更新为 sent。

推送输入状态

{
  "type": "typing",
  "data": { "senderId": "user_002" }
}

推送已读回执

{
  "type": "read",
  "data": {
    "conversationId": "conv_001",
    "readerId": "user_002"
  }
}

前端收到后将该会话中自己发送的消息 status 更新为 read。

心跳响应

{ "type": "pong" }

4.5 心跳与重连机制(前端已实现,后端需配合)

机制 说明
心跳间隔 前端每 30秒 发送 ping,后端收到后立即回复 pong
超时判定 前端 60秒 未收到 pong 则主动断开重连
重连策略 指数退避:1s → 2s → 4s → 8s → ... → 最大30s
离线消息 用户重新连接后,后端应推送离线期间收到的消息(按时间正序)
连接互踢 同一用户多端登录时,旧连接推送 {"type":"kicked"} 后关闭

5. 数据库表设计参考

5.1 会话表 (conversations)

CREATE TABLE conversations (
  id VARCHAR(32) PRIMARY KEY,
  user_a_id VARCHAR(32) NOT NULL COMMENT '用户AID较小者)',
  user_b_id VARCHAR(32) NOT NULL COMMENT '用户BID较大者)',
  last_message VARCHAR(128) COMMENT '最后消息摘要',
  last_message_time DATETIME COMMENT '最后消息时间',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE INDEX idx_users (user_a_id, user_b_id),
  INDEX idx_user_a (user_a_id, updated_at),
  INDEX idx_user_b (user_b_id, updated_at)
);

两人之间只有一个会话,user_a_id < user_b_id 保证唯一。

5.2 消息表 (messages)

CREATE TABLE messages (
  id VARCHAR(32) PRIMARY KEY,
  conversation_id VARCHAR(32) NOT NULL,
  sender_id VARCHAR(32) NOT NULL,
  receiver_id VARCHAR(32) NOT NULL,
  type ENUM('text', 'image') DEFAULT 'text',
  content TEXT NOT NULL,
  status ENUM('sent', 'delivered', 'read') DEFAULT 'sent',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_conv_time (conversation_id, created_at),
  INDEX idx_receiver_status (receiver_id, status)
);

5.3 未读计数表 (unread_counts)

CREATE TABLE unread_counts (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id VARCHAR(32) NOT NULL,
  conversation_id VARCHAR(32) NOT NULL,
  count INT DEFAULT 0,
  UNIQUE INDEX idx_user_conv (user_id, conversation_id),
  INDEX idx_user (user_id)
);

收到新消息时 count+1,标记已读时归零。


6. 业务规则汇总

规则 说明
酒友限制 仅酒友关系可发私信,非酒友返回 403
消息长度 文本消息最长 500 字
图片消息 content 为图片URL(先调上传接口),前端展示为图片气泡
内容安全 文本消息需接入微信 msgSecCheck 审核,图片需接入 imgSecCheck
会话自动创建 首次发消息时自动创建会话记录
未读计数 新消息到达时接收方 unread+1;调用标记已读或 WebSocket read 事件时归零
消息撤回 v1.0 暂不支持,后续扩展
离线推送 v1.0 暂不接入微信订阅消息推送,用户上线后拉取未读即可

7. 前端对接说明

7.1 前端调用位置

REST 接口common/api.js):

GetConversations()                          // GET  /chat/conversations
GetMessages({ conversationId, page })       // GET  /chat/messages
MarkRead({ conversationId })                // POST /chat/conversations/:id/read
GetUnreadCount()                            // GET  /chat/unread-count

WebSocketcommon/websocket.js):

wsManager.connect(token)                    // 建立连接
wsManager.send('chat', { receiverId, content, msgType, clientMsgId })
wsManager.send('typing', { receiverId })
wsManager.send('read', { conversationId, lastMsgId })
wsManager.on('message', handler)            // 收到新消息
wsManager.on('typing', handler)             // 对方正在输入
wsManager.on('read', handler)               // 对方已读
wsManager.on('ack', handler)                // 消息送达确认

7.2 注意事项

  1. 字段名严格一致:前端已按本文档字段名渲染,请勿更改命名
  2. timestamp 为毫秒时间戳:WebSocket 消息中的时间使用 long 型毫秒时间戳
  3. clientMsgId 必须原样返回:ACK 中需包含客户端发送的 clientMsgId,前端依赖此字段匹配本地消息
  4. conversationId 生成规则:建议用双方 userId 排序拼接(如 conv_{minId}_{maxId}),确保两人间唯一
  5. 头像可为空:avatar 字段允许空字符串,前端有默认占位头像
  6. 内容安全:消息内容需接入微信内容安全审核(msgSecCheck / imgSecCheck
  7. 幂等性:标记已读接口需幂等,重复调用不报错
  8. 离线消息:用户重连后需推送离线消息,或前端通过 REST 接口拉取(当前方案为 REST 拉取)