Files
2026-08-25 17:59:42 +08:00

112 lines
3.5 KiB
Go

package handler
import (
"errors"
"net/http"
"strings"
"juhe-factory/api/internal/model"
"juhe-factory/api/internal/service"
"github.com/gin-gonic/gin"
)
const adminContextKey = "admin_user"
type AdminAuth struct{ service *service.Auth }
func NewAdminAuth(service *service.Auth) *AdminAuth { return &AdminAuth{service: service} }
func (h *AdminAuth) Login(c *gin.Context) {
var body struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&body); err != nil {
fail(c, http.StatusBadRequest, "invalid_request", "请输入管理员账号和密码")
return
}
pair, err := h.service.Login(body.Username, body.Password)
if err != nil {
fail(c, http.StatusUnauthorized, "invalid_credentials", err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"data": pair})
}
func (h *AdminAuth) Refresh(c *gin.Context) {
var body struct {
RefreshToken string `json:"refresh_token" binding:"required"`
}
if err := c.ShouldBindJSON(&body); err != nil {
fail(c, http.StatusBadRequest, "invalid_request", "缺少刷新令牌")
return
}
pair, err := h.service.Refresh(body.RefreshToken)
if err != nil {
fail(c, http.StatusUnauthorized, "invalid_refresh_token", err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"data": pair})
}
func (h *AdminAuth) Logout(c *gin.Context) {
var body struct {
RefreshToken string `json:"refresh_token"`
}
_ = c.ShouldBindJSON(&body)
if err := h.service.Logout(body.RefreshToken); err != nil {
fail(c, http.StatusInternalServerError, "logout_failed", "退出登录失败")
return
}
c.Status(http.StatusNoContent)
}
func (h *AdminAuth) ChangePassword(c *gin.Context) {
var body struct {
CurrentPassword string `json:"current_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required"`
ConfirmPassword string `json:"confirm_password" binding:"required"`
}
if err := c.ShouldBindJSON(&body); err != nil {
fail(c, http.StatusBadRequest, "invalid_request", "请完整填写密码")
return
}
admin := currentAdmin(c)
if err := h.service.ChangePassword(admin.ID, body.CurrentPassword, body.NewPassword, body.ConfirmPassword); err != nil {
if errors.Is(err, service.ErrReauthFailed) || errors.Is(err, service.ErrAdminPasswordConfirmation) || errors.Is(err, service.ErrAdminPasswordLength) {
_ = h.service.Audit(admin, "password_change_failed", "admin-auth", admin.ID.String(), err.Error(), c.ClientIP(), c.GetString("trace_id"), nil)
fail(c, http.StatusBadRequest, "password_invalid", err.Error())
return
}
fail(c, http.StatusInternalServerError, "password_update_failed", "密码修改失败,请稍后重试")
return
}
_ = h.service.Audit(admin, "password_changed", "admin-auth", admin.ID.String(), "", c.ClientIP(), c.GetString("trace_id"), nil)
c.Status(http.StatusNoContent)
}
func (h *AdminAuth) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
raw := strings.TrimSpace(strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer "))
admin, err := h.service.Authenticate(raw)
if err != nil {
fail(c, http.StatusUnauthorized, "unauthorized", err.Error())
c.Abort()
return
}
c.Set(adminContextKey, admin)
c.Next()
}
}
func currentAdmin(c *gin.Context) *model.AdminUser {
value, _ := c.Get(adminContextKey)
admin, _ := value.(*model.AdminUser)
return admin
}
func fail(c *gin.Context, status int, code, message string) {
c.JSON(status, gin.H{"code": code, "message": message, "trace_id": c.GetString("trace_id")})
}