这是一个基于 Go 语言和 Gin 框架的用户注册系统后端项目。
- 用户注册功能
- 密码加盐哈希存储
- 用户信息查询(按ID和用户名)
- 文件上传系统(支持本地存储和S3)
- 多存储配置(灵活配置多个存储桶)
- 文件管理功能(上传、下载、删除、更新)
- RESTful API 设计
- PostgreSQL 数据库支持
- 统一响应格式
- CORS 跨域支持
- 完整的API文档 (Swagger/OpenAPI)
- 语言: Go 1.24.5
- 框架: Gin
- 数据库: PostgreSQL
- ORM: GORM
- UUID: Google UUID
- 文件存储: 本地存储 + AWS S3
- 文档: Swagger/OpenAPI
本项目提供了 Docker Compose 配置,可以一键启动应用所需的所有服务(PostgreSQL, Redis, Go Backend)。这是最简单、最推荐的运行方式。
首先,请确保你已经安装了 Docker
和 docker-compose
。
我们提供了两种开箱即用的配置:
docker-compose.multi-local.yml
: 使用本地文件系统作为存储。上传的文件会保存在项目根目录的uploads/
文件夹下。docker-compose.multi-s3.yml
: 使用 AWS S3 作为文件存储。
# 使用 multi-local 配置文件启动所有服务
docker-compose -f docker-compose.multi-local.yml up --build -d
在启动前,请务-必打开 docker-compose.multi-s3.yml
文件,并将其中所有 YOUR_...
占位符替换为你的真实 AWS S3 凭证。
# ...
# S3 存储 'primary' 的配置
FILE_STORAGE_S3_PRIMARY_REGION: "us-east-1" # <- 修改这里
FILE_STORAGE_S3_PRIMARY_BUCKET: "your-primary-bucket" # <- 修改这里
FILE_STORAGE_S3_PRIMARY_ACCESS_KEY: "YOUR_PRIMARY_ACCESS_KEY" # <- 修改这里
FILE_STORAGE_S3_PRIMARY_SECRET_KEY: "YOUR_PRIMARY_SECRET_KEY" # <- 修改这里
# ...
然后运行以下命令启动:
# 使用 multi-s3 配置文件启动所有服务
docker-compose -f docker-compose.multi-s3.yml up --build -d
服务启动后:
- 应用:
http://localhost:8080
- API 文档:
http://localhost:8080/swagger/index.html
# 查看所有服务的实时日志 (使用对应的 -f 文件)
docker-compose -f docker-compose.multi-local.yml logs -f
# 停止并移除所有容器、网络和卷
docker-compose -f docker-compose.multi-local.yml down
此方法适用于不使用 Docker,希望在本地手动配置和运行所有依赖的开发者。
-
克隆项目
git clone https://github.com/yuchen1204/backend_go cd backend
-
安装依赖
go mod tidy
-
生成API文档
# (在 Linux/macOS) chmod +x scripts/generate-docs.sh ./scripts/generate-docs.sh
-
设置环境变量 复制
configs/env.example
文件到项目根目录,并重命名为.env
。cp configs/env.example .env
然后编辑
.env
文件,至少需要配置好数据库、Redis和SMTP服务的连接信息。 -
启动 PostgreSQL 数据库和 Redis 你需要在本地手动安装并启动 PostgreSQL 和 Redis 服务,并确保已创建好应用所需的数据库。
# 示例: 在 Ubuntu 上安装 # sudo apt-get install postgresql postgresql-contrib redis-server # 创建数据库 createdb backend
-
运行应用
go run cmd/main.go
-
访问API文档 浏览器访问
http://localhost:8080/swagger/index.html
。
启动服务后,访问以下地址查看完整的交互式API文档:
🌐 Swagger UI: http://localhost:8080/swagger/index.html
# 生成API文档
chmod +x scripts/generate-docs.sh
./scripts/generate-docs.sh
# 或者手动生成
go install github.com/swaggo/swag/cmd/swag@latest
swag init -g cmd/main.go -o ./docs
- 交互式测试: 可以直接在浏览器中测试API
- 认证支持: 支持JWT Bearer Token认证
- 完整的请求/响应示例: 包含所有字段的详细说明
- 错误代码说明: 详细的错误响应文档
backend/
├── cmd/ # 应用程序入口
│ └── main.go
├── internal/ # 内部代码
│ ├── config/ # 配置相关
│ │ └── database.go
│ ├── handler/ # HTTP 处理器
│ │ ├── response.go
│ │ └── user_handler.go
│ ├── model/ # 数据模型
│ │ └── user.go
│ ├── repository/ # 数据访问层
│ │ └── user_repository.go
│ ├── router/ # 路由配置
│ │ └── router.go
│ └── service/ # 业务逻辑层
│ └── user_service.go
├── configs/ # 配置文件
│ └── env.example
├── go.mod
└── README.md
用户表包含以下字段:
id
: UUID 主键username
: 用户名(唯一)email
: 邮箱地址(唯一)password_salt
: 密码盐和哈希(格式:salt:hash)nickname
: 昵称bio
: 个人简介avatar
: 头像URLcreated_at
: 创建时间updated_at
: 更新时间deleted_at
: 软删除时间
- POST
/api/v1/users/send-code
- 描述: 在发送验证码前,会预先检查用户名和邮箱是否都未被注册。都通过后,才会向指定邮箱发送一个用于注册的6位数验证码(5分钟内有效)。
请求体示例:
{
"username": "testuser",
"email": "[email protected]"
}
响应示例:
{
"code": 200,
"message": "验证码已发送至您的邮箱,请注意查收",
"data": null,
"timestamp": 1640995200
}
- POST
/api/v1/users/register
- 描述: 使用邮箱验证码创建新用户账户
请求体示例:
{
"username": "testuser",
"email": "[email protected]",
"password": "password123",
"verification_code": "123456",
"nickname": "测试用户",
"bio": "这是我的个人简介",
"avatar": "https://example.com/avatar.jpg"
}
响应示例:
{
"code": 201,
"message": "注册成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"username": "testuser",
"email": "[email protected]",
"nickname": "测试用户",
"bio": "这是我的个人简介",
"avatar": "https://example.com/avatar.jpg",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
"timestamp": 1640995200
}
- POST
/api/v1/users/login
- 描述: 使用用户名和密码登录,成功后返回包含Access Token、Refresh Token和用户信息的对象。
请求体示例:
{
"username": "testuser",
"password": "password123"
}
响应示例:
{
"code": 200,
"message": "登录成功",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"username": "testuser",
"email": "[email protected]",
"nickname": "测试用户",
"bio": "这是我的个人简介",
"avatar": "https://example.com/avatar.jpg",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
},
"timestamp": 1640995200
}
- POST
/api/v1/users/refresh
- 描述: 使用有效的Refresh Token获取新的Access Token。
请求体示例:
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应示例:
{
"code": 200,
"message": "刷新成功",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"timestamp": 1640995200
}
- POST
/api/v1/users/logout
- 描述: 登出用户并撤销所有Token(Access Token和Refresh Token)。Access Token将被加入黑名单立即失效,Refresh Token也将被删除。
请求体示例:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应示例:
{
"code": 200,
"message": "登出成功",
"data": null,
"timestamp": 1640995200
}
- POST
/api/v1/users/send-reset-code
- 描述: 向指定邮箱发送用于重置密码的6位数验证码(5分钟内有效)。为了安全考虑,即使邮箱未注册也会返回成功,避免邮箱枚举攻击。
请求体示例:
{
"email": "[email protected]"
}
响应示例:
{
"code": 200,
"message": "验证码已发送至您的邮箱,请注意查收",
"data": null,
"timestamp": 1640995200
}
- POST
/api/v1/users/reset-password
- 描述: 使用邮箱验证码重置用户密码。重置成功后,该用户的所有refresh token将被撤销,需要重新登录。
请求体示例:
{
"email": "[email protected]",
"verification_code": "123456",
"new_password": "newpassword123"
}
响应示例:
{
"code": 200,
"message": "密码重置成功,请使用新密码登录",
"data": null,
"timestamp": 1640995200
}
- GET
/api/v1/users/me
- 描述: 需要在请求头中提供有效的Access Token来获取当前登录用户的详细信息。
- 认证:
Bearer Token
(仅接受Access Token)
请求头示例:
Authorization: Bearer <your-access-token>
响应示例:
{
"code": 200,
"message": "获取成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"username": "testuser",
// ... a UserResponse object
},
"timestamp": 1640995200
}
- PUT
/api/v1/users/me
- 描述: 更新当前登录用户的基本信息(昵称、简介、头像)。
- 认证:
Bearer Token
(仅接受Access Token)
请求头示例:
Authorization: Bearer <your-access-token>
请求体示例:
{
"nickname": "新昵称",
"bio": "我的新个人简介",
"avatar": "https://example.com/new-avatar.jpg"
}
注意事项:
- 所有字段都是可选的,只更新提供的字段
- 如果某个字段为空字符串或未提供,该字段不会被更新
avatar
字段如果提供,必须是有效的URL格式
响应示例:
{
"code": 200,
"message": "更新成功",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"username": "testuser",
"email": "[email protected]",
"nickname": "新昵称",
"bio": "我的新个人简介",
"avatar": "https://example.com/new-avatar.jpg",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T12:30:00Z"
},
"timestamp": 1640995200
}
- GET
/api/v1/users/{id}
- 描述: 通过用户ID获取用户详细信息
- GET
/api/v1/users/username/{username}
- 描述: 通过用户名获取用户详细信息
- GET
/health
- 描述: 服务健康状态检查
复制 configs/env.example
文件并根据需要修改配置:
# 服务器配置
PORT=8080
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=backend
DB_SSLMODE=disable
# Redis 配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# SMTP 邮件服务配置
SMTP_HOST=smtp.example.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=your-email-password
[email protected]
# 安全配置
MAX_IP_REQUESTS_PER_DAY=10
- POST
/api/v1/users/send-code
- 发送注册验证码 - POST
/api/v1/users/register
- 用户注册 - POST
/api/v1/users/login
- 用户登录 - POST
/api/v1/users/refresh
- 刷新访问Token - POST
/api/v1/users/logout
- 用户登出 - POST
/api/v1/users/send-reset-code
- 发送重置密码验证码 - POST
/api/v1/users/reset-password
- 重置密码 - GET
/api/v1/users/{id}
- 根据ID获取用户信息 - GET
/api/v1/users/username/{username}
- 根据用户名获取用户信息 - GET
/health
- 健康检查
- GET
/api/v1/users/me
- 获取当前用户信息 - PUT
/api/v1/users/me
- 更新当前用户信息
- GET
/api/v1/files/public
- 获取公开文件列表 - GET
/api/v1/files/storages
- 获取存储信息 - GET
/api/v1/files/{id}
- 获取文件详情(支持公开和私有)
- POST
/api/v1/files/upload
- 上传单个文件 - POST
/api/v1/files/upload-multiple
- 上传多个文件 - GET
/api/v1/files/my
- 获取当前用户文件列表 - PUT
/api/v1/files/{id}
- 更新文件信息 - DELETE
/api/v1/files/{id}
- 删除文件
- 启动服务器
- 访问 http://localhost:8080/swagger/index.html
- 在页面右上角点击"Authorize"按钮
- 输入Bearer Token:
Bearer your-access-token
- 直接在页面中测试各个API
详细的curl命令请参考下面的"详细API文档"部分。
# 安装swag工具
go install github.com/swaggo/swag/cmd/swag@latest
# 生成文档
swag init -g cmd/main.go -o ./docs
# 重新生成文档(开发时)
./scripts/generate-docs.sh
# 格式化代码
go fmt ./...
# 代码检查
go vet ./...
- 多存储支持: 同时支持本地存储和AWS S3存储
- 灵活配置: 可配置多个存储桶,每个存储桶独立配置
- 文件分类: 支持按类别组织文件(avatar、document、image等)
- 权限控制: 支持公开和私有文件访问控制
- 文件管理: 完整的CRUD操作(创建、读取、更新、删除)
# 支持多个本地存储
FILE_STORAGE_LOCAL_NAMES=default,avatar,document
FILE_STORAGE_LOCAL_DEFAULT_PATH=./uploads/default
FILE_STORAGE_LOCAL_DEFAULT_URL=http://localhost:8080/uploads/default
# 支持多个S3存储桶
FILE_STORAGE_S3_NAMES=main,backup
FILE_STORAGE_S3_MAIN_REGION=us-east-1
FILE_STORAGE_S3_MAIN_BUCKET=my-app-files
FILE_STORAGE_S3_MAIN_ACCESS_KEY=your-access-key
FILE_STORAGE_S3_MAIN_SECRET_KEY=your-secret-key
curl -X POST "http://localhost:8080/api/v1/files/upload" \
-H "Authorization: Bearer your-access-token" \
-F "[email protected]" \
-F "storage_name=avatar" \
-F "category=profile" \
-F "is_public=true"
curl -X GET "http://localhost:8080/api/v1/files/my?category=profile&page=1&page_size=10" \
-H "Authorization: Bearer your-access-token"
# 编译应用
go build -o backend cmd/main.go
# 运行应用
./backend
生产环境需要设置的关键环境变量:
JWT_SECRET
: JWT签名密钥(必须修改)DB_PASSWORD
: 数据库密码REDIS_PASSWORD
: Redis密码SMTP_*
: 邮件服务配置FILE_STORAGE_*
: 文件存储配置
- 密码使用加盐哈希存储
- 双Token机制:
- Access Token: 短期有效(默认30分钟),用于API访问
- Refresh Token: 长期有效(默认7天),仅用于刷新Access Token
- 提升安全性的同时保持良好的用户体验
- Token黑名单机制:
- Access Token黑名单: 登出时Access Token立即加入黑名单失效
- Refresh Token管理: 登出时删除Refresh Token,防止再次使用
- 确保用户登出后所有Token立即失效,消除安全隐患
- JWT会话管理:用户登录后使用JWT进行无状态认证。
- IP请求频率限制:限制每个IP每天请求验证码的次数,防止接口被恶意攻击。
- 响应中不包含敏感信息(密码)
- 输入验证和参数绑定
- 统一错误处理
- 使用分层架构设计(Handler -> Service -> Repository)
- 依赖注入模式
- 接口驱动开发
- GORM 自动数据库迁移
- 统一响应格式