From b7e803fa90414ca047eff153b677a3179ab7321c Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sat, 13 Jun 2026 16:21:09 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20Phase=206.1=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20Session=20REST=20API=20=E8=B7=AF=E7=94=B1=20(POST/D?= =?UTF-8?q?ELETE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/api/session.go | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 backend/internal/api/session.go diff --git a/backend/internal/api/session.go b/backend/internal/api/session.go new file mode 100644 index 0000000..135758b --- /dev/null +++ b/backend/internal/api/session.go @@ -0,0 +1,91 @@ +// Package api 提供 REST API 处理函数。 +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/hhs/camtalk/internal/models" + "github.com/hhs/camtalk/internal/session" +) + +// SessionHandler 提供会话相关的 REST 端点。 +type SessionHandler struct { + sessionMgr session.Manager +} + +// NewSessionHandler 创建 SessionHandler。 +func NewSessionHandler(sessionMgr session.Manager) *SessionHandler { + return &SessionHandler{sessionMgr: sessionMgr} +} + +// CreateSessionRequest POST /api/sessions 请求体(所有字段可选)。 +type CreateSessionRequest struct { + Config *models.SessionConfig `json:"config,omitempty"` +} + +// CreateSession POST /api/sessions — 创建新会话。 +func (h *SessionHandler) CreateSession(c *gin.Context) { + var req CreateSessionRequest + // 请求体可选,解析失败不报错(使用默认配置) + _ = c.ShouldBindJSON(&req) + + cfg := models.DefaultConfig() + if req.Config != nil { + cfg = *req.Config + } + + sessionID, err := h.sessionMgr.Create(c.Request.Context(), cfg) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to create session", + }) + return + } + + // 获取创建后的会话以返回 created_at + sess, err := h.sessionMgr.Get(c.Request.Context(), sessionID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to retrieve created session", + }) + return + } + + c.JSON(http.StatusCreated, gin.H{ + "session_id": sess.ID, + "created_at": sess.CreatedAt, + }) +} + +// DestroySession DELETE /api/sessions/:id — 销毁会话。 +func (h *SessionHandler) DestroySession(c *gin.Context) { + sessionID := c.Param("id") + + err := h.sessionMgr.Destroy(c.Request.Context(), sessionID) + if err != nil { + if err == session.ErrSessionNotFound { + c.JSON(http.StatusNotFound, gin.H{ + "code": "SESSION_NOT_FOUND", + "message": "session not found or already expired", + }) + return + } + c.JSON(http.StatusInternalServerError, gin.H{ + "code": "INTERNAL_ERROR", + "message": "failed to destroy session", + }) + return + } + + c.Status(http.StatusNoContent) +} + +// RegisterRoutes 注册会话相关路由到给定的路由组。 +func (h *SessionHandler) RegisterRoutes(rg *gin.RouterGroup) { + rg.POST("/sessions", h.CreateSession) + rg.DELETE("/sessions/:id", h.DestroySession) +} -- 2.49.1 From f07916ab0f19526666d21881f6fcc5153b661093 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sat, 13 Jun 2026 16:21:53 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20Phase=206.2=20-=20Health=20=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=E5=AF=B9=E9=BD=90=E6=8E=A5=E5=8F=A3=E6=96=87=E6=A1=A3?= =?UTF-8?q?=20(uptime=5Fseconds=20=E6=95=B4=E6=95=B0=E6=A0=BC=E5=BC=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/server/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 61edbaf..15029bd 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -106,7 +106,7 @@ func healthHandler(sessionMgr session.Manager) gin.HandlerFunc { c.JSON(200, gin.H{ "status": "ok", "version": "0.1.0", - "uptime": time.Since(startTime).String(), + "uptime_seconds": int(time.Since(startTime).Seconds()), "active_sessions": sessionMgr.ActiveCount(), }) } -- 2.49.1 From a6a6a16185b1c6063cb4b86bd44c56e66a42af96 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sat, 13 Jun 2026 16:22:53 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Phase=206.3=20-=20=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=20Session=20REST=20=E8=B7=AF=E7=94=B1=EF=BC=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/server/main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 15029bd..9b508f2 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" + "github.com/hhs/camtalk/internal/api" "github.com/hhs/camtalk/internal/ai/llm" "github.com/hhs/camtalk/internal/ai/stt" "github.com/hhs/camtalk/internal/ai/tts" @@ -61,11 +62,15 @@ func main() { r.Use(gin.Recovery()) // REST API - api := r.Group("/api") + apiGroup := r.Group("/api") { - api.GET("/health", healthHandler(sessionMgr)) + apiGroup.GET("/health", healthHandler(sessionMgr)) } + // Session REST 端点 + sessionHandler := api.NewSessionHandler(sessionMgr) + sessionHandler.RegisterRoutes(apiGroup) + // WebSocket r.GET("/ws", ws.ServeWS(sessionMgr, orch)) -- 2.49.1