refactor: 重构后端架构为 go-zero 框架,优化项目结构
主要变更: - 采用 go-zero 框架替代 Gin,提升开发效率 - 重构项目结构,API 文件模块化组织 - 将 model 移至 api/internal/model 目录 - 移除 common 包,改为标准 pkg 目录结构 - 实现统一的仓储模式,支持配置驱动数据库切换 - 简化测试策略,专注 API 集成测试 - 更新 CLAUDE.md 文档,提供详细的开发指导 技术栈更新: - 框架: Gin → go-zero v1.6.0+ - 代码生成: 引入 goctl 工具 - 架构模式: 四层架构 → go-zero 三层架构 (Handler→Logic→Model) - 项目布局: 遵循 Go 社区标准和 go-zero 最佳实践
This commit is contained in:
172
backend/internal/utils/file.go
Normal file
172
backend/internal/utils/file.go
Normal file
@ -0,0 +1,172 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetFileExtension 获取文件扩展名
|
||||
func GetFileExtension(filename string) string {
|
||||
return strings.ToLower(filepath.Ext(filename))
|
||||
}
|
||||
|
||||
// GetMimeType 根据文件扩展名获取MIME类型
|
||||
func GetMimeType(filename string) string {
|
||||
ext := GetFileExtension(filename)
|
||||
mimeType := mime.TypeByExtension(ext)
|
||||
if mimeType == "" {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
return mimeType
|
||||
}
|
||||
|
||||
// IsImageFile 检查是否为图片文件
|
||||
func IsImageFile(filename string) bool {
|
||||
imageExtensions := []string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".tiff"}
|
||||
ext := GetFileExtension(filename)
|
||||
|
||||
for _, imageExt := range imageExtensions {
|
||||
if ext == imageExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GenerateUniqueFilename 生成唯一的文件名
|
||||
func GenerateUniqueFilename(originalFilename string) string {
|
||||
ext := GetFileExtension(originalFilename)
|
||||
timestamp := time.Now().Unix()
|
||||
randomStr := GenerateRandomString(8)
|
||||
|
||||
return fmt.Sprintf("%d_%s%s", timestamp, randomStr, ext)
|
||||
}
|
||||
|
||||
// GenerateFilePath 生成文件路径
|
||||
func GenerateFilePath(baseDir, subDir, filename string) string {
|
||||
// 按日期组织文件夹
|
||||
now := time.Now()
|
||||
dateDir := now.Format("2006/01/02")
|
||||
|
||||
if subDir != "" {
|
||||
return filepath.Join(baseDir, subDir, dateDir, filename)
|
||||
}
|
||||
|
||||
return filepath.Join(baseDir, dateDir, filename)
|
||||
}
|
||||
|
||||
// EnsureDir 确保目录存在
|
||||
func EnsureDir(dirPath string) error {
|
||||
return os.MkdirAll(dirPath, 0755)
|
||||
}
|
||||
|
||||
// FileExists 检查文件是否存在
|
||||
func FileExists(filepath string) bool {
|
||||
_, err := os.Stat(filepath)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// GetFileSize 获取文件大小
|
||||
func GetFileSize(filepath string) (int64, error) {
|
||||
info, err := os.Stat(filepath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Size(), nil
|
||||
}
|
||||
|
||||
// CalculateFileMD5 计算文件MD5哈希
|
||||
func CalculateFileMD5(filepath string) (string, error) {
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hash := md5.New()
|
||||
if _, err := io.Copy(hash, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// CalculateFileSHA256 计算文件SHA256哈希
|
||||
func CalculateFileSHA256(filepath string) (string, error) {
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hash := sha256.New()
|
||||
if _, err := io.Copy(hash, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// CopyFile 复制文件
|
||||
func CopyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
// 确保目标目录存在
|
||||
if err := EnsureDir(filepath.Dir(dst)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, sourceFile)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func DeleteFile(filepath string) error {
|
||||
if !FileExists(filepath) {
|
||||
return nil // 文件不存在,认为删除成功
|
||||
}
|
||||
return os.Remove(filepath)
|
||||
}
|
||||
|
||||
// FormatFileSize 格式化文件大小为人类可读格式
|
||||
func FormatFileSize(bytes int64) string {
|
||||
const unit = 1024
|
||||
if bytes < unit {
|
||||
return fmt.Sprintf("%d B", bytes)
|
||||
}
|
||||
|
||||
div, exp := int64(unit), 0
|
||||
for n := bytes / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
|
||||
units := []string{"KB", "MB", "GB", "TB", "PB"}
|
||||
return fmt.Sprintf("%.1f %s", float64(bytes)/float64(div), units[exp])
|
||||
}
|
||||
|
||||
// GetImageDimensions 获取图片尺寸(需要额外的图片处理库)
|
||||
// 这里只是占位符,实际实现需要使用如 github.com/disintegration/imaging 等库
|
||||
func GetImageDimensions(filepath string) (width, height int, err error) {
|
||||
// TODO: 实现图片尺寸获取
|
||||
// 需要添加图片处理依赖
|
||||
return 0, 0, fmt.Errorf("not implemented")
|
||||
}
|
||||
Reference in New Issue
Block a user