初始化

This commit is contained in:
2026-07-03 15:58:29 +08:00
parent 3a942c882e
commit dc90e889e1
77 changed files with 12912 additions and 0 deletions

107
server/utils/file.go Normal file
View File

@@ -0,0 +1,107 @@
package utils
import (
"path/filepath"
"strings"
)
// GetExtension 获取文件扩展名(不含点号, 全小写)
// 例如: "photo.JPG" -> "jpg"
func GetExtension(filename string) string {
ext := filepath.Ext(filename)
if ext == "" {
return ""
}
return strings.ToLower(ext[1:]) // 去掉开头的点号
}
// GetMimeType 根据文件扩展名获取MIME类型
// 简单映射, 覆盖常见文件类型
func GetMimeType(extension string) string {
mimeMap := map[string]string{
// 图片
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"png": "image/png",
"gif": "image/gif",
"bmp": "image/bmp",
"webp": "image/webp",
"svg": "image/svg+xml",
"ico": "image/x-icon",
// 文档
"pdf": "application/pdf",
"doc": "application/msword",
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"xls": "application/vnd.ms-excel",
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"ppt": "application/vnd.ms-powerpoint",
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
// 文本
"txt": "text/plain",
"html": "text/html",
"htm": "text/html",
"css": "text/css",
"js": "application/javascript",
"json": "application/json",
"xml": "application/xml",
"csv": "text/csv",
"md": "text/markdown",
// 压缩包
"zip": "application/zip",
"rar": "application/x-rar-compressed",
"7z": "application/x-7z-compressed",
"tar": "application/x-tar",
"gz": "application/gzip",
// 音视频
"mp3": "audio/mpeg",
"wav": "audio/wav",
"mp4": "video/mp4",
"avi": "video/x-msvideo",
"mov": "video/quicktime",
"wmv": "video/x-ms-wmv",
"flv": "video/x-flv",
"mkv": "video/x-matroska",
}
if mime, ok := mimeMap[extension]; ok {
return mime
}
return "application/octet-stream"
}
// IsImageFile 判断是否为图片文件
func IsImageFile(extension string) bool {
imageExts := []string{"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "ico"}
for _, ext := range imageExts {
if extension == ext {
return true
}
}
return false
}
// IsVideoFile 判断是否为视频文件
func IsVideoFile(extension string) bool {
videoExts := []string{"mp4", "avi", "mov", "wmv", "flv", "mkv"}
for _, ext := range videoExts {
if extension == ext {
return true
}
}
return false
}
// IsDocumentFile 判断是否为文档文件
func IsDocumentFile(extension string) bool {
docExts := []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv"}
for _, ext := range docExts {
if extension == ext {
return true
}
}
return false
}

62
server/utils/hash.go Normal file
View File

@@ -0,0 +1,62 @@
package utils
import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"io"
"os"
"golang.org/x/crypto/bcrypt"
)
// HashPassword 使用bcrypt对密码进行哈希
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
// CheckPassword 验证密码是否匹配
func CheckPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// MD5Hash 计算字符串的MD5值
func MD5Hash(text string) string {
hash := md5.Sum([]byte(text))
return hex.EncodeToString(hash[:])
}
// FileMD5 计算文件的MD5值
// 用于秒传判断: 相同MD5的文件只需存储一份
func FileMD5(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 hex.EncodeToString(hash.Sum(nil)), nil
}
// FileSHA256 计算文件的SHA256值
func FileSHA256(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 hex.EncodeToString(hash.Sum(nil)), nil
}

50
server/utils/jwt.go Normal file
View File

@@ -0,0 +1,50 @@
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
// JWTClaims 自定义JWT声明
type JWTClaims struct {
UserID uint64 `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt.RegisteredClaims
}
// GenerateToken 生成JWT Token
// userID: 用户ID, username: 用户名, role: 角色, secret: 密钥, expirationHours: 过期时间(小时)
func GenerateToken(userID uint64, username, role, secret string, expirationHours int) (string, error) {
claims := JWTClaims{
UserID: userID,
Username: username,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expirationHours) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "seeyon-filesystem",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
// ParseToken 解析JWT Token
func ParseToken(tokenString, secret string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}

89
server/utils/response.go Normal file
View File

@@ -0,0 +1,89 @@
// Package utils 提供通用工具函数
package utils
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Response 统一API响应结构
type Response struct {
Code int `json:"code"` // 业务状态码, 0=成功
Message string `json:"message"` // 提示信息
Data interface{} `json:"data"` // 响应数据
}
// PageResponse 分页响应结构
type PageResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
Total int64 `json:"total"` // 总记录数
Page int `json:"page"` // 当前页码
Size int `json:"size"` // 每页大小
}
// Success 返回成功响应
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
// SuccessWithMessage 返回带消息的成功响应
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: message,
Data: data,
})
}
// Error 返回错误响应
func Error(c *gin.Context, httpCode int, bizCode int, message string) {
c.JSON(httpCode, Response{
Code: bizCode,
Message: message,
Data: nil,
})
}
// BadRequest 返回400错误
func BadRequest(c *gin.Context, message string) {
Error(c, http.StatusBadRequest, 400, message)
}
// Unauthorized 返回401错误
func Unauthorized(c *gin.Context, message string) {
Error(c, http.StatusUnauthorized, 401, message)
}
// Forbidden 返回403错误
func Forbidden(c *gin.Context, message string) {
Error(c, http.StatusForbidden, 403, message)
}
// NotFound 返回404错误
func NotFound(c *gin.Context, message string) {
Error(c, http.StatusNotFound, 404, message)
}
// ServerError 返回500错误
func ServerError(c *gin.Context, message string) {
Error(c, http.StatusInternalServerError, 500, message)
}
// PageSuccess 返回分页成功响应
func PageSuccess(c *gin.Context, data interface{}, total int64, page, size int) {
c.JSON(http.StatusOK, PageResponse{
Code: 0,
Message: "success",
Data: data,
Total: total,
Page: page,
Size: size,
})
}