Files
WarrenZhou e28bfec957 Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换)
- 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制)
- 增量同步:支持 -incr/-since 参数按存档日期增量入库
- 策略回测:净值曲线/10指标/基准对比,含基本面过滤
- 用户认证:用户名密码 + 邮箱验证码双登录方式
- 前端:三Tab SPA(实时行情/策略回测/数据管理)
2026-08-17 20:23:22 +08:00

168 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 ""
}