feat: 友链/书库/移动端导航/排行榜等多模块功能

新增模块:
- 友链(friendlink)后端处理 + 前端管理页与友链板
- 书库(library):导入、章节、封面、阅读页、横竖版自适应 AdaptiveCoverSlot
- 顶栏导航(header_nav)配置与 MobileTabBar/MobileRailDrawers 移动端抽屉
- 排行榜 service 测试、站点页面测试、时间线发布(timeline_release)

其它改动:
- 后端 handlers/services 全量小幅调整
- 前端组件、库函数、URL/品牌/站点 URL 工具更新
- Lightbox 图片、MdEntries 条目卡、coverColor 派生色相等前端能力
This commit is contained in:
2026-09-30 16:30:07 +08:00
parent e487bc2e92
commit 4f3f3a265c
105 changed files with 12371 additions and 852 deletions

View File

@@ -62,8 +62,8 @@ func InitDB(dsn string) error {
&ChatRoom{}, &ChatRoomMember{}, &ChatMessage{},
&PointLedger{}, &PostContentUnlock{}, &PostAttachment{}, &PostAttachmentUnlock{},
&Badge{}, &UserBadge{},
&PostPollVote{}, &PostLotteryEntry{}, &Ad{}, &SidebarWidget{},
&ImportRecord{},
&PostPollVote{}, &PostLotteryEntry{}, &Ad{}, &SidebarWidget{}, &FriendLink{},
&ImportRecord{}, &LibraryDoc{}, &LibraryFile{}, &LibrarySection{},
); err != nil {
return fmt.Errorf("自动迁移失败: %w", err)
}

View File

@@ -522,6 +522,52 @@ type SitePage struct {
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// LibraryDoc 书库条目(书籍/文档/纯 HTML 页展示;文件独立目录存储,仅经 API 消费)
type LibraryDoc struct {
ID uint `gorm:"primaryKey" json:"id"`
Slug string `gorm:"size:64;uniqueIndex;not null" json:"slug"`
Title string `gorm:"size:200;not null" json:"title"`
Description string `gorm:"type:text" json:"description"` // markdown 介绍,可空
CoverURL string `gorm:"size:512" json:"cover_url"` // 可选图片封面(/uploads/images)
Published bool `gorm:"not null;index" json:"published"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
// EntriesAuto 全章节条目卡模式:开启后章节内容免写 [entries] 壳,
// 「## 标题 + @tags/@summary/字段::值/@source 指令行」自动识别为条目卡(个别章节用 [entries:off] 例外)
EntriesAuto bool `gorm:"not null;default:false" json:"entries_auto"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// LibraryFile 书库文件(html/pdf/epub 等;存 data/library,不进公开静态目录)
type LibraryFile struct {
ID uint `gorm:"primaryKey" json:"id"`
DocID uint `gorm:"index;not null;default:0" json:"doc_id"`
UserID uint `gorm:"index;not null" json:"user_id"` // 上传管理员
Name string `gorm:"size:256;not null" json:"name"` // 原始文件名
StoredName string `gorm:"size:64;not null" json:"-"` // 磁盘文件名
Ext string `gorm:"size:16;not null;default:''" json:"ext"`
MIME string `gorm:"size:128;not null;default:application/octet-stream" json:"mime"`
Size int `gorm:"not null;default:0" json:"size"`
DownloadCount int `gorm:"not null;default:0" json:"download_count"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
CreatedAt time.Time `json:"created_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// LibrarySection 书库章节(两级树:ParentID 空=章,非空=节;正文 markdown)
type LibrarySection struct {
ID uint `gorm:"primaryKey" json:"id"`
DocID uint `gorm:"index;not null;default:0" json:"doc_id"`
ParentID *uint `gorm:"index" json:"parent_id"` // nil=章;非空=节,父必须是同条目的章
Title string `gorm:"size:200;not null" json:"title"`
Content string `gorm:"type:text" json:"content"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// SiteSetting 站点级键值设置(如历史在线峰值 peak_online)
type SiteSetting struct {
Key string `gorm:"primaryKey;size:64" json:"key"`
@@ -542,6 +588,24 @@ type SidebarWidget struct {
UpdatedAt time.Time `json:"updated_at"`
}
// FriendLink 友情链接;申请审批制,status=pending 经管理员 approve 后前台可见
type FriendLink struct {
ID uint `gorm:"primarykey" json:"id"`
Name string `gorm:"size:64;not null" json:"name"`
URL string `gorm:"size:512;not null" json:"url"`
Description string `gorm:"size:255" json:"description"`
LogoURL string `gorm:"size:512" json:"logo_url"`
Category string `gorm:"size:32;index" json:"category"`
ReciprocalURL string `gorm:"size:512" json:"reciprocal_url"` // 申请人填写的回链地址,供站长核对
SortOrder int `gorm:"not null;default:0;index" json:"sort_order"`
Status string `gorm:"size:16;not null;default:'pending';index" json:"status"` // pending|approved|rejected
ApplicantUserID *uint `gorm:"index" json:"applicant_user_id,omitempty"` // 后台直接新建时为 nil
RejectReason string `gorm:"size:255" json:"reject_reason,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// SiteDailyStats 站点日维度访问量(轻量 PV/UV)
type SiteDailyStats struct {
Date time.Time `gorm:"type:date;primaryKey" json:"date"`