refactor: admin 模块按 handler/model/service 分层

This commit is contained in:
root
2026-03-09 21:13:49 +08:00
parent 7591a443d9
commit f45940cbc5
11 changed files with 101 additions and 38 deletions
+56
View File
@@ -0,0 +1,56 @@
package admin
import (
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
adminhandler "wx_service/internal/admin/handler"
adminmodel "wx_service/internal/admin/model"
adminservice "wx_service/internal/admin/service"
)
type Admin = adminmodel.Admin
type Handler = adminhandler.Handler
type Service = adminservice.Service
type Claims = adminservice.Claims
type LoginResult = adminservice.LoginResult
type ListMiniProgramsQuery = adminservice.ListMiniProgramsQuery
type MiniProgramItem = adminservice.MiniProgramItem
type ListMiniProgramsResult = adminservice.ListMiniProgramsResult
type CreateMiniProgramInput = adminservice.CreateMiniProgramInput
type UpdateMiniProgramInput = adminservice.UpdateMiniProgramInput
type MiniProgramDetailStats = adminservice.MiniProgramDetailStats
type OverviewStats = adminservice.OverviewStats
type MiniProgramStatsItem = adminservice.MiniProgramStatsItem
type UserGrowthPoint = adminservice.UserGrowthPoint
var (
ErrInvalidCredentials = adminservice.ErrInvalidCredentials
ErrInvalidToken = adminservice.ErrInvalidToken
ErrMissingJWTSecret = adminservice.ErrMissingJWTSecret
ErrMiniProgramNotFound = adminservice.ErrMiniProgramNotFound
ErrMiniProgramHasUsers = adminservice.ErrMiniProgramHasUsers
ErrMiniProgramAppIDUsed = adminservice.ErrMiniProgramAppIDUsed
ErrInvalidInput = adminservice.ErrInvalidInput
)
const ContextAdminClaimsKey = adminhandler.ContextAdminClaimsKey
func NewService(db *gorm.DB, jwtSecret string, tokenTTL time.Duration, defaultUsername, defaultPassword string) *Service {
return adminservice.NewService(db, jwtSecret, tokenTTL, defaultUsername, defaultPassword)
}
func NewHandler(svc *Service) *Handler {
return adminhandler.NewHandler(svc)
}
func AuthMiddleware(svc *Service) gin.HandlerFunc {
return adminhandler.AuthMiddleware(svc)
}
func CurrentAdminClaims(c *gin.Context) (*Claims, bool) {
return adminhandler.CurrentAdminClaims(c)
}
@@ -1,4 +1,4 @@
package admin
package handler
import (
"errors"
@@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
adminservice "wx_service/internal/admin/service"
"wx_service/internal/model"
)
@@ -24,11 +25,11 @@ func (h *Handler) Login(c *gin.Context) {
result, err := h.svc.Login(c.Request.Context(), req.Username, req.Password)
if err != nil {
switch {
case errors.Is(err, ErrInvalidCredentials):
case errors.Is(err, adminservice.ErrInvalidCredentials):
c.JSON(http.StatusUnauthorized, model.Error(http.StatusUnauthorized, "用户名或密码错误"))
case errors.Is(err, ErrMissingJWTSecret):
case errors.Is(err, adminservice.ErrMissingJWTSecret):
c.JSON(http.StatusServiceUnavailable, model.Error(http.StatusServiceUnavailable, "管理员认证未配置,请检查 JWT_SECRET"))
case errors.Is(err, ErrInvalidInput):
case errors.Is(err, adminservice.ErrInvalidInput):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "用户名和密码不能为空"))
default:
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "admin login failed"))
@@ -1,4 +1,4 @@
package admin
package handler
import (
"errors"
@@ -6,13 +6,15 @@ import (
"strings"
"github.com/gin-gonic/gin"
adminservice "wx_service/internal/admin/service"
)
type Handler struct {
svc *Service
svc *adminservice.Service
}
func NewHandler(svc *Service) *Handler {
func NewHandler(svc *adminservice.Service) *Handler {
return &Handler{svc: svc}
}
@@ -1,4 +1,4 @@
package admin
package handler
import (
"net/http"
@@ -6,12 +6,13 @@ import (
"github.com/gin-gonic/gin"
adminservice "wx_service/internal/admin/service"
"wx_service/internal/model"
)
const ContextAdminClaimsKey = "adminClaims"
func AuthMiddleware(svc *Service) gin.HandlerFunc {
func AuthMiddleware(svc *adminservice.Service) gin.HandlerFunc {
return func(c *gin.Context) {
token := extractBearerToken(c.GetHeader("Authorization"))
if token == "" {
@@ -30,12 +31,12 @@ func AuthMiddleware(svc *Service) gin.HandlerFunc {
}
}
func CurrentAdminClaims(c *gin.Context) (*Claims, bool) {
func CurrentAdminClaims(c *gin.Context) (*adminservice.Claims, bool) {
value, ok := c.Get(ContextAdminClaimsKey)
if !ok {
return nil, false
}
claims, ok := value.(*Claims)
claims, ok := value.(*adminservice.Claims)
return claims, ok
}
@@ -1,4 +1,4 @@
package admin
package handler
import (
"errors"
@@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin"
adminservice "wx_service/internal/admin/service"
"wx_service/internal/model"
)
@@ -15,7 +16,7 @@ func (h *Handler) ListMiniPrograms(c *gin.Context) {
page, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page", "1")))
pageSize, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page_size", "20")))
data, err := h.svc.ListMiniPrograms(c.Request.Context(), ListMiniProgramsQuery{
data, err := h.svc.ListMiniPrograms(c.Request.Context(), adminservice.ListMiniProgramsQuery{
Page: page,
PageSize: pageSize,
Keyword: c.Query("keyword"),
@@ -36,7 +37,7 @@ func (h *Handler) GetMiniProgram(c *gin.Context) {
data, err := h.svc.GetMiniProgram(c.Request.Context(), id)
if err != nil {
if errors.Is(err, ErrMiniProgramNotFound) {
if errors.Is(err, adminservice.ErrMiniProgramNotFound) {
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
return
}
@@ -55,7 +56,7 @@ func (h *Handler) GetMiniProgramStats(c *gin.Context) {
data, err := h.svc.GetMiniProgramStats(c.Request.Context(), id)
if err != nil {
if errors.Is(err, ErrMiniProgramNotFound) {
if errors.Is(err, adminservice.ErrMiniProgramNotFound) {
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
return
}
@@ -79,7 +80,7 @@ func (h *Handler) CreateMiniProgram(c *gin.Context) {
return
}
data, err := h.svc.CreateMiniProgram(c.Request.Context(), CreateMiniProgramInput{
data, err := h.svc.CreateMiniProgram(c.Request.Context(), adminservice.CreateMiniProgramInput{
Name: req.Name,
AppID: req.AppID,
AppSecret: req.AppSecret,
@@ -87,9 +88,9 @@ func (h *Handler) CreateMiniProgram(c *gin.Context) {
})
if err != nil {
switch {
case errors.Is(err, ErrInvalidInput):
case errors.Is(err, adminservice.ErrInvalidInput):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "name/app_id/app_secret are required"))
case errors.Is(err, ErrMiniProgramAppIDUsed):
case errors.Is(err, adminservice.ErrMiniProgramAppIDUsed):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "app_id already exists"))
default:
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create mini-program failed"))
@@ -119,7 +120,7 @@ func (h *Handler) UpdateMiniProgram(c *gin.Context) {
return
}
data, err := h.svc.UpdateMiniProgram(c.Request.Context(), id, UpdateMiniProgramInput{
data, err := h.svc.UpdateMiniProgram(c.Request.Context(), id, adminservice.UpdateMiniProgramInput{
Name: req.Name,
AppID: req.AppID,
AppSecret: req.AppSecret,
@@ -127,11 +128,11 @@ func (h *Handler) UpdateMiniProgram(c *gin.Context) {
})
if err != nil {
switch {
case errors.Is(err, ErrMiniProgramNotFound):
case errors.Is(err, adminservice.ErrMiniProgramNotFound):
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
case errors.Is(err, ErrInvalidInput):
case errors.Is(err, adminservice.ErrInvalidInput):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "name/app_id are required"))
case errors.Is(err, ErrMiniProgramAppIDUsed):
case errors.Is(err, adminservice.ErrMiniProgramAppIDUsed):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "app_id already exists"))
default:
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update mini-program failed"))
@@ -151,9 +152,9 @@ func (h *Handler) DeleteMiniProgram(c *gin.Context) {
err = h.svc.DeleteMiniProgram(c.Request.Context(), id)
if err != nil {
switch {
case errors.Is(err, ErrMiniProgramNotFound):
case errors.Is(err, adminservice.ErrMiniProgramNotFound):
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
case errors.Is(err, ErrMiniProgramHasUsers):
case errors.Is(err, adminservice.ErrMiniProgramHasUsers):
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mini program has related users"))
default:
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete mini-program failed"))
@@ -1,4 +1,4 @@
package admin
package handler
import (
"net/http"
@@ -1,4 +1,4 @@
package admin
package model
import (
"time"
@@ -1,4 +1,4 @@
package admin
package service
import (
"context"
@@ -7,6 +7,8 @@ import (
"strings"
"time"
adminmodel "wx_service/internal/admin/model"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
@@ -14,7 +16,7 @@ import (
func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
var count int64
if err := s.db.WithContext(ctx).Model(&Admin{}).Count(&count).Error; err != nil {
if err := s.db.WithContext(ctx).Model(&adminmodel.Admin{}).Count(&count).Error; err != nil {
return err
}
if count > 0 {
@@ -35,7 +37,7 @@ func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
return err
}
record := &Admin{
record := &adminmodel.Admin{
Username: username,
Password: string(hashedPassword),
Role: "super_admin",
@@ -50,8 +52,8 @@ func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
}
type LoginResult struct {
Token string `json:"token"`
Admin *Admin `json:"admin"`
Token string `json:"token"`
Admin *adminmodel.Admin `json:"admin"`
}
func (s *Service) Login(ctx context.Context, username, password string) (*LoginResult, error) {
@@ -60,7 +62,7 @@ func (s *Service) Login(ctx context.Context, username, password string) (*LoginR
return nil, ErrInvalidInput
}
var admin Admin
var admin adminmodel.Admin
if err := s.db.WithContext(ctx).Where("username = ?", username).First(&admin).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrInvalidCredentials
@@ -111,7 +113,7 @@ func (s *Service) ParseToken(token string) (*Claims, error) {
return claims, nil
}
func (s *Service) generateToken(admin *Admin) (string, error) {
func (s *Service) generateToken(admin *adminmodel.Admin) (string, error) {
if len(s.jwtSecret) == 0 {
return "", ErrMissingJWTSecret
}
@@ -132,8 +134,8 @@ func (s *Service) generateToken(admin *Admin) (string, error) {
return t.SignedString(s.jwtSecret)
}
func (s *Service) GetProfile(ctx context.Context, adminID uint) (*Admin, error) {
var admin Admin
func (s *Service) GetProfile(ctx context.Context, adminID uint) (*adminmodel.Admin, error) {
var admin adminmodel.Admin
if err := s.db.WithContext(ctx).
Select("id", "username", "role", "last_login_at", "created_at", "updated_at").
Where("id = ?", adminID).
@@ -1,4 +1,4 @@
package admin
package service
import (
"context"
@@ -1,4 +1,4 @@
package admin
package service
import (
"context"
@@ -1,4 +1,4 @@
package admin
package service
import (
"errors"