dual check v0.1
This commit is contained in:
1 parent
78bad1fa3d
commit
c137299016
36 files changed
+3727
-81
No files matched your search
@@ -0,0 +1,132 @@
|
||||
package dual_check
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
pkgws "cancerai.top/toolbox-server/internal/pkg/websocket"
|
||||
"cancerai.top/toolbox-server/internal/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
gorillaws "github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var upgrader = gorillaws.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool { return true },
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
type WSHandler struct {
|
||||
service *Service
|
||||
hub *pkgws.Hub
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewWSHandler(service *Service, hub *pkgws.Hub, logger *zap.Logger) *WSHandler {
|
||||
return &WSHandler{service: service, hub: hub, logger: logger}
|
||||
}
|
||||
|
||||
// Handle GET /api/v1/dual-check/ws?session_id=xxx&role=initiator|participant&nickname=xxx
|
||||
func (h *WSHandler) Handle(c *gin.Context) {
|
||||
sessionID := c.Query("session_id")
|
||||
role := c.Query("role")
|
||||
nickname := c.Query("nickname")
|
||||
|
||||
if sessionID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 4000, "message": "session_id required"})
|
||||
return
|
||||
}
|
||||
if role != "initiator" && role != "participant" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1004, "message": "role must be initiator or participant"})
|
||||
return
|
||||
}
|
||||
if nickname == "" || len(nickname) > 32 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 4000, "message": "nickname required (1-32)"})
|
||||
return
|
||||
}
|
||||
|
||||
// 先校验 session 存在
|
||||
info, err := h.service.CheckSession(c.Request.Context(), sessionID)
|
||||
if err != nil || !info.Exists {
|
||||
c.JSON(http.StatusNotFound, gin.H{"code": 1001, "message": "session not found"})
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
h.logger.Warn("ws upgrade failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
wsConn := &pkgws.Conn{
|
||||
Hub: h.hub,
|
||||
SessionID: sessionID,
|
||||
Role: role,
|
||||
WS: conn,
|
||||
Send: make(chan []byte, 256),
|
||||
}
|
||||
h.hub.Register(wsConn)
|
||||
|
||||
// 参与者首次连接:自动登记昵称(幂等)
|
||||
if role == "participant" {
|
||||
_, _ = h.service.JoinParticipant(c.Request.Context(), sessionID, nickname)
|
||||
// 广播 participant_joined 给房间其他人
|
||||
h.hub.Broadcast(sessionID, response.WSParticipantJoined(nickname))
|
||||
}
|
||||
|
||||
// 推一个 init 消息给新连接者,带上当前全量快照让它自己初始化 UI
|
||||
if sess, err := h.service.GetSession(c.Request.Context(), sessionID); err == nil {
|
||||
wsConn.Send <- marshalMsg(response.WSFullState(sess))
|
||||
}
|
||||
|
||||
h.logger.Info("ws connected",
|
||||
zap.String("session_id", truncateSessionID(sessionID)),
|
||||
zap.String("role", role),
|
||||
zap.String("nickname", nickname),
|
||||
)
|
||||
|
||||
go pkgws.WritePump(wsConn)
|
||||
// ReadPump 里的 Unregister 会触发 participant_left 广播
|
||||
go h.readLoop(wsConn, nickname)
|
||||
}
|
||||
|
||||
func (h *WSHandler) readLoop(c *pkgws.Conn, nickname string) {
|
||||
defer func() {
|
||||
c.WS.Close() // 先关,让 WritePump 也能退
|
||||
if c.Role == "initiator" {
|
||||
// 发起方断开:立即结束会话 → 广播给所有参与者 → 删除 Redis
|
||||
h.hub.Broadcast(c.SessionID, response.WSSessionEnded())
|
||||
if err := h.service.EndSession(context.Background(), c.SessionID); err != nil {
|
||||
h.logger.Warn("initiator disconnect end session err",
|
||||
zap.String("session_id", truncateSessionID(c.SessionID)),
|
||||
zap.Error(err))
|
||||
} else {
|
||||
h.logger.Info("initiator disconnected, session ended",
|
||||
zap.String("session_id", truncateSessionID(c.SessionID)),
|
||||
zap.String("nickname", nickname))
|
||||
}
|
||||
} else {
|
||||
// 参与者断开:广播 participant_left
|
||||
h.hub.Broadcast(c.SessionID, response.WSParticipantLeft(nickname))
|
||||
}
|
||||
c.Hub.Unregister(c) // 最后从房间移除
|
||||
}()
|
||||
c.WS.SetPongHandler(func(string) error { return nil })
|
||||
for {
|
||||
if _, _, err := c.WS.ReadMessage(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func marshalMsg(m response.WebSocketMessage) []byte {
|
||||
b, _ := json.Marshal(m)
|
||||
return b
|
||||
}
|
||||
|
||||
// 确保 fmt/json 被引用
|
||||
var _ = fmt.Sprintf
|
||||
Reference in new issue
Block a user