4--用户注册接口与BCrypt加密

我们已经把用户表创建完毕,接下来便是用户注册与登录的部分

go get -u golang.org/x/crypto/bcrypt加入bcrypt包

一. 统一相应封装

pkg/response/response.go

package response

import (
    "github.com/gin-gonic/gin"
)

type Response struct {
    Code int         `json:"code"`
    Msg  string      `json:"message"`
    Data interface{} `json:"data,omitempty"`
}

func Success(c *gin.Context, data interface{}) {
    c.JSON(200, Response{
        Code: 0,
        Msg:  "success",
        Data: data,
    })
}

func Fail(c *gin.Context, code int, msg string) {
    c.JSON(200, Response{
        Code: code,
        Msg:  msg,
    })
}

二. 用户数据访问层

internal/repository/user.go

package repository

import "github.com/Chuan81/secgo-mall/internal/model"

// CreateUser 创建一个新的用户记录
func CreateUser(user *model.User) error {
	return DB.Create(user).Error
}

// GetUserByEmail 根据邮箱查询用户
func GetUserByEmail(email string) (*model.User, error) {
	var user model.User
	err := DB.Where("email = ?", email).First(&user).Error
	if err != nil {
		return nil, err
	}
	return &user, nil
}

// GetUserByPhone 根据手机号查询用户
func GetUserByPhone(phone string) (*model.User, error) {
	var user model.User
	err := DB.Where("phone = ?", phone).First(&user).Error
	if err != nil {
		return nil, err
	}
	return &user, nil
}

// GetUserByUsername 根据用户名查询用户
func GetUserByUsername(username string) (*model.User, error) {
	var user model.User
	err := DB.Where("username = ?", username).First(&user).Error
	if err != nil {
		return nil, err
	}
	return &user, nil
}

三. 请求结构体(DTO)

DTO = Data Transfer Object(数据传输对象)
简单说就是:专门用来接收前端参数的"容器"

internal/api/handlers/dto/user.go

package dto

// RegisterRequest 定义了用户注册请求的DTO结构体,包含用户名、邮箱、手机号和密码等字段
type RegisterRequest struct {
	Username string `json:"username" binding:"required,min=3,max=20"`  // 用户名,必填,长度限制为3-20个字符
	Email    string `json:"email" binding:"required,email"`            // 邮箱,必填,必须是有效的邮箱格式
	Phone    string `json:"phone" binding:"required,e164"`             // 手机号,必填,必须符合E.164国际电话号码格式
	Password string `json:"password" binding:"required,min=6,max=255"` // 密码,必填,长度限制为6-255个字符
}

四. 用户业务逻辑层

internal/service/user.go

package service

import (
	"errors"

	"github.com/Chuan81/secgo-mall/internal/api/handlers/dto"
	"github.com/Chuan81/secgo-mall/internal/model"
	"github.com/Chuan81/secgo-mall/internal/repository"
	"golang.org/x/crypto/bcrypt"
)

func Register(req *dto.RegisterRequest) (*model.User, error) {
	// 检查用户名是否已存在
	if _, err := repository.GetUserByUsername(req.Username); err == nil {
		return nil, errors.New("username already exists")
	}

	// 检查邮箱是否已存在
	if _, err := repository.GetUserByEmail(req.Email); err == nil {
		return nil, errors.New("email already exists")
	}

	// 检查手机号是否已存在
	if _, err := repository.GetUserByPhone(req.Phone); err == nil {
		return nil, errors.New("phone number already exists")
	}

	// bcrypt 加密密码
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		return nil, err
	}

	// 创建用户
	user := &model.User{
		Username: req.Username,
		Email:    req.Email,
		Phone:    req.Phone,
		Password: string(hashedPassword),
	}
	if err := repository.CreateUser(user); err != nil {
		return nil, err
	}

	return user, nil
}

五. 用户控制器

internal/api/handlers/user.go

package handlers

import (
	"github.com/Chuan81/secgo-mall/internal/api/handlers/dto"
	"github.com/Chuan81/secgo-mall/internal/service"
	"github.com/Chuan81/secgo-mall/pkg/response"
	"github.com/gin-gonic/gin"
)

func Register(c *gin.Context) {
	var req dto.RegisterRequest

	// 绑定请求参数到DTO结构体,并进行验证
	if err := c.ShouldBindJSON(&req); err != nil {
		response.Fail(c, 400, "invalid request parameters: "+err.Error())
		return
	}

	// 调用服务层的注册逻辑
	user, err := service.Register(&req)
	if err != nil {
		response.Fail(c, 500, "registration failed: "+err.Error())
		return
	}

	// 注册成功,返回用户信息(不包含密码)
	response.Sucess(c, gin.H{
		"uid":       user.Uid,
		"username":  user.Username,
		"email":     user.Email,
		"phone":     user.Phone,
		"createdAt": user.CreatedAt,
	})
}

六. 添加路由

cmd/secgo-mall/main.go

import (
    // ... existing imports ...
    "github.com/Chuan81/secgo-mall/internal/api/handlers"
)

func main() {
    // ... existing code ...

    r := gin.Default()

    // 注册路由
    r.POST("/api/user/register", handlers.Register)

    r.GET("/ping", func(c *gin.Context) {
        // ...
    })

    // ... rest of code ...
}

七. 测试

在 Postman 中创建 admin 用户的步骤:

1. 新建请求

Method: POST

URL: http://localhost:8080/api/user/register

2. 配置 Body

在 Body 选项卡中选择 raw,格式选择 JSON,填写:

{
    "username": "admin",
    "email": "admin@secgo.com",
    "phone": "13800000000",
    "password": "admin123"
}

3. 发送请求

点击 Send 按钮,预期返回:

{
    "code": 0,
    "message": "success",
    "data": {
        "uid": 1,
        "username": "admin",
        "email": "admin@secgo.com",
        "phone": "13800000000",
        "createdAt": "2026-04-10T..."
    }
}

如果你的注册接口还未实现或者数据库未启动,会返回相应错误。

user_admin_register

admin_in_user_table

posted @ 2026-04-17 17:32  Chuan81  阅读(15)  评论(0)    收藏  举报