Skip to content

自定义处理器

当预置 API 无法满足需求时,可以创建自定义处理器。

基本自定义 API

定义自定义 API

go
package resources

import (
    "github.com/ilxqx/vef-framework-go/api"
    "github.com/gofiber/fiber/v2"
)

type UserResource struct {
    api.Resource
}

func NewUserResource() api.Resource {
    resource := &UserResource{
        Resource: api.NewResource("smp/sys/user"),
    }
    
    // Register custom API
    resource.RegisterApi("reset_password", resource.ResetPassword)
    resource.RegisterApi("change_status", resource.ChangeStatus)
    
    return resource
}

实现自定义处理器

go
// ResetPassword custom API handler
func (r *UserResource) ResetPassword(ctx fiber.Ctx) error {
    // Parse parameters
    var params struct {
        UserId      string `json:"userId" validate:"required"`
        NewPassword string `json:"newPassword" validate:"required,min=8"`
    }
    
    if err := ctx.BodyParser(&params); err != nil {
        return api.Error(ctx, "Invalid parameters")
    }
    
    // Validate parameters
    if err := api.Validate(&params); err != nil {
        return api.ValidationError(ctx, err)
    }
    
    // Business logic
    hashedPassword, _ := bcrypt.GenerateFromPassword(
        []byte(params.NewPassword),
        bcrypt.DefaultCost,
    )
    
    result := orm.DB().Model(&models.User{}).
        Where("id = ?", params.UserId).
        Update("password", string(hashedPassword))
    
    if result.Error != nil {
        return api.Error(ctx, "Failed to reset password")
    }
    
    return api.Success(ctx, nil)
}

参数注入

自动参数解析

go
type ResetPasswordParams struct {
    UserId      string `json:"userId" validate:"required"`
    NewPassword string `json:"newPassword" validate:"required,min=8"`
}

func (r *UserResource) ResetPassword(ctx fiber.Ctx, params ResetPasswordParams) error {
    // params is automatically parsed and validated
    
    hashedPassword, _ := bcrypt.GenerateFromPassword(
        []byte(params.NewPassword),
        bcrypt.DefaultCost,
    )
    
    result := orm.DB().Model(&models.User{}).
        Where("id = ?", params.UserId).
        Update("password", string(hashedPassword))
    
    if result.Error != nil {
        return api.Error(ctx, "Failed to reset password")
    }
    
    return api.Success(ctx, nil)
}

资源字段注入

go
// Resource field is automatically injected
func (r *UserResource) GetStatistics(ctx fiber.Ctx) error {
    resource := r.Resource // Access resource information
    
    var stats struct {
        TotalUsers  int64 `json:"totalUsers"`
        ActiveUsers int64 `json:"activeUsers"`
    }
    
    orm.DB().Model(&models.User{}).Count(&stats.TotalUsers)
    orm.DB().Model(&models.User{}).Where("is_active", true).Count(&stats.ActiveUsers)
    
    return api.Success(ctx, stats)
}

依赖注入

注入服务

go
type UserResource struct {
    api.Resource
    emailService *services.EmailService
    cacheService *services.CacheService
}

func NewUserResource(
    emailService *services.EmailService,
    cacheService *services.CacheService,
) api.Resource {
    resource := &UserResource{
        Resource:     api.NewResource("smp/sys/user"),
        emailService: emailService,
        cacheService: cacheService,
    }
    
    resource.RegisterApi("send_verification", resource.SendVerification)
    
    return resource
}

func (r *UserResource) SendVerification(ctx fiber.Ctx, params struct {
    UserId string `json:"userId" validate:"required"`
}) error {
    // Use injected services
    user, err := r.cacheService.GetUser(params.UserId)
    if err != nil {
        return api.Error(ctx, "User not found")
    }
    
    if err := r.emailService.SendVerificationEmail(user.Email); err != nil {
        return api.Error(ctx, "Failed to send email")
    }
    
    return api.Success(ctx, nil)
}

权限控制

设置 API 权限

go
func NewUserResource() api.Resource {
    resource := &UserResource{
        Resource: api.NewResource("smp/sys/user"),
    }
    
    // Register API with permission
    resource.RegisterApiWithPermission(
        "reset_password",
        "user:reset_password",
        resource.ResetPassword,
    )
    
    return resource
}

手动权限检查

go
func (r *UserResource) AdminOperation(ctx fiber.Ctx) error {
    // Check permission manually
    currentUser := contextx.GetCurrentUser(ctx)
    
    if !currentUser.HasPermission("admin:manage") {
        return api.Forbidden(ctx, "Permission denied")
    }
    
    // Perform admin operation
    return api.Success(ctx, nil)
}

响应格式

成功响应

go
// Return data
return api.Success(ctx, map[string]interface{}{
    "id":       user.Id,
    "username": user.Username,
})

// Return with message
return api.SuccessWithMessage(ctx, "Operation completed", data)

// Return empty success
return api.Success(ctx, nil)

错误响应

go
// General error
return api.Error(ctx, "Something went wrong")

// Validation error
return api.ValidationError(ctx, validationErrors)

// Not found
return api.NotFound(ctx, "User not found")

// Forbidden
return api.Forbidden(ctx, "Permission denied")

// Custom status code
return api.ErrorWithCode(ctx, 400, "Bad request")

事务处理

go
func (r *UserResource) TransferBalance(ctx fiber.Ctx, params struct {
    FromUserId string  `json:"fromUserId" validate:"required"`
    ToUserId   string  `json:"toUserId" validate:"required"`
    Amount     float64 `json:"amount" validate:"required,gt=0"`
}) error {
    // Execute in transaction
    err := orm.Transaction(func(tx *gorm.DB) error {
        // Deduct from sender
        if err := tx.Model(&models.User{}).
            Where("id = ?", params.FromUserId).
            Update("balance", gorm.Expr("balance - ?", params.Amount)).Error; err != nil {
            return err
        }
        
        // Add to receiver
        if err := tx.Model(&models.User{}).
            Where("id = ?", params.ToUserId).
            Update("balance", gorm.Expr("balance + ?", params.Amount)).Error; err != nil {
            return err
        }
        
        return nil
    })
    
    if err != nil {
        return api.Error(ctx, "Transfer failed")
    }
    
    return api.Success(ctx, nil)
}

完整示例

go
package resources

import (
    "my-app/internal/modules/user/models"
    "my-app/internal/services"
    
    "github.com/ilxqx/vef-framework-go/api"
    "github.com/ilxqx/vef-framework-go/apis"
    "github.com/ilxqx/vef-framework-go/contextx"
    "github.com/ilxqx/vef-framework-go/orm"
    "github.com/gofiber/fiber/v2"
    "golang.org/x/crypto/bcrypt"
)

type UserResource struct {
    api.Resource
    apis.FindPageApi[models.User, payloads.UserSearch]
    apis.CreateApi[models.User, payloads.UserParams]
    
    emailService *services.EmailService
}

func NewUserResource(emailService *services.EmailService) api.Resource {
    resource := &UserResource{
        Resource:     api.NewResource("smp/sys/user"),
        emailService: emailService,
        
        FindPageApi: apis.NewFindPageApi[models.User, payloads.UserSearch](),
        CreateApi:   apis.NewCreateApi[models.User, payloads.UserParams](),
    }
    
    // Register custom APIs
    resource.RegisterApiWithPermission("reset_password", "user:reset_password", resource.ResetPassword)
    resource.RegisterApiWithPermission("change_status", "user:update", resource.ChangeStatus)
    resource.RegisterApi("get_profile", resource.GetProfile)
    
    return resource
}

func (r *UserResource) ResetPassword(ctx fiber.Ctx, params struct {
    UserId      string `json:"userId" validate:"required"`
    NewPassword string `json:"newPassword" validate:"required,min=8"`
}) error {
    hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(params.NewPassword), bcrypt.DefaultCost)
    
    result := orm.DB().Model(&models.User{}).
        Where("id = ?", params.UserId).
        Update("password", string(hashedPassword))
    
    if result.Error != nil {
        return api.Error(ctx, "Failed to reset password")
    }
    
    // Send notification email
    go r.emailService.SendPasswordResetNotification(params.UserId)
    
    return api.Success(ctx, nil)
}

func (r *UserResource) ChangeStatus(ctx fiber.Ctx, params struct {
    UserId   string `json:"userId" validate:"required"`
    IsActive bool   `json:"isActive"`
}) error {
    result := orm.DB().Model(&models.User{}).
        Where("id = ?", params.UserId).
        Update("is_active", params.IsActive)
    
    if result.Error != nil {
        return api.Error(ctx, "Failed to change status")
    }
    
    return api.Success(ctx, nil)
}

func (r *UserResource) GetProfile(ctx fiber.Ctx) error {
    currentUser := contextx.GetCurrentUser(ctx)
    
    var user models.User
    if err := orm.DB().
        Preload("Department").
        Preload("Roles").
        First(&user, "id = ?", currentUser.Id).Error; err != nil {
        return api.NotFound(ctx, "User not found")
    }
    
    // Remove sensitive data
    user.Password = ""
    
    return api.Success(ctx, user)
}

下一步

基于 Apache License 2.0 许可发布