mengstack-api/internal/modules/auth/interfaces/handler.go
MengStack Dev df809f1045
Some checks failed
CI / Build & Test (push) Failing after 1m31s
feat: user CRUD API + dashboard stats + kernel infrastructure
- Add user management endpoints (list/create/get/update/delete) with pagination and search
- Add dashboard stats endpoint with tenant/user/online counts and growth metrics
- Add tenant resolver middleware for multi-tenant request scoping
- Add i18n kernel with zh/en message files and AcceptLanguage middleware
- Add WebSocket hub/handler for real-time communication
- Add job scheduler kernel with cron support
- Add plugin sandbox for isolated execution
- Add storage kernel (local filesystem)
- Add event bus kernel for pub/sub
- Add cache kernel abstraction
- Add database migration runner and version upgrade checker
- Add rate limiting middleware with Redis backend
- Add SQL migrations for rbac, audit_logs, settings, notifications, examples
- Extend user repository with list/delete/count operations
- Register all module routes with tenant resolver
2026-10-03 03:42:58 +08:00

309 lines
8.7 KiB
Go
Raw 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 interfaces
import (
"strconv"
"mengstack/internal/kernel/response"
"mengstack/internal/modules/auth/application"
"mengstack/internal/modules/auth/domain"
"github.com/gin-gonic/gin"
)
type Handler struct {
svc *application.Service
}
func NewHandler(svc *application.Service) *Handler {
return &Handler{svc: svc}
}
// Register godoc
// @Summary 用户注册
// @Description 使用邮箱、用户名和密码注册新用户,返回 JWT token
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body domain.RegisterRequest true "注册信息"
// @Success 200 {object} response.Response{data=domain.TokenPair}
// @Failure 400 {object} response.Response
// @Failure 409 {object} response.Response
// @Router /auth/register [post]
func (h *Handler) Register(c *gin.Context) {
var req domain.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
tokens, err := h.svc.Register(c.Request.Context(), req)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, tokens)
}
// Login godoc
// @Summary 用户登录
// @Description 使用邮箱和密码登录,返回 JWT token
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body domain.LoginRequest true "登录信息"
// @Success 200 {object} response.Response{data=domain.TokenPair}
// @Failure 400 {object} response.Response
// @Failure 401 {object} response.Response
// @Router /auth/login [post]
func (h *Handler) Login(c *gin.Context) {
var req domain.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
tokens, err := h.svc.Login(c.Request.Context(), req)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, tokens)
}
// RefreshToken godoc
// @Summary 刷新 Token
// @Description 使用 refresh_token 获取新的 token 对
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body object true "{ \"refresh_token\": \"...\" }"
// @Success 200 {object} response.Response{data=domain.TokenPair}
// @Failure 400 {object} response.Response
// @Failure 401 {object} response.Response
// @Router /auth/refresh [post]
func (h *Handler) RefreshToken(c *gin.Context) {
var req struct {
RefreshToken string `json:"refresh_token" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
tokens, err := h.svc.RefreshToken(c.Request.Context(), req.RefreshToken)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, tokens)
}
// ChangePassword godoc
// @Summary 修改密码
// @Description 使用旧密码验证后设置新密码
// @Tags Auth
// @Accept json
// @Produce json
// @Param request body domain.ChangePasswordRequest true "密码修改请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 401 {object} response.Response
// @Security Bearer
// @Security TenantID
// @Router /password [post]
func (h *Handler) ChangePassword(c *gin.Context) {
userID := c.GetUint("user_id")
var req domain.ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
if err := h.svc.ChangePassword(c.Request.Context(), userID, req); err != nil {
response.HandleError(c, err)
return
}
response.Success(c, nil)
}
// GetProfile godoc
// @Summary 获取当前用户信息
// @Description 返回当前认证用户的个人资料
// @Tags Auth
// @Produce json
// @Success 200 {object} response.Response{data=domain.UserDTO}
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Security Bearer
// @Security TenantID
// @Router /profile [get]
func (h *Handler) GetProfile(c *gin.Context) {
userID := c.GetUint("user_id")
dto, err := h.svc.GetProfile(c.Request.Context(), userID)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, dto)
}
// Ping godoc
// @Summary 健康探测
// @Description 返回 pong,用于验证认证和多租户中间件是否正常
// @Tags System
// @Produce json
// @Success 200 {object} response.Response
// @Security Bearer
// @Security TenantID
// @Router /ping [get]
func (h *Handler) Ping(c *gin.Context) {
response.Success(c, gin.H{"message": "pong"})
}
// ListUsers godoc
// @Summary 用户列表
// @Description 分页获取当前租户下的用户列表,支持搜索和状态过滤
// @Tags Users
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Param search query string false "搜索关键词"
// @Param status query int false "状态过滤"
// @Success 200 {object} response.Response
// @Security Bearer
// @Security TenantID
// @Router /users [get]
func (h *Handler) ListUsers(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
search := c.Query("search")
var status *int
if s := c.Query("status"); s != "" {
v, _ := strconv.Atoi(s)
status = &v
}
dtos, total, err := h.svc.ListUsers(c.Request.Context(), page, pageSize, search, status)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, gin.H{"items": dtos, "total": total, "page": page})
}
// GetUser godoc
// @Summary 获取用户详情
// @Tags Users
// @Produce json
// @Param id path int true "用户 ID"
// @Success 200 {object} response.Response{data=domain.UserDTO}
// @Security Bearer
// @Security TenantID
// @Router /users/{id} [get]
func (h *Handler) GetUser(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Fail(c, response.NewBadRequest("invalid user id"))
return
}
dto, err := h.svc.GetUser(c.Request.Context(), uint(id))
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, dto)
}
// CreateUser godoc
// @Summary 创建用户
// @Tags Users
// @Accept json
// @Produce json
// @Param request body domain.CreateUserRequest true "用户信息"
// @Success 200 {object} response.Response{data=domain.UserDTO}
// @Security Bearer
// @Security TenantID
// @Router /users [post]
func (h *Handler) CreateUser(c *gin.Context) {
var req domain.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
dto, err := h.svc.CreateUser(c.Request.Context(), req)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, dto)
}
// UpdateUser godoc
// @Summary 更新用户
// @Tags Users
// @Accept json
// @Produce json
// @Param id path int true "用户 ID"
// @Param request body domain.UpdateUserRequest true "更新内容"
// @Success 200 {object} response.Response{data=domain.UserDTO}
// @Security Bearer
// @Security TenantID
// @Router /users/{id} [put]
func (h *Handler) UpdateUser(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Fail(c, response.NewBadRequest("invalid user id"))
return
}
var req domain.UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, response.NewBadRequest("invalid request body"))
return
}
dto, err := h.svc.UpdateUser(c.Request.Context(), uint(id), req)
if err != nil {
response.HandleError(c, err)
return
}
response.Success(c, dto)
}
// DeleteUser godoc
// @Summary 删除用户
// @Tags Users
// @Param id path int true "用户 ID"
// @Success 200 {object} response.Response
// @Security Bearer
// @Security TenantID
// @Router /users/{id} [delete]
func (h *Handler) DeleteUser(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Fail(c, response.NewBadRequest("invalid user id"))
return
}
if err := h.svc.DeleteUser(c.Request.Context(), uint(id)); err != nil {
response.HandleError(c, err)
return
}
response.Success(c, nil)
}