基础项目
Some checks failed
CI / init (pull_request) Has been cancelled
CI / Frontend node 18.16.0 (pull_request) Has been cancelled
CI / Backend go (1.22) (pull_request) Has been cancelled
CI / release-pr (pull_request) Has been cancelled
CI / devops-test (1.22, 18.16.0) (pull_request) Has been cancelled
CI / release-please (pull_request) Has been cancelled
CI / devops-prod (1.22, 18.x) (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled

This commit is contained in:
2026-04-26 15:32:21 +08:00
parent cc40d743cb
commit 1e33640629
102 changed files with 4088 additions and 197 deletions

View File

@@ -0,0 +1,9 @@
package book
import "time"
type HardDeleteModel struct {
ID uint `json:"id" form:"id" gorm:"primarykey;comment:主键"`
CreatedAt time.Time `json:"createdAt" form:"createdAt" gorm:"comment:创建时间"`
UpdatedAt time.Time `json:"updatedAt" form:"updatedAt" gorm:"comment:更新时间"`
}

50
server/model/book/book.go Normal file
View File

@@ -0,0 +1,50 @@
package book
import "time"
const (
BookEraTagUnknown = "unknown"
BookEraTagAncient = "ancient"
BookEraTagHan = "han"
BookEraTagTang = "tang"
BookEraTagSong = "song"
BookEraTagYuan = "yuan"
BookEraTagMing = "ming"
BookEraTagQing = "qing"
BookEraTagModern = "modern"
BookEraTagContemporary = "contemporary"
BookCompletionStatusCompleted = "completed"
BookCompletionStatusSerializing = "serializing"
BookPublishStatusDraft = "draft"
BookPublishStatusOffShelf = "off_shelf"
BookPublishStatusOnShelf = "on_shelf"
BookAuthorStatusEnabled = "enabled"
BookAuthorStatusDisabled = "disabled"
BookCommentStatusNormal = "normal"
BookCommentStatusHidden = "hidden"
)
type Book struct {
HardDeleteModel
Title string `json:"title" form:"title" gorm:"type:varchar(255);not null;comment:书名主标题"`
Subtitle string `json:"subtitle" form:"subtitle" gorm:"type:varchar(255);comment:书籍副标题"`
BookType string `json:"bookType" form:"bookType" gorm:"type:varchar(64);not null;index;comment:书籍类型字典值,对应 book_type"`
EraTag string `json:"eraTag" form:"eraTag" gorm:"type:varchar(32);not null;default:unknown;index;comment:时代标签字典值,对应 book_era_tag"`
CoverUrl string `json:"coverUrl" form:"coverUrl" gorm:"type:varchar(500);comment:封面图片 URL"`
Publisher string `json:"publisher" form:"publisher" gorm:"type:varchar(128);comment:出版社名称"`
PublishedAt *time.Time `json:"publishedAt" form:"publishedAt" gorm:"type:date;comment:出版日期"`
Intro string `json:"intro" form:"intro" gorm:"type:text;comment:书籍简介"`
HotScore int64 `json:"hotScore" form:"hotScore" gorm:"not null;default:0;comment:热度聚合值"`
Rating float64 `json:"rating" form:"rating" gorm:"type:numeric(3,1);not null;default:0.0;comment:书籍评分,范围 0-10"`
CommentCount int64 `json:"commentCount" form:"commentCount" gorm:"not null;default:0;comment:点评数聚合值"`
WordCount int64 `json:"wordCount" form:"wordCount" gorm:"not null;default:0;comment:书籍总字数"`
CompletionStatus string `json:"completionStatus" form:"completionStatus" gorm:"type:varchar(32);not null;default:serializing;index;comment:书籍完结状态字典值,对应 book_completion_status"`
PublishStatus string `json:"publishStatus" form:"publishStatus" gorm:"type:varchar(32);not null;default:draft;index;comment:书籍上下架状态字典值,对应 book_publish_status"`
SeriesID *uint `json:"seriesId" form:"seriesId" gorm:"index:idx_book_series_id_series_sort;comment:所属系列 ID可为空"`
SeriesSort int `json:"seriesSort" form:"seriesSort" gorm:"not null;default:0;index:idx_book_series_id_series_sort;comment:同系列内展示排序"`
RawTxtUrl string `json:"rawTxtUrl" form:"rawTxtUrl" gorm:"type:varchar(500);comment:原始 txt 文件 URL"`
}
func (Book) TableName() string {
return "book"
}

View File

@@ -0,0 +1,13 @@
package book
type BookAuthor struct {
HardDeleteModel
Name string `json:"name" form:"name" gorm:"type:varchar(128);not null;uniqueIndex:uk_book_author_name;comment:作者名称"`
AuthorStatus string `json:"authorStatus" form:"authorStatus" gorm:"type:varchar(32);not null;default:enabled;index;comment:作者状态字典值,对应 book_author_status"`
Intro string `json:"intro" form:"intro" gorm:"type:text;comment:作者简介"`
CoverUrl string `json:"coverUrl" form:"coverUrl" gorm:"type:varchar(500);comment:作者头像或封面 URL"`
}
func (BookAuthor) TableName() string {
return "book_author"
}

View File

@@ -0,0 +1,12 @@
package book
type BookAuthorRelation struct {
HardDeleteModel
BookID uint `json:"bookId" form:"bookId" gorm:"not null;uniqueIndex:uk_book_author_relation_book_id_author_id;index:idx_book_author_relation_book_id_author_sort;comment:书籍 ID"`
AuthorID uint `json:"authorId" form:"authorId" gorm:"not null;uniqueIndex:uk_book_author_relation_book_id_author_id;index;comment:作者 ID"`
AuthorSort int `json:"authorSort" form:"authorSort" gorm:"not null;default:1;index:idx_book_author_relation_book_id_author_sort;comment:作者展示排序"`
}
func (BookAuthorRelation) TableName() string {
return "book_author_relation"
}

View File

@@ -0,0 +1,16 @@
package book
type BookChapter struct {
HardDeleteModel
BookID uint `json:"bookId" form:"bookId" gorm:"not null;uniqueIndex:uk_book_chapter_book_id_chapter_no;index:idx_book_chapter_book_id_is_enabled_is_readable;comment:书籍 ID"`
Title string `json:"title" form:"title" gorm:"type:varchar(255);not null;comment:章节标题"`
ChapterNo int `json:"chapterNo" form:"chapterNo" gorm:"not null;uniqueIndex:uk_book_chapter_book_id_chapter_no;comment:章节序号"`
IsReadable bool `json:"isReadable" form:"isReadable" gorm:"not null;default:false;index:idx_book_chapter_book_id_is_enabled_is_readable;comment:是否开放阅读"`
ContentFileUrl string `json:"contentFileUrl" form:"contentFileUrl" gorm:"type:varchar(500);not null;comment:章节正文文件 URL"`
TotalLines int `json:"totalLines" form:"totalLines" gorm:"not null;default:0;comment:正文总行数"`
IsEnabled bool `json:"isEnabled" form:"isEnabled" gorm:"not null;default:true;index:idx_book_chapter_book_id_is_enabled_is_readable;comment:章节是否启用"`
}
func (BookChapter) TableName() string {
return "book_chapter"
}

View File

@@ -0,0 +1,16 @@
package book
type BookComment struct {
HardDeleteModel
MemberUserID uint `json:"memberUserId" form:"memberUserId" gorm:"not null;index;comment:会员用户 ID"`
BookID uint `json:"bookId" form:"bookId" gorm:"not null;index;index:idx_book_comment_book_id_chapter_id_line_index;comment:书籍 ID"`
ChapterID uint `json:"chapterId" form:"chapterId" gorm:"not null;default:0;index:idx_book_comment_book_id_chapter_id_line_index;comment:章节 ID0 表示整书评论"`
LineIndex int `json:"lineIndex" form:"lineIndex" gorm:"not null;default:0;index:idx_book_comment_book_id_chapter_id_line_index;comment:文本行序号0 表示整书或整章"`
Content string `json:"content" form:"content" gorm:"type:text;not null;comment:评论内容"`
LikeCount int64 `json:"likeCount" form:"likeCount" gorm:"not null;default:0;comment:点赞聚合数"`
CommentStatus string `json:"commentStatus" form:"commentStatus" gorm:"type:varchar(32);not null;default:normal;index;comment:评论状态字典值,对应 book_comment_status"`
}
func (BookComment) TableName() string {
return "book_comment"
}

View File

@@ -0,0 +1,14 @@
package book
import "time"
type BookCommentLikeRecord struct {
HardDeleteModel
CommentID uint `json:"commentId" form:"commentId" gorm:"not null;uniqueIndex:uk_book_comment_like_record_comment_id_member_user_id;comment:评论 ID"`
MemberUserID uint `json:"memberUserId" form:"memberUserId" gorm:"not null;uniqueIndex:uk_book_comment_like_record_comment_id_member_user_id;index;comment:会员用户 ID"`
LikedAt time.Time `json:"likedAt" form:"likedAt" gorm:"not null;default:CURRENT_TIMESTAMP;index;comment:点赞时间"`
}
func (BookCommentLikeRecord) TableName() string {
return "book_comment_like_record"
}

View File

@@ -0,0 +1,14 @@
package book
import "time"
type BookFavoriteRecord struct {
HardDeleteModel
MemberUserID uint `json:"memberUserId" form:"memberUserId" gorm:"not null;uniqueIndex:uk_book_favorite_record_member_user_id_book_id;index:idx_book_favorite_record_member_user_id_favorited_at;comment:会员用户 ID"`
BookID uint `json:"bookId" form:"bookId" gorm:"not null;uniqueIndex:uk_book_favorite_record_member_user_id_book_id;index;comment:书籍 ID"`
FavoritedAt time.Time `json:"favoritedAt" form:"favoritedAt" gorm:"not null;default:CURRENT_TIMESTAMP;index:idx_book_favorite_record_member_user_id_favorited_at;comment:收藏时间"`
}
func (BookFavoriteRecord) TableName() string {
return "book_favorite_record"
}

View File

@@ -0,0 +1,29 @@
package book
import "testing"
func TestBookModelTableNames(t *testing.T) {
tests := []struct {
name string
got string
want string
}{
{name: "book", got: Book{}.TableName(), want: "book"},
{name: "book_author", got: BookAuthor{}.TableName(), want: "book_author"},
{name: "book_author_relation", got: BookAuthorRelation{}.TableName(), want: "book_author_relation"},
{name: "book_chapter", got: BookChapter{}.TableName(), want: "book_chapter"},
{name: "book_comment", got: BookComment{}.TableName(), want: "book_comment"},
{name: "book_comment_like_record", got: BookCommentLikeRecord{}.TableName(), want: "book_comment_like_record"},
{name: "book_favorite_record", got: BookFavoriteRecord{}.TableName(), want: "book_favorite_record"},
{name: "book_read_record", got: BookReadRecord{}.TableName(), want: "book_read_record"},
{name: "book_series", got: BookSeries{}.TableName(), want: "book_series"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.got != tt.want {
t.Fatalf("TableName() = %q, want %q", tt.got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,18 @@
package book
import "time"
type BookReadRecord struct {
HardDeleteModel
MemberUserID uint `json:"memberUserId" form:"memberUserId" gorm:"not null;uniqueIndex:uk_book_read_record_member_user_id_book_id;index:idx_book_read_record_member_user_id_last_read_at;comment:会员用户 ID"`
BookID uint `json:"bookId" form:"bookId" gorm:"not null;uniqueIndex:uk_book_read_record_member_user_id_book_id;index;comment:书籍 ID"`
BookTitleSnapshot string `json:"bookTitleSnapshot" form:"bookTitleSnapshot" gorm:"type:varchar(255);not null;comment:书名快照"`
ReadProgress float64 `json:"readProgress" form:"readProgress" gorm:"type:numeric(5,2);not null;default:0.00;comment:阅读进度百分比"`
ChapterID uint `json:"chapterId" form:"chapterId" gorm:"not null;comment:续读章节 ID"`
LineIndex int `json:"lineIndex" form:"lineIndex" gorm:"not null;comment:续读文本行序号"`
LastReadAt time.Time `json:"lastReadAt" form:"lastReadAt" gorm:"not null;default:CURRENT_TIMESTAMP;index:idx_book_read_record_member_user_id_last_read_at;comment:最后阅读时间"`
}
func (BookReadRecord) TableName() string {
return "book_read_record"
}

View File

@@ -0,0 +1,13 @@
package book
type BookSeries struct {
HardDeleteModel
Name string `json:"name" form:"name" gorm:"type:varchar(128);not null;index;comment:系列名称"`
CoverUrl string `json:"coverUrl" form:"coverUrl" gorm:"type:varchar(500);comment:系列封面图片 URL"`
Intro string `json:"intro" form:"intro" gorm:"type:text;comment:系列简介"`
IsEnabled bool `json:"isEnabled" form:"isEnabled" gorm:"not null;default:true;index;comment:系列是否启用"`
}
func (BookSeries) TableName() string {
return "book_series"
}

View File

@@ -0,0 +1,64 @@
package request
import commonReq "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
type BookSearch struct {
commonReq.PageInfo
Title string `json:"title" form:"title"`
BookType string `json:"bookType" form:"bookType"`
EraTag string `json:"eraTag" form:"eraTag"`
CompletionStatus string `json:"completionStatus" form:"completionStatus"`
PublishStatus string `json:"publishStatus" form:"publishStatus"`
SeriesID *uint `json:"seriesId" form:"seriesId"`
}
type BookAuthorSearch struct {
commonReq.PageInfo
Name string `json:"name" form:"name"`
AuthorStatus string `json:"authorStatus" form:"authorStatus"`
}
type BookAuthorRelationSearch struct {
commonReq.PageInfo
BookID *uint `json:"bookId" form:"bookId"`
AuthorID *uint `json:"authorId" form:"authorId"`
}
type BookChapterSearch struct {
commonReq.PageInfo
BookID *uint `json:"bookId" form:"bookId"`
IsReadable *bool `json:"isReadable" form:"isReadable"`
IsEnabled *bool `json:"isEnabled" form:"isEnabled"`
}
type BookCommentSearch struct {
commonReq.PageInfo
MemberUserID *uint `json:"memberUserId" form:"memberUserId"`
BookID *uint `json:"bookId" form:"bookId"`
ChapterID *uint `json:"chapterId" form:"chapterId"`
CommentStatus string `json:"commentStatus" form:"commentStatus"`
}
type BookCommentLikeRecordSearch struct {
commonReq.PageInfo
CommentID *uint `json:"commentId" form:"commentId"`
MemberUserID *uint `json:"memberUserId" form:"memberUserId"`
}
type BookFavoriteRecordSearch struct {
commonReq.PageInfo
MemberUserID *uint `json:"memberUserId" form:"memberUserId"`
BookID *uint `json:"bookId" form:"bookId"`
}
type BookReadRecordSearch struct {
commonReq.PageInfo
MemberUserID *uint `json:"memberUserId" form:"memberUserId"`
BookID *uint `json:"bookId" form:"bookId"`
}
type BookSeriesSearch struct {
commonReq.PageInfo
Name string `json:"name" form:"name"`
IsEnabled *bool `json:"isEnabled" form:"isEnabled"`
}

View File

@@ -0,0 +1,31 @@
package response
import "github.com/flipped-aurora/gin-vue-admin/server/model/book"
type BookResponse struct {
Book book.Book `json:"book"`
}
type BookAuthorResponse struct {
BookAuthor book.BookAuthor `json:"bookAuthor"`
}
type BookAuthorRelationResponse struct {
BookAuthorRelation book.BookAuthorRelation `json:"bookAuthorRelation"`
}
type BookChapterResponse struct {
BookChapter book.BookChapter `json:"bookChapter"`
}
type BookCommentResponse struct {
BookComment book.BookComment `json:"bookComment"`
}
type BookCommentLikeRecordResponse struct {
BookCommentLikeRecord book.BookCommentLikeRecord `json:"bookCommentLikeRecord"`
}
type BookFavoriteRecordResponse struct {
BookFavoriteRecord book.BookFavoriteRecord `json:"bookFavoriteRecord"`
}
type BookReadRecordResponse struct {
BookReadRecord book.BookReadRecord `json:"bookReadRecord"`
}
type BookSeriesResponse struct {
BookSeries book.BookSeries `json:"bookSeries"`
}