Files
soul-yongping/soul-api/internal/handler/admin_chapters.go
卡若 5724fba877 feat: 小程序超级个体/个人资料/CKB获客;VIP列表展示过滤;管理端与API联调
- 超级个体:去掉首位特例;列表仅展示有头像且非微信默认昵称(vip.go)
- 个人资料:居中头像、低调联系方式、点头像优先走存客宝 lead(ckbLeadToken)
- 阅读页分享朋友圈复制与 toast 去重
- soul-api: miniprogram users 带 ckbLeadToken;其它 handler 与路由调整
- 脚本:content_upload、miniprogram 上传辅助等

Made-with: Cursor
2026-03-22 08:34:28 +08:00

234 lines
6.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"net/http"
"strconv"
"soul-api/internal/cache"
"soul-api/internal/database"
"soul-api/internal/model"
"github.com/gin-gonic/gin"
)
// AdminChaptersList GET /api/admin/chapters 从 chapters 表组树part -> chapters -> sections
func AdminChaptersList(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
if page < 1 {
page = 1
}
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
if pageSize < 1 {
pageSize = 20
}
if pageSize > 200 {
pageSize = 200
}
var list []model.Chapter
if err := database.DB().Order("sort_order ASC, id ASC").Find(&list).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{
"structure": []interface{}{},
"records": []interface{}{},
"stats": nil,
"page": page,
"pageSize": pageSize,
"totalPages": 0,
"total": 0,
}})
return
}
type section struct {
ID string `json:"id"`
Title string `json:"title"`
Price float64 `json:"price"`
IsFree bool `json:"isFree"`
Status string `json:"status"`
EditionStandard *bool `json:"editionStandard,omitempty"`
EditionPremium *bool `json:"editionPremium,omitempty"`
}
type sectionRecord struct {
ID string `json:"id"`
PartID string `json:"partId"`
PartTitle string `json:"partTitle"`
ChapterID string `json:"chapterId"`
ChapterTitle string `json:"chapterTitle"`
Title string `json:"title"`
Price float64 `json:"price"`
IsFree bool `json:"isFree"`
Status string `json:"status"`
EditionStandard *bool `json:"editionStandard,omitempty"`
EditionPremium *bool `json:"editionPremium,omitempty"`
}
type chapter struct {
ID string `json:"id"`
Title string `json:"title"`
Sections []section `json:"sections"`
}
type part struct {
ID string `json:"id"`
Title string `json:"title"`
Type string `json:"type"`
Chapters []chapter `json:"chapters"`
}
partMap := make(map[string]*part)
chapterMap := make(map[string]map[string]*chapter)
records := make([]sectionRecord, 0, len(list))
for _, row := range list {
if partMap[row.PartID] == nil {
partMap[row.PartID] = &part{ID: row.PartID, Title: row.PartTitle, Type: "part", Chapters: []chapter{}}
chapterMap[row.PartID] = make(map[string]*chapter)
}
p := partMap[row.PartID]
if chapterMap[row.PartID][row.ChapterID] == nil {
ch := chapter{ID: row.ChapterID, Title: row.ChapterTitle, Sections: []section{}}
p.Chapters = append(p.Chapters, ch)
chapterMap[row.PartID][row.ChapterID] = &p.Chapters[len(p.Chapters)-1]
}
ch := chapterMap[row.PartID][row.ChapterID]
price := 1.0
if row.Price != nil {
price = *row.Price
}
isFree := false
if row.IsFree != nil {
isFree = *row.IsFree
}
st := "published"
if row.Status != nil {
st = *row.Status
}
ch.Sections = append(ch.Sections, section{
ID: row.ID, Title: row.SectionTitle, Price: price, IsFree: isFree, Status: st,
EditionStandard: row.EditionStandard, EditionPremium: row.EditionPremium,
})
records = append(records, sectionRecord{
ID: row.ID,
PartID: row.PartID,
PartTitle: row.PartTitle,
ChapterID: row.ChapterID,
ChapterTitle: row.ChapterTitle,
Title: row.SectionTitle,
Price: price,
IsFree: isFree,
Status: st,
EditionStandard: row.EditionStandard,
EditionPremium: row.EditionPremium,
})
}
structure := make([]part, 0, len(partMap))
for _, p := range partMap {
structure = append(structure, *p)
}
var total int64
database.DB().Model(&model.Chapter{}).Count(&total)
totalPages := 0
if pageSize > 0 {
totalPages = (int(total) + pageSize - 1) / pageSize
}
start := (page - 1) * pageSize
if start > len(records) {
start = len(records)
}
end := start + pageSize
if end > len(records) {
end = len(records)
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"structure": structure,
"records": records[start:end],
"stats": gin.H{"totalSections": total},
"page": page,
"pageSize": pageSize,
"totalPages": totalPages,
"total": total,
},
})
}
// AdminChaptersAction POST/PUT/DELETE /api/admin/chapters
func AdminChaptersAction(c *gin.Context) {
var body struct {
Action string `json:"action"`
ID string `json:"id"`
ChapterID string `json:"chapterId"` // 前端兼容section id
SectionTitle string `json:"sectionTitle"`
Ids []string `json:"ids"` // reorder新顺序的 section id 列表
Price *float64 `json:"price"`
IsFree *bool `json:"isFree"`
Status *string `json:"status"`
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
}
resolveID := func() string {
if body.ID != "" {
return body.ID
}
return body.ChapterID
}
db := database.DB()
if body.Action == "updatePrice" {
id := resolveID()
if id != "" && body.Price != nil {
db.Model(&model.Chapter{}).Where("id = ?", id).Update("price", *body.Price)
}
}
if body.Action == "toggleFree" {
id := resolveID()
if id != "" && body.IsFree != nil {
db.Model(&model.Chapter{}).Where("id = ?", id).Update("is_free", *body.IsFree)
}
}
if body.Action == "updateStatus" {
id := resolveID()
if id != "" && body.Status != nil {
db.Model(&model.Chapter{}).Where("id = ?", id).Update("status", *body.Status)
}
}
if body.Action == "rename" {
id := resolveID()
if id != "" && body.SectionTitle != "" {
db.Model(&model.Chapter{}).Where("id = ?", id).Update("section_title", body.SectionTitle)
}
}
if body.Action == "delete" {
id := resolveID()
if id != "" {
cache.InvalidateChapterContentByID(id)
db.Where("id = ?", id).Delete(&model.Chapter{})
}
}
if body.Action == "reorder" && len(body.Ids) > 0 {
for i, id := range body.Ids {
if id != "" {
db.Model(&model.Chapter{}).Where("id = ?", id).Update("sort_order", i)
}
}
}
if body.Action == "updateEdition" {
id := resolveID()
if id != "" {
updates := make(map[string]interface{})
if body.EditionStandard != nil {
updates["edition_standard"] = *body.EditionStandard
}
if body.EditionPremium != nil {
updates["edition_premium"] = *body.EditionPremium
}
if len(updates) > 0 {
db.Model(&model.Chapter{}).Where("id = ?", id).Updates(updates)
}
}
}
cache.InvalidateBookParts()
InvalidateChaptersByPartCache()
cache.InvalidateBookCache()
c.JSON(http.StatusOK, gin.H{"success": true})
}