Some checks failed
CI / Build & Test (push) Failing after 1m31s
- 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
309 lines
8.7 KiB
Go
309 lines
8.7 KiB
Go
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)
|
||
}
|