书籍列表接口新增参数

This commit is contained in:
2025-08-13 15:19:42 +08:00
parent 6ccc87f2bf
commit 8afe651c64
201 changed files with 6987 additions and 1066 deletions

View File

@ -180,13 +180,34 @@ func (s *sChapter) AppList(ctx context.Context, in *model.ChapterAppListIn) (out
return nil, ecode.Fail.Sub("read_record_query_failed")
}
// 查询购买记录
purchaseRecords := make([]struct {
ChapterId int64 `json:"chapterId"`
}, 0)
err = dao.UserChapterPurchases.Ctx(ctx).
Fields("chapter_id").
Where(dao.UserChapterPurchases.Columns().UserId, in.UserId).
Where(dao.UserChapterPurchases.Columns().BookId, in.BookId).
WhereIn(dao.UserChapterPurchases.Columns().ChapterId, chapterIds).
Scan(&purchaseRecords)
if err != nil {
return nil, ecode.Fail.Sub("purchase_record_query_failed")
}
// 构建阅读记录映射
readMap := make(map[int64]*gtime.Time)
for _, record := range readRecords {
readMap[record.ChapterId] = record.ReadAt
}
// 为每个章节设置阅读进度
// 构建购买记录映射
purchaseMap := make(map[int64]bool)
for _, record := range purchaseRecords {
purchaseMap[record.ChapterId] = true
}
// 为每个章节设置阅读进度和购买状态
for i := range out.List {
if readAt, exists := readMap[out.List[i].Id]; exists {
out.List[i].ReadAt = readAt
@ -195,6 +216,19 @@ func (s *sChapter) AppList(ctx context.Context, in *model.ChapterAppListIn) (out
out.List[i].ReadProgress = 0 // 未读
out.List[i].ReadAt = nil
}
// 设置购买状态
if out.List[i].IsLocked == 0 {
// 免费章节,直接设置为已购买
out.List[i].IsPurchased = true
} else {
// 付费章节,根据购买记录设置
if purchaseMap[out.List[i].Id] {
out.List[i].IsPurchased = true
} else {
out.List[i].IsPurchased = false
}
}
}
}
}
@ -473,7 +507,7 @@ func (s *sChapter) AppProgress(ctx context.Context, in *model.ChapterAppProgress
readChapters, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
Where(dao.UserReadRecords.Columns().ChapterId, ">", 0).
WhereGT(dao.UserReadRecords.Columns().ChapterId, 0).
Count()
if err != nil {
return ecode.Fail.Sub("read_chapter_count_failed")
@ -525,3 +559,211 @@ func (s *sChapter) AppProgress(ctx context.Context, in *model.ChapterAppProgress
out.Success = true
return out, nil
}
// AppBatchProgress uploads batch reading progress for app
func (s *sChapter) AppBatchProgress(ctx context.Context, in *model.ChapterAppBatchProgressIn) (out *model.ChapterAppProgressOut, err error) {
out = &model.ChapterAppProgressOut{}
// 必须指定用户ID
if in.UserId == 0 {
return nil, ecode.Fail.Sub("user_id_required")
}
// 必须指定书籍ID
if in.BookId == 0 {
return nil, ecode.Fail.Sub("book_id_required")
}
// 必须指定章节列表
if len(in.Chapters) == 0 {
return nil, ecode.Fail.Sub("chapters_required")
}
// 检查用户是否存在
exist, err := dao.Users.Ctx(ctx).Where(dao.Users.Columns().Id, in.UserId).Exist()
if err != nil {
return nil, ecode.Fail.Sub("user_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("user_not_found")
}
// 验证所有章节进度
for _, chapter := range in.Chapters {
if chapter.ChapterId == 0 {
return nil, ecode.Fail.Sub("chapter_id_required")
}
if chapter.Progress < 0 || chapter.Progress > 100 {
return nil, ecode.Fail.Sub("progress_invalid")
}
}
// 检查所有章节是否存在
chapterIds := make([]int64, 0)
for _, chapter := range in.Chapters {
chapterIds = append(chapterIds, chapter.ChapterId)
}
exist, err = dao.Chapters.Ctx(ctx).
WhereIn(dao.Chapters.Columns().Id, chapterIds).
Where(dao.Chapters.Columns().BookId, in.BookId).
Exist()
if err != nil {
return nil, ecode.Fail.Sub("chapter_query_failed")
}
if !exist {
return nil, ecode.NotFound.Sub("chapter_not_found")
}
// 开启事务处理
if err := dao.UserReadRecords.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 批量处理每个章节的进度
for _, chapter := range in.Chapters {
// 1. 更新或创建阅读记录
exist, err := dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
Where(dao.UserReadRecords.Columns().ChapterId, chapter.ChapterId).
Exist()
if err != nil {
return ecode.Fail.Sub("read_record_query_failed")
}
if exist {
// 更新现有记录
_, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
Where(dao.UserReadRecords.Columns().ChapterId, chapter.ChapterId).
Data(do.UserReadRecords{
Progress: chapter.Progress,
ReadAt: gtime.Now(),
}).Update()
if err != nil {
return ecode.Fail.Sub("read_record_update_failed")
}
} else {
// 创建新记录
_, err = dao.UserReadRecords.Ctx(ctx).TX(tx).Data(do.UserReadRecords{
UserId: in.UserId,
BookId: in.BookId,
ChapterId: chapter.ChapterId,
Progress: chapter.Progress,
ReadAt: gtime.Now(),
}).Insert()
if err != nil {
return ecode.Fail.Sub("read_record_create_failed")
}
}
}
// 2. 更新或创建历史记录(使用最后一个章节作为当前阅读章节)
lastChapter := in.Chapters[len(in.Chapters)-1]
exist, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
Where(dao.UserReadHistory.Columns().BookId, in.BookId).
Exist()
if err != nil {
return ecode.Fail.Sub("history_query_failed")
}
if exist {
// 更新现有历史记录
_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
Where(dao.UserReadHistory.Columns().UserId, in.UserId).
Where(dao.UserReadHistory.Columns().BookId, in.BookId).
Data(do.UserReadHistory{
ChapterId: lastChapter.ChapterId,
ReadAt: gtime.Now(),
}).Update()
if err != nil {
return ecode.Fail.Sub("history_update_failed")
}
} else {
// 创建新历史记录
_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).Data(do.UserReadHistory{
UserId: in.UserId,
BookId: in.BookId,
ChapterId: lastChapter.ChapterId,
ReadAt: gtime.Now(),
}).Insert()
if err != nil {
return ecode.Fail.Sub("history_create_failed")
}
}
// 3. 更新书架记录(如果存在)
exist, err = dao.Bookshelves.Ctx(ctx).TX(tx).
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().BookId, in.BookId).
Exist()
if err != nil {
return ecode.Fail.Sub("bookshelf_query_failed")
}
if exist {
// 计算阅读进度百分比
totalChapters, err := dao.Chapters.Ctx(ctx).TX(tx).
Where(dao.Chapters.Columns().BookId, in.BookId).
Count()
if err != nil {
return ecode.Fail.Sub("chapter_count_failed")
}
var readChapters int
if totalChapters > 0 {
readChapters, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
Where(dao.UserReadRecords.Columns().UserId, in.UserId).
Where(dao.UserReadRecords.Columns().BookId, in.BookId).
WhereGT(dao.UserReadRecords.Columns().ChapterId, 0).
Count()
if err != nil {
return ecode.Fail.Sub("read_chapter_count_failed")
}
}
readPercent := 0.0
if totalChapters > 0 {
readPercent = float64(readChapters) / float64(totalChapters) * 100
if readPercent > 100 {
readPercent = 100
}
}
// 判断是否为最后一章
lastChapterInfo, err := dao.Chapters.Ctx(ctx).TX(tx).
Where(dao.Chapters.Columns().BookId, in.BookId).
OrderDesc(dao.Chapters.Columns().Sort).
One()
if err != nil {
return ecode.Fail.Sub("chapter_query_failed")
}
readStatus := 1 // 默认为正在读
if lastChapterInfo != nil && lastChapterInfo["id"].Int64() == lastChapter.ChapterId {
readStatus = 2 // 如果是最后一章,标记为已读完
}
// 更新书架记录
_, err = dao.Bookshelves.Ctx(ctx).TX(tx).
Where(dao.Bookshelves.Columns().UserId, in.UserId).
Where(dao.Bookshelves.Columns().BookId, in.BookId).
Data(do.Bookshelves{
LastReadChapterId: lastChapter.ChapterId,
LastReadPercent: readPercent,
LastReadAt: gtime.Now(),
ReadStatus: readStatus,
}).Update()
if err != nil {
return ecode.Fail.Sub("bookshelf_update_failed")
}
}
return nil
}); err != nil {
return nil, err
}
out.Success = true
return out, nil
}