package handlers import ( "net/http" "strconv" "time" "giteapm/internal/middleware" "giteapm/internal/models" "github.com/gin-gonic/gin" ) type CreateTaskRequest struct { StoryID *uint `json:"story_id"` ProjectID uint `json:"project_id" binding:"required"` Title string `json:"title" binding:"required"` Description string `json:"description"` Priority string `json:"priority"` TaskType string `json:"task_type"` AssigneeID *uint `json:"assignee_id"` EstimatedHours *float64 `json:"estimated_hours"` DueDate *time.Time `json:"due_date"` } type LogTimeRequest struct { Hours float64 `json:"hours" binding:"required"` Description string `json:"description"` LogDate time.Time `json:"log_date" binding:"required"` } type LinkGiteaObjectRequest struct { RepoID uint `json:"repo_id" binding:"required"` RepoName string `json:"repo_name" binding:"required"` RelationType string `json:"relation_type" binding:"required"` ObjectID string `json:"object_id" binding:"required"` ObjectNumber *uint `json:"object_number"` Title string `json:"title"` URL string `json:"url"` } func (h *Handlers) ListTasks(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) offset := (page - 1) * limit filters := make(map[string]interface{}) if projectID := c.Query("project_id"); projectID != "" { if id, err := strconv.ParseUint(projectID, 10, 32); err == nil { filters["project_id"] = uint(id) } } if storyID := c.Query("story_id"); storyID != "" { if id, err := strconv.ParseUint(storyID, 10, 32); err == nil { filters["story_id"] = uint(id) } } if status := c.Query("status"); status != "" { filters["status"] = status } if assigneeID := c.Query("assignee_id"); assigneeID != "" { if id, err := strconv.ParseUint(assigneeID, 10, 32); err == nil { filters["assignee_id"] = uint(id) } } tasks, total, err := models.ListTasks(offset, limit, filters) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "获取任务列表失败")) return } c.JSON(http.StatusOK, PaginatedSuccessResponse(tasks, total, page, limit)) } func (h *Handlers) CreateTask(c *gin.Context) { var req CreateTaskRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "请求参数无效")) return } currentUser, err := middleware.GetCurrentUser(c) if err != nil { c.JSON(http.StatusUnauthorized, ErrorResponse(401, "获取当前用户失败")) return } task := &models.Task{ StoryID: req.StoryID, ProjectID: req.ProjectID, Title: req.Title, Description: req.Description, Status: "todo", Priority: req.Priority, TaskType: req.TaskType, AssigneeID: req.AssigneeID, ReporterID: currentUser.ID, EstimatedHours: req.EstimatedHours, DueDate: req.DueDate, } if task.Priority == "" { task.Priority = "medium" } if task.TaskType == "" { task.TaskType = "feature" } if err := models.CreateTask(task); err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "创建任务失败")) return } c.JSON(http.StatusCreated, SuccessResponse(task)) } func (h *Handlers) GetTask(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } task, err := models.GetTaskByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, ErrorResponse(404, "任务不存在")) return } c.JSON(http.StatusOK, SuccessResponse(task)) } func (h *Handlers) UpdateTask(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } task, err := models.GetTaskByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, ErrorResponse(404, "任务不存在")) return } var updateData map[string]interface{} if err := c.ShouldBindJSON(&updateData); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "请求参数无效")) return } if title, ok := updateData["title"].(string); ok { task.Title = title } if description, ok := updateData["description"].(string); ok { task.Description = description } if status, ok := updateData["status"].(string); ok { task.Status = status } if priority, ok := updateData["priority"].(string); ok { task.Priority = priority } if assigneeID, ok := updateData["assignee_id"].(float64); ok { id := uint(assigneeID) task.AssigneeID = &id } if err := models.UpdateTask(task); err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "更新任务失败")) return } if task.StoryID != nil { story, err := models.GetStoryByID(*task.StoryID) if err == nil { story.UpdateProgress() } } c.JSON(http.StatusOK, SuccessResponse(task)) } func (h *Handlers) DeleteTask(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } if err := models.DeleteTask(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "删除任务失败")) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "删除成功"})) } func (h *Handlers) LogTime(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } var req LogTimeRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "请求参数无效")) return } currentUser, err := middleware.GetCurrentUser(c) if err != nil { c.JSON(http.StatusUnauthorized, ErrorResponse(401, "获取当前用户失败")) return } task, err := models.GetTaskByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, ErrorResponse(404, "任务不存在")) return } if err := task.LogTime(currentUser.ID, req.Hours, req.Description, req.LogDate); err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "记录工时失败")) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "工时记录成功"})) } func (h *Handlers) GetTimeLogs(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } timeLogs, err := models.GetTimeLogsByTask(uint(id)) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "获取工时记录失败")) return } c.JSON(http.StatusOK, SuccessResponse(timeLogs)) } func (h *Handlers) LinkGiteaObject(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } var req LinkGiteaObjectRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "请求参数无效")) return } task, err := models.GetTaskByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, ErrorResponse(404, "任务不存在")) return } if err := task.LinkGiteaObject(req.RepoID, req.RepoName, req.RelationType, req.ObjectID, req.ObjectNumber, req.Title, req.URL); err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "关联Gitea对象失败")) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "关联成功"})) } func (h *Handlers) GetGiteaRelations(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse(400, "无效的任务ID")) return } relations, err := models.GetGiteaRelationsByTask(uint(id)) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(500, "获取Gitea关联失败")) return } c.JSON(http.StatusOK, SuccessResponse(relations)) }