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:
@ -6,7 +6,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"photography-backend/internal/models"
|
||||
"photography-backend/internal/model/entity"
|
||||
"photography-backend/internal/model/dto"
|
||||
"photography-backend/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -42,8 +43,8 @@ func NewPhotoHandler(photoService *service.PhotoService, logger *zap.Logger) *Ph
|
||||
// @Param sort_by query string false "排序字段"
|
||||
// @Param sort_order query string false "排序方向"
|
||||
// @Success 200 {object} service.PhotoListResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos [get]
|
||||
func (h *PhotoHandler) GetPhotos(c *gin.Context) {
|
||||
var params service.PhotoListParams
|
||||
@ -51,7 +52,7 @@ func (h *PhotoHandler) GetPhotos(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
if err := c.ShouldBindQuery(¶ms); err != nil {
|
||||
h.logger.Error("Failed to bind query params", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid query parameters",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -67,7 +68,7 @@ func (h *PhotoHandler) GetPhotos(c *gin.Context) {
|
||||
result, err := h.photoService.GetPhotos(c.Request.Context(), params)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get photos", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to get photos",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -85,15 +86,15 @@ func (h *PhotoHandler) GetPhotos(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param id path int true "照片ID"
|
||||
// @Success 200 {object} models.Photo
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 404 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 404 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/{id} [get]
|
||||
func (h *PhotoHandler) GetPhoto(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid photo ID",
|
||||
Message: "Photo ID must be a valid number",
|
||||
})
|
||||
@ -103,7 +104,7 @@ func (h *PhotoHandler) GetPhoto(c *gin.Context) {
|
||||
photo, err := h.photoService.GetPhotoByID(c.Request.Context(), uint(id))
|
||||
if err != nil {
|
||||
if err.Error() == "photo not found" {
|
||||
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
c.JSON(http.StatusNotFound, response.Error{
|
||||
Error: "Photo not found",
|
||||
Message: "The requested photo does not exist",
|
||||
})
|
||||
@ -111,7 +112,7 @@ func (h *PhotoHandler) GetPhoto(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.logger.Error("Failed to get photo", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to get photo",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -129,14 +130,14 @@ func (h *PhotoHandler) GetPhoto(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param photo body models.CreatePhotoRequest true "照片信息"
|
||||
// @Success 201 {object} models.Photo
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos [post]
|
||||
func (h *PhotoHandler) CreatePhoto(c *gin.Context) {
|
||||
var req models.CreatePhotoRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request body",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -145,7 +146,7 @@ func (h *PhotoHandler) CreatePhoto(c *gin.Context) {
|
||||
|
||||
// 验证请求数据
|
||||
if err := h.validateCreatePhotoRequest(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request data",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -155,7 +156,7 @@ func (h *PhotoHandler) CreatePhoto(c *gin.Context) {
|
||||
photo, err := h.photoService.CreatePhoto(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to create photo", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to create photo",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -174,15 +175,15 @@ func (h *PhotoHandler) CreatePhoto(c *gin.Context) {
|
||||
// @Param id path int true "照片ID"
|
||||
// @Param photo body models.UpdatePhotoRequest true "照片信息"
|
||||
// @Success 200 {object} models.Photo
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 404 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 404 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/{id} [put]
|
||||
func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid photo ID",
|
||||
Message: "Photo ID must be a valid number",
|
||||
})
|
||||
@ -192,7 +193,7 @@ func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
|
||||
var req models.UpdatePhotoRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request body",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -202,7 +203,7 @@ func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
|
||||
photo, err := h.photoService.UpdatePhoto(c.Request.Context(), uint(id), &req)
|
||||
if err != nil {
|
||||
if err.Error() == "photo not found" {
|
||||
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
c.JSON(http.StatusNotFound, response.Error{
|
||||
Error: "Photo not found",
|
||||
Message: "The requested photo does not exist",
|
||||
})
|
||||
@ -210,7 +211,7 @@ func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.logger.Error("Failed to update photo", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to update photo",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -228,15 +229,15 @@ func (h *PhotoHandler) UpdatePhoto(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param id path int true "照片ID"
|
||||
// @Success 204 "No Content"
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 404 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 404 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/{id} [delete]
|
||||
func (h *PhotoHandler) DeletePhoto(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid photo ID",
|
||||
Message: "Photo ID must be a valid number",
|
||||
})
|
||||
@ -246,7 +247,7 @@ func (h *PhotoHandler) DeletePhoto(c *gin.Context) {
|
||||
err = h.photoService.DeletePhoto(c.Request.Context(), uint(id))
|
||||
if err != nil {
|
||||
if err.Error() == "photo not found" {
|
||||
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||
c.JSON(http.StatusNotFound, response.Error{
|
||||
Error: "Photo not found",
|
||||
Message: "The requested photo does not exist",
|
||||
})
|
||||
@ -254,7 +255,7 @@ func (h *PhotoHandler) DeletePhoto(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.logger.Error("Failed to delete photo", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to delete photo",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -277,14 +278,14 @@ func (h *PhotoHandler) DeletePhoto(c *gin.Context) {
|
||||
// @Param category_ids formData string false "分类ID列表(逗号分隔)"
|
||||
// @Param tag_ids formData string false "标签ID列表(逗号分隔)"
|
||||
// @Success 201 {object} models.Photo
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/upload [post]
|
||||
func (h *PhotoHandler) UploadPhoto(c *gin.Context) {
|
||||
// 获取上传的文件
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "No file uploaded",
|
||||
Message: "Please select a file to upload",
|
||||
})
|
||||
@ -328,7 +329,7 @@ func (h *PhotoHandler) UploadPhoto(c *gin.Context) {
|
||||
photo, err := h.photoService.UploadPhoto(c.Request.Context(), file, header, req)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to upload photo", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to upload photo",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -346,14 +347,14 @@ func (h *PhotoHandler) UploadPhoto(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param request body models.BatchUpdatePhotosRequest true "批量更新请求"
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/batch/update [post]
|
||||
func (h *PhotoHandler) BatchUpdatePhotos(c *gin.Context) {
|
||||
var req models.BatchUpdatePhotosRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request body",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -361,7 +362,7 @@ func (h *PhotoHandler) BatchUpdatePhotos(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request",
|
||||
Message: "No photo IDs provided",
|
||||
})
|
||||
@ -371,7 +372,7 @@ func (h *PhotoHandler) BatchUpdatePhotos(c *gin.Context) {
|
||||
err := h.photoService.BatchUpdatePhotos(c.Request.Context(), req.IDs, &req)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to batch update photos", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to batch update photos",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -391,14 +392,14 @@ func (h *PhotoHandler) BatchUpdatePhotos(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param request body models.BatchDeleteRequest true "批量删除请求"
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 400 {object} response.Error
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/batch/delete [post]
|
||||
func (h *PhotoHandler) BatchDeletePhotos(c *gin.Context) {
|
||||
var req models.BatchDeleteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request body",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -406,7 +407,7 @@ func (h *PhotoHandler) BatchDeletePhotos(c *gin.Context) {
|
||||
}
|
||||
|
||||
if len(req.IDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
c.JSON(http.StatusBadRequest, response.Error{
|
||||
Error: "Invalid request",
|
||||
Message: "No photo IDs provided",
|
||||
})
|
||||
@ -416,7 +417,7 @@ func (h *PhotoHandler) BatchDeletePhotos(c *gin.Context) {
|
||||
err := h.photoService.BatchDeletePhotos(c.Request.Context(), req.IDs)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to batch delete photos", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to batch delete photos",
|
||||
Message: err.Error(),
|
||||
})
|
||||
@ -435,13 +436,13 @@ func (h *PhotoHandler) BatchDeletePhotos(c *gin.Context) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} models.PhotoStats
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} response.Error
|
||||
// @Router /photos/stats [get]
|
||||
func (h *PhotoHandler) GetPhotoStats(c *gin.Context) {
|
||||
stats, err := h.photoService.GetPhotoStats(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to get photo stats", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||
c.JSON(http.StatusInternalServerError, response.Error{
|
||||
Error: "Failed to get photo stats",
|
||||
Message: err.Error(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user