package handler import ( "fmt" "html" "net/http" "strings" "unicode/utf8" "soul-api/internal/config" "soul-api/internal/database" "soul-api/internal/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // H5ReadPage GET /read/:id 朋友圈/外部链接落地页 // 渲染文章预览内容(按 unpaid_preview_percent 截取),底部显示「打开小程序继续阅读」按钮 // 支持 ?ref=xxx 分销参数透传 func H5ReadPage(c *gin.Context) { sectionID := c.Param("id") if sectionID == "" { c.Data(http.StatusBadRequest, "text/html; charset=utf-8", []byte(h5Error("缺少文章 ID"))) return } ref := c.Query("ref") db := database.DB() var ch model.Chapter if err := db.Where("id = ?", sectionID).First(&ch).Error; err != nil { if err == gorm.ErrRecordNotFound { c.Data(http.StatusNotFound, "text/html; charset=utf-8", []byte(h5Error("文章不存在"))) return } c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte(h5Error("加载失败"))) return } percent := getUnpaidPreviewPercent(db) preview := h5PreviewContent(ch.Content, percent) title := ch.SectionTitle if title == "" { title = ch.ChapterTitle } partTitle := "" if ch.PartTitle != "" { partTitle = ch.PartTitle } chapterTitle := "" if ch.ChapterTitle != "" && ch.ChapterTitle != title { chapterTitle = ch.ChapterTitle } cfg := config.Get() appID := cfg.WechatAppID mpPath := fmt.Sprintf("pages/read/read?id=%s&action=pay", sectionID) if ref != "" { mpPath += "&ref=" + ref } pageHTML := h5BuildPage(h5PageData{ Title: title, PartTitle: partTitle, ChapterTitle: chapterTitle, Preview: preview, Percent: percent, SectionID: sectionID, Ref: ref, AppID: appID, MpPath: mpPath, }) c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(pageHTML)) } func h5PreviewContent(content string, percent int) string { total := utf8.RuneCountInString(content) if total == 0 { return "" } if percent < 1 { percent = 1 } if percent > 100 { percent = 100 } limit := total * percent / 100 if limit < 100 { limit = 100 } if limit > total { limit = total } runes := []rune(content) return string(runes[:limit]) } func h5Error(msg string) string { return fmt.Sprintf(`
%s
`, html.EscapeString(msg)) } type h5PageData struct { Title, PartTitle, ChapterTitle string Preview string Percent int SectionID, Ref string AppID, MpPath string } func h5BuildPage(d h5PageData) string { escapedTitle := html.EscapeString(d.Title) escapedPart := html.EscapeString(d.PartTitle) escapedChapter := html.EscapeString(d.ChapterTitle) contentHTML := h5ContentToHTML(d.Preview) subtitle := "" if escapedPart != "" || escapedChapter != "" { parts := []string{} if escapedPart != "" { parts = append(parts, escapedPart) } if escapedChapter != "" { parts = append(parts, escapedChapter) } subtitle = fmt.Sprintf(`%s
`, strings.Join(parts, " · ")) } mpBtn := fmt.Sprintf( `已预览 %d%% · 打开小程序购买并阅读全文
") sb.WriteString(html.EscapeString(trimmed[2:])) sb.WriteString("") } else { sb.WriteString("
") sb.WriteString(html.EscapeString(trimmed)) sb.WriteString("
") } } return sb.String() }