package handler import ( "encoding/json" "strconv" "dd_fiber_api/internal/camp" "dd_fiber_api/internal/camp/service" "github.com/gofiber/fiber/v2" ) // TaskHandler 任务处理器 type TaskHandler struct { taskService *service.TaskService } // NewTaskHandler 创建任务处理器 func NewTaskHandler(taskService *service.TaskService) *TaskHandler { return &TaskHandler{ taskService: taskService, } } // CreateTask 创建任务 func (h *TaskHandler) CreateTask(c *fiber.Ctx) error { // 先解析为 map 以处理 task_type 的数字到字符串转换 var rawReq map[string]interface{} if err := c.BodyParser(&rawReq); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "请求参数解析失败: " + err.Error(), }) } // 转换 task_type:如果前端传的是数字,转换为字符串 if taskTypeVal, ok := rawReq["task_type"]; ok { switch v := taskTypeVal.(type) { case float64: // JSON 解析数字为 float64 switch int(v) { case 0: rawReq["task_type"] = "unknown" case 1: rawReq["task_type"] = "image_text" case 2: rawReq["task_type"] = "video" case 3: rawReq["task_type"] = "subjective" case 4: rawReq["task_type"] = "objective" case 5: rawReq["task_type"] = "essay" default: rawReq["task_type"] = "unknown" } case int: switch v { case 0: rawReq["task_type"] = "unknown" case 1: rawReq["task_type"] = "image_text" case 2: rawReq["task_type"] = "video" case 3: rawReq["task_type"] = "subjective" case 4: rawReq["task_type"] = "objective" case 5: rawReq["task_type"] = "essay" default: rawReq["task_type"] = "unknown" } } } // 将转换后的 map 转换为 CreateTaskRequest var req camp.CreateTaskRequest if campID, ok := rawReq["camp_id"].(string); ok { req.CampID = campID } if sectionID, ok := rawReq["section_id"].(string); ok { req.SectionID = sectionID } if taskTypeStr, ok := rawReq["task_type"].(string); ok { req.TaskType = camp.TaskType(taskTypeStr) } if title, ok := rawReq["title"].(string); ok { req.Title = title } // Content 和 Condition 需要特殊处理,因为它们可能是 JSON 对象 if content, ok := rawReq["content"]; ok { contentBytes, err := json.Marshal(content) if err == nil { req.Content = contentBytes } else { req.Content = []byte("{}") } } if condition, ok := rawReq["condition"]; ok { conditionBytes, err := json.Marshal(condition) if err == nil { req.Condition = conditionBytes } else { req.Condition = []byte("{}") } } if pid, ok := rawReq["prerequisite_task_id"].(string); ok { req.PrerequisiteTaskID = pid } // 验证必填字段 if req.CampID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "打卡营ID不能为空", }) } if req.SectionID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "小节ID不能为空", }) } if req.TaskType == "" || req.TaskType == camp.TaskTypeUnknown { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "任务类型不能为空", }) } // 验证 Content 和 Condition 是否为有效的 JSON if len(req.Content) == 0 { req.Content = []byte("{}") } if len(req.Condition) == 0 { req.Condition = []byte("{}") } resp, err := h.taskService.CreateTask(&req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "创建任务失败: " + err.Error(), }) } if !resp.Success { return c.Status(fiber.StatusBadRequest).JSON(resp) } return c.Status(fiber.StatusOK).JSON(resp) } // GetTask 获取任务 // 支持两种方式: // 1. 路径参数:GET /api/v1/camp/tasks/:id // 2. 查询参数:GET /api/v1/camp/tasks/detail?id=xxx func (h *TaskHandler) GetTask(c *fiber.Ctx) error { // 优先从查询参数获取,如果没有则从路径参数获取 id := c.Query("id") if id == "" { id = c.Params("id") } // 如果路径参数是 "detail",说明是查询参数方式,需要从查询参数获取 if id == "detail" { id = c.Query("id") } if id == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "任务ID不能为空", }) } // 获取 user_id(可选) userID := c.Query("user_id") resp, err := h.taskService.GetTask(id, userID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "获取任务失败: " + err.Error(), }) } if !resp.Success { return c.Status(fiber.StatusNotFound).JSON(resp) } return c.Status(fiber.StatusOK).JSON(resp) } // UpdateTask 更新任务 func (h *TaskHandler) UpdateTask(c *fiber.Ctx) error { // 先解析为 map 以处理 task_type 的数字到字符串转换 var rawReq map[string]interface{} if err := c.BodyParser(&rawReq); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "请求参数解析失败: " + err.Error(), }) } // 转换 task_type:如果前端传的是数字,转换为字符串 if taskTypeVal, ok := rawReq["task_type"]; ok { switch v := taskTypeVal.(type) { case float64: // JSON 解析数字为 float64 switch int(v) { case 0: rawReq["task_type"] = "unknown" case 1: rawReq["task_type"] = "image_text" case 2: rawReq["task_type"] = "video" case 3: rawReq["task_type"] = "subjective" case 4: rawReq["task_type"] = "objective" case 5: rawReq["task_type"] = "essay" default: rawReq["task_type"] = "unknown" } case int: switch v { case 0: rawReq["task_type"] = "unknown" case 1: rawReq["task_type"] = "image_text" case 2: rawReq["task_type"] = "video" case 3: rawReq["task_type"] = "subjective" case 4: rawReq["task_type"] = "objective" case 5: rawReq["task_type"] = "essay" default: rawReq["task_type"] = "unknown" } } } // 将转换后的 map 转换为 UpdateTaskRequest var req camp.UpdateTaskRequest if id, ok := rawReq["id"].(string); ok { req.ID = id } if campID, ok := rawReq["camp_id"].(string); ok { req.CampID = campID } if sectionID, ok := rawReq["section_id"].(string); ok { req.SectionID = sectionID } if taskTypeStr, ok := rawReq["task_type"].(string); ok { req.TaskType = camp.TaskType(taskTypeStr) } if title, ok := rawReq["title"].(string); ok { req.Title = title } // Content 和 Condition 需要特殊处理,因为它们可能是 JSON 对象 if content, ok := rawReq["content"]; ok { contentBytes, err := json.Marshal(content) if err == nil { req.Content = contentBytes } else { req.Content = []byte("{}") } } if condition, ok := rawReq["condition"]; ok { conditionBytes, err := json.Marshal(condition) if err == nil { req.Condition = conditionBytes } else { req.Condition = []byte("{}") } } if pid, ok := rawReq["prerequisite_task_id"].(string); ok { req.PrerequisiteTaskID = pid } // 从 URL 参数获取 ID(如果请求体中没有) if req.ID == "" { req.ID = c.Query("id") if req.ID == "" { req.ID = c.Params("id") } } if req.ID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "任务ID不能为空", }) } // 验证 Content 和 Condition 是否为有效的 JSON if len(req.Content) == 0 { req.Content = []byte("{}") } if len(req.Condition) == 0 { req.Condition = []byte("{}") } resp, err := h.taskService.UpdateTask(&req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "更新任务失败: " + err.Error(), }) } if !resp.Success { return c.Status(fiber.StatusBadRequest).JSON(resp) } return c.Status(fiber.StatusOK).JSON(resp) } // DeleteTask 删除任务 func (h *TaskHandler) DeleteTask(c *fiber.Ctx) error { var id string // 支持从 JSON body 获取(前端可能传 {"id":"xxx"} 或 {"params":{"id":"xxx"}}) if c.Get("Content-Type") != "" && len(c.Body()) > 0 { var body struct { ID string `json:"id"` Params struct { ID string `json:"id"` } `json:"params"` } if err := c.BodyParser(&body); err == nil { if body.ID != "" { id = body.ID } else if body.Params.ID != "" { id = body.Params.ID } } } if id == "" { id = c.Query("id") } if id == "" { id = c.Params("id") } if id == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "message": "任务ID不能为空", }) } resp, err := h.taskService.DeleteTask(id) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "删除任务失败: " + err.Error(), }) } if !resp.Success { return c.Status(fiber.StatusNotFound).JSON(resp) } return c.Status(fiber.StatusOK).JSON(resp) } // ListTasks 列出任务 func (h *TaskHandler) ListTasks(c *fiber.Ctx) error { // 显式从 Query 读取所有参数,确保 GET 请求的 camp_id、section_id 等筛选被正确接收(admin 与 client 通用) page, _ := strconv.Atoi(c.Query("page", "1")) pageSize, _ := strconv.Atoi(c.Query("page_size", "10")) req := camp.ListTasksRequest{ Keyword: c.Query("keyword"), CampID: c.Query("camp_id"), SectionID: c.Query("section_id"), Page: page, PageSize: pageSize, } if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 { req.PageSize = 10 } if req.PageSize > 100 { req.PageSize = 100 } if s := c.Query("task_type"); s != "" { // 支持前端传数字(0=unknown,1=image_text,2=video,3=subjective,4=objective,5=essay)或字符串 switch s { case "0": req.TaskType = camp.TaskTypeUnknown case "1": req.TaskType = camp.TaskTypeImageText case "2": req.TaskType = camp.TaskTypeVideo case "3": req.TaskType = camp.TaskTypeSubjective case "4": req.TaskType = camp.TaskTypeObjective case "5": req.TaskType = camp.TaskTypeEssay default: req.TaskType = camp.TaskType(s) } } if req.TaskType == "" { req.TaskType = camp.TaskTypeUnknown } resp, err := h.taskService.ListTasks(&req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "message": "获取任务列表失败: " + err.Error(), }) } return c.Status(fiber.StatusOK).JSON(resp) }