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:
xujiang
2025-07-10 15:05:52 +08:00
parent a2f2f66f88
commit 39a42695d3
52 changed files with 6047 additions and 2349 deletions

View 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")
}