Initialize LineUp app server

This commit is contained in:
2026-08-07 13:36:56 +08:00
commit d6f6a92bab
24 changed files with 2184 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
package modules
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/lineup/app-server/internal/wkapi"
"github.com/spf13/viper"
)
// UserModule 用户模块: 登录 / 注册
type UserModule struct {
vp *viper.Viper
wk *wkapi.Client
}
// RegisterRoutes 注册路由
func (m *UserModule) RegisterRoutes(r *gin.Engine) {
r.POST("/login", m.handleLogin)
}
// LoginRequest 登录请求
type LoginRequest struct {
Phone string `json:"phone" binding:"required"`
Code string `json:"code" binding:"required"`
}
// LoginResponse 登录响应
type LoginResponse struct {
UID string `json:"uid"`
Token string `json:"token"`
IMAddr string `json:"im_addr"` // WuKongIM WS地址
AgentUID string `json:"agent_uid"` // Agent的UID
ChannelID string `json:"channel_id"` // Agent频道ID
ChannelType uint8 `json:"channel_type"` // Agent频道类型
}
// handleLogin 处理登录请求
func (m *UserModule) handleLogin(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误: phone和code必填"})
return
}
// 生成用户 UID
uid := fmt.Sprintf("user_%s", req.Phone)
// 生成 Token (随机32字节)
token := generateToken()
// 注册 Token 到 WuKongIM
if err := m.wk.RegisterToken(uid, token); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("注册Token失败: %v", err)})
return
}
// 确保 Agent 频道存在并将用户加入订阅者
agentChannelID := m.vp.GetString("agent.channelID")
agentUID := m.vp.GetString("agent.uid")
agentChannelType := uint8(m.vp.GetInt("agent.channelType"))
if agentChannelType == 0 {
agentChannelType = 2
}
// 创建/更新频道,确保 Agent 和用户都是订阅者
m.wk.UpsertChannel(agentChannelID, agentChannelType, []string{agentUID})
// 将用户加入频道订阅者
m.wk.SubscriberAdd(agentChannelID, agentChannelType, []string{uid})
// 获取 WebSocket 地址
route, err := m.wk.GetRoute()
wsAddr := m.vp.GetString("wukongim.wsAddr")
if err == nil && route.WSAddr != "" {
wsAddr = route.WSAddr
}
c.JSON(http.StatusOK, LoginResponse{
UID: uid,
Token: token,
IMAddr: wsAddr,
AgentUID: m.vp.GetString("agent.uid"),
ChannelID: m.vp.GetString("agent.channelID"),
ChannelType: uint8(m.vp.GetInt("agent.channelType")),
})
}
// generateToken 生成32字节随机Token
func generateToken() string {
b := make([]byte, 32)
rand.Read(b)
return hex.EncodeToString(b)
}