feat: 完善照片更新和删除业务逻辑

- 实现照片更新功能 (updatePhotoLogic.go)
  - 支持部分字段更新 (title, description, category_id)
  - 添加用户权限验证,只能更新自己的照片
  - 添加分类存在性验证
  - 完善错误处理和响应格式

- 实现照片删除功能 (deletePhotoLogic.go)
  - 添加用户权限验证,只能删除自己的照片
  - 同时删除数据库记录和文件系统文件
  - 安全的文件删除处理

- 更新Handler使用统一响应格式
  - updatePhotoHandler.go: 使用response.Response统一处理
  - deletePhotoHandler.go: 使用response.Response统一处理

- 添加完整API测试用例 (test_photo_crud.http)
  - 涵盖正常场景和错误场景测试
  - 包含权限验证测试

- 更新项目进度 (TASK_PROGRESS.md)
  - 完成率从8%提升到12%
  - 更新API接口状态
  - 记录技术成果和里程碑
This commit is contained in:
xujiang
2025-07-10 18:08:22 +08:00
parent 1e828e03fe
commit 5cbdc5af73
6 changed files with 468 additions and 14 deletions

View File

@ -3,8 +3,11 @@ package photo
import (
"context"
"photography-backend/internal/model"
"photography-backend/internal/svc"
"photography-backend/internal/types"
"photography-backend/pkg/errorx"
fileUtil "photography-backend/pkg/utils/file"
"github.com/zeromicro/go-zero/core/logx"
)
@ -25,7 +28,57 @@ func NewDeletePhotoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delet
}
func (l *DeletePhotoLogic) DeletePhoto(req *types.DeletePhotoRequest) (resp *types.DeletePhotoResponse, err error) {
// todo: add your logic here and delete this line
// 1. 获取当前用户ID (从JWT中间件获取)
userID := l.ctx.Value("userID")
if userID == nil {
return nil, errorx.NewWithCode(errorx.AuthError)
}
currentUserID := userID.(int64)
return
// 2. 查询照片是否存在
photo, err := l.svcCtx.PhotoModel.FindOne(l.ctx, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, errorx.NewWithCode(errorx.PhotoNotFound)
}
logx.Errorf("查询照片失败: %v", err)
return nil, errorx.NewWithCode(errorx.ServerError)
}
// 3. 检查权限 - 只有照片所有者可以删除
if photo.UserId != currentUserID {
return nil, errorx.NewWithCode(errorx.Forbidden)
}
// 4. 删除数据库记录
err = l.svcCtx.PhotoModel.Delete(l.ctx, req.Id)
if err != nil {
logx.Errorf("删除照片记录失败: %v", err)
return nil, errorx.NewWithCode(errorx.ServerError)
}
// 5. 删除文件系统中的文件
// 删除原图
if photo.FilePath != "" {
if err := fileUtil.DeleteFile(photo.FilePath); err != nil {
logx.Errorf("删除原图文件失败: %v", err)
// 注意:即使文件删除失败,我们也不回滚数据库操作,因为文件可能已经被手动删除
}
}
// 删除缩略图
if photo.ThumbnailPath != "" {
if err := fileUtil.DeleteFile(photo.ThumbnailPath); err != nil {
logx.Errorf("删除缩略图文件失败: %v", err)
// 注意:即使文件删除失败,我们也不回滚数据库操作
}
}
// 6. 返回成功响应
return &types.DeletePhotoResponse{
BaseResponse: types.BaseResponse{
Code: errorx.Success,
Message: "照片删除成功",
},
}, nil
}

View File

@ -2,9 +2,13 @@ package photo
import (
"context"
"database/sql"
"time"
"photography-backend/internal/model"
"photography-backend/internal/svc"
"photography-backend/internal/types"
"photography-backend/pkg/errorx"
"github.com/zeromicro/go-zero/core/logx"
)
@ -25,7 +29,75 @@ func NewUpdatePhotoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Updat
}
func (l *UpdatePhotoLogic) UpdatePhoto(req *types.UpdatePhotoRequest) (resp *types.UpdatePhotoResponse, err error) {
// todo: add your logic here and delete this line
// 1. 获取当前用户ID (从JWT中间件获取)
userID := l.ctx.Value("userID")
if userID == nil {
return nil, errorx.NewWithCode(errorx.AuthError)
}
currentUserID := userID.(int64)
return
// 2. 查询照片是否存在
photo, err := l.svcCtx.PhotoModel.FindOne(l.ctx, req.Id)
if err != nil {
if err == model.ErrNotFound {
return nil, errorx.NewWithCode(errorx.PhotoNotFound)
}
logx.Errorf("查询照片失败: %v", err)
return nil, errorx.NewWithCode(errorx.ServerError)
}
// 3. 检查权限 - 只有照片所有者可以更新
if photo.UserId != currentUserID {
return nil, errorx.NewWithCode(errorx.Forbidden)
}
// 4. 验证分类是否存在 (如果要更新分类)
if req.CategoryId > 0 {
_, err = l.svcCtx.CategoryModel.FindOne(l.ctx, req.CategoryId)
if err != nil {
if err == model.ErrNotFound {
return nil, errorx.NewWithCode(errorx.CategoryNotFound)
}
logx.Errorf("查询分类失败: %v", err)
return nil, errorx.NewWithCode(errorx.ServerError)
}
}
// 5. 更新照片信息
if req.Title != "" {
photo.Title = req.Title
}
if req.Description != "" {
photo.Description = sql.NullString{String: req.Description, Valid: true}
}
if req.CategoryId > 0 {
photo.CategoryId = req.CategoryId
}
photo.UpdatedAt = time.Now()
// 6. 保存到数据库
err = l.svcCtx.PhotoModel.Update(l.ctx, photo)
if err != nil {
logx.Errorf("更新照片失败: %v", err)
return nil, errorx.NewWithCode(errorx.ServerError)
}
// 7. 构造响应
return &types.UpdatePhotoResponse{
BaseResponse: types.BaseResponse{
Code: errorx.Success,
Message: "照片更新成功",
},
Data: types.Photo{
Id: photo.Id,
Title: photo.Title,
Description: photo.Description.String,
FilePath: photo.FilePath,
ThumbnailPath: photo.ThumbnailPath,
UserId: photo.UserId,
CategoryId: photo.CategoryId,
CreatedAt: photo.CreatedAt.Unix(),
UpdatedAt: photo.UpdatedAt.Unix(),
},
}, nil
}