341 lines
10 KiB
Go
341 lines
10 KiB
Go
|
|
// 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})
|
|||
|
|
}
|