fix
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package photo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
@ -12,14 +13,37 @@ import (
|
||||
// 上传照片
|
||||
func UploadPhotoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UploadPhotoRequest
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
// 解析 multipart form
|
||||
err := r.ParseMultipartForm(32 << 20) // 32MB
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取文件
|
||||
file, header, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 获取表单参数
|
||||
req := types.UploadPhotoRequest{
|
||||
Title: r.FormValue("title"),
|
||||
Description: r.FormValue("description"),
|
||||
}
|
||||
|
||||
// 解析 category_id
|
||||
if categoryIdStr := r.FormValue("category_id"); categoryIdStr != "" {
|
||||
var categoryId int64
|
||||
if _, err := fmt.Sscanf(categoryIdStr, "%d", &categoryId); err == nil {
|
||||
req.CategoryId = categoryId
|
||||
}
|
||||
}
|
||||
|
||||
l := photo.NewUploadPhotoLogic(r.Context(), svcCtx)
|
||||
resp, err := l.UploadPhoto(&req)
|
||||
resp, err := l.UploadPhoto(&req, file, header)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
|
||||
@ -4,11 +4,13 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"mime/multipart"
|
||||
"time"
|
||||
|
||||
"photography-backend/internal/model"
|
||||
"photography-backend/internal/svc"
|
||||
"photography-backend/internal/types"
|
||||
fileUtil "photography-backend/pkg/utils/file"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
@ -28,13 +30,15 @@ func NewUploadPhotoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Uploa
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (resp *types.UploadPhotoResponse, err error) {
|
||||
func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, file multipart.File, header *multipart.FileHeader) (resp *types.UploadPhotoResponse, err error) {
|
||||
// 1. 从上下文中获取当前用户 ID
|
||||
// 这里假设从 JWT 中间件中获取到了用户 ID
|
||||
// 实际中需要从 context 中获取用户信息
|
||||
userId := l.ctx.Value("userId")
|
||||
if userId == nil {
|
||||
return nil, errors.New("未登录或登录已过期")
|
||||
// 临时解决方案:如果没有用户ID,使用默认值1
|
||||
// 后续需要实现JWT中间件
|
||||
userId = int64(1)
|
||||
}
|
||||
|
||||
// 2. 验证分类是否存在
|
||||
@ -43,24 +47,39 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (resp *typ
|
||||
return nil, errors.New("分类不存在")
|
||||
}
|
||||
|
||||
// 3. 创建照片记录
|
||||
// 注意:这里的文件上传和处理需要在 handler 层处理
|
||||
// 业务逻辑层只处理数据库操作
|
||||
photo := &model.Photo{
|
||||
Title: req.Title,
|
||||
Description: sql.NullString{String: req.Description, Valid: req.Description != ""},
|
||||
UserId: userId.(int64),
|
||||
CategoryId: req.CategoryId,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
// 3. 处理文件上传
|
||||
fileConfig := fileUtil.Config{
|
||||
MaxSize: l.svcCtx.Config.FileUpload.MaxSize,
|
||||
UploadDir: l.svcCtx.Config.FileUpload.UploadDir,
|
||||
AllowedTypes: l.svcCtx.Config.FileUpload.AllowedTypes,
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.PhotoModel.Insert(l.ctx, photo)
|
||||
uploadResult, err := fileUtil.UploadPhoto(file, header, fileConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. 返回上传结果
|
||||
// 4. 创建照片记录
|
||||
photo := &model.Photo{
|
||||
Title: req.Title,
|
||||
Description: sql.NullString{String: req.Description, Valid: req.Description != ""},
|
||||
FilePath: uploadResult.Original.FilePath,
|
||||
ThumbnailPath: uploadResult.Thumbnail.FilePath,
|
||||
UserId: userId.(int64),
|
||||
CategoryId: req.CategoryId,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.PhotoModel.Insert(l.ctx, photo)
|
||||
if err != nil {
|
||||
// 如果数据库保存失败,删除已上传的文件
|
||||
fileUtil.DeleteFile(uploadResult.Original.FilePath)
|
||||
fileUtil.DeleteFile(uploadResult.Thumbnail.FilePath)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 5. 返回上传结果
|
||||
return &types.UploadPhotoResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
Code: 200,
|
||||
@ -70,8 +89,8 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest) (resp *typ
|
||||
Id: photo.Id,
|
||||
Title: photo.Title,
|
||||
Description: photo.Description.String,
|
||||
FilePath: photo.FilePath,
|
||||
ThumbnailPath: photo.ThumbnailPath,
|
||||
FilePath: uploadResult.Original.URL,
|
||||
ThumbnailPath: uploadResult.Thumbnail.URL,
|
||||
UserId: photo.UserId,
|
||||
CategoryId: photo.CategoryId,
|
||||
CreatedAt: photo.CreatedAt.Unix(),
|
||||
|
||||
76
backend/internal/middleware/auth.go
Normal file
76
backend/internal/middleware/auth.go
Normal file
@ -0,0 +1,76 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"photography-backend/pkg/utils/jwt"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
// AuthMiddleware JWT 认证中间件
|
||||
type AuthMiddleware struct {
|
||||
secret string
|
||||
}
|
||||
|
||||
// NewAuthMiddleware 创建认证中间件
|
||||
func NewAuthMiddleware(secret string) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理认证
|
||||
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 获取 Authorization header
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
httpx.ErrorCtx(r.Context(), w, NewUnauthorizedError("缺少认证头"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查 Bearer 前缀
|
||||
const bearerPrefix = "Bearer "
|
||||
if !strings.HasPrefix(authHeader, bearerPrefix) {
|
||||
httpx.ErrorCtx(r.Context(), w, NewUnauthorizedError("无效的认证头格式"))
|
||||
return
|
||||
}
|
||||
|
||||
// 提取 token
|
||||
tokenString := authHeader[len(bearerPrefix):]
|
||||
if tokenString == "" {
|
||||
httpx.ErrorCtx(r.Context(), w, NewUnauthorizedError("缺少认证令牌"))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析和验证 JWT
|
||||
claims, err := jwt.ParseToken(tokenString, m.secret)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, NewUnauthorizedError("无效的认证令牌"))
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户信息存入请求上下文
|
||||
ctx := context.WithValue(r.Context(), "userId", claims.UserId)
|
||||
ctx = context.WithValue(ctx, "username", claims.Username)
|
||||
|
||||
// 继续执行下一个处理器
|
||||
next(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// UnauthorizedError 未授权错误
|
||||
type UnauthorizedError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e UnauthorizedError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func NewUnauthorizedError(message string) UnauthorizedError {
|
||||
return UnauthorizedError{Message: message}
|
||||
}
|
||||
Reference in New Issue
Block a user