feat: 内容管理第5批优化 - Bug修复 + 分享功能 + 代付功能
1. Bug修复: - 修复Markdown星号/下划线在小程序端原样显示问题(markdownToHtml增加__和_支持,contentParser增加Markdown格式剥离) - 修复@提及无反应(MentionSuggestion使用ref保持persons最新值,解决闭包捕获空数组问题) - 修复#链接标签点击"未找到小程序配置"(增加appId直接跳转降级路径) 2. 分享功能优化: - "分享到朋友圈"改为"分享给好友"(open-type从shareTimeline改为share) - 90%收益提示移到分享按钮下方 - 阅读20%后向上滑动弹出分享浮层提示(4秒自动消失) 3. 代付功能: - 后端:新增UserBalance/BalanceTransaction/GiftUnlock三个模型 - 后端:新增8个余额相关API(查询/充值/充值确认/代付/领取/退款/交易记录/礼物信息) - 小程序:阅读页新增"代付分享"按钮,支持用余额为好友解锁章节 - 分享链接携带gift参数,好友打开自动领取解锁 Made-with: Cursor
This commit is contained in:
@@ -24,7 +24,8 @@ var excludeParts = []string{"序言", "尾声", "附录"}
|
||||
// 支持 excludeFixed=1:排除序言、尾声、附录(目录页固定模块,不参与中间篇章)
|
||||
func BookAllChapters(c *gin.Context) {
|
||||
db := database.DB()
|
||||
q := db.Model(&model.Chapter{})
|
||||
q := db.Model(&model.Chapter{}).
|
||||
Select("mid, id, part_id, part_title, chapter_id, chapter_title, section_title, word_count, is_free, price, sort_order, status, is_new, edition_standard, edition_premium, hot_score, created_at, updated_at")
|
||||
if c.Query("excludeFixed") == "1" {
|
||||
for _, p := range excludeParts {
|
||||
q = q.Where("part_title NOT LIKE ?", "%"+p+"%")
|
||||
@@ -430,7 +431,8 @@ func escapeLikeBook(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// BookSearch GET /api/book/search?q= 章节搜索(与 /api/search 逻辑一致)
|
||||
// BookSearch GET /api/book/search?q= 章节搜索
|
||||
// 优化:先搜标题(快),再搜内容(慢),不加载完整 content
|
||||
func BookSearch(c *gin.Context) {
|
||||
q := strings.TrimSpace(c.Query("q"))
|
||||
if q == "" {
|
||||
@@ -438,26 +440,57 @@ func BookSearch(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
pattern := "%" + escapeLikeBook(q) + "%"
|
||||
var list []model.Chapter
|
||||
err := database.DB().Model(&model.Chapter{}).
|
||||
Where("section_title LIKE ? OR content LIKE ?", pattern, pattern).
|
||||
Order("sort_order ASC, id ASC").
|
||||
Limit(20).
|
||||
Find(&list).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "results": []interface{}{}, "total": 0, "keyword": q})
|
||||
return
|
||||
db := database.DB()
|
||||
|
||||
type row struct {
|
||||
ID string `gorm:"column:id"`
|
||||
MID uint `gorm:"column:mid"`
|
||||
SectionTitle string `gorm:"column:section_title"`
|
||||
PartTitle string `gorm:"column:part_title"`
|
||||
ChapterTitle string `gorm:"column:chapter_title"`
|
||||
IsFree *bool `gorm:"column:is_free"`
|
||||
}
|
||||
lowerQ := strings.ToLower(q)
|
||||
results := make([]gin.H, 0, len(list))
|
||||
for _, ch := range list {
|
||||
matchType := "content"
|
||||
if strings.Contains(strings.ToLower(ch.SectionTitle), lowerQ) {
|
||||
matchType = "title"
|
||||
|
||||
var titleHits []row
|
||||
db.Model(&model.Chapter{}).
|
||||
Select("id, mid, section_title, part_title, chapter_title, is_free").
|
||||
Where("section_title LIKE ?", pattern).
|
||||
Order("sort_order ASC, id ASC").
|
||||
Limit(15).
|
||||
Find(&titleHits)
|
||||
|
||||
titleIDs := make(map[string]bool, len(titleHits))
|
||||
for _, h := range titleHits {
|
||||
titleIDs[h.ID] = true
|
||||
}
|
||||
|
||||
remaining := 20 - len(titleHits)
|
||||
var contentHits []row
|
||||
if remaining > 0 {
|
||||
cq := db.Model(&model.Chapter{}).
|
||||
Select("id, mid, section_title, part_title, chapter_title, is_free").
|
||||
Where("content LIKE ?", pattern)
|
||||
if len(titleIDs) > 0 {
|
||||
ids := make([]string, 0, len(titleIDs))
|
||||
for id := range titleIDs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
cq = cq.Where("id NOT IN ?", ids)
|
||||
}
|
||||
cq.Order("sort_order ASC, id ASC").Limit(remaining).Find(&contentHits)
|
||||
}
|
||||
|
||||
results := make([]gin.H, 0, len(titleHits)+len(contentHits))
|
||||
for _, ch := range titleHits {
|
||||
results = append(results, gin.H{
|
||||
"id": ch.ID, "mid": ch.MID, "title": ch.SectionTitle, "part": ch.PartTitle, "chapter": ch.ChapterTitle,
|
||||
"isFree": ch.IsFree, "matchType": matchType,
|
||||
"isFree": ch.IsFree, "matchType": "title",
|
||||
})
|
||||
}
|
||||
for _, ch := range contentHits {
|
||||
results = append(results, gin.H{
|
||||
"id": ch.ID, "mid": ch.MID, "title": ch.SectionTitle, "part": ch.PartTitle, "chapter": ch.ChapterTitle,
|
||||
"isFree": ch.IsFree, "matchType": "content",
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "results": results, "total": len(results), "keyword": q})
|
||||
|
||||
Reference in New Issue
Block a user