Update mini program development documentation and enhance user interface elements

- Added a new entry for the latest mini program development rules and APIs in the evolution index.
- Updated the skill documentation to include guidelines on privacy authorization and capability detection.
- Modified the read and settings pages to improve user experience with new input styles and layout adjustments.
- Implemented user-select functionality for text elements in the read page to enhance interactivity.
- Refined CSS styles for better responsiveness and visual consistency across various components.
This commit is contained in:
Alex-larget
2026-03-14 16:23:01 +08:00
parent 8778a42429
commit c936371165
27 changed files with 510 additions and 68 deletions

View File

@@ -3,7 +3,6 @@ package handler
import (
"context"
"encoding/json"
"math"
"net/http"
"sort"
"time"
@@ -87,7 +86,9 @@ func computeArticleRankingSections(db *gorm.DB) ([]sectionListItem, error) {
return sections, nil
}
// computeSectionsWithHotScore 内部:计算 hotScore,可选设置 isPinned
// computeSectionsWithHotScore 内部:按排名分算法计算 hotScore
// 热度积分 = 阅读权重×阅读排名分 + 新度权重×新度排名分 + 付款权重×付款排名分
// 阅读量前20名: 第1名=20分...第20名=1分最近更新前30篇: 第1名=30分...第30名=1分付款数前20名: 第1名=20分...第20名=1分
func computeSectionsWithHotScore(db *gorm.DB, setPinned bool) ([]sectionListItem, error) {
var rows []model.Chapter
if err := db.Select(listSelectCols).Order("sort_order ASC, id ASC").Find(&rows).Error; err != nil {
@@ -123,7 +124,7 @@ func computeSectionsWithHotScore(db *gorm.DB, setPinned bool) ([]sectionListItem
payCountMap[r.ProductID] = r.Cnt
}
}
readWeight, payWeight, recencyWeight := 0.5, 0.3, 0.2
readWeight, payWeight, recencyWeight := 0.1, 0.4, 0.5 // 默认与截图一致
var cfg model.SystemConfig
if err := db.Where("config_key = ?", "article_ranking_weights").First(&cfg).Error; err == nil && len(cfg.ConfigValue) > 0 {
var v struct {
@@ -132,13 +133,13 @@ func computeSectionsWithHotScore(db *gorm.DB, setPinned bool) ([]sectionListItem
PayWeight float64 `json:"payWeight"`
}
if err := json.Unmarshal(cfg.ConfigValue, &v); err == nil {
if v.ReadWeight > 0 {
if v.ReadWeight >= 0 {
readWeight = v.ReadWeight
}
if v.PayWeight > 0 {
if v.PayWeight >= 0 {
payWeight = v.PayWeight
}
if v.RecencyWeight > 0 {
if v.RecencyWeight >= 0 {
recencyWeight = v.RecencyWeight
}
}
@@ -156,7 +157,52 @@ func computeSectionsWithHotScore(db *gorm.DB, setPinned bool) ([]sectionListItem
pinnedSet[id] = true
}
}
now := time.Now()
// 1. 阅读量排名:按 readCount 降序前20名得 20~1 分
type idCnt struct {
id string
cnt int64
}
readRank := make([]idCnt, 0, len(rows))
for _, r := range rows {
readRank = append(readRank, idCnt{r.ID, readCountMap[r.ID]})
}
sort.Slice(readRank, func(i, j int) bool { return readRank[i].cnt > readRank[j].cnt })
readRankScoreMap := make(map[string]float64)
for i := 0; i < len(readRank) && i < 20; i++ {
readRankScoreMap[readRank[i].id] = float64(20 - i)
}
// 2. 新度排名:按 updated_at 降序最近更新在前前30篇得 30~1 分
recencyRank := make([]struct {
id string
updatedAt time.Time
}, 0, len(rows))
for _, r := range rows {
recencyRank = append(recencyRank, struct {
id string
updatedAt time.Time
}{r.ID, r.UpdatedAt})
}
sort.Slice(recencyRank, func(i, j int) bool {
return recencyRank[i].updatedAt.After(recencyRank[j].updatedAt)
})
recencyRankScoreMap := make(map[string]float64)
for i := 0; i < len(recencyRank) && i < 30; i++ {
recencyRankScoreMap[recencyRank[i].id] = float64(30 - i)
}
// 3. 付款数排名:按 payCount 降序前20名得 20~1 分
payRank := make([]idCnt, 0, len(rows))
for _, r := range rows {
payRank = append(payRank, idCnt{r.ID, payCountMap[r.ID]})
}
sort.Slice(payRank, func(i, j int) bool { return payRank[i].cnt > payRank[j].cnt })
payRankScoreMap := make(map[string]float64)
for i := 0; i < len(payRank) && i < 20; i++ {
payRankScoreMap[payRank[i].id] = float64(20 - i)
}
sections := make([]sectionListItem, 0, len(rows))
for _, r := range rows {
price := 1.0
@@ -165,15 +211,15 @@ func computeSectionsWithHotScore(db *gorm.DB, setPinned bool) ([]sectionListItem
}
readCnt := readCountMap[r.ID]
payCnt := payCountMap[r.ID]
recencyScore := 0.0
if !r.UpdatedAt.IsZero() {
days := now.Sub(r.UpdatedAt).Hours() / 24
recencyScore = math.Max(0, (30-days)/30)
if recencyScore > 1 {
recencyScore = 1
}
readRankScore := readRankScoreMap[r.ID]
recencyRankScore := recencyRankScoreMap[r.ID]
payRankScore := payRankScoreMap[r.ID]
// 热度积分 = 阅读权重×阅读排名分 + 新度权重×新度排名分 + 付款权重×付款排名分
hot := readWeight*readRankScore + recencyWeight*recencyRankScore + payWeight*payRankScore
// 若章节有手动覆盖的 hot_score>0则优先使用
if r.HotScore > 0 {
hot = float64(r.HotScore)
}
hot := float64(readCnt)*readWeight + float64(payCnt)*payWeight + recencyScore*recencyWeight
item := sectionListItem{
ID: r.ID,
MID: r.MID,