Files
Mycontent/soul-api/internal/handler/ckb_content.go
2026-04-13 14:32:32 +08:00

144 lines
3.6 KiB
Go

package handler
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"soul-api/internal/config"
"soul-api/internal/database"
"soul-api/internal/model"
)
// AdminCKBPushContent POST /api/admin/ckb/push-content
// 将选中章节推送到存客宝内容库(当前为内部标记 + 生成可拉取摘要;存客宝侧通过 GET /api/ckb/content 拉取)
func AdminCKBPushContent(c *gin.Context) {
var body struct {
ChapterIDs []string `json:"chapterIds" binding:"required"`
}
if err := c.ShouldBindJSON(&body); err != nil || len(body.ChapterIDs) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "请选择要推送的章节"})
return
}
db := database.DB()
var chapters []model.Chapter
if err := db.Where("id IN ?", body.ChapterIDs).Find(&chapters).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "查询章节失败: " + err.Error()})
return
}
if len(chapters) == 0 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "未找到指定章节"})
return
}
results := make([]gin.H, 0, len(chapters))
for _, ch := range chapters {
status := "published"
if ch.Status != nil {
status = *ch.Status
}
if err := db.Model(&model.Chapter{}).Where("id = ?", ch.ID).
Update("status", "published").Error; err != nil {
results = append(results, gin.H{"id": ch.ID, "title": ch.SectionTitle, "status": "failed", "error": err.Error()})
continue
}
_ = status
results = append(results, gin.H{"id": ch.ID, "title": ch.SectionTitle, "status": "success"})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": fmt.Sprintf("已推送 %d 条内容", len(results)),
"results": results,
})
}
// CKBContentList GET /api/ckb/content
// 供存客宝拉取内容列表(通过 X-API-Key 或 apiKey query 鉴权)
func CKBContentList(c *gin.Context) {
apiKey := c.GetHeader("X-API-Key")
if apiKey == "" {
apiKey = c.Query("apiKey")
}
expectedKey := getCkbLeadApiKey()
if apiKey == "" || apiKey != expectedKey {
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "无效的 API Key"})
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
db := database.DB()
var total int64
db.Model(&model.Chapter{}).Where("status = ? OR status IS NULL", "published").Count(&total)
var chapters []model.Chapter
db.Select("id, section_title, content, created_at, updated_at").
Where("status = ? OR status IS NULL", "published").
Order("sort_order ASC, mid ASC").
Offset((page - 1) * pageSize).
Limit(pageSize).
Find(&chapters)
baseURL := "https://soulapi.quwanzhi.com"
if cfg := config.Get(); cfg != nil && cfg.BaseURL != "" {
baseURL = cfg.BaseURL
}
data := make([]gin.H, 0, len(chapters))
for _, ch := range chapters {
summary := strings.TrimSpace(ch.Content)
summary = stripHTMLTags(summary)
if len([]rune(summary)) > 200 {
summary = string([]rune(summary)[:200]) + "..."
}
data = append(data, gin.H{
"id": ch.ID,
"title": ch.SectionTitle,
"summary": summary,
"url": baseURL + "/read/" + ch.ID,
"createdAt": ch.CreatedAt,
"updatedAt": ch.UpdatedAt,
})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": data,
"total": total,
"page": page,
"pageSize": pageSize,
})
}
func stripHTMLTags(s string) string {
var out strings.Builder
inTag := false
for _, r := range s {
if r == '<' {
inTag = true
continue
}
if r == '>' {
inTag = false
continue
}
if !inTag {
out.WriteRune(r)
}
}
return out.String()
}