package handler import ( "time" "github.com/gin-gonic/gin" "github.com/sundynix/pets-be/internal/middleware" "github.com/sundynix/pets-be/internal/service" "github.com/sundynix/pets-be/pkg/response" ) // ListTasks GET /api/pets/:id/tasks?date=YYYY-MM-DD func (h *Handler) ListTasks(c *gin.Context) { var date *time.Time if q := c.Query("date"); q != "" { if t, err := time.ParseInLocation("2006-01-02", q, time.Local); err == nil { date = &t } } tasks, err := h.svc.ListTasks(middleware.UserID(c), idParam(c, "id"), date) if err != nil { respondErr(c, err) return } response.OK(c, tasks) } // 任务的增删改统一走 /plan-tasks 那套 —— 「今日任务」和「计划节点」合并之后, // /tasks 这边只保留「读某天的」和「勾选」,重复的 CRUD 删掉了。 // 留着两套会出现「首页管理弹层加的任务」和「计划页加的节点」进不同的表。 // ToggleTask POST /api/tasks/:id/toggle func (h *Handler) ToggleTask(c *gin.Context) { task, err := h.svc.ToggleTask(middleware.UserID(c), idParam(c, "id")) if err != nil { respondErr(c, err) return } response.OK(c, task) } // CompleteAllTasks POST /api/pets/:id/tasks/complete-all func (h *Handler) CompleteAllTasks(c *gin.Context) { tasks, err := h.svc.CompleteAllTasks(middleware.UserID(c), idParam(c, "id")) if err != nil { respondErr(c, err) return } response.OK(c, tasks) } // GetPlan GET /api/pets/:id/plan func (h *Handler) GetPlan(c *gin.Context) { plan, err := h.svc.GetPlan(middleware.UserID(c), idParam(c, "id")) if err != nil { respondErr(c, err) return } response.OK(c, plan) } // DayPlan GET /api/pets/:id/day-plan?date=YYYY-MM-DD func (h *Handler) DayPlan(c *gin.Context) { res, err := h.svc.DayPlan(middleware.UserID(c), idParam(c, "id"), c.Query("date")) if err != nil { respondErr(c, err) return } response.OK(c, res) } // PlanCalendar GET /api/pets/:id/plan/calendar?month=YYYY-MM func (h *Handler) PlanCalendar(c *gin.Context) { now := time.Now() year, month := now.Year(), int(now.Month()) if q := c.Query("month"); q != "" { if t, err := time.Parse("2006-01", q); err == nil { year, month = t.Year(), int(t.Month()) } } res, err := h.svc.Calendar(middleware.UserID(c), idParam(c, "id"), year, month) if err != nil { respondErr(c, err) return } response.OK(c, res) } type aiPlanReq struct { Input string `json:"input"` } // CreateAIPlan POST /api/pets/:id/ai-plan func (h *Handler) CreateAIPlan(c *gin.Context) { var req aiPlanReq if err := c.ShouldBindJSON(&req); err != nil { response.FailParams(c, err.Error()) return } plan, err := h.svc.CreateAIPlan(middleware.UserID(c), idParam(c, "id"), req.Input) if err != nil { respondErr(c, err) return } response.OK(c, plan) } // ApplyAIPlan POST /api/ai-plan/:id/apply func (h *Handler) ApplyAIPlan(c *gin.Context) { plan, err := h.svc.ApplyAIPlan(middleware.UserID(c), idParam(c, "id")) if err != nil { respondErr(c, err) return } response.OK(c, plan) } // TogglePlanTask POST /api/plan-tasks/:id/toggle func (h *Handler) TogglePlanTask(c *gin.Context) { if err := h.svc.TogglePlanTask(middleware.UserID(c), idParam(c, "id")); err != nil { respondErr(c, err) return } response.OK(c, gin.H{"ok": true}) } // planTaskReq 用户自己加/改的计划节点 type planTaskReq struct { Date string `json:"date"` Title string `json:"title"` Description string `json:"description"` SheetType string `json:"sheet_type"` Daily bool `json:"daily"` } func (r planTaskReq) input() service.PlanTaskInput { return service.PlanTaskInput{Date: r.Date, Title: r.Title, Description: r.Description, SheetType: r.SheetType, Daily: r.Daily} } // AddPlanTask POST /api/pets/:id/plan-tasks 往 30 天计划里加一条 func (h *Handler) AddPlanTask(c *gin.Context) { var req planTaskReq if err := c.ShouldBindJSON(&req); err != nil { response.FailParams(c, err.Error()) return } list, err := h.svc.AddPlanTask(middleware.UserID(c), idParam(c, "id"), req.input()) if err != nil { respondErr(c, err) return } // daily 会一次建多条,统一返回数组,前端不用分两种情况处理 response.OK(c, list) } // UpdatePlanTask PUT /api/plan-tasks/:id func (h *Handler) UpdatePlanTask(c *gin.Context) { var req planTaskReq if err := c.ShouldBindJSON(&req); err != nil { response.FailParams(c, err.Error()) return } if err := h.svc.UpdatePlanTask(middleware.UserID(c), idParam(c, "id"), req.input()); err != nil { respondErr(c, err) return } response.OK(c, nil) } // DeletePlanTask DELETE /api/plan-tasks/:id func (h *Handler) DeletePlanTask(c *gin.Context) { if err := h.svc.DeletePlanTask(middleware.UserID(c), idParam(c, "id")); err != nil { respondErr(c, err) return } response.OK(c, nil) } // PlanMonthStatus GET /api/pets/:id/plan/month-status // 首页「管理计划」那个提醒角标要的:本月剩几件、下月排了没有 func (h *Handler) PlanMonthStatus(c *gin.Context) { st, err := h.svc.PlanMonthStatus(middleware.UserID(c), idParam(c, "id")) if err != nil { respondErr(c, err) return } response.OK(c, st) } // CareTemplateOptions GET /api/care-templates?species=cat&stage=幼年期 // 建档最后一步和计划页的「套用方案」都读这个 func (h *Handler) CareTemplateOptions(c *gin.Context) { response.OK(c, h.svc.ListTemplateOptions(c.Query("species"), c.Query("stage"))) } type applyTemplateReq struct { TemplateID string `json:"template_id"` MonthOffset int `json:"month_offset"` // 0 本月,1 下月 } // ApplyTemplate POST /api/pets/:id/apply-template func (h *Handler) ApplyTemplate(c *gin.Context) { var req applyTemplateReq if err := c.ShouldBindJSON(&req); err != nil { response.FailParams(c, err.Error()) return } if req.TemplateID == "" { response.FailParams(c, "缺少方案 id") return } res, err := h.svc.ApplyTemplate(middleware.UserID(c), idParam(c, "id"), req.TemplateID, req.MonthOffset) if err != nil { respondErr(c, err) return } response.OK(c, res) }