Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
168
internal/controller/auth.go
Normal file
168
internal/controller/auth.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// Package controller 定义 MVC 架构中的 Controller 层。
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"stock-analysis/internal/model"
|
||||
"stock-analysis/internal/service"
|
||||
)
|
||||
|
||||
// AuthController 提供用户认证相关的 HTTP 接口。
|
||||
type AuthController struct {
|
||||
svc *service.AuthService
|
||||
}
|
||||
|
||||
// NewAuthController 创建认证控制器。
|
||||
func NewAuthController(svc *service.AuthService) *AuthController {
|
||||
return &AuthController{svc: svc}
|
||||
}
|
||||
|
||||
// CurrentUser 返回当前请求对应的登录用户,未登录返回错误。
|
||||
func (c *AuthController) CurrentUser(r *http.Request) (*model.User, error) {
|
||||
token := extractToken(r)
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("未登录")
|
||||
}
|
||||
return c.svc.ValidateToken(r.Context(), token)
|
||||
}
|
||||
|
||||
// sendCodeRequest 发送验证码请求。
|
||||
type sendCodeRequest struct {
|
||||
Email string `json:"email"`
|
||||
Purpose string `json:"purpose"` // register 或 login
|
||||
}
|
||||
|
||||
// SendCode 处理 POST /api/auth/send-code,发送邮箱验证码。
|
||||
func (c *AuthController) SendCode(w http.ResponseWriter, r *http.Request) {
|
||||
var req sendCodeRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if req.Purpose == "" {
|
||||
req.Purpose = "register"
|
||||
}
|
||||
if err := c.svc.SendVerifyCode(r.Context(), req.Email, req.Purpose); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"message": "验证码已发送"})
|
||||
}
|
||||
|
||||
// registerRequest 注册请求。
|
||||
type registerRequest struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Code string `json:"code"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// Register 处理 POST /api/auth/register,邮箱+验证码注册。
|
||||
func (c *AuthController) Register(w http.ResponseWriter, r *http.Request) {
|
||||
var req registerRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
user, err := c.svc.Register(r.Context(), req.Username, req.Email, req.Code, req.Password)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
})
|
||||
}
|
||||
|
||||
// loginRequest 登录请求。
|
||||
type loginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Code string `json:"code"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// Login 处理 POST /api/auth/login。
|
||||
// 支持两种登录方式:
|
||||
// - 用户名+密码:{ username, password }
|
||||
// - 邮箱+验证码:{ email, code }
|
||||
func (c *AuthController) Login(w http.ResponseWriter, r *http.Request) {
|
||||
var req loginRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
var token string
|
||||
var err error
|
||||
|
||||
if req.Username != "" {
|
||||
// 用户名+密码登录
|
||||
user, token, err = c.svc.LoginByUsername(r.Context(), req.Username, req.Password)
|
||||
} else if req.Code != "" {
|
||||
// 邮箱+验证码登录
|
||||
user, token, err = c.svc.Login(r.Context(), req.Email, req.Code)
|
||||
} else {
|
||||
writeError(w, http.StatusBadRequest, fmt.Errorf("请输入用户名密码或邮箱验证码"))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
writeError(w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"token": token,
|
||||
})
|
||||
}
|
||||
|
||||
// Me 处理 GET /api/auth/me,返回当前登录用户信息。
|
||||
func (c *AuthController) Me(w http.ResponseWriter, r *http.Request) {
|
||||
token := extractToken(r)
|
||||
if token == "" {
|
||||
writeError(w, http.StatusUnauthorized, fmt.Errorf("未登录"))
|
||||
return
|
||||
}
|
||||
u, err := c.svc.ValidateToken(r.Context(), token)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusUnauthorized, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"id": u.ID,
|
||||
"username": u.Username,
|
||||
"email": u.Email,
|
||||
})
|
||||
}
|
||||
|
||||
// Logout 处理 POST /api/auth/logout,注销当前会话。
|
||||
func (c *AuthController) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
token := extractToken(r)
|
||||
if token != "" {
|
||||
c.svc.Logout(token)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"message": "已退出"})
|
||||
}
|
||||
|
||||
// extractToken 从 Cookie 或 Authorization 头中提取 token。
|
||||
func extractToken(r *http.Request) string {
|
||||
if c, err := r.Cookie("auth_token"); err == nil && c.Value != "" {
|
||||
return c.Value
|
||||
}
|
||||
if h := r.Header.Get("Authorization"); h != "" {
|
||||
if strings.HasPrefix(h, "Bearer ") {
|
||||
return strings.TrimPrefix(h, "Bearer ")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user