// Soul创业派对 - 导师模块(stitch_soul) // 小程序:GET /api/miniprogram/mentors 列表、GET /api/miniprogram/mentors/:id 详情、POST /api/miniprogram/mentors/:id/book 预约 // 管理端:db 组 CRUD + 预约列表 package handler import ( "net/http" "strconv" "strings" "soul-api/internal/database" "soul-api/internal/model" "github.com/gin-gonic/gin" ) // MiniprogramMentorsList GET /api/miniprogram/mentors 导师列表(支持 q 搜索、skill 筛选) func MiniprogramMentorsList(c *gin.Context) { db := database.DB() q := c.Query("q") skill := c.Query("skill") query := db.Model(&model.Mentor{}).Where("(enabled IS NULL OR enabled = 1)") if q != "" { query = query.Where("name LIKE ? OR intro LIKE ? OR tags LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%") } if skill != "" { query = query.Where("tags LIKE ?", "%"+skill+"%") } var list []model.Mentor if err := query.Order("sort ASC, id ASC").Find(&list).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } // 转为前端友好格式(tags 拆成数组) type mentorItem struct { model.Mentor TagsArr []string `json:"tagsArr"` } result := make([]mentorItem, len(list)) for i, m := range list { result[i] = mentorItem{Mentor: m} if m.Tags != "" { result[i].TagsArr = strings.Split(m.Tags, ",") for j := range result[i].TagsArr { result[i].TagsArr[j] = strings.TrimSpace(result[i].TagsArr[j]) } } } c.JSON(http.StatusOK, gin.H{"success": true, "data": result}) } // MiniprogramMentorsDetail GET /api/miniprogram/mentors/:id 导师详情 func MiniprogramMentorsDetail(c *gin.Context) { idStr := c.Param("id") id, err := strconv.Atoi(idStr) if err != nil || id <= 0 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "无效的导师ID"}) return } db := database.DB() var m model.Mentor if err := db.Where("id = ? AND (enabled IS NULL OR enabled = 1)", id).First(&m).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "导师不存在"}) return } tagsArr := []string{} if m.Tags != "" { for _, s := range strings.Split(m.Tags, ",") { if t := strings.TrimSpace(s); t != "" { tagsArr = append(tagsArr, t) } } } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "id": m.ID, "avatar": m.Avatar, "name": m.Name, "intro": m.Intro, "tags": m.Tags, "tagsArr": tagsArr, "priceSingle": m.PriceSingle, "priceHalfYear": m.PriceHalfYear, "priceYear": m.PriceYear, "quote": m.Quote, "whyFind": m.WhyFind, "offering": m.Offering, "judgmentStyle": m.JudgmentStyle, }, }) } // MiniprogramMentorsBook POST /api/miniprogram/mentors/:id/book 创建预约(选择咨询类型后创建,后续走支付) func MiniprogramMentorsBook(c *gin.Context) { idStr := c.Param("id") mentorID, err := strconv.Atoi(idStr) if err != nil || mentorID <= 0 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "无效的导师ID"}) return } var body struct { UserID int `json:"userId" binding:"required"` ConsultationType string `json:"consultationType" binding:"required"` // single, half_year, year } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数不完整"}) return } if body.ConsultationType != "single" && body.ConsultationType != "half_year" && body.ConsultationType != "year" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "consultationType 需为 single/half_year/year"}) return } db := database.DB() var mentor model.Mentor if err := db.Where("id = ? AND (enabled IS NULL OR enabled = 1)", mentorID).First(&mentor).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "导师不存在"}) return } var amount float64 switch body.ConsultationType { case "single": if mentor.PriceSingle != nil { amount = *mentor.PriceSingle } case "half_year": if mentor.PriceHalfYear != nil { amount = *mentor.PriceHalfYear } case "year": if mentor.PriceYear != nil { amount = *mentor.PriceYear } } if amount <= 0 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "该咨询类型暂未配置价格"}) return } consult := model.MentorConsultation{ UserID: body.UserID, MentorID: mentorID, ConsultationType: body.ConsultationType, Amount: amount, Status: "created", } if err := db.Create(&consult).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } // 返回预约单,前端可据此调 pay 接口(productType: mentor_consultation, productId: consult.ID) c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ "id": consult.ID, "mentorId": mentorID, "consultationType": body.ConsultationType, "amount": amount, "status": "created", }, }) } // DBMentorsList GET /api/db/mentors 管理端导师列表 func DBMentorsList(c *gin.Context) { db := database.DB() var list []model.Mentor if err := db.Order("sort ASC, id ASC").Find(&list).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) } // DBMentorsAction POST 新增 / PUT 更新 / DELETE 删除 导师 func DBMentorsAction(c *gin.Context) { db := database.DB() method := c.Request.Method if method == "POST" { var body struct { Name string `json:"name" binding:"required"` Avatar string `json:"avatar"` Intro string `json:"intro"` Tags string `json:"tags"` PriceSingle *float64 `json:"priceSingle"` PriceHalfYear *float64 `json:"priceHalfYear"` PriceYear *float64 `json:"priceYear"` Quote string `json:"quote"` WhyFind string `json:"whyFind"` Offering string `json:"offering"` JudgmentStyle string `json:"judgmentStyle"` Sort int `json:"sort"` Enabled *bool `json:"enabled"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "name 不能为空"}) return } m := model.Mentor{ Name: body.Name, Avatar: body.Avatar, Intro: body.Intro, Tags: body.Tags, PriceSingle: body.PriceSingle, PriceHalfYear: body.PriceHalfYear, PriceYear: body.PriceYear, Quote: body.Quote, WhyFind: body.WhyFind, Offering: body.Offering, JudgmentStyle: body.JudgmentStyle, Sort: body.Sort, Enabled: body.Enabled, } if m.Enabled == nil { trueVal := true m.Enabled = &trueVal } if err := db.Create(&m).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": m}) return } if method == "PUT" { var body struct { ID int `json:"id" binding:"required"` Name *string `json:"name"` Avatar *string `json:"avatar"` Intro *string `json:"intro"` Tags *string `json:"tags"` PriceSingle *float64 `json:"priceSingle"` PriceHalfYear *float64 `json:"priceHalfYear"` PriceYear *float64 `json:"priceYear"` Quote *string `json:"quote"` WhyFind *string `json:"whyFind"` Offering *string `json:"offering"` JudgmentStyle *string `json:"judgmentStyle"` Sort *int `json:"sort"` Enabled *bool `json:"enabled"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "id 不能为空"}) return } updates := map[string]interface{}{} if body.Name != nil { updates["name"] = *body.Name } if body.Avatar != nil { updates["avatar"] = *body.Avatar } if body.Intro != nil { updates["intro"] = *body.Intro } if body.Tags != nil { updates["tags"] = *body.Tags } if body.PriceSingle != nil { updates["price_single"] = *body.PriceSingle } if body.PriceHalfYear != nil { updates["price_half_year"] = *body.PriceHalfYear } if body.PriceYear != nil { updates["price_year"] = *body.PriceYear } if body.Quote != nil { updates["quote"] = *body.Quote } if body.WhyFind != nil { updates["why_find"] = *body.WhyFind } if body.Offering != nil { updates["offering"] = *body.Offering } if body.JudgmentStyle != nil { updates["judgment_style"] = *body.JudgmentStyle } if body.Sort != nil { updates["sort"] = *body.Sort } if body.Enabled != nil { updates["enabled"] = *body.Enabled } if len(updates) == 0 { c.JSON(http.StatusOK, gin.H{"success": true, "message": "无更新"}) return } if err := db.Model(&model.Mentor{}).Where("id = ?", body.ID).Updates(updates).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "更新成功"}) return } if method == "DELETE" { id := c.Query("id") if id == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "id 不能为空"}) return } if err := db.Where("id = ?", id).Delete(&model.Mentor{}).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "message": "删除成功"}) return } c.JSON(http.StatusOK, gin.H{"success": false, "error": "不支持的请求方法"}) } // DBMentorConsultationsList GET /api/db/mentor-consultations 预约列表(支持 status 筛选) func DBMentorConsultationsList(c *gin.Context) { db := database.DB() status := c.Query("status") query := db.Model(&model.MentorConsultation{}) if status != "" { query = query.Where("status = ?", status) } var list []model.MentorConsultation if err := query.Order("created_at DESC").Find(&list).Error; err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"success": true, "data": list}) }