更新
This commit is contained in:
@@ -1,330 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// listSelectCols 列表/导出不加载 content,大幅加速
|
||||
var listSelectCols = []string{
|
||||
"id", "section_title", "price", "is_free", "is_new",
|
||||
"part_id", "part_title", "chapter_id", "chapter_title", "sort_order",
|
||||
}
|
||||
|
||||
// sectionListItem 与前端 SectionListItem 一致(小写驼峰)
|
||||
type sectionListItem struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Price float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree,omitempty"`
|
||||
IsNew *bool `json:"isNew,omitempty"` // stitch_soul:标记最新新增
|
||||
PartID string `json:"partId"`
|
||||
PartTitle string `json:"partTitle"`
|
||||
ChapterID string `json:"chapterId"`
|
||||
ChapterTitle string `json:"chapterTitle"`
|
||||
FilePath *string `json:"filePath,omitempty"`
|
||||
}
|
||||
|
||||
// DBBookAction GET/POST/PUT /api/db/book
|
||||
func DBBookAction(c *gin.Context) {
|
||||
db := database.DB()
|
||||
switch c.Request.Method {
|
||||
case http.MethodGet:
|
||||
action := c.Query("action")
|
||||
id := c.Query("id")
|
||||
switch action {
|
||||
case "list":
|
||||
var rows []model.Chapter
|
||||
if err := db.Select(listSelectCols).Order("sort_order ASC, id ASC").Find(&rows).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error(), "sections": []sectionListItem{}})
|
||||
return
|
||||
}
|
||||
sections := make([]sectionListItem, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
price := 1.0
|
||||
if r.Price != nil {
|
||||
price = *r.Price
|
||||
}
|
||||
sections = append(sections, sectionListItem{
|
||||
ID: r.ID,
|
||||
Title: r.SectionTitle,
|
||||
Price: price,
|
||||
IsFree: r.IsFree,
|
||||
IsNew: r.IsNew,
|
||||
PartID: r.PartID,
|
||||
PartTitle: r.PartTitle,
|
||||
ChapterID: r.ChapterID,
|
||||
ChapterTitle: r.ChapterTitle,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "sections": sections, "total": len(sections)})
|
||||
return
|
||||
case "read":
|
||||
if id == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id"})
|
||||
return
|
||||
}
|
||||
var ch model.Chapter
|
||||
if err := db.Where("id = ?", id).First(&ch).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "章节不存在"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
price := 1.0
|
||||
if ch.Price != nil {
|
||||
price = *ch.Price
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"section": gin.H{
|
||||
"id": ch.ID,
|
||||
"title": ch.SectionTitle,
|
||||
"price": price,
|
||||
"content": ch.Content,
|
||||
"isNew": ch.IsNew,
|
||||
"partId": ch.PartID,
|
||||
"partTitle": ch.PartTitle,
|
||||
"chapterId": ch.ChapterID,
|
||||
"chapterTitle": ch.ChapterTitle,
|
||||
},
|
||||
})
|
||||
return
|
||||
case "export":
|
||||
var rows []model.Chapter
|
||||
if err := db.Select(listSelectCols).Order("sort_order ASC, id ASC").Find(&rows).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
sections := make([]sectionListItem, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
price := 1.0
|
||||
if r.Price != nil {
|
||||
price = *r.Price
|
||||
}
|
||||
sections = append(sections, sectionListItem{
|
||||
ID: r.ID, Title: r.SectionTitle, Price: price, IsFree: r.IsFree, IsNew: r.IsNew,
|
||||
PartID: r.PartID, PartTitle: r.PartTitle, ChapterID: r.ChapterID, ChapterTitle: r.ChapterTitle,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "sections": sections})
|
||||
return
|
||||
default:
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "无效的 action"})
|
||||
return
|
||||
}
|
||||
case http.MethodPost:
|
||||
var body struct {
|
||||
Action string `json:"action"`
|
||||
Data []importItem `json:"data"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
|
||||
return
|
||||
}
|
||||
switch body.Action {
|
||||
case "sync":
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "同步完成(Gin 无文件源时可从 DB 已存在数据视为已同步)"})
|
||||
return
|
||||
case "import":
|
||||
imported, failed := 0, 0
|
||||
for _, item := range body.Data {
|
||||
price := 1.0
|
||||
if item.Price != nil {
|
||||
price = *item.Price
|
||||
}
|
||||
isFree := false
|
||||
if item.IsFree != nil {
|
||||
isFree = *item.IsFree
|
||||
}
|
||||
wordCount := len(item.Content)
|
||||
status := "published"
|
||||
ch := model.Chapter{
|
||||
ID: item.ID,
|
||||
PartID: strPtr(item.PartID, "part-1"),
|
||||
PartTitle: strPtr(item.PartTitle, "未分类"),
|
||||
ChapterID: strPtr(item.ChapterID, "chapter-1"),
|
||||
ChapterTitle: strPtr(item.ChapterTitle, "未分类"),
|
||||
SectionTitle: item.Title,
|
||||
Content: item.Content,
|
||||
WordCount: &wordCount,
|
||||
IsFree: &isFree,
|
||||
Price: &price,
|
||||
Status: &status,
|
||||
}
|
||||
err := db.Where("id = ?", item.ID).First(&model.Chapter{}).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
err = db.Create(&ch).Error
|
||||
} else if err == nil {
|
||||
err = db.Model(&model.Chapter{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
|
||||
"section_title": ch.SectionTitle,
|
||||
"content": ch.Content,
|
||||
"word_count": ch.WordCount,
|
||||
"is_free": ch.IsFree,
|
||||
"price": ch.Price,
|
||||
}).Error
|
||||
}
|
||||
if err != nil {
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
imported++
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "导入完成", "imported": imported, "failed": failed})
|
||||
return
|
||||
default:
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "无效的 action"})
|
||||
return
|
||||
}
|
||||
case http.MethodPut:
|
||||
var body struct {
|
||||
Action string `json:"action"`
|
||||
// reorder:新顺序,支持跨篇跨章时附带 partId/chapterId
|
||||
IDs []string `json:"ids"`
|
||||
Items []reorderItem `json:"items"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
IsNew *bool `json:"isNew"` // stitch_soul:标记最新新增
|
||||
EditionStandard *bool `json:"editionStandard"` // 是否属于普通版
|
||||
EditionPremium *bool `json:"editionPremium"` // 是否属于增值版
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
|
||||
return
|
||||
}
|
||||
if body.Action == "reorder" {
|
||||
// 立即返回成功,后台异步执行排序更新
|
||||
if len(body.Items) > 0 {
|
||||
items := make([]reorderItem, len(body.Items))
|
||||
copy(items, body.Items)
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
go func() {
|
||||
db := database.DB()
|
||||
for i, it := range items {
|
||||
if it.ID == "" {
|
||||
continue
|
||||
}
|
||||
up := map[string]interface{}{"sort_order": i}
|
||||
if it.PartID != "" {
|
||||
up["part_id"] = it.PartID
|
||||
}
|
||||
if it.PartTitle != "" {
|
||||
up["part_title"] = it.PartTitle
|
||||
}
|
||||
if it.ChapterID != "" {
|
||||
up["chapter_id"] = it.ChapterID
|
||||
}
|
||||
if it.ChapterTitle != "" {
|
||||
up["chapter_title"] = it.ChapterTitle
|
||||
}
|
||||
_ = db.WithContext(context.Background()).Model(&model.Chapter{}).Where("id = ?", it.ID).Updates(up).Error
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
if len(body.IDs) > 0 {
|
||||
ids := make([]string, len(body.IDs))
|
||||
copy(ids, body.IDs)
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
go func() {
|
||||
db := database.DB()
|
||||
for i, id := range ids {
|
||||
if id != "" {
|
||||
_ = db.WithContext(context.Background()).Model(&model.Chapter{}).Where("id = ?", id).Update("sort_order", i).Error
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
}
|
||||
if body.ID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id"})
|
||||
return
|
||||
}
|
||||
price := 1.0
|
||||
if body.Price != nil {
|
||||
price = *body.Price
|
||||
}
|
||||
isFree := false
|
||||
if body.IsFree != nil {
|
||||
isFree = *body.IsFree
|
||||
}
|
||||
wordCount := len(body.Content)
|
||||
updates := map[string]interface{}{
|
||||
"section_title": body.Title,
|
||||
"content": body.Content,
|
||||
"word_count": wordCount,
|
||||
"price": price,
|
||||
"is_free": isFree,
|
||||
}
|
||||
if body.IsNew != nil {
|
||||
updates["is_new"] = *body.IsNew
|
||||
}
|
||||
if body.EditionStandard != nil {
|
||||
updates["edition_standard"] = *body.EditionStandard
|
||||
}
|
||||
if body.EditionPremium != nil {
|
||||
updates["edition_premium"] = *body.EditionPremium
|
||||
}
|
||||
err := db.Model(&model.Chapter{}).Where("id = ?", body.ID).Updates(updates).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "不支持的请求方法"})
|
||||
}
|
||||
|
||||
type reorderItem struct {
|
||||
ID string `json:"id"`
|
||||
PartID string `json:"partId"`
|
||||
PartTitle string `json:"partTitle"`
|
||||
ChapterID string `json:"chapterId"`
|
||||
ChapterTitle string `json:"chapterTitle"`
|
||||
}
|
||||
|
||||
type importItem struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
PartID *string `json:"partId"`
|
||||
PartTitle *string `json:"partTitle"`
|
||||
ChapterID *string `json:"chapterId"`
|
||||
ChapterTitle *string `json:"chapterTitle"`
|
||||
}
|
||||
|
||||
func strPtr(s *string, def string) string {
|
||||
if s != nil && *s != "" {
|
||||
return *s
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// DBBookDelete DELETE /api/db/book
|
||||
func DBBookDelete(c *gin.Context) {
|
||||
id := c.Query("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id"})
|
||||
return
|
||||
}
|
||||
if err := database.DB().Where("id = ?", id).Delete(&model.Chapter{}).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
Reference in New Issue
Block a user