包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。 - 工作台画布:节点拖放、连线模式、右键菜单、AI 助手 - 后端:连接器 API、专员种子数据 - 导航:左侧导航、工坊、市场、控制台
170 lines
4.6 KiB
Go
170 lines
4.6 KiB
Go
package api
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"eaisalestrain/backend/internal/model"
|
|
"eaisalestrain/backend/internal/store"
|
|
"eaisalestrain/backend/internal/web"
|
|
)
|
|
|
|
// courseView 课程详情视图(含绑定产品)
|
|
func courseView(c *gin.Context, co model.Course) {
|
|
out := gin.H{
|
|
"id": co.ID,
|
|
"code": co.Code,
|
|
"name": co.Name,
|
|
"category": co.Category,
|
|
"target_customers": co.TargetCustomers,
|
|
"forbidden_customers": co.ForbiddenCustomers,
|
|
"scripts": co.Scripts,
|
|
"sales_process": co.SalesProcess,
|
|
"objection_handling": co.ObjectionHandling,
|
|
"delivery_pitfalls": co.DeliveryPitfalls,
|
|
"report_rules": co.ReportRules,
|
|
"related_product_id": co.RelatedProductID,
|
|
"status": co.Status,
|
|
"created_at": co.CreatedAt,
|
|
"updated_at": co.UpdatedAt,
|
|
}
|
|
if co.RelatedProductID != nil {
|
|
var p model.Product
|
|
if store.DB.Where("id = ? AND status != ?", *co.RelatedProductID, "inactive").First(&p).Error == nil {
|
|
out["product"] = gin.H{"id": p.ID, "code": p.Code, "name": p.Name, "category": p.Category}
|
|
}
|
|
}
|
|
|
|
var medias []model.MediaFile
|
|
store.DB.Where("bind_type = ? AND bind_id = ? AND status = ?", "course", co.ID, "approved").
|
|
Order("id ASC").Find(&medias)
|
|
if len(medias) > 0 {
|
|
items := make([]gin.H, 0, len(medias))
|
|
for _, m := range medias {
|
|
items = append(items, gin.H{
|
|
"id": m.ID,
|
|
"filename": m.Filename,
|
|
"file_ext": m.FileExt,
|
|
"preview_url": "/api/media/preview/" + strconv.FormatUint(uint64(m.ID), 10),
|
|
})
|
|
}
|
|
out["medias"] = items
|
|
}
|
|
web.OK(c, out)
|
|
}
|
|
|
|
// ListCourses GET /api/courses?category=&status=
|
|
func ListCourses(c *gin.Context) {
|
|
q := store.DB.Model(&model.Course{})
|
|
if cat := c.Query("category"); cat != "" {
|
|
q = q.Where("category = ?", cat)
|
|
}
|
|
switch st := c.Query("status"); st {
|
|
case "": // 默认仅 active(员工浏览)
|
|
q = q.Where("status = ?", "active")
|
|
case "all": // 管理员维护全量
|
|
default:
|
|
q = q.Where("status = ?", st)
|
|
}
|
|
var items []model.Course
|
|
if err := q.Order("id ASC").Find(&items).Error; err != nil {
|
|
web.Fail(c, web.NewBadRequest("查询课程失败"))
|
|
return
|
|
}
|
|
web.OK(c, items)
|
|
}
|
|
|
|
// GetCourse GET /api/courses/{id}
|
|
func GetCourse(c *gin.Context) {
|
|
id, ok := parseID(c, "id")
|
|
if !ok {
|
|
return
|
|
}
|
|
var co model.Course
|
|
if err := store.DB.First(&co, id).Error; err != nil {
|
|
web.Fail(c, web.NewNotFoundError("课程不存在"))
|
|
return
|
|
}
|
|
courseView(c, co)
|
|
}
|
|
|
|
// CreateCourse POST /api/courses (admin)
|
|
func CreateCourse(c *gin.Context) {
|
|
var co model.Course
|
|
if err := c.ShouldBindJSON(&co); err != nil {
|
|
web.Fail(c, web.NewBadRequest("请求参数错误"))
|
|
return
|
|
}
|
|
if co.Code == "" || co.Name == "" || co.Category == "" {
|
|
web.Fail(c, web.NewBadRequest("编号、名称、分类为必填"))
|
|
return
|
|
}
|
|
var count int64
|
|
store.DB.Model(&model.Course{}).Where("code = ?", co.Code).Count(&count)
|
|
if count > 0 {
|
|
web.Fail(c, web.NewConflictError("课程编号已存在"))
|
|
return
|
|
}
|
|
if co.Status == "" {
|
|
co.Status = "active"
|
|
}
|
|
if err := store.DB.Create(&co).Error; err != nil {
|
|
web.Fail(c, web.NewBadRequest("创建课程失败"))
|
|
return
|
|
}
|
|
web.OK(c, co)
|
|
}
|
|
|
|
// UpdateCourse PUT /api/courses/{id} (admin) —— 支持绑定/解绑产品
|
|
func UpdateCourse(c *gin.Context) {
|
|
id, ok := parseID(c, "id")
|
|
if !ok {
|
|
return
|
|
}
|
|
var co model.Course
|
|
if err := store.DB.First(&co, id).Error; err != nil {
|
|
web.Fail(c, web.NewNotFoundError("课程不存在"))
|
|
return
|
|
}
|
|
var req model.Course
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
web.Fail(c, web.NewBadRequest("请求参数错误"))
|
|
return
|
|
}
|
|
if req.Name == "" || req.Category == "" {
|
|
web.Fail(c, web.NewBadRequest("名称、分类为必填"))
|
|
return
|
|
}
|
|
if req.Status == "" {
|
|
req.Status = "active"
|
|
}
|
|
|
|
if req.Code != "" && req.Code != co.Code {
|
|
var count int64
|
|
store.DB.Model(&model.Course{}).Where("code = ? AND id <> ?", req.Code, id).Count(&count)
|
|
if count > 0 {
|
|
web.Fail(c, web.NewConflictError("课程编号已存在"))
|
|
return
|
|
}
|
|
co.Code = req.Code
|
|
}
|
|
co.Name = req.Name
|
|
co.Category = req.Category
|
|
co.TargetCustomers = req.TargetCustomers
|
|
co.ForbiddenCustomers = req.ForbiddenCustomers
|
|
co.Scripts = req.Scripts
|
|
co.SalesProcess = req.SalesProcess
|
|
co.ObjectionHandling = req.ObjectionHandling
|
|
co.DeliveryPitfalls = req.DeliveryPitfalls
|
|
co.ReportRules = req.ReportRules
|
|
co.RelatedProductID = req.RelatedProductID
|
|
co.Status = req.Status
|
|
|
|
if err := store.DB.Save(&co).Error; err != nil {
|
|
web.Fail(c, web.NewBadRequest("更新课程失败"))
|
|
return
|
|
}
|
|
courseView(c, co)
|
|
}
|