## 后端架构 (Go + Gin + GORM) - ✅ 完整的分层架构 (API/Service/Repository) - ✅ PostgreSQL数据库设计和迁移脚本 - ✅ JWT认证系统和权限控制 - ✅ 用户、照片、分类、标签等核心模型 - ✅ 中间件系统 (认证、CORS、日志) - ✅ 配置管理和环境变量支持 - ✅ 结构化日志和错误处理 - ✅ Makefile构建和部署脚本 ## 管理后台架构 (React + TypeScript) - ✅ Vite + React 18 + TypeScript现代化架构 - ✅ 路由系统和状态管理 (Zustand + TanStack Query) - ✅ 基于Radix UI的组件库基础 - ✅ 认证流程和权限控制 - ✅ 响应式设计和主题系统 ## 数据库设计 - ✅ 用户表 (角色权限、认证信息) - ✅ 照片表 (元数据、EXIF、状态管理) - ✅ 分类表 (层级结构、封面图片) - ✅ 标签表 (使用统计、标签云) - ✅ 关联表 (照片-标签多对多) ## 技术特点 - 🚀 高性能: Gin框架 + GORM ORM - 🔐 安全: JWT认证 + 密码加密 + 权限控制 - 📊 监控: 结构化日志 + 健康检查 - 🎨 现代化: React 18 + TypeScript + Vite - 📱 响应式: Tailwind CSS + Radix UI 参考文档: docs/development/saved-docs/
99 lines
3.9 KiB
Go
99 lines
3.9 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Photo 照片模型
|
|
type Photo struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
Title string `gorm:"size:255;not null" json:"title"`
|
|
Description string `gorm:"type:text" json:"description"`
|
|
Filename string `gorm:"size:255;not null" json:"filename"`
|
|
FilePath string `gorm:"size:500;not null" json:"file_path"`
|
|
FileSize int64 `json:"file_size"`
|
|
MimeType string `gorm:"size:100" json:"mime_type"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
CategoryID uint `json:"category_id"`
|
|
Category *Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
|
|
Tags []Tag `gorm:"many2many:photo_tags;" json:"tags,omitempty"`
|
|
EXIF string `gorm:"type:jsonb" json:"exif"`
|
|
TakenAt *time.Time `json:"taken_at"`
|
|
Location string `gorm:"size:255" json:"location"`
|
|
IsPublic bool `gorm:"default:true" json:"is_public"`
|
|
Status string `gorm:"size:20;default:draft" json:"status"`
|
|
ViewCount int `gorm:"default:0" json:"view_count"`
|
|
LikeCount int `gorm:"default:0" json:"like_count"`
|
|
UserID uint `gorm:"not null" json:"user_id"`
|
|
User *User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
|
}
|
|
|
|
// TableName 返回照片表名
|
|
func (Photo) TableName() string {
|
|
return "photos"
|
|
}
|
|
|
|
// PhotoStatus 照片状态常量
|
|
const (
|
|
StatusDraft = "draft"
|
|
StatusPublished = "published"
|
|
StatusArchived = "archived"
|
|
)
|
|
|
|
// CreatePhotoRequest 创建照片请求
|
|
type CreatePhotoRequest struct {
|
|
Title string `json:"title" binding:"required,max=255"`
|
|
Description string `json:"description"`
|
|
CategoryID uint `json:"category_id" binding:"required"`
|
|
TagIDs []uint `json:"tag_ids"`
|
|
TakenAt *time.Time `json:"taken_at"`
|
|
Location string `json:"location" binding:"max=255"`
|
|
IsPublic *bool `json:"is_public"`
|
|
Status string `json:"status" binding:"omitempty,oneof=draft published archived"`
|
|
}
|
|
|
|
// UpdatePhotoRequest 更新照片请求
|
|
type UpdatePhotoRequest struct {
|
|
Title *string `json:"title" binding:"omitempty,max=255"`
|
|
Description *string `json:"description"`
|
|
CategoryID *uint `json:"category_id"`
|
|
TagIDs []uint `json:"tag_ids"`
|
|
TakenAt *time.Time `json:"taken_at"`
|
|
Location *string `json:"location" binding:"omitempty,max=255"`
|
|
IsPublic *bool `json:"is_public"`
|
|
Status *string `json:"status" binding:"omitempty,oneof=draft published archived"`
|
|
}
|
|
|
|
// PhotoListParams 照片列表查询参数
|
|
type PhotoListParams struct {
|
|
Page int `form:"page,default=1" binding:"min=1"`
|
|
Limit int `form:"limit,default=20" binding:"min=1,max=100"`
|
|
CategoryID uint `form:"category_id"`
|
|
TagID uint `form:"tag_id"`
|
|
UserID uint `form:"user_id"`
|
|
Status string `form:"status" binding:"omitempty,oneof=draft published archived"`
|
|
Search string `form:"search"`
|
|
SortBy string `form:"sort_by,default=created_at" binding:"omitempty,oneof=created_at taken_at title view_count like_count"`
|
|
SortOrder string `form:"sort_order,default=desc" binding:"omitempty,oneof=asc desc"`
|
|
Year int `form:"year"`
|
|
Month int `form:"month" binding:"min=1,max=12"`
|
|
}
|
|
|
|
// PhotoResponse 照片响应
|
|
type PhotoResponse struct {
|
|
*Photo
|
|
ThumbnailURLs map[string]string `json:"thumbnail_urls,omitempty"`
|
|
}
|
|
|
|
// PhotoListResponse 照片列表响应
|
|
type PhotoListResponse struct {
|
|
Photos []PhotoResponse `json:"photos"`
|
|
Total int64 `json:"total"`
|
|
Page int `json:"page"`
|
|
Limit int `json:"limit"`
|
|
} |