refactor: admin 模块按 handler/model/service 分层
This commit is contained in:
@@ -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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
adminservice "wx_service/internal/admin/service"
|
||||||
"wx_service/internal/model"
|
"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)
|
result, err := h.svc.Login(c.Request.Context(), req.Username, req.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, ErrInvalidCredentials):
|
case errors.Is(err, adminservice.ErrInvalidCredentials):
|
||||||
c.JSON(http.StatusUnauthorized, model.Error(http.StatusUnauthorized, "用户名或密码错误"))
|
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"))
|
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, "用户名和密码不能为空"))
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "用户名和密码不能为空"))
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "admin login failed"))
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "admin login failed"))
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -6,13 +6,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
adminservice "wx_service/internal/admin/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
svc *Service
|
svc *adminservice.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(svc *Service) *Handler {
|
func NewHandler(svc *adminservice.Service) *Handler {
|
||||||
return &Handler{svc: svc}
|
return &Handler{svc: svc}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -6,12 +6,13 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
adminservice "wx_service/internal/admin/service"
|
||||||
"wx_service/internal/model"
|
"wx_service/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ContextAdminClaimsKey = "adminClaims"
|
const ContextAdminClaimsKey = "adminClaims"
|
||||||
|
|
||||||
func AuthMiddleware(svc *Service) gin.HandlerFunc {
|
func AuthMiddleware(svc *adminservice.Service) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
token := extractBearerToken(c.GetHeader("Authorization"))
|
token := extractBearerToken(c.GetHeader("Authorization"))
|
||||||
if token == "" {
|
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)
|
value, ok := c.Get(ContextAdminClaimsKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
claims, ok := value.(*Claims)
|
claims, ok := value.(*adminservice.Claims)
|
||||||
return claims, ok
|
return claims, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
+14
-13
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
adminservice "wx_service/internal/admin/service"
|
||||||
"wx_service/internal/model"
|
"wx_service/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ func (h *Handler) ListMiniPrograms(c *gin.Context) {
|
|||||||
page, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page", "1")))
|
page, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page", "1")))
|
||||||
pageSize, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page_size", "20")))
|
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,
|
Page: page,
|
||||||
PageSize: pageSize,
|
PageSize: pageSize,
|
||||||
Keyword: c.Query("keyword"),
|
Keyword: c.Query("keyword"),
|
||||||
@@ -36,7 +37,7 @@ func (h *Handler) GetMiniProgram(c *gin.Context) {
|
|||||||
|
|
||||||
data, err := h.svc.GetMiniProgram(c.Request.Context(), id)
|
data, err := h.svc.GetMiniProgram(c.Request.Context(), id)
|
||||||
if err != nil {
|
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"))
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -55,7 +56,7 @@ func (h *Handler) GetMiniProgramStats(c *gin.Context) {
|
|||||||
|
|
||||||
data, err := h.svc.GetMiniProgramStats(c.Request.Context(), id)
|
data, err := h.svc.GetMiniProgramStats(c.Request.Context(), id)
|
||||||
if err != nil {
|
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"))
|
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -79,7 +80,7 @@ func (h *Handler) CreateMiniProgram(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := h.svc.CreateMiniProgram(c.Request.Context(), CreateMiniProgramInput{
|
data, err := h.svc.CreateMiniProgram(c.Request.Context(), adminservice.CreateMiniProgramInput{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
AppID: req.AppID,
|
AppID: req.AppID,
|
||||||
AppSecret: req.AppSecret,
|
AppSecret: req.AppSecret,
|
||||||
@@ -87,9 +88,9 @@ func (h *Handler) CreateMiniProgram(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
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"))
|
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"))
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "app_id already exists"))
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create mini-program failed"))
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "create mini-program failed"))
|
||||||
@@ -119,7 +120,7 @@ func (h *Handler) UpdateMiniProgram(c *gin.Context) {
|
|||||||
return
|
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,
|
Name: req.Name,
|
||||||
AppID: req.AppID,
|
AppID: req.AppID,
|
||||||
AppSecret: req.AppSecret,
|
AppSecret: req.AppSecret,
|
||||||
@@ -127,11 +128,11 @@ func (h *Handler) UpdateMiniProgram(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, ErrMiniProgramNotFound):
|
case errors.Is(err, adminservice.ErrMiniProgramNotFound):
|
||||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
|
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"))
|
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"))
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "app_id already exists"))
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "update mini-program failed"))
|
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)
|
err = h.svc.DeleteMiniProgram(c.Request.Context(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, ErrMiniProgramNotFound):
|
case errors.Is(err, adminservice.ErrMiniProgramNotFound):
|
||||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "mini program not found"))
|
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"))
|
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mini program has related users"))
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete mini-program failed"))
|
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "delete mini-program failed"))
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
adminmodel "wx_service/internal/admin/model"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -14,7 +16,7 @@ import (
|
|||||||
|
|
||||||
func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
|
func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
|
||||||
var count int64
|
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
|
return err
|
||||||
}
|
}
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
@@ -35,7 +37,7 @@ func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
record := &Admin{
|
record := &adminmodel.Admin{
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: string(hashedPassword),
|
Password: string(hashedPassword),
|
||||||
Role: "super_admin",
|
Role: "super_admin",
|
||||||
@@ -50,8 +52,8 @@ func (s *Service) EnsureDefaultAdmin(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoginResult struct {
|
type LoginResult struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Admin *Admin `json:"admin"`
|
Admin *adminmodel.Admin `json:"admin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Login(ctx context.Context, username, password string) (*LoginResult, error) {
|
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
|
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 err := s.db.WithContext(ctx).Where("username = ?", username).First(&admin).Error; err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrInvalidCredentials
|
return nil, ErrInvalidCredentials
|
||||||
@@ -111,7 +113,7 @@ func (s *Service) ParseToken(token string) (*Claims, error) {
|
|||||||
return claims, nil
|
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 {
|
if len(s.jwtSecret) == 0 {
|
||||||
return "", ErrMissingJWTSecret
|
return "", ErrMissingJWTSecret
|
||||||
}
|
}
|
||||||
@@ -132,8 +134,8 @@ func (s *Service) generateToken(admin *Admin) (string, error) {
|
|||||||
return t.SignedString(s.jwtSecret)
|
return t.SignedString(s.jwtSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetProfile(ctx context.Context, adminID uint) (*Admin, error) {
|
func (s *Service) GetProfile(ctx context.Context, adminID uint) (*adminmodel.Admin, error) {
|
||||||
var admin Admin
|
var admin adminmodel.Admin
|
||||||
if err := s.db.WithContext(ctx).
|
if err := s.db.WithContext(ctx).
|
||||||
Select("id", "username", "role", "last_login_at", "created_at", "updated_at").
|
Select("id", "username", "role", "last_login_at", "created_at", "updated_at").
|
||||||
Where("id = ?", adminID).
|
Where("id = ?", adminID).
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package admin
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
Reference in New Issue
Block a user