初始化功能
This commit is contained in:
209
internal/api/handler.go
Normal file
209
internal/api/handler.go
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* package api
|
||||
* 作用:处理 HTTP 请求接口,包括消息发送、历史记录、系统状态检查等。
|
||||
* 说明:V2 版本中消息发送主要通过 HTTP 接口进行,而非 WebSocket 直接推送。
|
||||
*/
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
"xk-websocket-v2/internal/manager"
|
||||
"xk-websocket-v2/internal/model"
|
||||
"xk-websocket-v2/internal/service"
|
||||
"xk-websocket-v2/internal/turnserver"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// ==========================================
|
||||
// 消息发送相关接口
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* SendHandler
|
||||
* 功能:通用消息发送接口 (HTTP -> WebSocket)。
|
||||
* 路径:POST /api/send
|
||||
* 逻辑:支持通过 target_client_id 或 receiver_user_id 发送。
|
||||
* 重要:必须正确处理 SenderClientID,防止多端同步时误通知发送端自己。
|
||||
*/
|
||||
func SendHandler(c *gin.Context) {
|
||||
var req model.SendMessageReq
|
||||
// 1. 绑定并校验 JSON 参数
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的JSON参数"})
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 获取发送者 ID (从 Header 中获取,模拟鉴权)
|
||||
// 生产环境应从 JWT Token 中解析 UserID
|
||||
senderID := c.GetHeader("X-User-ID")
|
||||
if senderID == "" {
|
||||
senderID = "system" // 默认为系统消息
|
||||
}
|
||||
|
||||
// 3. [关键修复] 获取发送端的 WebSocket ClientID
|
||||
// 前端在调用此接口时,必须带上自己的 socket_client_id
|
||||
// 如果前端没传 (兼容旧代码),则 fallback 到 api-gateway,这会导致发送端自己也收到"其他设备接听"通知
|
||||
clientID := req.SenderClientID
|
||||
if clientID == "" {
|
||||
clientID = "api-gateway"
|
||||
}
|
||||
|
||||
// 4. 构造临时的发送者客户端对象
|
||||
// 这个对象将传递给 Service 层,用于识别消息来源
|
||||
mockClient := &manager.Client{
|
||||
UserID: senderID,
|
||||
ID: clientID,
|
||||
}
|
||||
|
||||
// 5. 调用核心业务逻辑处理消息
|
||||
service.ChatSvc.HandleUserMessage(mockClient, &req)
|
||||
|
||||
// 6. 返回成功响应
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
|
||||
/**
|
||||
* SendToUserHandler (V1 兼容)
|
||||
* 功能:专门用于给指定用户发送消息。
|
||||
* 路径:POST /api/send-to-user
|
||||
*/
|
||||
func SendToUserHandler(c *gin.Context) {
|
||||
// 复用 SendHandler,因为 V2 的 SendHandler 已经支持 ReceiverUserID
|
||||
SendHandler(c)
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 用户与连接管理接口
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* BindHandler
|
||||
* 功能:手动绑定 ClientID 和 UserID。
|
||||
* 路径:POST /api/bind
|
||||
* 场景:当 WebSocket 连接建立后,客户端通过 HTTP 接口补充用户信息。
|
||||
*/
|
||||
func BindHandler(c *gin.Context) {
|
||||
var req model.BindReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
// 调用服务层进行绑定
|
||||
service.ChatSvc.BindUserByClientID(req.ClientID, req.UserID)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "success"})
|
||||
}
|
||||
|
||||
/**
|
||||
* CheckUserOnlineHandler
|
||||
* 功能:检查用户是否在线。
|
||||
* 路径:GET /api/check-user-online
|
||||
*/
|
||||
func CheckUserOnlineHandler(c *gin.Context) {
|
||||
userID := c.Query("user_id")
|
||||
// 调用服务层查询 Redis
|
||||
isOnline := service.ChatSvc.IsUserOnline(userID)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "success", "result": isOnline})
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 数据查询接口
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* HistoryHandler
|
||||
* 功能:获取历史消息记录 (分页)。
|
||||
* 路径:GET /api/messages
|
||||
*/
|
||||
func HistoryHandler(c *gin.Context) {
|
||||
roomID := c.Query("room_id")
|
||||
var msgs []model.ChatMessage
|
||||
|
||||
// 简单查询最近 50 条
|
||||
// 生产环境应添加 page, page_size 参数
|
||||
result := service.ChatSvc.DB.Where("room_id = ?", roomID).
|
||||
Order("created_at desc").Limit(50).Find(&msgs)
|
||||
|
||||
if result.Error != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "db error"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": msgs})
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncMessagesHandler
|
||||
* 功能:全量同步消息 (V1 兼容)。
|
||||
* 路径:GET /api/messages/sync
|
||||
*/
|
||||
func SyncMessagesHandler(c *gin.Context) {
|
||||
HistoryHandler(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* ContactListHandler
|
||||
* 功能:获取联系人列表 (模拟数据)。
|
||||
* 路径:GET /api/contacts
|
||||
* 说明:用于前端展示登录后的好友列表。
|
||||
*/
|
||||
func ContactListHandler(c *gin.Context) {
|
||||
// 模拟 10 个用户数据
|
||||
users := []model.UserContact{
|
||||
{ID: "1001", Name: "张三 (我)", Avatar: "张", Desc: "Golang 专家"},
|
||||
{ID: "1002", Name: "李琦 (妻)", Avatar: "李", Desc: "在线"},
|
||||
{ID: "1003", Name: "王医生", Avatar: "医", Desc: "主任医师"},
|
||||
{ID: "1004", Name: "客服小蜜", Avatar: "客", Desc: "全天在线"},
|
||||
{ID: "1005", Name: "技术支持", Avatar: "技", Desc: "请重启试试"},
|
||||
{ID: "1006", Name: "财务小赵", Avatar: "财", Desc: "报销单请提交"},
|
||||
{ID: "1007", Name: "运维阿强", Avatar: "运", Desc: "服务器维护中"},
|
||||
{ID: "1008", Name: "测试小丽", Avatar: "测", Desc: "Bug 太多了"},
|
||||
{ID: "1009", Name: "HR", Avatar: "人", Desc: "本月考勤异常"},
|
||||
{ID: "1010", Name: "老板", Avatar: "老", Desc: "今晚开会"},
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": users})
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 系统与 WebRTC 接口
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* HealthHandler
|
||||
* 功能:服务健康检查。
|
||||
* 路径:GET /api/health
|
||||
*/
|
||||
func HealthHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"node": viper.GetString("app.node_id"),
|
||||
"time": time.Now().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* ICEHandler
|
||||
* 功能:获取 TURN/STUN 服务器配置及临时凭证。
|
||||
* 路径:GET /api/ice-servers
|
||||
* 用途:WebRTC 前端在建立 PeerConnection 前需调用此接口。
|
||||
*/
|
||||
func ICEHandler(c *gin.Context) {
|
||||
userID := c.Query("user_id")
|
||||
|
||||
// 生成临时凭证 (HMAC-SHA1)
|
||||
username, credential := turnserver.GenerateCredentials(userID)
|
||||
|
||||
// 从配置读取公网IP和端口
|
||||
ip := viper.GetString("turn.public_ip")
|
||||
port := viper.GetInt("turn.listen_port")
|
||||
|
||||
// 构造配置返回给前端
|
||||
cfg := model.ICEServerConfig{
|
||||
Urls: []string{fmt.Sprintf("turn:%s:%d", ip, port)},
|
||||
Username: username,
|
||||
Credential: credential,
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": []model.ICEServerConfig{cfg}})
|
||||
}
|
||||
Reference in New Issue
Block a user