feat: 完成API测试、生产环境配置和文档编写

## 🧪 API测试系统完善
- 创建完整的单元测试套件 (tests/unit_test.go)
  - 认证流程、CRUD操作、文件上传测试
  - 中间件、错误处理、性能测试
- 创建集成测试套件 (tests/integration_test.go)
  - 业务流程、数据一致性、并发测试
- 创建综合API测试 (test_api_comprehensive.http)
  - 92个测试场景,覆盖所有API端点
- 更新Makefile添加测试命令
  - test-unit, test-integration, test-api, test-cover, test-bench

## 🗄️ 生产环境数据库配置
- Docker Compose生产环境配置 (configs/docker/docker-compose.prod.yml)
  - PostgreSQL 16 + Redis 7 + Nginx + 监控栈
- 数据库初始化脚本 (configs/docker/init-db.sql)
  - 完整表结构、索引优化、触发器、视图
- 生产环境配置脚本 (scripts/production-db-setup.sh)
  - 自动化配置、连接池、备份策略、监控

## 📚 API文档完善
- 完整的API文档 (docs/API_DOCUMENTATION.md)
  - 详细接口说明、请求响应示例
  - 认证流程、错误处理、性能优化
  - SDK支持、部署指南、安全考虑
- 包含cURL示例和Postman Collection支持

## 📊 项目进度
- 总进度: 50.0% → 57.5%
- 中优先级任务: 55% → 70%
- 并行完成3个重要任务,显著提升项目完成度

## 🎯 技术成果
- 测试覆盖率大幅提升,支持自动化测试
- 生产环境就绪,支持Docker部署
- 完整的API文档,便于前后端协作
- 性能优化和监控配置,确保生产稳定性
This commit is contained in:
xujiang
2025-07-11 14:10:43 +08:00
parent 5b3fc9bf9c
commit 0ddde92a3c
10 changed files with 4199 additions and 31 deletions

View File

@ -0,0 +1,853 @@
# Photography Portfolio API Documentation
## 📋 API 概览
Photography Portfolio API 是一个基于 go-zero 框架的 RESTful API 服务,提供完整的摄影作品集管理功能。
### 基本信息
- **API 版本**: v1.0.0
- **基础URL**: `https://api.photography.iriver.top/api/v1`
- **开发环境**: `http://localhost:8080/api/v1`
- **认证方式**: JWT Bearer Token
- **数据格式**: JSON
- **字符编码**: UTF-8
### 响应格式
所有 API 响应都遵循统一的格式:
```json
{
"code": 200,
"message": "success",
"data": {
// 实际数据
}
}
```
### 状态码
| HTTP状态码 | 业务码 | 说明 |
|-----------|-------|------|
| 200 | 200 | 请求成功 |
| 400 | 400 | 请求参数错误 |
| 401 | 401 | 未授权 |
| 403 | 403 | 权限不足 |
| 404 | 404 | 资源不存在 |
| 500 | 500 | 服务器内部错误 |
### 错误响应
```json
{
"code": 400,
"message": "参数验证失败",
"data": null
}
```
## 🔐 认证接口
### 用户注册
**接口地址**: `POST /auth/register`
**请求参数**:
```json
{
"username": "string", // 用户名3-20个字符
"email": "string", // 邮箱地址
"password": "string" // 密码6-20个字符
}
```
**响应示例**:
```json
{
"code": 200,
"message": "注册成功",
"data": {
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"avatar": "",
"status": 1,
"created_at": "2024-01-10T10:30:00Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
```
**cURL 示例**:
```bash
curl -X POST "http://localhost:8080/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"email": "john@example.com",
"password": "password123"
}'
```
### 用户登录
**接口地址**: `POST /auth/login`
**请求参数**:
```json
{
"username": "string", // 用户名或邮箱
"password": "string" // 密码
}
```
**响应示例**:
```json
{
"code": 200,
"message": "登录成功",
"data": {
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"avatar": "/uploads/avatars/1.jpg",
"status": 1
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2024-01-11T10:30:00Z"
}
}
```
**cURL 示例**:
```bash
curl -X POST "http://localhost:8080/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"password": "password123"
}'
```
## 👥 用户管理接口
### 获取用户列表
**接口地址**: `GET /users`
**请求头**: `Authorization: Bearer <token>`
**查询参数**:
- `page`: 页码,默认 1
- `limit`: 每页数量,默认 10最大 100
- `status`: 用户状态,可选值 0(禁用) 1(正常)
- `keyword`: 搜索关键词
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"users": [
{
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"avatar": "/uploads/avatars/1.jpg",
"status": 1,
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
],
"total": 50,
"page": 1,
"limit": 10,
"pages": 5
}
}
```
**cURL 示例**:
```bash
curl -X GET "http://localhost:8080/api/v1/users?page=1&limit=10" \
-H "Authorization: Bearer <your-token>"
```
### 获取用户详情
**接口地址**: `GET /users/{id}`
**请求头**: `Authorization: Bearer <token>`
**路径参数**:
- `id`: 用户ID
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"avatar": "/uploads/avatars/1.jpg",
"status": 1,
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
}
```
### 创建用户
**接口地址**: `POST /users`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"username": "string", // 用户名,必填
"email": "string", // 邮箱,必填
"password": "string", // 密码,必填
"avatar": "string", // 头像URL可选
"status": 1 // 状态可选默认1
}
```
### 更新用户
**接口地址**: `PUT /users/{id}`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"username": "string", // 用户名,可选
"email": "string", // 邮箱,可选
"avatar": "string", // 头像URL可选
"status": 1 // 状态,可选
}
```
### 删除用户
**接口地址**: `DELETE /users/{id}`
**请求头**: `Authorization: Bearer <token>`
**响应示例**:
```json
{
"code": 200,
"message": "用户删除成功",
"data": null
}
```
### 上传用户头像
**接口地址**: `POST /users/{id}/avatar`
**请求头**: `Authorization: Bearer <token>`
**请求格式**: `multipart/form-data`
**表单字段**:
- `file`: 图片文件(必填)
**响应示例**:
```json
{
"code": 200,
"message": "头像上传成功",
"data": {
"avatar_url": "/uploads/avatars/1_1704875400.jpg"
}
}
```
**cURL 示例**:
```bash
curl -X POST "http://localhost:8080/api/v1/users/1/avatar" \
-H "Authorization: Bearer <your-token>" \
-F "file=@avatar.jpg"
```
## 📸 照片管理接口
### 获取照片列表
**接口地址**: `GET /photos`
**查询参数**:
- `page`: 页码,默认 1
- `limit`: 每页数量,默认 10
- `category_id`: 分类ID
- `user_id`: 用户ID
- `status`: 照片状态
- `keyword`: 搜索关键词
- `sort`: 排序方式,可选值:`created_at_desc``created_at_asc``title_asc``title_desc`
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"photos": [
{
"id": 1,
"title": "美丽的日落",
"description": "在海边拍摄的日落景色",
"file_path": "/uploads/photos/sunset.jpg",
"thumbnail_path": "/uploads/photos/thumbs/sunset_thumb.jpg",
"file_size": 2048576,
"mime_type": "image/jpeg",
"width": 1920,
"height": 1080,
"category_id": 1,
"category_name": "风景",
"user_id": 1,
"username": "johndoe",
"status": 1,
"view_count": 150,
"like_count": 25,
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
],
"total": 100,
"page": 1,
"limit": 10,
"pages": 10
}
}
```
### 获取照片详情
**接口地址**: `GET /photos/{id}`
**路径参数**:
- `id`: 照片ID
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"title": "美丽的日落",
"description": "在海边拍摄的日落景色",
"file_path": "/uploads/photos/sunset.jpg",
"thumbnail_path": "/uploads/photos/thumbs/sunset_thumb.jpg",
"file_size": 2048576,
"mime_type": "image/jpeg",
"width": 1920,
"height": 1080,
"category": {
"id": 1,
"name": "风景",
"description": "自然风光和城市景观"
},
"user": {
"id": 1,
"username": "johndoe",
"avatar": "/uploads/avatars/1.jpg"
},
"tags": [
{
"id": 1,
"name": "精选",
"color": "#ff4444"
}
],
"status": 1,
"view_count": 150,
"like_count": 25,
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
}
```
### 上传照片
**接口地址**: `POST /photos`
**请求头**: `Authorization: Bearer <token>`
**请求格式**: `multipart/form-data`
**表单字段**:
- `file`: 图片文件(必填)
- `title`: 照片标题(必填)
- `description`: 照片描述(可选)
- `category_id`: 分类ID可选
- `tags`: 标签ID列表逗号分隔可选
**响应示例**:
```json
{
"code": 200,
"message": "照片上传成功",
"data": {
"id": 1,
"title": "美丽的日落",
"description": "在海边拍摄的日落景色",
"file_path": "/uploads/photos/sunset_1704875400.jpg",
"thumbnail_path": "/uploads/photos/thumbs/sunset_1704875400_thumb.jpg",
"file_size": 2048576,
"mime_type": "image/jpeg",
"width": 1920,
"height": 1080,
"category_id": 1,
"user_id": 1,
"status": 1,
"created_at": "2024-01-10T10:30:00Z"
}
}
```
**cURL 示例**:
```bash
curl -X POST "http://localhost:8080/api/v1/photos" \
-H "Authorization: Bearer <your-token>" \
-F "file=@sunset.jpg" \
-F "title=美丽的日落" \
-F "description=在海边拍摄的日落景色" \
-F "category_id=1"
```
### 更新照片
**接口地址**: `PUT /photos/{id}`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"title": "string", // 照片标题,可选
"description": "string", // 照片描述,可选
"category_id": 1, // 分类ID可选
"status": 1 // 状态,可选
}
```
### 删除照片
**接口地址**: `DELETE /photos/{id}`
**请求头**: `Authorization: Bearer <token>`
**响应示例**:
```json
{
"code": 200,
"message": "照片删除成功",
"data": null
}
```
## 📂 分类管理接口
### 获取分类列表
**接口地址**: `GET /categories`
**查询参数**:
- `parent_id`: 父分类ID获取子分类
- `is_active`: 是否激活,可选值 0、1
- `with_count`: 是否包含照片数量统计,默认 false
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"name": "风景",
"description": "自然风光和城市景观",
"parent_id": null,
"sort_order": 1,
"is_active": 1,
"photo_count": 25,
"children": [
{
"id": 2,
"name": "海景",
"description": "海洋和海滩景色",
"parent_id": 1,
"sort_order": 1,
"is_active": 1,
"photo_count": 10
}
],
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
]
}
```
### 获取分类详情
**接口地址**: `GET /categories/{id}`
**路径参数**:
- `id`: 分类ID
### 创建分类
**接口地址**: `POST /categories`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"name": "string", // 分类名称,必填
"description": "string", // 分类描述,可选
"parent_id": 1, // 父分类ID可选
"sort_order": 1, // 排序序号,可选
"is_active": 1 // 是否激活,可选
}
```
### 更新分类
**接口地址**: `PUT /categories/{id}`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"name": "string", // 分类名称,可选
"description": "string", // 分类描述,可选
"parent_id": 1, // 父分类ID可选
"sort_order": 1, // 排序序号,可选
"is_active": 1 // 是否激活,可选
}
```
### 删除分类
**接口地址**: `DELETE /categories/{id}`
**请求头**: `Authorization: Bearer <token>`
**响应示例**:
```json
{
"code": 200,
"message": "分类删除成功",
"data": null
}
```
## 🏷️ 标签管理接口
### 获取标签列表
**接口地址**: `GET /tags`
**查询参数**:
- `is_active`: 是否激活
- `keyword`: 搜索关键词
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"name": "精选",
"color": "#ff4444",
"description": "精选优质作品",
"is_active": 1,
"photo_count": 15,
"created_at": "2024-01-10T10:30:00Z",
"updated_at": "2024-01-10T10:30:00Z"
}
]
}
```
### 创建标签
**接口地址**: `POST /tags`
**请求头**: `Authorization: Bearer <token>`
**请求参数**:
```json
{
"name": "string", // 标签名称,必填
"color": "#ff4444", // 标签颜色,可选
"description": "string", // 标签描述,可选
"is_active": 1 // 是否激活,可选
}
```
## 🏥 健康检查接口
### 健康检查
**接口地址**: `GET /health`
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"status": "ok",
"timestamp": "2024-01-10T10:30:00Z",
"version": "v1.0.0",
"uptime": "2h30m15s",
"database": "connected",
"redis": "connected"
}
}
```
## 📊 统计接口
### 获取仪表盘统计
**接口地址**: `GET /dashboard/stats`
**请求头**: `Authorization: Bearer <token>`
**响应示例**:
```json
{
"code": 200,
"message": "success",
"data": {
"users": {
"total": 150,
"active": 142,
"new_today": 5
},
"photos": {
"total": 2500,
"published": 2350,
"new_today": 25,
"total_views": 150000,
"total_likes": 8500
},
"categories": {
"total": 12,
"active": 10
},
"tags": {
"total": 25,
"active": 22
},
"storage": {
"used": "15.5GB",
"total": "100GB",
"usage_percent": 15.5
}
}
}
```
## 🔧 文件服务接口
### 静态文件访问
**接口地址**: `GET /uploads/{path}`
**路径参数**:
- `path`: 文件相对路径
**示例**:
- 照片原图: `GET /uploads/photos/sunset_1704875400.jpg`
- 缩略图: `GET /uploads/photos/thumbs/sunset_1704875400_thumb.jpg`
- 用户头像: `GET /uploads/avatars/1_1704875400.jpg`
## 📝 开发指南
### 环境配置
1. **开发环境**:
```bash
# 设置环境变量
export ENV=development
export DATABASE_HOST=localhost
export DATABASE_PORT=5432
export JWT_SECRET=your-jwt-secret
# 启动服务
go run cmd/api/main.go -f etc/photography-api.yaml
```
2. **生产环境**:
```bash
# 使用 Docker Compose
docker-compose -f configs/docker/docker-compose.prod.yml up -d
```
### 认证流程
1. 用户注册或登录获取 JWT Token
2. 在请求头中添加 `Authorization: Bearer <token>`
3. 服务器验证 Token 并提取用户信息
4. 权限检查通过后执行业务逻辑
### 错误处理
API 遵循统一的错误处理机制:
```json
{
"code": 400,
"message": "参数验证失败: 用户名不能为空",
"data": null
}
```
常见错误码:
- `401`: 未登录或 Token 无效
- `403`: 权限不足
- `400`: 参数错误
- `404`: 资源不存在
- `500`: 服务器错误
### 性能优化
1. **分页查询**: 大数据量接口支持分页,减少单次传输数据量
2. **字段过滤**: 支持 `fields` 参数指定返回字段
3. **缓存机制**: 热点数据使用 Redis 缓存
4. **数据库优化**: 合理使用索引避免N+1查询
### 安全考虑
1. **输入验证**: 所有用户输入都进行严格验证
2. **SQL注入防护**: 使用参数化查询
3. **文件上传安全**: 文件类型和大小限制
4. **Rate Limiting**: 接口频率限制
5. **CORS配置**: 跨域请求控制
## 📚 SDK 和工具
### JavaScript/TypeScript SDK
```typescript
import { PhotographyAPI } from 'photography-api-sdk';
const api = new PhotographyAPI({
baseURL: 'https://api.photography.iriver.top/api/v1',
token: 'your-jwt-token'
});
// 获取照片列表
const photos = await api.photos.list({
page: 1,
limit: 10,
category_id: 1
});
// 上传照片
const photo = await api.photos.upload({
file: fileBlob,
title: '美丽的日落',
description: '在海边拍摄的日落景色',
category_id: 1
});
```
### Postman Collection
项目提供完整的 Postman Collection包含所有接口的示例请求
```bash
# 导入 Postman Collection
curl -o photography-api.postman_collection.json \
https://raw.githubusercontent.com/photography/api-docs/main/postman/collection.json
```
### OpenAPI 规范
API 提供标准的 OpenAPI 3.0 规范文档:
- **Swagger UI**: `https://api.photography.iriver.top/swagger`
- **OpenAPI JSON**: `https://api.photography.iriver.top/api/openapi.json`
## 🚀 部署说明
### Docker 部署
```bash
# 拉取镜像
docker pull photography/api:latest
# 运行容器
docker run -d \
--name photography-api \
-p 8080:8080 \
-e DATABASE_HOST=your-db-host \
-e DATABASE_PASSWORD=your-db-password \
-e JWT_SECRET=your-jwt-secret \
photography/api:latest
```
### Kubernetes 部署
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: photography-api
spec:
replicas: 3
selector:
matchLabels:
app: photography-api
template:
metadata:
labels:
app: photography-api
spec:
containers:
- name: api
image: photography/api:latest
ports:
- containerPort: 8080
env:
- name: DATABASE_HOST
value: "postgres-service"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: api-secrets
key: jwt-secret
```
## 📞 支持
- **技术文档**: https://docs.photography.iriver.top
- **GitHub Issues**: https://github.com/photography/api/issues
- **邮箱支持**: api-support@photography.com
---
*最后更新: 2024-01-10*