// 复盘接口(仅 KPT 法) package app import ( "fmt" "math/rand" "os" "simple-memo/global" "simple-memo/models" "simple-memo/utils" "time" "gorm.io/gorm" "github.com/gin-gonic/gin" ) // -------------------------- 1. 定义 Handler 接口 -------------------------- type ReviewHandler interface { SaveReview(c *gin.Context) // 保存复盘 GetReview(c *gin.Context) // 获取单日复盘 ReviewList(c *gin.Context) // 获取复盘列表 DeleteReview(c *gin.Context) // 删除复盘 } // -------------------------- 2. 实现结构体 -------------------------- type reviewHandler struct { db *gorm.DB } func NewReviewHandler() ReviewHandler { return &reviewHandler{ db: global.DB, } } // -------------------------- 3. 请求结构体 -------------------------- // SaveReviewReq 保存复盘请求 type SaveReviewReq struct { Date string `json:"date" binding:"required"` // YYYY-MM-DD Kpt SaveReviewKPTDto `json:"kpt"` } type SaveReviewKPTDto struct { Keep string `json:"keep"` Problem string `json:"problem"` Try string `json:"try"` } // GetReviewReq 获取单日复盘请求 type GetReviewReq struct { Date string `json:"date" binding:"required"` } // ReviewListReq 获取复盘列表请求 type ReviewListReq struct { StartDate string `json:"start_date"` EndDate string `json:"end_date"` Keyword string `json:"keyword"` Page int `json:"page"` PageSize int `json:"page_size"` } // DeleteReviewReq 删除复盘请求 type DeleteReviewReq struct { ID uint `json:"id" binding:"required"` } // -------------------------- 4. 接口实现 -------------------------- // SaveReview 保存复盘 func (h *reviewHandler) SaveReview(c *gin.Context) { userID := c.GetUint("userID") var req SaveReviewReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } var review models.Review isNewRecord := false err := h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&review).Error if err == gorm.ErrRecordNotFound { review = models.Review{ UserID: userID, Date: req.Date, Keep: utils.FilterSensitive(req.Kpt.Keep), Problem: utils.FilterSensitive(req.Kpt.Problem), Try: utils.FilterSensitive(req.Kpt.Try), } isNewRecord = true } else if err != nil { utils.Fail(c, "查询失败") return } else { review.Keep = utils.FilterSensitive(req.Kpt.Keep) review.Problem = utils.FilterSensitive(req.Kpt.Problem) review.Try = utils.FilterSensitive(req.Kpt.Try) } if isNewRecord { if err := h.db.Create(&review).Error; err != nil { utils.Fail(c, "保存失败") return } } else { if err := h.db.Save(&review).Error; err != nil { utils.Fail(c, "保存失败") return } } var growthResult any debugMode := os.Getenv("DEBUG_MODE") if isNewRecord { today := time.Now().Format("2006-01-02") if req.Date == today { if debugMode == "true" { rand.Seed(time.Now().UnixNano()) levelUp := rand.Intn(10) < 2 hasAchievement := rand.Intn(10) < 3 growthResult = MockGrowthRewards(levelUp, hasAchievement) } else { growthHandler := NewGrowthHandler() growthResult = growthHandler.AddExpInternal(userID, "review", fmt.Sprintf("%d", review.ID), nil) } } } utils.Ok(c, gin.H{ "success": true, "growth": growthResult, "review": review, "isNewRecord": isNewRecord, }) } // GetReview 获取单日复盘 func (h *reviewHandler) GetReview(c *gin.Context) { userID := c.GetUint("userID") var req GetReviewReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } var review models.Review err := h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&review).Error if err == gorm.ErrRecordNotFound { utils.Ok(c, nil) return } else if err != nil { utils.Fail(c, "查询失败") return } utils.Ok(c, review) } // ReviewList 获取复盘列表 func (h *reviewHandler) ReviewList(c *gin.Context) { userID := c.GetUint("userID") var req ReviewListReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } if req.Page <= 0 { req.Page = 1 } if req.PageSize <= 0 { req.PageSize = 20 } db := h.db.Model(&models.Review{}).Where("user_id = ?", userID) if req.StartDate != "" && req.EndDate != "" { db = db.Where("date BETWEEN ? AND ?", req.StartDate, req.EndDate) } else if req.StartDate != "" { db = db.Where("date >= ?", req.StartDate) } else if req.EndDate != "" { db = db.Where("date <= ?", req.EndDate) } if req.Keyword != "" { keyword := "%" + req.Keyword + "%" db = db.Where("(keep LIKE ? OR problem LIKE ? OR try LIKE ?)", keyword, keyword, keyword) } var total int64 db.Count(&total) var list []models.Review offset := (req.Page - 1) * req.PageSize err := db.Order("date DESC").Offset(offset).Limit(req.PageSize).Find(&list).Error if err != nil { utils.Fail(c, "查询失败") return } type ReviewListItem struct { ID uint `json:"id"` Date string `json:"date"` Summary string `json:"summary"` } result := make([]ReviewListItem, len(list)) for i, r := range list { summary := r.Keep if len(summary) > 50 { summary = summary[:50] + "..." } result[i] = ReviewListItem{ ID: r.ID, Date: r.Date, Summary: summary, } } utils.Ok(c, gin.H{ "list": result, "total": total, "page": req.Page, "pageSize": req.PageSize, }) } // DeleteReview 删除复盘 func (h *reviewHandler) DeleteReview(c *gin.Context) { userID := c.GetUint("userID") var req DeleteReviewReq if err := c.ShouldBindJSON(&req); err != nil { utils.Fail(c, "参数解析失败") return } var review models.Review err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&review).Error if err == gorm.ErrRecordNotFound { utils.Fail(c, "复盘不存在") return } else if err != nil { utils.Fail(c, "查询失败") return } if err := h.db.Delete(&review).Error; err != nil { utils.Fail(c, "删除失败") return } growthHandler := NewGrowthHandler() today := time.Now().Format("2006-01-02") deducted := growthHandler.DeductExpInternal(userID, "review", fmt.Sprintf("%d", req.ID), today) utils.Ok(c, gin.H{ "success": true, "deducted_exp": deducted, }) }