U:调整文件
This commit is contained in:
1 parent
2d74d18262
commit
725bc466c1
8 files changed
+1226
-215
No files matched your search
@@ -16,7 +16,46 @@
|
||||
"Bash(del \"D:\\go-argus\\src\\rear\\api_tests.http\")",
|
||||
"Bash(curl:*)",
|
||||
"Bash(pkill:*)",
|
||||
"Bash(git commit:*)"
|
||||
"Bash(git commit:*)",
|
||||
"Bash(go:*)",
|
||||
"WebSearch",
|
||||
"Bash(where go)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" version)",
|
||||
"Bash(set PATH=C:Program FilesGobin)",
|
||||
"Bash(%PATH%)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" build -v .)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" env CGO_ENABLED)",
|
||||
"Bash(gcc:*)",
|
||||
"Bash(where choco)",
|
||||
"Read(//d//**)",
|
||||
"Read(//d/EdgeDownload/**)",
|
||||
"Bash(.rear.exe)",
|
||||
"Bash(./rear.exe)",
|
||||
"Bash(cmd /c:*)",
|
||||
"Bash(set CGO_ENABLED=0)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" build -v -o rear.exe .)",
|
||||
"Bash(powershell:*)",
|
||||
"Read(//c/TDM-GCC-64-1/bin/**)",
|
||||
"Bash(set PATH=%PATH%)",
|
||||
"Bash(C:TDM-GCC-64-1bin)",
|
||||
"Bash(\"C:\\TDM-GCC-64-1\\bin\\gcc.exe\" --version)",
|
||||
"Bash(set CGO_ENABLED=1)",
|
||||
"Bash(set CC=C:TDM-GCC-64-1bingcc.exe)",
|
||||
"Bash(del rear.exe)",
|
||||
"Bash(export CGO_ENABLED=1)",
|
||||
"Bash(export:*)",
|
||||
"Bash(export PATH=$PATH:/c/TDM-GCC-64-1/bin)",
|
||||
"Bash(\"/c/Program Files/Go/bin/go.exe\" build -x -v -o rear.exe .)",
|
||||
"Bash(\"/c/Program Files/Go/bin/go.exe\" build -v -o rear.exe .)",
|
||||
"Bash(/c/TDM-GCC-64-1/bin/gcc.exe --version)",
|
||||
"Bash(taskkill:*)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" env)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" env -w CGO_ENABLED=1)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" list -m all)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" get github.com/glebarez/sqlite)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" mod tidy)",
|
||||
"Bash(\"C:\\Program Files\\Go\\bin\\go.exe\" build -v -o rear_pure_go.exe .)",
|
||||
"Bash(netsh:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
+1158
-206
@@ -1,283 +1,1235 @@
|
||||
# Argus 照片管理系统 - 项目详细分析
|
||||
# Argus 照片管理系统 - Claude 开发指南
|
||||
|
||||
这是一个基于Go语言开发的照片管理系统,主要功能是提供照片管理服务,为前端提供HTTP API接口。
|
||||
> **文档说明**: 此文档专为 Claude AI 助手设计,用于快速理解项目架构、功能模块、开发规范等关键信息。
|
||||
> **最后更新**: 2025-10-03
|
||||
|
||||
## 项目基本信息
|
||||
---
|
||||
|
||||
- **项目名称**: Argus 照片管理系统
|
||||
## 📋 项目概览
|
||||
|
||||
### 基本信息
|
||||
- **项目名称**: Argus 照片管理系统 (后端服务)
|
||||
- **开发语言**: Go 1.24
|
||||
- **项目类型**: HTTP 服务端应用
|
||||
- **主要功能**: 照片管理、图像处理、缩略图生成、EXIF信息提取
|
||||
- **部署方式**: 独立可执行文件
|
||||
- **项目类型**: HTTP RESTful API 服务
|
||||
- **核心功能**: 照片管理、图像处理、EXIF 提取、缩略图生成、实时通信
|
||||
- **部署方式**: 独立可执行文件 (支持 Windows/Linux/macOS)
|
||||
- **数据库**: SQLite (默认) / MySQL (可选)
|
||||
- **Web 框架**: Gin (v1.10.1)
|
||||
- **ORM**: GORM (v1.30.0)
|
||||
|
||||
## 项目结构分析
|
||||
### 设计目标
|
||||
1. **高性能图像处理**: 利用外部工具链 (ExifTool/ImageMagick/LibVips) 实现高效图像处理
|
||||
2. **并发任务管理**: 基于 Go 协程的高并发任务调度系统,支持动态并发数调整
|
||||
3. **可扩展架构**: 清晰的分层架构,便于功能扩展和维护
|
||||
4. **实时通信**: SSE (Server-Sent Events) 实现任务进度实时推送
|
||||
5. **跨平台支持**: 自动管理平台特定的外部工具依赖
|
||||
|
||||
### 主要目录结构
|
||||
---
|
||||
|
||||
## 🏗️ 项目架构
|
||||
|
||||
### 目录结构
|
||||
```
|
||||
D:\go-argus\src\rear\
|
||||
├── main.go # 应用主入口
|
||||
├── go.mod/go.sum # Go模块依赖文件
|
||||
├── main.go # 应用入口,服务启动逻辑
|
||||
├── go.mod/go.sum # Go 模块依赖
|
||||
├── config.yaml # 配置文件
|
||||
├── README.md # 项目说明文档
|
||||
├── internal/ # 内部业务代码
|
||||
│ ├── api/ # API层
|
||||
│ ├── config/ # 配置管理
|
||||
│ ├── container/ # 依赖注入容器
|
||||
│ ├── db/ # 数据库操作
|
||||
│ ├── handler/ # HTTP处理器
|
||||
│ ├── model/ # 数据模型
|
||||
│ ├── repositories/ # 数据访问层
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── service/ # 业务服务
|
||||
│ ├── utils/ # 内部工具函数
|
||||
│ └── workflow/ # 工作流程
|
||||
├── pkg/ # 公共包
|
||||
│ ├── img_utils/ # 图像工具
|
||||
│ ├── logger/ # 日志工具
|
||||
│ ├── sse/ # Server-Sent Events
|
||||
│ └── utils/ # 通用工具
|
||||
├── tools/ # 外部工具链
|
||||
├── CLAUDE.md # Claude AI 开发指南 (本文件)
|
||||
├── README.md # 用户文档
|
||||
│
|
||||
├── internal/ # 内部业务代码 (不对外暴露)
|
||||
│ ├── api/ # API 层 - 对外接口定义
|
||||
│ ├── config/ # 配置管理 - 配置加载/解析/验证
|
||||
│ ├── container/ # 依赖注入容器 - 管理仓库和任务容器
|
||||
│ ├── db/ # 数据库管理 - 连接/迁移/写任务队列
|
||||
│ ├── handler/ # HTTP 处理器 - 处理请求和响应
|
||||
│ ├── model/ # 数据模型 - 业务模型和数据表定义
|
||||
│ ├── repositories/ # 数据访问层 - 数据库 CRUD 操作
|
||||
│ ├── router/ # 路由配置 - HTTP 路由注册
|
||||
│ ├── service/ # 业务服务 - 中间件和业务逻辑
|
||||
│ ├── utils/ # 内部工具 - 任务调度/系统工具/外部工具管理
|
||||
│ └── workflow/ # 工作流 - 图像处理任务流程
|
||||
│
|
||||
├── pkg/ # 公共包 (可对外暴露)
|
||||
│ ├── img_utils/ # 图像工具 - 格式检测/转换
|
||||
│ ├── logger/ # 日志工具 - Zap 日志封装
|
||||
│ ├── sse/ # SSE 实时通信 - 客户端/管理器
|
||||
│ ├── utils/ # 通用工具 - 文件/哈希/系统工具
|
||||
│ └── system/ # 系统信息 - CPU/内存/磁盘/网络监控
|
||||
│
|
||||
├── tools/ # 外部工具链 (平台特定)
|
||||
│ ├── windows_amd64/ # Windows 64位工具
|
||||
│ ├── linux_amd64/ # Linux 64位工具
|
||||
│ └── darwin_arm64/ # macOS ARM64工具
|
||||
│
|
||||
├── scripts/ # 构建脚本
|
||||
├── logs/ # 日志文件
|
||||
├── thumbnail/ # 缩略图存储
|
||||
├── db/ # 数据库文件 (SQLite)
|
||||
└── examples/ # 示例代码
|
||||
```
|
||||
|
||||
## 技术栈详细分析
|
||||
### 分层架构设计
|
||||
|
||||
### 核心Go模块依赖
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ HTTP Layer │
|
||||
│ (Gin Router + Middleware + CORS + Error Handler) │
|
||||
└─────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────────────────────┐
|
||||
│ Handler Layer │
|
||||
│ (LibraryHandler, ExifHandler, PhotoHandler, etc) │
|
||||
└─────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────────────────────┐
|
||||
│ Repository Layer │
|
||||
│ (LibraryRepo, ExifRepo, PhotoRepo, UserRepo) │
|
||||
└─────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────────────────────┐
|
||||
│ Database Layer │
|
||||
│ (GORM + SQLite/MySQL + 写队列) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
|
||||
#### Web框架
|
||||
- **gin-gonic/gin v1.10.1** - 主要的HTTP Web框架
|
||||
- **gin-contrib/cors v1.7.5** - CORS跨域处理
|
||||
并行的任务处理流:
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Task Workflow Layer │
|
||||
│ (ImgTask + TaskManager + External Tools) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 核心技术栈
|
||||
|
||||
### Go 依赖库
|
||||
|
||||
#### Web 框架
|
||||
- **gin-gonic/gin v1.10.1** - 主 HTTP 框架
|
||||
- **gin-contrib/cors v1.7.5** - CORS 跨域处理
|
||||
- **gin-contrib/pprof v1.5.3** - 性能分析工具
|
||||
|
||||
#### 数据库ORM
|
||||
- **gorm.io/gorm v1.30.0** - 主要ORM框架
|
||||
- **gorm.io/driver/sqlite v1.6.0** - SQLite数据库驱动
|
||||
- **gorm.io/driver/mysql v1.6.0** - MySQL数据库驱动
|
||||
#### 数据库 ORM
|
||||
- **gorm.io/gorm v1.30.0** - ORM 框架
|
||||
- **gorm.io/driver/sqlite v1.6.0** - SQLite 驱动
|
||||
- **gorm.io/driver/mysql v1.6.0** - MySQL 驱动
|
||||
|
||||
#### 日志系统
|
||||
- **go.uber.org/zap v1.27.0** - 结构化日志库
|
||||
- **go.uber.org/zap v1.27.0** - 高性能结构化日志
|
||||
- **gopkg.in/natefinch/lumberjack.v2 v2.2.1** - 日志轮转
|
||||
|
||||
#### 工具库
|
||||
- **google/uuid v1.6.0** - UUID生成
|
||||
- **google/uuid v1.6.0** - UUID 生成
|
||||
- **h2non/filetype v1.1.3** - 文件类型检测
|
||||
- **gopkg.in/yaml.v3 v3.0.1** - YAML配置文件解析
|
||||
- **gopkg.in/yaml.v3 v3.0.1** - YAML 配置解析
|
||||
|
||||
### 外部工具依赖
|
||||
### 外部工具链 (跨平台)
|
||||
|
||||
#### 图像处理工具
|
||||
1. **ExifTool** - EXIF信息提取工具
|
||||
2. **ImageMagick** - 图像处理和格式转换
|
||||
3. **LibVips** - 高性能图像处理库
|
||||
1. **ExifTool** - EXIF 元数据提取 (Perl 工具)
|
||||
2. **ImageMagick** - 图像格式转换和处理 (magick.exe)
|
||||
3. **LibVips** - 高性能图像处理库 (vips.exe)
|
||||
|
||||
## 核心功能模块分析
|
||||
#### 工具管理策略
|
||||
- **开发模式**: 自动从 `tools/{platform}/` 解压工具到 `AppDir/tools/`
|
||||
- **生产模式**: 手动配置工具路径或使用系统安装的工具
|
||||
- **平台检测**: 自动识别 `windows_amd64`, `linux_amd64`, `darwin_arm64` 等
|
||||
|
||||
### 1. 配置管理模块 (internal/config/)
|
||||
- **config.go** - 主配置管理,支持多种配置项
|
||||
- **database.go** - 数据库配置
|
||||
- **loader.go** - 配置文件加载器
|
||||
---
|
||||
|
||||
#### 主要配置结构:
|
||||
- **App配置**: 端口、模式、超时设置
|
||||
- **数据库配置**: 支持SQLite和MySQL
|
||||
- **图像配置**: 缩略图格式、质量、大小设置
|
||||
- **工具配置**: 外部工具路径配置
|
||||
- **任务配置**: 并发数、队列容量等
|
||||
## 📦 功能模块详解
|
||||
|
||||
### 2. 数据模型 (internal/model/)
|
||||
#### 核心数据表:
|
||||
- **Photo表** (tables/photo_table.go) - 照片基础信息 (路径、哈希、尺寸、格式等)
|
||||
- **PhotoExif表** (tables/exif_table.go) - EXIF元数据信息,通过PhotoHash关联Photo表
|
||||
- **LibraryTable** - 图片库路径管理
|
||||
- **User表** - 用户信息
|
||||
### 1. 配置管理模块 (`internal/config/`)
|
||||
|
||||
#### EXIF表设计详情:
|
||||
**PhotoExif表结构**包含以下主要字段组:
|
||||
- **主键**: Hash (直接使用照片Hash,不使用外键关联)
|
||||
- **基础文件信息**: 文件名、大小、尺寸、类型、修改时间等
|
||||
- **摄影参数**: 相机厂商型号、ISO、曝光时间、光圈、焦距等
|
||||
- **GPS信息**: 经纬度坐标
|
||||
- **描述信息**: 标题、描述
|
||||
- **扩展字段**: JSON格式存储其他EXIF字段
|
||||
- **系统字段**: CreatedAt, UpdatedAt
|
||||
**文件**: `config.go`, `loader.go`, `database.go`
|
||||
|
||||
**功能**:
|
||||
- 加载 `config.yaml` 配置文件
|
||||
- 支持默认配置和用户自定义配置合并
|
||||
- 配置热更新 (未来扩展)
|
||||
|
||||
**关键配置项**:
|
||||
```yaml
|
||||
app:
|
||||
port: "9484" # HTTP 端口
|
||||
mode: "release" # debug/release
|
||||
development: true # 开发/生产模式切换
|
||||
|
||||
database:
|
||||
type: "sqlite" # sqlite/mysql
|
||||
path: "db/db.sqlite" # SQLite 路径
|
||||
|
||||
image:
|
||||
thumbnail_format: "jpg" # jpg/webp
|
||||
thumbnail_size: [512, 720] # 缩略图尺寸
|
||||
thumbnail_quality: 80 # 质量 (1-100)
|
||||
|
||||
task:
|
||||
concurrency: 0 # 并发数 (0=自动)
|
||||
auto_adjust: true # 动态调整
|
||||
```
|
||||
|
||||
**设计目标**: 灵活配置,支持多环境部署
|
||||
|
||||
---
|
||||
|
||||
### 2. 数据模型 (`internal/model/tables/`)
|
||||
|
||||
#### 核心数据表
|
||||
|
||||
**Photo 表** (`photo_table.go`)
|
||||
```go
|
||||
type Photo struct {
|
||||
ImgPath string // 图片路径
|
||||
ImgName string // 图片名称
|
||||
Hash string // SHA256 哈希 (唯一索引)
|
||||
Width int // 宽度
|
||||
Height int // 高度
|
||||
AspectRatio float32 // 宽高比
|
||||
FileSize int64 // 文件大小
|
||||
Format string // 格式 (jpg/png/etc)
|
||||
TakenAt *time.Time // 拍摄时间
|
||||
Rating int // 评分 (0-5)
|
||||
ViewCount int // 访问次数
|
||||
}
|
||||
```
|
||||
|
||||
**PhotoExif 表** (`exif_table.go`)
|
||||
```go
|
||||
type PhotoExif struct {
|
||||
Hash string // 照片哈希 (主键)
|
||||
|
||||
// 基础文件信息
|
||||
FileName string
|
||||
FileSize int64
|
||||
ImageWidth int
|
||||
ImageHeight int
|
||||
MIMEType string
|
||||
ModifyDate string
|
||||
|
||||
// EXIF 摄影参数
|
||||
Model string // 相机型号 (索引)
|
||||
Make string // 相机厂商 (索引)
|
||||
ISO int // ISO 值 (索引)
|
||||
GPSLatitude float64 // GPS 纬度 (索引)
|
||||
GPSLongitude float64 // GPS 经度 (索引)
|
||||
ExposureTime float64 // 曝光时间
|
||||
Aperture float64 // 光圈
|
||||
FocalLength float64 // 焦距
|
||||
LensID string // 镜头ID
|
||||
DateTimeOrig string // 拍摄时间 (索引)
|
||||
|
||||
// 扩展字段 (JSON)
|
||||
OtherFields JSONMap // 其他 EXIF 字段
|
||||
}
|
||||
```
|
||||
|
||||
**设计特点**:
|
||||
- 使用Hash作为主键,简化表关系
|
||||
- 提供直接的model转换方法 (FromParsedExif/ToParsedExif)
|
||||
- 支持便捷查询方法 (HasGPS, HasCameraInfo等)
|
||||
- 无复杂的处理状态管理,专注于存储和展示
|
||||
- **Hash 作为关联键**: Photo 和 PhotoExif 通过 Hash 关联,简化设计
|
||||
- **索引优化**: 常用查询字段 (相机型号、ISO、GPS、时间) 建立索引
|
||||
- **JSON 扩展字段**: 灵活存储非标准 EXIF 字段
|
||||
- **无外键约束**: 避免级联删除复杂性,提升性能
|
||||
|
||||
### 3. 数据访问层 (internal/repositories/)
|
||||
- **BaseService** - 基础服务,处理写操作协程
|
||||
- **LibraryRepository** - 图片库数据访问
|
||||
- **UserRepository** - 用户数据访问
|
||||
- **ExifRepository** - EXIF数据访问,支持多种查询方式
|
||||
---
|
||||
|
||||
### 4. 业务处理器 (internal/handler/)
|
||||
- **LibraryHandler** - 图片库管理 (添加、删除、更新、索引)
|
||||
- **DevImageHandler** - 开发工具,EXIF信息获取
|
||||
- **ExifHandler** - EXIF信息管理 (查询、筛选、统计)
|
||||
- **SSEHandler** - Server-Sent Events实时通信
|
||||
- **UserHandler** - 用户管理
|
||||
### 3. 数据访问层 (`internal/repositories/`)
|
||||
|
||||
### 5. 图像处理工作流 (internal/workflow/)
|
||||
#### 图像任务处理 (img_task.go):
|
||||
- **任务状态管理**: pending, running, paused, failed, done
|
||||
- **处理步骤**:
|
||||
1. 初始化 → 2. 验证格式 → 3. 读取文件 → 4. 计算哈希
|
||||
5. 提取EXIF → 6. 格式转换 → 7. 生成缩略图 → 8. 保存数据库 → 9. 智能分析
|
||||
- **任务管理器**: 支持并发处理、暂停恢复、自动调优
|
||||
- **系统监控**: CPU/内存使用率监控,动态调整并发数
|
||||
**BaseService** (`base_service.go`)
|
||||
- 管理写操作协程,避免数据库锁竞争
|
||||
- 单例模式,全局唯一写队列
|
||||
|
||||
### 6. 工具管理 (internal/utils/tools/)
|
||||
- **ExifTool** - EXIF数据提取
|
||||
- **ImageMagick** - 图像格式转换
|
||||
- **LibVips** - 高性能图像处理
|
||||
**LibraryRepository** (`library_repository.go`)
|
||||
- 图片库路径的 CRUD 操作
|
||||
- 支持启用/禁用图片库
|
||||
|
||||
### 7. SSE实时通信 (pkg/sse/)
|
||||
- **Manager** - SSE连接管理器
|
||||
- **Client** - 客户端连接管理
|
||||
- **Event** - 事件数据结构
|
||||
- 支持广播、单点发送、心跳检测
|
||||
**PhotoRepository** (`photo_repository.go`)
|
||||
- 照片基础信息 CRUD
|
||||
- 支持按时间范围、评分、格式查询
|
||||
|
||||
## API接口梳理
|
||||
**ExifRepository** (`exif_repository.go`)
|
||||
- EXIF 信息查询和统计
|
||||
- 支持多种筛选: 相机型号、ISO 范围、GPS、时间范围
|
||||
- 提供统计接口: 相机使用统计、时间线分布
|
||||
|
||||
**设计目标**: 封装数据库操作,提供清晰的业务接口
|
||||
|
||||
---
|
||||
|
||||
### 4. HTTP 处理器 (`internal/handler/`)
|
||||
|
||||
**LibraryHandler** (`library_handler.go`)
|
||||
- 图片库管理: 添加/删除/更新/查询
|
||||
- 启动图片索引任务
|
||||
|
||||
**PhotoHandler** (`photo_handler.go`)
|
||||
- 照片文件获取 (原图/缩略图)
|
||||
- 照片列表查询 (分页、时间范围)
|
||||
- 时间线统计
|
||||
|
||||
**ExifHandler** (`exif_handler.go`)
|
||||
- EXIF 信息查询 (单个/批量/分页)
|
||||
- EXIF 筛选: 相机、ISO、光圈、GPS
|
||||
- EXIF 统计: 相机使用量、时间分布
|
||||
|
||||
**FileSystemHandler** (`filesystem_handler.go`)
|
||||
- 文件系统浏览 (新增功能)
|
||||
- 磁盘使用情况查询
|
||||
- 文件搜索
|
||||
|
||||
**FileOperationsHandler** (对应功能)
|
||||
- 创建目录/删除/移动/复制文件
|
||||
|
||||
**SSEHandler** (`sse_handler.go`)
|
||||
- SSE 连接管理
|
||||
- 实时推送任务进度
|
||||
|
||||
**设计目标**: 薄层处理器,主要负责参数验证和响应格式化
|
||||
|
||||
---
|
||||
|
||||
### 5. 图像处理工作流 (`internal/workflow/`)
|
||||
|
||||
**ImgTask** (`img_task.go`)
|
||||
|
||||
**任务状态管理**:
|
||||
```go
|
||||
const (
|
||||
StatusPending = "pending" // 等待处理
|
||||
StatusRunning = "running" // 正在处理
|
||||
StatusPaused = "paused" // 已暂停
|
||||
StatusFailed = "failed" // 处理失败
|
||||
StatusDone = "done" // 处理完成
|
||||
)
|
||||
```
|
||||
|
||||
**处理步骤**:
|
||||
1. **Initializing** - 初始化任务
|
||||
2. **Validating** - 验证文件格式
|
||||
3. **ReadingFile** - 读取文件内容
|
||||
4. **CalculatingHash** - 计算 SHA256 哈希
|
||||
5. **ExtractingExif** - 提取 EXIF 信息
|
||||
6. **ConvertingFormat** - 格式转换 (如需)
|
||||
7. **GeneratingThumbnails** - 生成多尺寸缩略图
|
||||
8. **SavingToDatabase** - 保存到数据库
|
||||
9. **IntelligentAnalysis** - 智能分析 (AI 标签等)
|
||||
|
||||
**TaskManager** (任务管理器)
|
||||
- **并发控制**: 根据 CPU 核心数自动调整并发数
|
||||
- **系统监控**: 监控 CPU/内存使用率,动态调整
|
||||
- **暂停/恢复**: 支持任务暂停和恢复
|
||||
- **进度通知**: 通过 SSE 推送实时进度
|
||||
|
||||
**设计目标**: 高效、可监控、可恢复的图像处理流水线
|
||||
|
||||
---
|
||||
|
||||
### 6. 外部工具管理 (`internal/utils/tools/`)
|
||||
|
||||
**ExifTool** (`exif_tool.go`)
|
||||
- 功能: 提取 EXIF 元数据
|
||||
- 输出: JSON 格式
|
||||
- 性能: 快速读取,无需解码图像
|
||||
|
||||
**ImageMagick** (`image_magick_tool.go`)
|
||||
- 功能: 图像格式转换、裁剪、缩放
|
||||
- 命令: `magick convert`
|
||||
- 支持: 200+ 图像格式
|
||||
|
||||
**LibVips** (`libvips_tool.go`)
|
||||
- 功能: 高性能缩略图生成
|
||||
- 特点: 流式处理,低内存占用
|
||||
- 性能: 比 ImageMagick 快 4-8 倍
|
||||
|
||||
**ToolManager** (`tool_manager.go`)
|
||||
- 工具路径查找和验证
|
||||
- 版本检测
|
||||
- 跨平台兼容性处理
|
||||
|
||||
**设计目标**: 统一的工具调用接口,易于扩展新工具
|
||||
|
||||
---
|
||||
|
||||
### 7. SSE 实时通信 (`pkg/sse/`)
|
||||
|
||||
**Manager** (`manager.go`)
|
||||
- 管理所有 SSE 客户端连接
|
||||
- 支持广播和单点发送
|
||||
- 心跳检测 (30s 间隔)
|
||||
- 自动清理超时连接
|
||||
|
||||
**Event** (事件结构)
|
||||
```go
|
||||
type Event struct {
|
||||
ID string // 事件ID
|
||||
Event string // 事件类型
|
||||
Data string // 事件数据 (JSON)
|
||||
Retry int // 重试间隔 (ms)
|
||||
}
|
||||
```
|
||||
|
||||
**使用场景**:
|
||||
- 图片索引任务进度推送
|
||||
- 图像处理状态更新
|
||||
- 系统通知
|
||||
|
||||
**设计目标**: 低延迟、高并发的实时通信
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 工具函数库
|
||||
|
||||
### pkg/utils/ (通用工具)
|
||||
|
||||
#### FileUtils (`file_utils.go`)
|
||||
**功能**: 文件和目录操作
|
||||
```go
|
||||
// 基础操作
|
||||
FileUtils.Exists(path string) bool // 检查文件/目录是否存在
|
||||
FileUtils.IsDir(path string) bool // 是否为目录
|
||||
FileUtils.IsFile(path string) bool // 是否为文件
|
||||
|
||||
// 目录操作
|
||||
FileUtils.GetDirectories(dirPath string) // 获取所有子目录
|
||||
FileUtils.GetAllFiles(dirPath, recursive) // 递归获取文件
|
||||
FileUtils.GetFilteredFiles(path, recursive, types) // 按类型筛选文件
|
||||
FileUtils.CreateDir(dirPath string) // 创建目录
|
||||
|
||||
// 文件操作
|
||||
FileUtils.CopyFile(src, dst string) // 复制文件
|
||||
FileUtils.CopyDir(src, dst string) // 复制目录
|
||||
FileUtils.MoveFile(src, dst string) // 移动/重命名
|
||||
FileUtils.Delete(path string) // 删除文件/目录
|
||||
|
||||
// 文件读写
|
||||
FileUtils.ReadFile(filePath string) // 读取文件内容
|
||||
FileUtils.ReadFileLines(filePath string) // 按行读取
|
||||
FileUtils.WriteFile(filePath, content) // 写入文件
|
||||
FileUtils.AppendFile(filePath, content) // 追加内容
|
||||
|
||||
// 文件信息
|
||||
FileUtils.GetFileSize(filePath string) // 获取文件大小
|
||||
FileUtils.GetModTime(filePath string) // 获取修改时间
|
||||
FileUtils.GetExtension(filePath string) // 获取扩展名
|
||||
FileUtils.GetFileName(filePath string) // 获取文件名 (不含扩展名)
|
||||
FileUtils.GetDirSize(dirPath string) // 获取目录大小
|
||||
FileUtils.FormatFileSize(bytes int64) // 格式化文件大小
|
||||
|
||||
// 哈希计算
|
||||
FileUtils.GetFileMD5(filePath string) // 计算 MD5
|
||||
FileUtils.GetFileSHA256(filePath string) // 计算 SHA256
|
||||
|
||||
// 搜索
|
||||
FileUtils.SearchFiles(dirPath, pattern, recursive) // 搜索文件
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **始终使用 FileUtils**,不要直接调用 `os` 包
|
||||
- ✅ **错误处理**: 所有方法都返回 error,必须检查
|
||||
- ✅ **路径安全**: 避免路径遍历攻击
|
||||
|
||||
#### HashUtils (`hash_utils.go`)
|
||||
**功能**: 文件和字符串哈希计算
|
||||
```go
|
||||
// 文件哈希
|
||||
HashUtils.HashFile(filename, hashType) // 通用文件哈希
|
||||
HashUtils.MD5File(filename) // MD5 快捷方法
|
||||
HashUtils.SHA256File(filename) // SHA256 快捷方法
|
||||
HashUtils.SHA512File(filename) // SHA512 快捷方法
|
||||
|
||||
// 字符串哈希
|
||||
HashUtils.HashString(data, hashType) // 通用字符串哈希
|
||||
HashUtils.MD5String(data) // MD5 快捷方法
|
||||
HashUtils.SHA256String(data) // SHA256 快捷方法
|
||||
|
||||
// 高级功能
|
||||
HashUtils.HashFileWithProgress(filename, hashType, callback) // 带进度回调
|
||||
HashUtils.HashMultipleFiles(filenames, hashType) // 并发计算多个文件
|
||||
HashUtils.CompareFiles(file1, file2, hashType) // 比较文件
|
||||
HashUtils.FindDuplicates(filenames, hashType) // 查找重复文件
|
||||
HashUtils.HashFileMultipleAlgorithms(filename, types) // 多算法计算
|
||||
|
||||
// 缩略图路径生成
|
||||
HashUtils.HashThumbPath(basePath, hash, filename, ext) // 生成缩略图路径
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **照片哈希**: 统一使用 `SHA256`
|
||||
- ✅ **并发计算**: 批量处理时使用 `HashMultipleFiles`
|
||||
- ✅ **进度反馈**: 大文件使用 `HashFileWithProgress`
|
||||
|
||||
#### SysUtils (`sys_utils.go` + 平台特定文件)
|
||||
**功能**: 系统信息获取
|
||||
```go
|
||||
sysUtil := NewSysUtils()
|
||||
|
||||
// 基础信息
|
||||
sysUtil.GetSystemInfo() // 操作系统/架构/CPU核心数
|
||||
sysUtil.GetCurrentProcessInfo() // 当前进程信息
|
||||
sysUtil.GetSystemUptime() // 系统运行时间
|
||||
|
||||
// 硬件信息
|
||||
sysUtil.GetCPUInfo() // CPU 型号/核心数/频率
|
||||
sysUtil.GetMemoryInfo() // 内存使用情况
|
||||
sysUtil.GetDiskUsage(path) // 磁盘使用情况
|
||||
sysUtil.GetNetworkInterfaces() // 网络接口信息
|
||||
|
||||
// 格式化
|
||||
sysUtil.FormatBytes(bytes) // 格式化字节数 (1.2GB)
|
||||
|
||||
// 完整打印
|
||||
sysUtil.PrintSystemInfo() // 打印所有系统信息
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **性能监控**: 用于任务管理器动态调整并发数
|
||||
- ✅ **跨平台**: 自动选择平台特定实现
|
||||
|
||||
### pkg/system/ (系统信息模块)
|
||||
|
||||
**新增功能**: 完整的系统信息获取和监控
|
||||
|
||||
```go
|
||||
// 设备管理
|
||||
DeviceManager.GetAllDevices() // 获取所有设备
|
||||
DeviceManager.GetDeviceByPath(path) // 按路径获取设备
|
||||
DeviceManager.GetDeviceUsage(path) // 设备使用情况
|
||||
|
||||
// 进程管理
|
||||
ProcessManager.GetCurrentProcess() // 当前进程信息
|
||||
ProcessManager.GetProcessByPID(pid) // 按PID获取进程
|
||||
ProcessManager.ListProcesses() // 列出所有进程
|
||||
|
||||
// 网络工具
|
||||
NetworkUtils.GetLocalIP() // 获取本地IP
|
||||
NetworkUtils.GetMACAddress() // 获取MAC地址
|
||||
NetworkUtils.IsPortAvailable(port) // 检查端口是否可用
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **监控**: 用于系统监控和性能分析
|
||||
- ✅ **集成**: 与 `pkg/utils/SysUtils` 互补使用
|
||||
|
||||
### pkg/logger/ (日志工具)
|
||||
|
||||
**功能**: 基于 Zap 的高性能日志
|
||||
```go
|
||||
// 初始化
|
||||
logConfig := logger.DefaultConfig()
|
||||
logConfig.Level = logger.DebugLevel
|
||||
logConfig.LogPath = "./logs"
|
||||
logger.InitDefaultLogger(logConfig)
|
||||
|
||||
// 基础日志
|
||||
logger.Debug("调试信息")
|
||||
logger.Info("普通信息")
|
||||
logger.Warn("警告信息")
|
||||
logger.Error("错误信息")
|
||||
logger.Fatal("致命错误")
|
||||
|
||||
// 格式化日志
|
||||
logger.Infof("处理文件: %s", filename)
|
||||
logger.Errorf("处理失败: %v", err)
|
||||
|
||||
// 结构化日志
|
||||
logger.Info("任务完成",
|
||||
zap.String("task_id", id),
|
||||
zap.Int("duration", elapsed),
|
||||
zap.Error(err))
|
||||
```
|
||||
|
||||
**配置项**:
|
||||
```go
|
||||
type LogConfig struct {
|
||||
Level LogLevel // 日志级别
|
||||
LogPath string // 日志路径
|
||||
MaxSize int // 单文件最大 (MB)
|
||||
MaxBackups int // 保留文件数
|
||||
MaxAge int // 保留天数
|
||||
Compress bool // 是否压缩
|
||||
EnableConsole bool // 是否输出到控制台
|
||||
EnableCaller bool // 是否显示调用者
|
||||
}
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **统一使用 logger 包**,不要使用 `log` 或 `fmt.Println`
|
||||
- ✅ **结构化日志**: 关键信息使用 `zap` 字段
|
||||
- ✅ **日志级别**: Debug (开发) / Info (生产) / Warn (警告) / Error (错误)
|
||||
|
||||
### pkg/sse/ (SSE 实时通信)
|
||||
|
||||
**功能**: Server-Sent Events 管理
|
||||
```go
|
||||
// 创建管理器
|
||||
opts := sse.DefaultOptions()
|
||||
manager := sse.NewManager(opts)
|
||||
|
||||
// 注册客户端
|
||||
manager.HandleSSE(c *gin.Context)
|
||||
|
||||
// 发送事件
|
||||
event := &sse.Event{
|
||||
Event: "progress",
|
||||
Data: `{"percent": 50, "message": "处理中..."}`,
|
||||
}
|
||||
manager.Broadcast(event) // 广播
|
||||
manager.SendToClient(clientID, event) // 单点发送
|
||||
|
||||
// 关闭
|
||||
manager.Close()
|
||||
```
|
||||
|
||||
**使用规范**:
|
||||
- ✅ **任务进度**: 图像处理任务实时推送进度
|
||||
- ✅ **事件命名**: 使用清晰的事件名 (`progress`, `error`, `complete`)
|
||||
- ✅ **JSON 数据**: Data 字段使用 JSON 格式
|
||||
|
||||
---
|
||||
|
||||
## 📐 开发规范
|
||||
|
||||
### 1. 代码组织原则
|
||||
|
||||
#### 不要重复造轮子
|
||||
- ✅ **优先使用已有工具**: 检查 `pkg/utils/` 和 `internal/utils/` 是否已有实现
|
||||
- ✅ **扩展而非重写**: 如果功能不足,扩展现有工具函数
|
||||
- ❌ **禁止重复实现**: 不要在多处实现相同功能
|
||||
|
||||
#### 工具函数使用规范
|
||||
```go
|
||||
// ✅ 正确: 使用 FileUtils
|
||||
if !utils.FileUtils.Exists(filePath) {
|
||||
return errors.New("文件不存在")
|
||||
}
|
||||
|
||||
// ❌ 错误: 直接使用 os 包
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return errors.New("文件不存在")
|
||||
}
|
||||
```
|
||||
|
||||
#### 引入规范
|
||||
```go
|
||||
// ✅ 正确: 分组引入,清晰分类
|
||||
import (
|
||||
// 标准库
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
// 第三方库
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
// 项目内部包
|
||||
"rear/internal/config"
|
||||
"rear/pkg/logger"
|
||||
"rear/pkg/utils"
|
||||
)
|
||||
|
||||
// ✅ 正确: 别名避免冲突
|
||||
import (
|
||||
"rear/internal/utils"
|
||||
utilsPkg "rear/pkg/utils"
|
||||
)
|
||||
```
|
||||
|
||||
### 2. 错误处理规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 详细的错误上下文
|
||||
hash, err := utils.HashUtils.SHA256File(filePath)
|
||||
if err != nil {
|
||||
logger.Errorf("计算文件哈希失败: %v", err,
|
||||
zap.String("file_path", filePath))
|
||||
return fmt.Errorf("计算文件哈希失败: %w", err)
|
||||
}
|
||||
|
||||
// ✅ 正确: 使用 errors.Is/As 判断错误类型
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("文件不存在: %s", filePath)
|
||||
}
|
||||
|
||||
// ❌ 错误: 忽略错误
|
||||
hash, _ := utils.HashUtils.SHA256File(filePath)
|
||||
```
|
||||
|
||||
### 3. 日志规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 结构化日志
|
||||
logger.Info("开始处理图片",
|
||||
zap.String("file_path", filePath),
|
||||
zap.String("hash", hash),
|
||||
zap.Int("width", width),
|
||||
zap.Int("height", height))
|
||||
|
||||
// ✅ 正确: 错误日志包含上下文
|
||||
logger.Errorf("EXIF提取失败: %v", err,
|
||||
zap.String("file_path", filePath),
|
||||
zap.String("tool", "exiftool"))
|
||||
|
||||
// ❌ 错误: 使用 fmt.Println
|
||||
fmt.Println("开始处理图片:", filePath)
|
||||
```
|
||||
|
||||
### 4. 并发安全规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 使用互斥锁保护共享数据
|
||||
type TaskManager struct {
|
||||
mu sync.Mutex
|
||||
tasks map[string]*Task
|
||||
}
|
||||
|
||||
func (tm *TaskManager) AddTask(task *Task) {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
tm.tasks[task.ID] = task
|
||||
}
|
||||
|
||||
// ✅ 正确: 使用 context 控制协程生命周期
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
go func(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case result := <-resultChan:
|
||||
// 处理结果
|
||||
}
|
||||
}(ctx)
|
||||
```
|
||||
|
||||
### 5. 数据库操作规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 使用 Repository 层
|
||||
photo := &tables.Photo{
|
||||
ImgPath: filePath,
|
||||
Hash: hash,
|
||||
}
|
||||
err := photoRepo.Create(photo)
|
||||
|
||||
// ✅ 正确: 使用写队列避免锁竞争
|
||||
photoRepo.AsyncCreate(photo)
|
||||
|
||||
// ❌ 错误: 直接使用 GORM
|
||||
db.Create(&photo)
|
||||
```
|
||||
|
||||
### 6. HTTP 接口规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 统一的响应格式
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
Message: "Success",
|
||||
Data: result,
|
||||
})
|
||||
|
||||
// ✅ 正确: 参数验证
|
||||
var req AddLibraryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("Invalid request: %v", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 正确: 使用 gin 的参数绑定
|
||||
hash := c.Param("hash")
|
||||
page := c.DefaultQuery("page", "1")
|
||||
```
|
||||
|
||||
### 7. 配置管理规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 使用全局配置
|
||||
port := config.CONFIG.Port
|
||||
thumbnailSize := config.GetDefaultThumbnailSize()
|
||||
|
||||
// ✅ 正确: 环境判断
|
||||
if config.IsDevelopment() {
|
||||
// 开发环境逻辑
|
||||
} else {
|
||||
// 生产环境逻辑
|
||||
}
|
||||
|
||||
// ❌ 错误: 硬编码配置
|
||||
port := "8080"
|
||||
```
|
||||
|
||||
### 8. 命名规范
|
||||
|
||||
```go
|
||||
// ✅ 正确: 清晰的命名
|
||||
func (h *PhotoHandler) GetPhotoByHash(c *gin.Context) {}
|
||||
func calculateFileHash(filePath string) (string, error) {}
|
||||
|
||||
// ✅ 正确: 常量使用全大写
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusRunning = "running"
|
||||
)
|
||||
|
||||
// ✅ 正确: 接口使用 -er 后缀
|
||||
type Repository interface {
|
||||
Create(entity interface{}) error
|
||||
Update(entity interface{}) error
|
||||
}
|
||||
|
||||
// ❌ 错误: 模糊的命名
|
||||
func handle(c *gin.Context) {}
|
||||
func process(data string) error {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常见开发场景
|
||||
|
||||
### 场景 1: 添加新的 API 接口
|
||||
|
||||
**步骤**:
|
||||
1. 在 `internal/handler/` 创建或扩展 Handler
|
||||
2. 在 `internal/router/router.go` 注册路由
|
||||
3. 如果需要数据库操作,在 `internal/repositories/` 添加方法
|
||||
4. 使用统一的响应格式 `model.Response`
|
||||
|
||||
**示例**: 添加获取照片详情接口
|
||||
```go
|
||||
// 1. 在 PhotoHandler 添加方法
|
||||
func (h *PhotoHandler) GetPhotoDetail(c *gin.Context) {
|
||||
hash := c.Param("hash")
|
||||
|
||||
photo, err := h.photoRepo.GetByHash(hash)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{
|
||||
Code: http.StatusNotFound,
|
||||
Message: "照片不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
Message: "Success",
|
||||
Data: photo,
|
||||
})
|
||||
}
|
||||
|
||||
// 2. 在 router.go 注册
|
||||
photo.GET("/:hash/detail", photoHandler.GetPhotoDetail)
|
||||
```
|
||||
|
||||
### 场景 2: 添加新的工具函数
|
||||
|
||||
**步骤**:
|
||||
1. 确定工具类型: 通用工具 (`pkg/utils/`) 还是内部工具 (`internal/utils/`)
|
||||
2. 选择合适的文件或创建新文件
|
||||
3. 遵循现有命名规范
|
||||
4. 添加完整的注释和错误处理
|
||||
|
||||
**示例**: 添加图片尺寸检测
|
||||
```go
|
||||
// pkg/utils/image_utils.go
|
||||
|
||||
type imageUtilsStruct struct{}
|
||||
|
||||
var ImageUtils = imageUtilsStruct{}
|
||||
|
||||
// GetImageDimensions 获取图片尺寸 (不解码整个图像)
|
||||
func (imageUtilsStruct) GetImageDimensions(filePath string) (width, height int, err error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
config, _, err := image.DecodeConfig(file)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("解码图片配置失败: %w", err)
|
||||
}
|
||||
|
||||
return config.Width, config.Height, nil
|
||||
}
|
||||
```
|
||||
|
||||
**使用**:
|
||||
```go
|
||||
width, height, err := utils.ImageUtils.GetImageDimensions(filePath)
|
||||
if err != nil {
|
||||
logger.Errorf("获取图片尺寸失败: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### 场景 3: 添加新的数据表
|
||||
|
||||
**步骤**:
|
||||
1. 在 `internal/model/tables/` 创建表结构
|
||||
2. 在 `internal/db/migration.go` 添加迁移
|
||||
3. 在 `internal/repositories/` 创建 Repository
|
||||
4. 在 `internal/container/container.go` 注册 Repository
|
||||
|
||||
**示例**: 添加标签表
|
||||
```go
|
||||
// 1. internal/model/tables/tag_table.go
|
||||
type Tag struct {
|
||||
BaseModel
|
||||
Name string `gorm:"uniqueIndex;size:50" json:"name"`
|
||||
Color string `gorm:"size:20" json:"color"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
func (Tag) TableName() string {
|
||||
return "tags"
|
||||
}
|
||||
|
||||
// 2. internal/db/migration.go
|
||||
func AutoMigrate() error {
|
||||
return GetDB().AutoMigrate(
|
||||
// ... 现有表
|
||||
&tables.Tag{},
|
||||
)
|
||||
}
|
||||
|
||||
// 3. internal/repositories/tag_repository.go
|
||||
type TagRepository struct {
|
||||
BaseRepository
|
||||
}
|
||||
|
||||
func NewTagRepository() *TagRepository {
|
||||
return &TagRepository{
|
||||
BaseRepository: BaseRepository{tableName: "tags"},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TagRepository) GetAllTags() ([]tables.Tag, error) {
|
||||
var tags []tables.Tag
|
||||
err := GetDB().Find(&tags).Error
|
||||
return tags, err
|
||||
}
|
||||
|
||||
// 4. internal/container/container.go
|
||||
type DbContainer struct {
|
||||
// ... 现有 Repo
|
||||
TagRepo *repositories.TagRepository
|
||||
}
|
||||
|
||||
func NewContainer() *DbContainer {
|
||||
return &DbContainer{
|
||||
// ... 现有 Repo
|
||||
TagRepo: repositories.NewTagRepository(),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 场景 4: 添加新的外部工具支持
|
||||
|
||||
**步骤**:
|
||||
1. 在 `internal/utils/tools/` 创建工具封装
|
||||
2. 在 `tool_manager.go` 注册工具
|
||||
3. 下载工具到 `tools/{platform}/toolname/`
|
||||
|
||||
**示例**: 添加 FFmpeg 支持
|
||||
```go
|
||||
// internal/utils/tools/ffmpeg_tool.go
|
||||
type FFmpegTool struct {
|
||||
execPath string
|
||||
}
|
||||
|
||||
func NewFFmpegTool(execPath string) *FFmpegTool {
|
||||
return &FFmpegTool{execPath: execPath}
|
||||
}
|
||||
|
||||
func (t *FFmpegTool) ExtractVideoThumbnail(videoPath, outputPath string) error {
|
||||
cmd := exec.Command(t.execPath,
|
||||
"-i", videoPath,
|
||||
"-ss", "00:00:01",
|
||||
"-vframes", "1",
|
||||
outputPath)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("FFmpeg 执行失败: %w, 输出: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// internal/utils/tool_manager.go
|
||||
func InitializeFromConfig(appDir *string) error {
|
||||
// ... 现有工具初始化
|
||||
|
||||
// FFmpeg
|
||||
ffmpegPath := findToolPath("ffmpeg", "ffmpeg.exe", appDir)
|
||||
FFmpeg = NewFFmpegTool(ffmpegPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 API 接口速查
|
||||
|
||||
### 基础接口
|
||||
- `GET /` - 基础响应
|
||||
- `GET /health` - 健康检查
|
||||
|
||||
### API v1接口组 (/api/v1)
|
||||
- `GET /api/v1` - API版本信息
|
||||
### API v1 接口
|
||||
|
||||
#### 用户管理 (/api/v1/users)
|
||||
#### 用户管理 (`/api/v1/users`)
|
||||
- `GET /api/v1/users` - 获取用户列表
|
||||
- `GET /api/v1/users/:id` - 获取特定用户
|
||||
- `POST /api/v1/users` - 创建用户
|
||||
- `PUT /api/v1/users/:id` - 更新用户
|
||||
- `DELETE /api/v1/users/:id` - 删除用户
|
||||
|
||||
#### 图片库管理 (/api/v1/library)
|
||||
#### 图片库管理 (`/api/v1/library`)
|
||||
- `GET /api/v1/library` - 获取图片库列表
|
||||
- `POST /api/v1/library` - 添加图片库路径
|
||||
- Body: `{"path": "/path/to/library"}`
|
||||
- `PUT /api/v1/library` - 更新图片库
|
||||
- Body: `{"path": "/path", "is_enable": true}`
|
||||
- `DELETE /api/v1/library` - 删除图片库
|
||||
- Query: `?path=/path/to/library`
|
||||
- `POST /api/v1/library/indexed` - 启动图片索引任务
|
||||
- Body: `{"path": "/path/to/library"}`
|
||||
|
||||
#### EXIF信息管理 (/api/v1/exif)
|
||||
- `GET /api/v1/exif` - 获取所有EXIF记录 (支持分页)
|
||||
- `GET /api/v1/exif/:hash` - 根据照片哈希获取EXIF信息
|
||||
- `GET /api/v1/exif/statistics` - 获取EXIF统计信息
|
||||
#### EXIF 信息管理 (`/api/v1/exif`)
|
||||
- `GET /api/v1/exif` - 获取所有 EXIF 记录
|
||||
- Query: `?page=1&page_size=20`
|
||||
- `GET /api/v1/exif/:hash` - 根据哈希获取 EXIF
|
||||
- `GET /api/v1/exif/statistics` - 获取统计信息
|
||||
- `GET /api/v1/exif/cameras` - 获取所有相机型号列表
|
||||
- `GET /api/v1/exif/cameras/stats` - 获取相机使用统计
|
||||
- `GET /api/v1/exif/search` - 搜索EXIF信息 (关键词搜索)
|
||||
- `GET /api/v1/exif/gps` - 获取包含GPS信息的照片
|
||||
- `GET /api/v1/exif/iso` - 根据ISO范围筛选照片
|
||||
- `GET /api/v1/exif/aperture` - 根据光圈范围筛选照片
|
||||
- `GET /api/v1/exif/camera` - 根据相机型号筛选照片
|
||||
- `GET /api/v1/exif/search` - 搜索 EXIF
|
||||
- Query: `?keyword=keyword`
|
||||
- `GET /api/v1/exif/gps` - 获取包含 GPS 的照片
|
||||
- `GET /api/v1/exif/iso` - 根据 ISO 筛选
|
||||
- Query: `?min=100&max=3200`
|
||||
- `GET /api/v1/exif/aperture` - 根据光圈筛选
|
||||
- Query: `?min=1.4&max=5.6`
|
||||
- `GET /api/v1/exif/camera` - 根据相机筛选
|
||||
- Query: `?model=Canon EOS R5`
|
||||
|
||||
### 开发接口组 (/dev)
|
||||
- `GET /dev/tool/exiftool/get_exif` - 获取图片EXIF信息
|
||||
#### 照片管理 (`/api/v1/photo`, `/api/v1/photos`)
|
||||
- `GET /api/v1/photo/:hash` - 获取图像文件
|
||||
- Query: `?size=720` (缩略图尺寸)
|
||||
- `GET /api/v1/photos/timeline` - 获取时间线统计
|
||||
- `GET /api/v1/photos` - 获取照片列表
|
||||
- Query: `?page=1&page_size=20&start_date=2024-01-01&end_date=2024-12-31`
|
||||
|
||||
## 图像处理流程分析
|
||||
#### 资产信息 (`/api/v1/assets`)
|
||||
- `GET /api/v1/assets/:hash` - 获取图像详细信息
|
||||
|
||||
### 缩略图生成策略
|
||||
1. **路径结构**: `/thumbnail/{hash前2位}/{hash第3-4位}/{hash剩余部分}/{尺寸}.jpg`
|
||||
2. **支持格式**: JPG、WebP
|
||||
3. **尺寸配置**: 可配置多种缩略图尺寸
|
||||
4. **质量控制**: 可配置图像质量参数
|
||||
#### 文件系统 (`/api/v1/filesystem`)
|
||||
- `GET /api/v1/filesystem/browse` - 浏览文件系统
|
||||
- Query: `?path=/path/to/dir`
|
||||
- `GET /api/v1/filesystem/disk-usage` - 获取磁盘使用情况
|
||||
- Query: `?path=/path`
|
||||
- `GET /api/v1/filesystem/item` - 获取文件系统项目信息
|
||||
- Query: `?path=/path/to/item`
|
||||
- `GET /api/v1/filesystem/search` - 搜索文件
|
||||
- Query: `?path=/path&keyword=keyword`
|
||||
- `POST /api/v1/filesystem/directory` - 创建目录
|
||||
- Body: `{"path": "/path/to/new/dir"}`
|
||||
- `DELETE /api/v1/filesystem/item` - 删除文件或目录
|
||||
- Query: `?path=/path/to/item`
|
||||
- `PUT /api/v1/filesystem/item/move` - 移动/重命名
|
||||
- Body: `{"src": "/path/src", "dst": "/path/dst"}`
|
||||
- `POST /api/v1/filesystem/item/copy` - 复制文件或目录
|
||||
- Body: `{"src": "/path/src", "dst": "/path/dst"}`
|
||||
|
||||
### 图像处理工作流
|
||||
1. **文件验证** - 检查文件格式和有效性
|
||||
2. **哈希计算** - 生成文件唯一标识
|
||||
3. **EXIF提取** - 获取图像元数据信息
|
||||
4. **格式转换** - 统一转换为支持的格式
|
||||
5. **缩略图生成** - 生成多尺寸缩略图
|
||||
6. **数据库存储** - 保存图像信息和元数据
|
||||
7. **智能分析** - 异步调用AI分析服务
|
||||
#### 开发接口 (`/dev`)
|
||||
- `GET /dev/tool/exiftool/get_exif` - 获取图片 EXIF 信息
|
||||
- Query: `?path=/path/to/image.jpg`
|
||||
|
||||
### 任务并发控制
|
||||
- **动态并发数**: 根据CPU核心数自动调整
|
||||
- **内存监控**: 基于内存使用率调整处理速度
|
||||
- **队列管理**: 支持任务队列和优先级
|
||||
- **错误处理**: 失败重试和错误日志记录
|
||||
---
|
||||
|
||||
## 开发和运维要点
|
||||
## 🚀 性能优化策略
|
||||
|
||||
### 配置文件重要设置
|
||||
```yaml
|
||||
app:
|
||||
port: "8080" # 服务端口
|
||||
mode: "release" # 运行模式 (debug/release)
|
||||
development: true # 开发模式开关
|
||||
### 1. 图像处理优化
|
||||
- **并发处理**: 根据 CPU 核心数自动调整并发数
|
||||
- **流式处理**: 使用 LibVips 的流式处理,避免大文件占用内存
|
||||
- **缓存策略**: 缩略图按哈希存储,避免重复生成
|
||||
- **智能跳过**: 检查哈希值,已存在的照片跳过处理
|
||||
|
||||
logging:
|
||||
level: "debug" # 日志级别
|
||||
### 2. 数据库优化
|
||||
- **索引**: 常用查询字段建立索引 (Hash, Model, ISO, GPS, DateTime)
|
||||
- **写队列**: 所有写操作通过队列,避免锁竞争
|
||||
- **批量操作**: 使用批量插入/更新
|
||||
- **连接池**: 合理配置数据库连接池
|
||||
|
||||
database:
|
||||
type: "sqlite" # 数据库类型
|
||||
```
|
||||
### 3. 内存管理
|
||||
- **缓冲区**: 根据文件大小动态调整缓冲区 (64KB - 1MB)
|
||||
- **哈希计算**: 分块读取,避免一次加载整个文件
|
||||
- **垃圾回收**: 定期清理临时文件和过期缓存
|
||||
|
||||
### 启动流程
|
||||
1. **配置加载** - 加载config.yaml配置文件
|
||||
2. **日志初始化** - 设置日志系统
|
||||
3. **工具初始化** - 检查和初始化外部工具
|
||||
4. **数据库初始化** - 连接数据库并执行迁移
|
||||
5. **容器初始化** - 依赖注入容器设置
|
||||
6. **任务管理器** - 启动图像处理任务管理器
|
||||
7. **HTTP服务** - 启动Web服务器
|
||||
### 4. 系统监控
|
||||
- **动态调整**: 监控 CPU/内存使用率,自动调整并发数
|
||||
- **任务暂停**: 系统资源不足时自动暂停任务
|
||||
- **优雅降级**: 外部工具失败时使用备用方案
|
||||
|
||||
### 构建部署
|
||||
- **构建脚本**: `scripts/build.sh`
|
||||
- **支持平台**: Windows (amd64), Linux (amd64), macOS (amd64/arm64)
|
||||
- **工具链管理**: 自动解压和配置外部工具
|
||||
---
|
||||
|
||||
## 性能特性
|
||||
## 🔒 安全特性
|
||||
|
||||
### 高并发处理
|
||||
- **协程池**: 基于Go协程的高并发处理
|
||||
- **任务队列**: 支持大容量任务队列
|
||||
- **负载均衡**: 自动调整并发数以优化性能
|
||||
|
||||
### 内存管理
|
||||
- **内存监控**: 实时监控内存使用情况
|
||||
- **垃圾回收**: 优化的内存回收策略
|
||||
- **缓存管理**: 缩略图缓存和文件缓存
|
||||
|
||||
### 监控调试
|
||||
- **性能分析**: 集成pprof性能分析工具
|
||||
- **结构化日志**: 使用zap高性能日志库
|
||||
- **系统监控**: CPU、内存、协程数量监控
|
||||
|
||||
## 扩展性设计
|
||||
|
||||
### 数据库支持
|
||||
- **多数据库**: 同时支持SQLite和MySQL
|
||||
- **ORM抽象**: 使用GORM进行数据库操作抽象
|
||||
- **迁移管理**: 自动化数据库结构迁移
|
||||
|
||||
### 图像格式扩展
|
||||
- **格式检测**: 自动识别图像格式
|
||||
- **转换引擎**: 多种图像处理引擎支持
|
||||
- **插件架构**: 支持新格式插件扩展
|
||||
|
||||
### API版本管理
|
||||
- **版本隔离**: API接口版本化管理
|
||||
- **向后兼容**: 保持API向后兼容性
|
||||
- **文档化**: 完整的API文档
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 文件安全
|
||||
- **路径验证**: 防止路径遍历攻击
|
||||
- **文件类型检查**: 严格的文件类型验证
|
||||
### 1. 文件安全
|
||||
- **路径验证**: 防止路径遍历攻击 (`.`, `..`, `~`)
|
||||
- **文件类型检查**: 使用 `h2non/filetype` 验证真实文件类型
|
||||
- **哈希验证**: 文件完整性检查
|
||||
|
||||
### 网络安全
|
||||
- **CORS配置**: 跨域请求控制
|
||||
- **请求限制**: 防止恶意请求
|
||||
- **日志审计**: 完整的访问日志记录
|
||||
### 2. 网络安全
|
||||
- **CORS 配置**: 可配置允许的来源
|
||||
- **请求限制**: 防止恶意请求 (未来扩展)
|
||||
- **日志审计**: 完整的访问日志
|
||||
|
||||
这个项目是一个功能完整、架构清晰的照片管理系统,具有良好的扩展性和维护性。
|
||||
### 3. 数据安全
|
||||
- **SQL 注入防护**: 使用 GORM 参数化查询
|
||||
- **XSS 防护**: 输出数据转义
|
||||
- **敏感信息**: 不在日志中输出敏感路径
|
||||
|
||||
---
|
||||
|
||||
## 🐛 调试技巧
|
||||
|
||||
### 1. 日志级别调整
|
||||
```yaml
|
||||
# config.yaml
|
||||
logging:
|
||||
level: "debug" # 开发: debug, 生产: info
|
||||
```
|
||||
|
||||
### 2. 性能分析
|
||||
```go
|
||||
// 启用 pprof (debug 模式)
|
||||
// 访问 http://localhost:9484/debug/pprof/
|
||||
```
|
||||
|
||||
### 3. SSE 调试
|
||||
```bash
|
||||
# 使用 curl 测试 SSE
|
||||
curl -N http://localhost:9484/api/v1/sse
|
||||
```
|
||||
|
||||
### 4. 数据库调试
|
||||
```go
|
||||
// 在 GORM 查询前添加
|
||||
db.Debug().Find(&photos)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 未来扩展方向
|
||||
|
||||
### 计划中的功能
|
||||
1. **AI 智能分析**: 图像标签、人脸识别、场景分类
|
||||
2. **视频支持**: FFmpeg 集成,视频缩略图生成
|
||||
3. **相似图片查找**: 感知哈希 (pHash) 算法
|
||||
4. **批量编辑**: EXIF 批量修改、批量重命名
|
||||
5. **分享功能**: 生成分享链接、权限控制
|
||||
6. **多用户支持**: 用户认证、权限管理
|
||||
7. **插件系统**: 支持自定义处理流程
|
||||
|
||||
### 性能优化计划
|
||||
1. **缓存层**: Redis 缓存热点数据
|
||||
2. **CDN 集成**: 缩略图 CDN 加速
|
||||
3. **分布式处理**: 多机分布式任务处理
|
||||
4. **WebP 支持**: 更高压缩比的缩略图格式
|
||||
|
||||
---
|
||||
|
||||
## 📖 参考资源
|
||||
|
||||
### Go 语言资源
|
||||
- [Go 官方文档](https://golang.org/doc/)
|
||||
- [Effective Go](https://golang.org/doc/effective_go)
|
||||
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||
|
||||
### 框架文档
|
||||
- [Gin 文档](https://gin-gonic.com/docs/)
|
||||
- [GORM 文档](https://gorm.io/docs/)
|
||||
- [Zap 日志库](https://github.com/uber-go/zap)
|
||||
|
||||
### 外部工具文档
|
||||
- [ExifTool](https://exiftool.org/)
|
||||
- [ImageMagick](https://imagemagick.org/)
|
||||
- [LibVips](https://www.libvips.org/)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 开发检查清单
|
||||
|
||||
### 添加新功能前
|
||||
- [ ] 检查是否有现成工具函数可用
|
||||
- [ ] 确认设计符合现有架构
|
||||
- [ ] 考虑错误处理和日志记录
|
||||
- [ ] 评估性能影响
|
||||
|
||||
### 编写代码时
|
||||
- [ ] 遵循命名规范
|
||||
- [ ] 添加完整的错误处理
|
||||
- [ ] 使用结构化日志
|
||||
- [ ] 考虑并发安全
|
||||
- [ ] 添加必要的注释
|
||||
|
||||
### 提交代码前
|
||||
- [ ] 代码格式化 (`go fmt`)
|
||||
- [ ] 运行测试 (`go test`)
|
||||
- [ ] 检查日志输出
|
||||
- [ ] 验证 API 接口
|
||||
- [ ] 更新本文档 (如需)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 快速定位代码
|
||||
|
||||
### 我想找...
|
||||
- **添加 API 路由**: `internal/router/router.go`
|
||||
- **修改配置**: `config.yaml` + `internal/config/`
|
||||
- **数据库表结构**: `internal/model/tables/`
|
||||
- **业务逻辑**: `internal/handler/`
|
||||
- **数据库操作**: `internal/repositories/`
|
||||
- **工具函数**: `pkg/utils/` 或 `internal/utils/`
|
||||
- **日志配置**: `pkg/logger/logger.go`
|
||||
- **任务处理**: `internal/workflow/img_task.go`
|
||||
- **外部工具调用**: `internal/utils/tools/`
|
||||
|
||||
---
|
||||
|
||||
## 📞 开发规范总结
|
||||
|
||||
### 核心原则
|
||||
1. **DRY (Don't Repeat Yourself)**: 不重复造轮子
|
||||
2. **KISS (Keep It Simple, Stupid)**: 保持简单
|
||||
3. **单一职责**: 每个函数/模块只做一件事
|
||||
4. **错误处理**: 永远不要忽略错误
|
||||
5. **日志记录**: 关键操作都要记录日志
|
||||
6. **并发安全**: 共享数据必须加锁
|
||||
7. **测试**: 关键功能编写单元测试
|
||||
|
||||
### 禁止事项
|
||||
- ❌ 不要直接使用 `os` 包操作文件 (使用 `FileUtils`)
|
||||
- ❌ 不要使用 `fmt.Println` 输出日志 (使用 `logger`)
|
||||
- ❌ 不要忽略错误返回值
|
||||
- ❌ 不要硬编码配置 (使用 `config.CONFIG`)
|
||||
- ❌ 不要直接使用 GORM (使用 Repository)
|
||||
- ❌ 不要在多处实现相同功能
|
||||
|
||||
### 推荐做法
|
||||
- ✅ 使用已有工具函数
|
||||
- ✅ 结构化日志
|
||||
- ✅ 完整的错误处理
|
||||
- ✅ 清晰的命名
|
||||
- ✅ 合理的注释
|
||||
- ✅ 并发安全
|
||||
- ✅ 单元测试
|
||||
|
||||
---
|
||||
|
||||
**本文档由 Claude AI 维护,最后更新: 2025-10-03**
|
||||
@@ -2,7 +2,7 @@
|
||||
# 只包含需要自定义的配置项,其他将使用默认值
|
||||
|
||||
app:
|
||||
port: "9484"
|
||||
port: "3001"
|
||||
mode: "release"
|
||||
# 开发阶段标识 - 设置为 false 或删除此行启用生产模式
|
||||
development: true
|
||||
|
||||
+8
-3
@@ -6,15 +6,14 @@ require (
|
||||
github.com/gin-contrib/cors v1.7.5
|
||||
github.com/gin-contrib/pprof v1.5.3
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
gorm.io/gorm v1.30.0
|
||||
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -22,8 +21,10 @@ require (
|
||||
github.com/bytedance/sonic v1.13.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
@@ -36,10 +37,10 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.14 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
@@ -49,4 +50,8 @@ require (
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
+19
-4
@@ -11,6 +11,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
||||
@@ -21,6 +23,10 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@@ -36,6 +42,8 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
@@ -58,8 +66,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -69,6 +75,9 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -114,8 +123,14 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user