AI图像生成网站
目录
一、项目介绍
二、雪花算法
三、JWT认证与令牌桶算法
四、项目架构
五、图床上传与图像生成API搭建
六、项目测试与优化
四、项目架构
本项目的后端基于Golang和Gin框架开发,主要包括的模块有:
bash
backend/
├── controller // 路由控制器
├── dao // 数据访问模块
│ ├── mysql // MySQL 数据库操作
│ └── api // 调用第三方 API 的模块
├── logic // 核心业务逻辑
├── models // 数据结构定义
├── pkg // 工具包(JWT、Snowflake 等)
└── router // 路由定义
- controller: 负责处理 HTTP 请求并将请求数据传递给逻辑层,控制器层作为入口,将不同的请求指向相应的业务逻辑。
- dao/mysql: 负责数据库操作,封装了数据查询和持久化逻辑,便于管理数据库交互。
- logic: 业务逻辑层,实现具体的业务功能,比如用户的登录注册和相关的账号管理。
- model: 用于定义数据模型与表结构,是项目中数据对象的核心描述。
以用户登录功能为例,从前端请求到后端处理的完整调用流程如下:
1. 前端发送请求
Vue.js 前端会通过表单收集用户输入的 username
和password
,并调用后端登录 API(如 POST /api/login
)。请求中会包含 JSON 格式的用户凭证。
2. 路由层解析请求
后端的路由通过 controller
文件夹中定义的控制器来接收前端的 HTTP 请求。例如:
go
v1.POST("/login", controller.LoginHandler)
3.控制器层处理请求
控制器负责将请求转发到逻辑层,并对输入参数进行基本的校验:
go
// LoginHandler 登录业务
func LoginHandler(c *gin.Context) {
// 1、获取请求参数及参数校验
var u *models.LoginForm
if err := c.ShouldBindJSON(&u); err != nil {
// 请求参数有误,直接返回响应
zap.L().Error("Login with invalid param", zap.Error(err))
// 判断err是不是 validator.ValidationErrors类型的errors
errs, ok := err.(validator.ValidationErrors)
if !ok {
// 非validator.ValidationErrors类型错误直接返回
ResponseError(c, CodeInvalidParams) // 请求参数错误
return
}
// validator.ValidationErrors类型错误则进行翻译
ResponseErrorWithMsg(c, CodeInvalidParams, removeTopStruct(errs.Translate(trans)))
return
}
// 2、业务逻辑处理------登录
user, err := logic.Login(u)
if err != nil {
zap.L().Error("logic.Login failed", zap.String("username", u.UserName), zap.Error(err))
if err.Error() == mysql.ErrorUserNotExit {
ResponseError(c, CodeUserNotExist)
return
}
ResponseError(c, CodeInvalidParams)
return
}
// 3、返回响应
ResponseSuccess(c, gin.H{
"user_id": fmt.Sprintf("%d", user.UserID), //js识别的最大值:id值大于1<<53-1 int64: i<<63-1
"user_name": user.UserName,
"access_token": user.AccessToken,
"refresh_token": user.RefreshToken,
"avatarImageUrl": user.Avatar,
"gender": user.Gender,
})
}
这里使用了 models.LoginForm
来解析和校验前端传递的 JSON 数据。
go
// LoginForm 登录请求参数
type LoginForm struct {
UserName string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
4. 逻辑层处理核心业务逻辑
logic
文件夹中封装了核心的业务逻辑,例如验证用户凭证、生成令牌等:
go
func Login(username, password string) (string, error) {
user = &models.User{
UserName: p.UserName,
Password: p.Password,
}
if err := mysql.Login(user); err != nil {
return nil, err
}
// 生成JWT
accessToken, refreshToken, err := jwt.GenToken(user.UserID, user.UserName)
if err != nil {
return
}
user.AccessToken = accessToken
user.RefreshToken = refreshToken
return
}
5. 数据访问层与数据库交互
dao/mysql
中的方法负责与数据库交互,如查询用户信息:
go
// Login 登录业务
func Login(user *models.User) (err error) {
originPassword := user.Password // 记录一下原始密码(用户登录的密码)
sqlStr := "select user_id, username, password,avatar,gender from user where username = ?"
err = db.Get(user, sqlStr, user.UserName)
// 查询数据库出错
if err != nil && err != sql.ErrNoRows {
return err
}
// 用户不存在
if err == sql.ErrNoRows {
return errors.New(ErrorUserNotExit)
}
// 生成加密密码与查询到的密码比较
password := encryptPassword([]byte(originPassword))
if user.Password != password {
return errors.New(ErrorPasswordWrong)
}
return nil
}
6. 返回响应
数据流逐步返回到控制器,最终通过 JSON 格式的响应返回给前端。
go
// 3、返回响应
ResponseSuccess(c, gin.H{
"user_id": fmt.Sprintf("%d", user.UserID), //js识别的最大值:id值大于1<<53-1 int64: i<<63-1
"user_name": user.UserName,
"access_token": user.AccessToken,
"refresh_token": user.RefreshToken,
"avatarImageUrl": user.Avatar,
"gender": user.Gender,
})
一些项目中的model建立技巧
1. 字段标签
(1)json 标签
指定 JSON 数据的序列化和反序列化的字段名,如:
go
UserID uint64 `json:"user_id,string"`
json:"user_id,string"
表示 JSON 中的 user_id
字段是字符串格式,但在程序中使用 uint64 类型。适用于后端接收到的数字 ID 被前端以字符串形式传递的场景,避免因类型不匹配导致解析失败。
(2) db 标签
用于指定数据库中表字段的映射,如:
go
Avatar string `db:"avatar"`
可以避免数据库字段名与代码字段名不一致引发问题。
(3) 多标签结合
字段可以同时使用 json
和 db
标签,分别处理 JSON 和数据库交互需求,如:
go
Email string `json:"email" db:"email"`
这样做可以使同一字段同时适配 JSON 序列化和数据库映射,代码更加清晰统一。
2. 自定义 UnmarshalJSON
方法
在前端提交到后端的字段中,某些字段(username
和 password
)可能是某个业务逻辑的必须项,而Golang 的默认 JSON 反序列化无法验证字段是否缺失和符合项目要求,这种情况下,我们可以通过自定义UnmarshalJSON
方法来解决这个问题。
实现细节
定义一个嵌套的 required
临时结构体,声明必需字段:
go
required := struct {
Avatar string `json:"avatar" db:"avatar"`
UserName string `json:"username" db:"username"`
Password string `json:"password" db:"password"`
Email string `json:"email" db:"email"`
Gender int `json:"gender" db:"gender"`
}{}
然后通过 json.Unmarshal 解析 JSON 数据:
go
err = json.Unmarshal(data, &required)
验证必填字段是否存在:
go
if len(required.UserName) == 0 {
err = errors.New("缺少必填字段username")
} else if len(required.Password) == 0 {
err = errors.New("缺少必填字段password")
}
如果校验通过,将值赋给 User 结构体:
go
u.Avatar = required.Avatar
u.UserName = required.UserName
3. binding
标签
binding
标签是 Gin 框架提供的参数校验机制,使用方法如下:
go
UserName string `json:"username" binding:"required"`
其中,binding:"required"
表示字段为必填项。 如果前端未提供 username
,请求会被 Gin 自动拒绝,返回 400 Bad Request。