package handlers import ( "errors" "fmt" "go-todo-api/constants" "go-todo-api/models" "go-todo-api/services" "net/http" "strconv" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // 辅助函数:从 Gin Context 中获取用户 ID // ❗ 修正:将此函数放在包级别 (不在任何结构体内) func getUserIDFromContext(c *gin.Context) (uint, error) { // Note: UserIDKey must be imported from the constants package // ... (Your implementation using constants.UserIDKey) userIDStr, exists := c.Get(constants.UserIDKey) if !exists { return 0, errors.New("User ID not found in context") } // 转换 string 到 uint userIDUint, err := strconv.ParseUint(userIDStr.(string), 10, 32) if err != nil { return 0, errors.New("Invalid user ID format in context") } return uint(userIDUint), nil } // TodoHandler 结构体持有 Service 接口,用于处理 HTTP 请求 type TodoHandler struct { // Handler 层依赖于 Service 接口 Service services.TodoService } // NewTodoHandler 实例化一个新的 TodoHandler func NewTodoHandler(service services.TodoService) *TodoHandler { return &TodoHandler{Service: service} } // FindAllTodosHandler 处理 GET /todos 请求 func (h *TodoHandler) FindAllTodosHandler(c *gin.Context) { userID, err := getUserIDFromContext(c) // 1. 获取 UserID if err != nil || userID == 0 { // 401 错误,使用自定义代码 CodeInvalidAuth c.JSON(http.StatusUnauthorized, constants.StandardResponse{ Code: constants.CodeInvalidAuth, Message: "Unauthorized or Invalid User ID", }) return } // 1. 调用 Service 层获取数据 (Service 层调用 Repository 层) todos, err := h.Service.FindAllTodos(userID) if err != nil { // 500 内部错误,使用自定义代码 CodeInternalError c.JSON(http.StatusInternalServerError, constants.StandardResponse{ Code: constants.CodeInternalError, Message: fmt.Sprintf("Failed to retrieve todos: %v", err), }) return } // 200 成功响应,使用 CodeSuccess // 2. 返回 JSON 响应 (Handler 负责 HTTP 细节) c.JSON(http.StatusOK, constants.StandardResponse{ Code: constants.CodeSuccess, Message: "Successfully retrieved todos", Data: todos, }) } // FindTodoByIDHandler 处理 GET /todos/:id 请求 func (h *TodoHandler) FindTodoByIDHandler(c *gin.Context) { userID, err := getUserIDFromContext(c) // 1. 获取 UserID if err != nil || userID == 0 { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized or Invalid User ID"}) return } // 1. 获取 URL 参数 ID idStr := c.Param("id") // 将字符串 ID 转换为无符号整数 (uint) id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Todo ID format"}) return } // 2. 调用 Service 层获取数据 todo, err := h.Service.FindTodoByID(uint(id), userID) if err != nil { // 检查是否是“未找到记录”错误 if errors.Is(err, gorm.ErrRecordNotFound) { // 404 资源未找到,使用 CodeResourceNotFound c.JSON(http.StatusNotFound, constants.StandardResponse{ Code: constants.CodeResourceNotFound, Message: "Todo item not found", }) return } // 其他数据库错误 c.JSON(http.StatusInternalServerError, constants.StandardResponse{ Code: constants.CodeInternalError, Message: fmt.Sprintf("Failed to retrieve todo: %v", err), }) return } // 3. 返回 JSON 响应 c.JSON(http.StatusOK, constants.StandardResponse{ Code: constants.CodeSuccess, Message: "Successfully retrieved todo", Data: todo, }) } // DeleteTodoByIDHandler 处理 DELETE /todos/:id 请求 func (h *TodoHandler) DeleteTodoByIDHandler(c *gin.Context) { userID, err := getUserIDFromContext(c) // 1. 获取 UserID if err != nil || userID == 0 { // 401 错误,使用自定义代码 CodeInvalidAuth c.JSON(http.StatusUnauthorized, constants.StandardResponse{ Code: constants.CodeInvalidAuth, Message: "Unauthorized or Invalid User ID", }) return } // 1. 获取并解析 ID idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, constants.StandardResponse{ Code: constants.CodeInvalidAuth, Message: "Invalid Todo ID format", }) return } // 2. 调用 Service 层执行删除逻辑 err = h.Service.DeleteTodoByID(uint(id), userID) if err != nil { // 检查是否是“未找到记录”错误(Repository层返回的gorm.ErrRecordNotFound) if errors.Is(err, gorm.ErrRecordNotFound) { // 404 资源未找到,使用 CodeResourceNotFound c.JSON(http.StatusNotFound, constants.StandardResponse{ Code: constants.CodeResourceNotFound, Message: "Todo item not found", }) return } // 其他数据库错误 c.JSON(http.StatusInternalServerError, constants.StandardResponse{ Code: constants.CodeInternalError, Message: fmt.Sprintf("Failed to delete todo item: %v", err), }) return } // 3. 返回成功响应 c.JSON(http.StatusOK, constants.StandardResponse{ Code: constants.CodeSuccess, Message: "Todo deleted successfully (soft deleted)", }) } // CreateTodoHandler 处理 POST /todos 请求,创建新的待办事项 func (h *TodoHandler) CreateTodoHandler(c *gin.Context) { var newTodo models.Todo // 1. 绑定前端 JSON 数据 // Todo 结构体中应有 binding:"required" 标签来确保数据完整性 if err := c.ShouldBindJSON(&newTodo); err != nil { c.JSON(http.StatusBadRequest, constants.StandardResponse{ Code: constants.CodeValidationError, Message: err.Error(), }) return } userID, err := getUserIDFromContext(c) // 获取 UserID if err != nil || userID == 0 { // ❗ 核心修正:新增检查 userID == 0 // 如果获取失败或者 ID 为 0,视为未授权或无效令牌 c.JSON(http.StatusUnauthorized, constants.StandardResponse{ Code: constants.CodeInvalidAuth, Message: "Unauthorized or Invalid User ID", }) return } // 2. 调用 Service 层创建数据 (Service 会调用 Repository) if err := h.Service.CreateTodo(&newTodo, userID); err != nil { c.JSON(http.StatusInternalServerError, constants.StandardResponse{ Code: constants.CodeInternalError, Message: fmt.Sprintf("Failed to save todo item: %v", err), }) return } // 3. 返回成功响应 // newTodo 现在包含了 GORM 自动生成的 ID 和时间戳 c.JSON(http.StatusCreated, constants.StandardResponse{ Code: constants.CodeSuccess, Message: "Todo created successfully (via Service)", Data: newTodo, }) } // UpdateTodoHandler 处理 PATCH /todos/:id 请求 func (h *TodoHandler) UpdateTodoHandler(c *gin.Context) { userID, err := getUserIDFromContext(c) // 1. 获取 UserID if err != nil || userID == 0 { c.JSON(http.StatusUnauthorized, constants.StandardResponse{ Code: constants.CodeInvalidAuth, Message: "Unauthorized or Invalid User ID", }) return } // 1. 获取并解析 ID idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, constants.StandardResponse{ Code: constants.CodeValidationError, Message: "Invalid Todo ID format", }) return } // 2. 接收更新的 JSON 数据到 map 中(用于部分更新) var input map[string]interface{} if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, constants.StandardResponse{ Code: constants.CodeValidationError, Message: err.Error(), }) return } // 3. 调用 Service 层执行更新逻辑 // 注意:Service 层将负责查找记录,然后执行更新 updatedTodo, err := h.Service.UpdateTodo(uint(id), userID, input) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, constants.StandardResponse{ Code: constants.CodeResourceNotFound, Message: "Todo item not found", }) return } c.JSON(http.StatusInternalServerError, constants.StandardResponse{ Code: constants.CodeInternalError, Message: fmt.Sprintf("Failed to update todo item: %v", err), }) return } // 4. 返回更新后的记录 c.JSON(http.StatusOK, constants.StandardResponse{ Code: constants.CodeSuccess, Message: "Todo updated successfully", Data: updatedTodo, }) }